Hi,
I was trying simulate a highly redundant spring-mass system using Bullet in combination with GameKit.
The simulation (see the code below) contains a number of spheres (about 50) with a mass of 0.1(kg) at random locations.
Between some pairs of spheres I want to put springs, so I'm using the 6dof spring constraint (so I don't have to change my code to allow more movements) with only one unconstrained axis (the first linear one).
The pairs are a subset of a Delaunay triangulation of the spheres' locations (i.e. so that only pairs of nearby spheres are selected).
Of course the resulting system is highly redundant most of the time, but this is exactly the goal of the experiment, so reducing the number of springs is not an option (I'd rather prefer to add some more...).
So to setup the simulation, I first create a set of spheres, and then I start adding constraints between pairs (A,B) of spheres.
For the transform in frame A, I take the offset (B.getPosition()-A.getPosition()) divided by two as the origin and use projection of the vector from A to B on A's original x-axis to compute a rotation matrix.
I reduced the internal time step to 1ms and set the solver's number of iterations to 20 to increase the accuracy.
If I run this, the simulation crashes after a few frames with the following exception:
gamekit-read-only/OgreLite/OgreMain/src/OgreNode.cpp:393: virtual void Ogre::Node::setOrientation(const Ogre::Quaternion&): Assertion `!q.isNaN() && "Invalid orientation supplied as parameter"' failed.
The error is generated by gkPhysicsController::setTransform() at gkPhysicsController.cpp:601 0xb07a8d, trying to update position of the objects in Ogre (using NaN for the rotation).
Basically my simulation exploded, because if I run the simulation in slow-motion, I can see the objects drift away into infinity before crashing.
I don't see why this happens, but I guess something's wrong with the transforms for the constraints?
So here's the code with the necessary comments:
Code: Select all
unsigned num_points = 50;
float sparseness = 0.3;
gkVector3 center(0,0,1);
for(unsigned i=0;i<num_points;++i){
gkVector3 point;
//generate random points (locations)
point.x = center.x+(float)rand()/(float)RAND_MAX-0.5;
point.y = center.y+(float)rand()/(float)RAND_MAX-0.5;
point.z = center.z+(float)rand()/(float)RAND_MAX-0.5;
input.push_back(point);
gkString new_name = "Ball_";char buff[10]; sprintf(buff,"%u",i);
new_name.append(buff);
//create a new ball
gkGameObject* newball = scene->getObject("Sphere")->clone(new_name);
//add it to the scene
newball->setOwner(0);
newball->setActiveLayer(true);
engine.getActiveScene()->addObject(newball);
newball->createInstance();
newball->setPosition(point);
newball->getAttachedBody()->getBody()->setActivationState(DISABLE_DEACTIVATION);
newball->setOrientation(gkEuler(0,0,0));
balls.push_back(newball);
}
d.triangulate(input,output); //delaunay triangulation
for(unsigned i=0;i<output.size();++i){
gkVector3 pa = input[output[i].first];
gkGameObject* ba = balls[output[i].first];// A
gkVector3 pb = input[output[i].second];
gkGameObject* bb = balls[output[i].second]; //B
if(((float)rand()/(float)RAND_MAX)>sparseness){
joints.push_back(std::pair<size_type,size_type>(output[i].first,output[i].second));
btTransform ta,tb; //transforms in A and B's frames
ta.setIdentity();
gkVector3 offset = bb->getPosition()-ba->getPosition(); //vector from A to B
btVector3 offset_b(offset.x,offset.y,offset.z); //in Bullet
btVector3 noffset_b = offset_b.normalized(); //normalize the vector (not strictly necessary)
noffset_b/=noffset_b.x(); //assures that we have a valid rotation matrix
ta.setOrigin(offset_b/2); //place the joint in the middle of A and B
//create the rotation matrix (the x-axis is projected onto the vector from A to B)
btMatrix3x3 rotmat = btMatrix3x3(noffset_b.x(),noffset_b.y(),noffset_b.z(),0,1,0,0,0,1).transpose();
btScalar det = rotmat.determinant();
ta.setBasis(rotmat);
//just a check, but it is always equal to one
gkPrintf("det %f\n ",ta.getBasis().determinant());
//the same for B
tb.setIdentity();
tb.setOrigin(-offset_b/2);
tb.setBasis(rotmat);
//Create the constraint
btGeneric6DofSpringConstraint * constraint = new btGeneric6DofSpringConstraint(*ba->getAttachedBody()->getBody(),*bb->getAttachedBody()->getBody(),ta,tb,true);
//Fix every direction except for the first (x)
constraint->setLinearLowerLimit(btVector3(1,0,0));
constraint->setLinearUpperLimit(btVector3(-1,0,0));
constraint->setAngularLowerLimit(btVector3(0,0,0));
constraint->setAngularUpperLimit(btVector3(0,0,0));
constraint->enableSpring(0,true); //enable the internal spring for the first direction
constraint->setStiffness(0, 10); //spring constant
constraint->setDamping(0,0.8);
constraint->setEquilibriumPoint();
//add the constraint to the physics engine
engine.getActiveScene()->getDynamicsWorld()->getBulletWorld()->addConstraint(constraint);
}
Instead I exported the Bullet world through: engine.getActiveScene()->getDynamicsWorld()->exportBullet("buggyworld.zip"); which you can find attached to this post.
Any ideas on how to fix this would be appreciated!
Thanks