3 Bullet Continuous Collision Detection and Physics Library
4 Copyright (c) 2003-2006,2008 Erwin Coumans http://continuousphysics.com/Bullet/
6 This software is provided 'as-is', without any express or implied warranty.
7 In no event will the authors be held liable for any damages arising from the use of this software.
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it freely,
10 subject to the following restrictions:
12 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
13 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
14 3. This notice may not be removed or altered from any source distribution.
17 #include "TerrainDemo.h" // always include our own header first!
19 #include "btBulletDynamicsCommon.h"
20 #include "BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h"
22 #include "GLDebugDrawer.h"
24 #include "GL_ShapeDrawer.h"
26 #include "GlutStuff.h"
27 #include "GLDebugFont.h"
31 // constants -------------------------------------------------------------------
32 static const float s_gravity = 9.8; // 9.8 m/s^2
34 static const int s_gridSize = 64 + 1; // must be (2^N) + 1
35 static const float s_gridSpacing = 5.0;
37 static const float s_gridHeightScale = 0.2;
39 // the singularity at the center of the radial model means we need a lot of
40 // finely-spaced time steps to get the physics right.
41 // These numbers are probably too aggressive for a real game!
42 static const int s_requestedHz = 180;
43 static const float s_engineTimeStep = 1.0 / s_requestedHz;
45 // delta phase: radians per second
46 static const float s_deltaPhase = 0.25 * 2.0 * SIMD_PI;
48 // what type of terrain is generated?
50 eRadial = 1, // deterministic
51 eFractal = 2 // random
55 typedef unsigned char byte_t;
59 ////////////////////////////////////////////////////////////////////////////////
61 // static helper methods
63 // Only used within this file (helpers and terrain generation, etc)
65 ////////////////////////////////////////////////////////////////////////////////
81 btAssert(!"bad terrain model type");
97 return "UnsignedChar";
106 btAssert(!"bad heightfield data type");
131 btAssert(!"bad up axis");
143 btScalar regularValue,
147 btAssert(upAxis >= 0 && upAxis <= 2 && "bad up axis");
149 btVector3 v(regularValue, regularValue, regularValue);
157 // TODO: it would probably cleaner to have a struct per data type, so
158 // you could lookup byte sizes, conversion functions, etc.
159 static int getByteSize
168 size = sizeof(float);
172 size = sizeof(unsigned char);
176 size = sizeof(short);
180 btAssert(!"Bad heightfield data type");
200 float * pf = (float *) p;
206 unsigned char * pu = (unsigned char *) p;
207 return ((*pu) * s_gridHeightScale);
212 short * ps = (short *) p;
213 return ((*ps) * s_gridHeightScale);
217 btAssert(!"bad type");
235 btAssert(i >= 0 && i < s_gridSize);
236 btAssert(j >= 0 && j < s_gridSize);
238 int bpe = getByteSize(type);
239 btAssert(bpe > 0 && "bad bytes per element");
241 int idx = (j * s_gridSize) + i;
242 long offset = ((long) bpe) * idx;
244 byte_t * p = grid + offset;
246 return convertToFloat(p, type);
259 btAssert(p && "null");
264 float * pf = (float *) p;
271 unsigned char * pu = (unsigned char *) p;
272 *pu = (unsigned char) (value / s_gridHeightScale);
278 short * ps = (short *) p;
279 *ps = (short) (value / s_gridHeightScale);
284 btAssert(!"bad type");
290 // creates a radially-varying heightfield
301 btAssert(bytesPerElement > 0);
304 float period = 0.5 / s_gridSpacing;
306 float min_r = 3.0 * sqrt(s_gridSpacing);
307 float magnitude = 50.0 * sqrt(s_gridSpacing);
309 // pick a base_phase such that phase = 0 results in max height
310 // (this way, if you create a heightfield with phase = 0,
311 // you can rely on the min/max heights that result)
312 float base_phase = (0.5 * SIMD_PI) - (period * min_r);
316 float cx = 0.5 * s_gridSize * s_gridSpacing;
317 float cy = cx; // assume square grid
319 for (int i = 0; i < s_gridSize; ++i) {
320 float x = i * s_gridSpacing;
321 for (int j = 0; j < s_gridSize; ++j) {
322 float y = j * s_gridSpacing;
327 float r = sqrt((dx * dx) + (dy * dy));
333 z = (1.0 / r) * sin(period * r + phase);
336 } else if (z < -period) {
339 z = floor + magnitude * z;
341 convertFromFloat(p, z, type);
342 p += bytesPerElement;
355 return (0.33 * s_gridSpacing * s_gridSize * step * (rand() - (0.5 * RAND_MAX))) / (1.0 * RAND_MAX * s_gridSize);
369 //std::cerr << "Grid:\n";
373 for (int j = 0; j < max; ++j) {
374 for (int i = 0; i < max; ++i) {
375 long offset = j * s_gridSize + i;
376 float z = convertToFloat(grid + offset * bytesPerElement, type);
377 sprintf(buffer, "%6.2f", z);
378 //std::cerr << " " << buffer;
394 float old_val = convertToFloat(p, type);
396 convertFromFloat(p, new_val, type);
402 // creates a random, fractal heightfield
413 btAssert(bytesPerElement > 0);
415 btAssert(step < s_gridSize);
417 int newStep = step / 2;
418 // std::cerr << "Computing grid with step = " << step << ": before\n";
419 // dumpGrid(grid, bytesPerElement, type, step + 1);
421 // special case: starting (must set four corners)
422 if (s_gridSize - 1 == step) {
423 // pick a non-zero (possibly negative) base elevation for testing
424 float base = randomHeight(step / 2);
426 convertFromFloat(grid, base, type);
427 convertFromFloat(grid + step * bytesPerElement, base, type);
428 convertFromFloat(grid + step * s_gridSize * bytesPerElement, base, type);
429 convertFromFloat(grid + (step * s_gridSize + step) * bytesPerElement, base, type);
432 // determine elevation of each corner
433 float c00 = convertToFloat(grid, type);
434 float c01 = convertToFloat(grid + step * bytesPerElement, type);
435 float c10 = convertToFloat(grid + (step * s_gridSize) * bytesPerElement, type);
436 float c11 = convertToFloat(grid + (step * s_gridSize + step) * bytesPerElement, type);
439 updateHeight(grid + newStep * bytesPerElement, 0.5 * (c00 + c01) + randomHeight(step), type);
442 updateHeight(grid + (newStep * s_gridSize) * bytesPerElement, 0.5 * (c00 + c10) + randomHeight(step), type);
445 updateHeight(grid + (newStep * s_gridSize + step) * bytesPerElement, 0.5 * (c01 + c11) + randomHeight(step), type);
448 updateHeight(grid + (step * s_gridSize + newStep) * bytesPerElement, 0.5 * (c10 + c11) + randomHeight(step), type);
451 updateHeight(grid + (newStep * s_gridSize + newStep) * bytesPerElement, 0.25 * (c00 + c01 + c10 + c11) + randomHeight(step), type);
453 // std::cerr << "Computing grid with step = " << step << ": after\n";
454 // dumpGrid(grid, bytesPerElement, type, step + 1);
462 setFractal(grid, bytesPerElement, type, newStep);
463 setFractal(grid + newStep * bytesPerElement, bytesPerElement, type, newStep);
464 setFractal(grid + (newStep * s_gridSize) * bytesPerElement, bytesPerElement, type, newStep);
465 setFractal(grid + ((newStep * s_gridSize) + newStep) * bytesPerElement, bytesPerElement, type, newStep);
471 getRawHeightfieldData
479 // std::cerr << "\nRegenerating terrain\n";
480 // std::cerr << " model = " << model << "\n";
481 // std::cerr << " type = " << type << "\n";
483 long nElements = ((long) s_gridSize) * s_gridSize;
484 // std::cerr << " nElements = " << nElements << "\n";
486 int bytesPerElement = getByteSize(type);
487 // std::cerr << " bytesPerElement = " << bytesPerElement << "\n";
488 btAssert(bytesPerElement > 0 && "bad bytes per element");
490 long nBytes = nElements * bytesPerElement;
491 // std::cerr << " nBytes = " << nBytes << "\n";
492 byte_t * raw = new byte_t[nBytes];
493 btAssert(raw && "out of memory");
495 // reseed randomization every 30 seconds
496 // srand(time(NULL) / 30);
498 // populate based on model
501 setRadial(raw, bytesPerElement, type);
505 for (int i = 0; i < nBytes; i++)
509 setFractal(raw, bytesPerElement, type, s_gridSize - 1);
513 btAssert(!"bad model type");
517 // inside if(0) so it keeps compiling but isn't
518 // exercised and doesn't cause warnings
519 // std::cerr << "final grid:\n";
520 dumpGrid(raw, bytesPerElement, type, s_gridSize - 1);
524 for (int i = 0; i < s_gridSize; ++i) {
525 for (int j = 0; j < s_gridSize; ++j) {
526 float z = getGridHeight(raw, i, j, type);
527 // std::cerr << "i=" << i << ", j=" << j << ": z=" << z << "\n";
544 if (maxHeight < -minHeight) {
545 maxHeight = -minHeight;
547 if (minHeight > -maxHeight) {
548 minHeight = -maxHeight;
551 // std::cerr << " minHeight = " << minHeight << "\n";
552 // std::cerr << " maxHeight = " << maxHeight << "\n";
559 ////////////////////////////////////////////////////////////////////////////////
563 ////////////////////////////////////////////////////////////////////////////////
565 /// class that demonstrates the btHeightfieldTerrainShape object
566 class TerrainDemo : public GlutDemoApplication {
568 // constructor, destructor ---------------------------------------------
572 virtual void initPhysics() {}
574 // public class methods ------------------------------------------------
575 void initialize(void);
577 // DemoApplication class interface methods -----------------------------
578 void clientMoveAndDisplay(void);
579 void keyboardCallback(unsigned char key, int x, int y);
583 // private helper methods ----------------------------------------------
584 void resetPhysics(void);
585 void clearWorld(void);
587 // private data members ------------------------------------------------
588 btDefaultCollisionConfiguration * m_collisionConfiguration;
589 btCollisionDispatcher * m_dispatcher;
590 btAxisSweep3 * m_overlappingPairCache;
591 btSequentialImpulseConstraintSolver * m_constraintSolver;
592 btAlignedObjectArray<btCollisionShape*> m_collisionShapes;
594 PHY_ScalarType m_type;
595 eTerrainModel m_model;
596 byte_t * m_rawHeightfieldData;
597 btScalar m_minHeight;
598 btScalar m_maxHeight;
599 float m_phase; // for dynamics
605 TerrainDemo::TerrainDemo(void)
607 m_collisionConfiguration(NULL),
609 m_overlappingPairCache(NULL),
610 m_constraintSolver(NULL),
614 m_rawHeightfieldData(NULL),
622 TerrainDemo::~TerrainDemo(void)
626 //delete dynamics world
627 delete m_dynamicsWorld;
630 delete m_constraintSolver;
633 delete m_overlappingPairCache;
638 delete m_collisionConfiguration;
644 ////////////////////////////////////////////////////////////////////////////////
646 // TerrainDemo -- public class methods
648 ////////////////////////////////////////////////////////////////////////////////
650 /// one-time class and physics initialization
651 void TerrainDemo::initialize(void)
653 // std::cerr << "initializing...\n";
655 // set up basic state
656 m_upAxis = 1; // start with Y-axis as "up"
658 m_model = eRadial;//eFractal;
661 // set up the physics world
662 m_collisionConfiguration = new btDefaultCollisionConfiguration();
663 m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration);
664 btVector3 worldMin(-1000,-1000,-1000);
665 btVector3 worldMax(1000,1000,1000);
666 m_overlappingPairCache = new btAxisSweep3(worldMin,worldMax);
667 m_constraintSolver = new btSequentialImpulseConstraintSolver();
668 m_dynamicsWorld = new btDiscreteDynamicsWorld(m_dispatcher,m_overlappingPairCache,m_constraintSolver,m_collisionConfiguration);
670 // initialize axis- or type-dependent physics from here
671 this->resetPhysics();
676 ////////////////////////////////////////////////////////////////////////////////
678 // TerrainDemo -- DemoApplication class interface methods
680 ////////////////////////////////////////////////////////////////////////////////
682 void TerrainDemo::clientMoveAndDisplay(void)
685 float us = getDeltaTimeMicroseconds();
686 float seconds = 1.0e-6 * us;
688 // we'll carefully iterate through each time step so we can update
689 // the dynamic model if necessary
690 long nStepsPerIteration = 1;
691 while (seconds > 1.0e-6) {
692 float dt = nStepsPerIteration * s_engineTimeStep;
697 // std::cerr << " Stepping through " << dt << " seconds\n";
699 // if dynamic and radial, go ahead and update the field
700 if (m_rawHeightfieldData && m_isDynamic && eRadial == m_model) {
701 m_phase += s_deltaPhase * dt;
702 if (m_phase > 2.0 * SIMD_PI) {
703 m_phase -= 2.0 * SIMD_PI;
705 int bpe = getByteSize(m_type);
706 btAssert(bpe > 0 && "Bad bytes per element");
707 setRadial(m_rawHeightfieldData, bpe, m_type, m_phase);
710 if (m_dynamicsWorld) {
711 m_dynamicsWorld->stepSimulation(dt,
712 nStepsPerIteration + 1, s_engineTimeStep);
717 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
724 static PHY_ScalarType nextType (PHY_ScalarType type)
742 void TerrainDemo::keyboardCallback(unsigned char key, int x, int y) {
746 m_model = (eFractal == m_model) ? eRadial : eFractal;
747 this->resetPhysics();
751 m_type = nextType(m_type);
752 this->resetPhysics();
760 this->resetPhysics();
764 m_isDynamic = !m_isDynamic;
767 // let demo base class handle!
768 DemoApplication::keyboardCallback(key, x, y);
773 static void doPrint(int x,int& y,int dy,const char * text)
775 GLDebugDrawString(x,y, text);
781 /// override the default display just so we can overlay a bit more text
782 void TerrainDemo::renderme(void)
784 // give base class a shot
785 DemoApplication::renderme();
787 // overlay any debug information
789 m_dynamicsWorld->debugDrawWorld();
791 // switch to orthographic
792 setOrthographicProjection();
794 // we'll draw on the right top of the screen
795 const int lineWidth = 200;
796 const int lineHeight = 16;
799 int xStart = m_glutScreenWidth - lineWidth;
800 int yStart = lineHeight;
802 sprintf(buffer, "Terrain Type: %s", getTerrainTypeName(m_model));
803 doPrint(xStart, yStart, lineHeight, buffer);
804 doPrint(xStart, yStart, lineHeight, "Press ',' to cycle terrain types");
805 doPrint(xStart, yStart, lineHeight, "");
807 sprintf(buffer, "Data Type: %s", getDataTypeName(m_type));
808 doPrint(xStart, yStart, lineHeight, buffer);
809 doPrint(xStart, yStart, lineHeight, "Press '/' to cycle data types");
810 doPrint(xStart, yStart, lineHeight, "");
812 sprintf(buffer, "'up' axis: %s", getUpAxisName(m_upAxis));
813 doPrint(xStart, yStart, lineHeight, buffer);
814 doPrint(xStart, yStart, lineHeight, "Press '\\' to cycle 'up' axes");
815 doPrint(xStart, yStart, lineHeight, "");
817 if (eRadial == m_model) {
818 sprintf(buffer, "Dynamic: %s", m_isDynamic ? "yes" : "no");
819 doPrint(xStart, yStart, lineHeight, buffer);
820 doPrint(xStart, yStart, lineHeight, "Press '[' to toggle dynamics");
826 ////////////////////////////////////////////////////////////////////////////////
828 // TerrainDemo -- private helper methods
830 ////////////////////////////////////////////////////////////////////////////////
832 /// called whenever key terrain attribute is changed
833 void TerrainDemo::resetPhysics(void)
835 // remove old heightfield
838 // reset gravity to point in appropriate direction
839 m_dynamicsWorld->setGravity(getUpVector(m_upAxis, 0.0, -s_gravity));
841 // get new heightfield of appropriate type
842 m_rawHeightfieldData =
843 getRawHeightfieldData(m_model, m_type, m_minHeight, m_maxHeight);
844 btAssert(m_rawHeightfieldData && "failed to create raw heightfield");
846 bool flipQuadEdges = false;
847 btHeightfieldTerrainShape * heightfieldShape =
848 new btHeightfieldTerrainShape(s_gridSize, s_gridSize,
849 m_rawHeightfieldData,
851 m_minHeight, m_maxHeight,
852 m_upAxis, m_type, flipQuadEdges);
853 btAssert(heightfieldShape && "null heightfield");
856 btVector3 localScaling = getUpVector(m_upAxis, s_gridSpacing, 1.0);
857 heightfieldShape->setLocalScaling(localScaling);
859 // stash this shape away
860 m_collisionShapes.push_back(heightfieldShape);
862 // set origin to middle of heightfield
865 tr.setOrigin(btVector3(0,-20,0));
867 // create ground object
869 localCreateRigidBody(mass, tr, heightfieldShape);
873 /// removes all objects and shapes from the world
874 void TerrainDemo::clearWorld(void)
876 //remove the rigidbodies from the dynamics world and delete them
878 for (i=m_dynamicsWorld->getNumCollisionObjects()-1; i>=0 ;i--)
880 btCollisionObject* obj = m_dynamicsWorld->getCollisionObjectArray()[i];
881 btRigidBody* body = btRigidBody::upcast(obj);
882 if (body && body->getMotionState())
884 delete body->getMotionState();
886 m_dynamicsWorld->removeCollisionObject( obj );
890 //delete collision shapes
891 for (int j=0;j<m_collisionShapes.size();j++)
893 btCollisionShape* shape = m_collisionShapes[j];
896 m_collisionShapes.clear();
898 // delete raw heightfield data
899 delete m_rawHeightfieldData;
900 m_rawHeightfieldData = NULL;
905 ////////////////////////////////////////////////////////////////////////////////
907 // TerrainDemo -- public API (exposed in header)
909 ////////////////////////////////////////////////////////////////////////////////
911 /// creates an object that demonstrates terrain
912 GlutDemoApplication * btCreateTerrainDemo(void)
914 TerrainDemo * demo = new TerrainDemo;
915 btAssert(demo && "out of memory");