[SOLVED] Simple 2d simulation, failing

Hieran_Del8
Posts: 11
Joined: Mon Nov 12, 2012 3:20 pm

[SOLVED] Simple 2d simulation, failing

Post by Hieran_Del8 »

I'm really new to physics engines, but not the math. I was putting together a 2d demo, encapsulating bullet in a class, and although I got it to work before, in imitating the 2d demo it is failing at btCollisionWorld::updateSingleAabb().

I've read the manual, searched the forums (found this similar problem at: http://bulletphysics.org/Bullet/phpBB3/ ... php?t=5195), and tried debugging by checking the call stack, reading the values. I haven't figured it out, but perhaps the problem is easily visible to more experienced eyes. Here's the code:

Code: Select all

class Physics
{
private:
	struct EntityShape
	{
		btConvexShape* childShape; //target shape
		btConvex2dShape* shape; //containing shape for 2d physics

		//ctor
		EntityShape() : childShape(NULL), shape(NULL) {}

		//dtor, deletes both containing shape and target shape
		~EntityShape() {if(shape) delete shape; shape = NULL; if(childShape) delete childShape; childShape = NULL;}

		//creates a 2d shape container from the passed in target shape
		void create(btConvexShape* ChildShape)
		{
			childShape = ChildShape;
			if(childShape == NULL)
				return;
			shape = new btConvex2dShape(childShape);
		}
	};

	btBroadphaseInterface *m_broadphase; //first sweep
	btDefaultCollisionConfiguration *m_config; //collision config
	btCollisionDispatcher *m_dispatcher; //second sweep/dispatcher
	btSequentialImpulseConstraintSolver *m_solver; //constraint solver
	btDiscreteDynamicsWorld *m_world; //world
	std::vector<EntityShape> m_templateShapes; //all the collision shapes, with the 2d containers
	std::vector<btRigidBody*> m_bodies; //all the rigid bodies
public:
	//ctor
	Physics()
		: m_broadphase(NULL), m_config(NULL), m_dispatcher(NULL), m_solver(NULL), m_world(NULL)
	{
		//make new broadphase interface
		m_broadphase = new btDbvtBroadphase();

		//collision config and dispatcher
		m_config = new btDefaultCollisionConfiguration();
		m_dispatcher = new btCollisionDispatcher( m_config );

		//2d solver setup
		btVoronoiSimplexSolver *simplex = new btVoronoiSimplexSolver();
		btMinkowskiPenetrationDepthSolver *pdSolver = new btMinkowskiPenetrationDepthSolver();

		btConvex2dConvex2dAlgorithm::CreateFunc *convexAlgo2d = new btConvex2dConvex2dAlgorithm::CreateFunc(simplex, pdSolver);

		m_dispatcher->registerCollisionCreateFunc(CONVEX_2D_SHAPE_PROXYTYPE,CONVEX_2D_SHAPE_PROXYTYPE,convexAlgo2d);
		m_dispatcher->registerCollisionCreateFunc(BOX_2D_SHAPE_PROXYTYPE,CONVEX_2D_SHAPE_PROXYTYPE,convexAlgo2d);
		m_dispatcher->registerCollisionCreateFunc(CONVEX_2D_SHAPE_PROXYTYPE,BOX_2D_SHAPE_PROXYTYPE,convexAlgo2d);
		m_dispatcher->registerCollisionCreateFunc(BOX_2D_SHAPE_PROXYTYPE,BOX_2D_SHAPE_PROXYTYPE,new btBox2dBox2dCollisionAlgorithm::CreateFunc());

		//physics solver
		m_solver = new btSequentialImpulseConstraintSolver();

		//world setup
		m_world = new btDiscreteDynamicsWorld( m_dispatcher, m_broadphase, m_solver, m_config );
		m_world->setGravity( btVector3(0.0f, -10.0f, 0.0f) );
	}

	//dtor
	~Physics()
	{
		if( m_world != NULL )
			delete m_world;
		if( m_solver != NULL )
			delete m_solver;
		if( m_dispatcher != NULL )
			delete m_dispatcher;
		if( m_config != NULL )
			delete m_config;
		if( m_broadphase != NULL )
			delete m_broadphase;
	}

	//creates the simulation
	void createSimulation()
	{
		//local params
		btConvexShape *shape = NULL;
		btDefaultMotionState *motion = NULL;
		btRigidBody::btRigidBodyConstructionInfo ci(0.0f, 0, 0);
		btRigidBody *body = NULL;

		//object 1
		shape = new btBox2dShape( btVector3(20.0f, .5f, 0) );
		m_templateShapes.resize( m_templateShapes.size() + 1 );
		m_templateShapes.back().create( shape );
		motion = new btDefaultMotionState( btTransform( btQuaternion(0,0,0,1) ) );
		ci = btRigidBody::btRigidBodyConstructionInfo( 0.0f, motion, m_templateShapes.back().shape );
		ci.m_restitution = 1.0f;
		body = new btRigidBody(ci);
		m_bodies.push_back( body );

		m_world->addRigidBody( m_bodies.back() );

		//object 2
		shape = new btCylinderShapeZ( btVector3(1.0f, 1.0f, .05f) );
		m_templateShapes.resize( m_templateShapes.size() + 1 );
		m_templateShapes.back().create( shape );
		motion = new btDefaultMotionState( btTransform( btQuaternion(0,0,0,1), btVector3(0, 50, 0) ) );
		btVector3 fallInertia;
		m_templateShapes.back().shape->calculateLocalInertia(1.0f, fallInertia);
		ci = btRigidBody::btRigidBodyConstructionInfo( 1.0f, motion, m_templateShapes.back().shape, fallInertia );
		ci.m_restitution = .5f;
		body = new btRigidBody(ci);
		m_bodies.push_back( body );

		m_world->addRigidBody( m_bodies.back() );
	}

	//stops the simulation, deallocating shapes and objects
	void destroySimulation()
	{
		if( m_world )
		{
			while(m_world->getCollisionObjectArray().size() > 0)
			{
				btCollisionObject* obj = m_world->getCollisionObjectArray().at( m_world->getCollisionObjectArray().size() - 1 );
				btRigidBody* body = btRigidBody::upcast(obj);
				if( body && body->getMotionState() )
					delete body->getMotionState();
				m_world->removeCollisionObject( obj );
				if( obj )
				{
					delete obj;
				}
			}
		}

		m_bodies.clear();
		m_templateShapes.clear();
	}

	//steps the simulation by dt
	void stepSimulation(float dt)
	{
		m_world->stepSimulation(dt, 10);
	}

	//gets the world transform of the indexed rigid body
	XMMATRIX getWorldTransform(UINT index)
	{
		XMMATRIX transform = XMMatrixIdentity();
		if( index < m_bodies.size() )
		{
			btTransform& t = m_bodies[index]->getWorldTransform();
			XMFLOAT4X4 data;
			t.getOpenGLMatrix( (float*)&data );
			//not transposing here since XMMATRIX are row-major, just as OpenGL matrices
			transform = /*XMMatrixTranspose*/(XMLoadFloat4x4( &data ));
		}
		return transform;
	}
};
EDIT 11/14/12:
The problem was resolved by the third post.
Last edited by Hieran_Del8 on Wed Nov 14, 2012 7:05 pm, edited 1 time in total.
Hieran_Del8
Posts: 11
Joined: Mon Nov 12, 2012 3:20 pm

Re: Simple 2d simulation, failing

Post by Hieran_Del8 »

I think the error is somewhere in the object creation. I can create one object, or the other, and the call to updateSingleAabb() works. But when both are created I get the access violation.

Any help is appreciated, even if it's just pointing me in a direction.

Code: Select all

      //object 1
      shape = new btBox2dShape( btVector3(20.0f, .5f, 0) );
      m_templateShapes.resize( m_templateShapes.size() + 1 );
      m_templateShapes.back().create( shape );
      motion = new btDefaultMotionState( btTransform( btQuaternion(0,0,0,1) ) );
      ci = btRigidBody::btRigidBodyConstructionInfo( 0.0f, motion, m_templateShapes.back().shape );
      ci.m_restitution = 1.0f;
      body = new btRigidBody(ci);
      m_bodies.push_back( body );

      m_world->addRigidBody( m_bodies.back() );

      //object 2
      shape = new btCylinderShapeZ( btVector3(1.0f, 1.0f, .05f) );
      m_templateShapes.resize( m_templateShapes.size() + 1 );
      m_templateShapes.back().create( shape );
      motion = new btDefaultMotionState( btTransform( btQuaternion(0,0,0,1), btVector3(0, 50, 0) ) );
      btVector3 fallInertia;
      m_templateShapes.back().shape->calculateLocalInertia(1.0f, fallInertia);
      ci = btRigidBody::btRigidBodyConstructionInfo( 1.0f, motion, m_templateShapes.back().shape, fallInertia );
      ci.m_restitution = .5f;
      body = new btRigidBody(ci);
      m_bodies.push_back( body );

      m_world->addRigidBody( m_bodies.back() );
Hieran_Del8
Posts: 11
Joined: Mon Nov 12, 2012 3:20 pm

Re: Simple 2d simulation, failing

Post by Hieran_Del8 »

Ah, I finally found the error, and I think the reason was a premature deallocation. (At some point I should really look into tracing memory allocation... :roll: ) The offending code was here:

Code: Select all

   struct EntityShape
   {
      btConvexShape* childShape; //target shape
      btConvex2dShape* shape; //containing shape for 2d physics

      //ctor
      EntityShape() : childShape(NULL), shape(NULL) {}

      //dtor, deletes both containing shape and target shape
      ~EntityShape() {if(shape) delete shape; shape = NULL; if(childShape) delete childShape; childShape = NULL;}

      //creates a 2d shape container from the passed in target shape
      void create(btConvexShape* ChildShape)
      {
         childShape = ChildShape;
         if(childShape == NULL)
            return;
         shape = new btConvex2dShape(childShape);
      }
   };
I noticed that at one point, the memory location of "shape" in the second object was identical to the memory location of "childShape" of the first (which is probably highly coincidental, and thank goodness it happened!). I have a feeling this happened because of using a std::vector of EntityShape's, and calling resize(). The elements of the vector may have been destroyed during the resize operation to allocate a different memory location, and the destructor's job of EntityShape was to deallocate memory.

I've rewritten the EntityShape structure to not destroy objects in the destructor, but have an extra method do that. It now works with this change:

Code: Select all

	struct EntityShape
	{
		btConvexShape* childShape; //target shape
		btConvex2dShape* shape; //containing shape for 2d physics

		//ctor
		EntityShape(btConvexShape* ChildShape = NULL, btConvex2dShape* ParentShape = NULL) 
			: childShape(ChildShape), shape(ParentShape)
		{
			if( !shape && childShape )
				create( childShape );
		}

		//creates a 2d shape container from the passed in target shape
		void create(btConvexShape* ChildShape)
		{
			childShape = ChildShape;
			if(childShape == NULL)
				return;
			shape = new btConvex2dShape(childShape);
		}

		//destroys the shape container and child shape
		void destroy()
		{
			if(shape) 
				delete shape; 
			shape = NULL; 
			if(childShape) 
				delete childShape; 
			childShape = NULL;
		}
	};