Saxum/physics.cc

472 lines
19 KiB
C++
Raw Normal View History

2014-11-14 13:17:04 +00:00
#include "physics.hh"
#include "extern/bullet/Extras/Serialize/BulletWorldImporter/btBulletWorldImporter.h"
2014-11-14 13:17:04 +00:00
Physics::Physics() {
}
Physics::~Physics() {
}
2014-11-14 13:17:04 +00:00
void Physics::init() //prepares bullet by creating all initial classes
2014-11-14 13:17:04 +00:00
{
colConfig = new btDefaultCollisionConfiguration();
dispatcher = new btCollisionDispatcher(colConfig);
broadphase = new btDbvtBroadphase();
solver = new btSequentialImpulseConstraintSolver();
world = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,colConfig);
world->setGravity(btVector3(0,-10,-0));
2014-11-14 13:17:04 +00:00
}
void Physics::takeUpdateStep(float timeDiff)
{
world->stepSimulation(timeDiff); //allows the world to be simmulated correctly indipendant of the timedifferences between frames
counter++;
if(counter<10)
return;
for(unsigned i = 0; i < allPositionConstraints.size();i++) //this handles the spring constraints
{
if(allPositionConstraints[i].position != allPositionConstraints[i].body->getCenterOfMassPosition()) //if constraint != position of the body because otherwise dir = 0
{
btVector3 dir = allPositionConstraints[i].position - allPositionConstraints[i].body->getCenterOfMassPosition();
dir = dir*allPositionConstraints[i].strength;
allPositionConstraints[i].body->applyCentralForce(dir*allPositionConstraints[i].strength); //apply a foce upon the object pushing it towards the constraint position
}
}
btVector3 position = cameraBody->getCenterOfMassPosition() - playerBall->getCenterOfMassPosition(); //gets a vector from the player to the camera
2015-01-19 15:30:11 +00:00
position.normalize();
position *= 5;
2015-01-19 15:30:11 +00:00
position += playerBall->getCenterOfMassPosition(); //is the position 5 units away from the player in the direction of the camera
btVector3 dir = cameraBody->getCenterOfMassPosition() - position;
float str = cameraBody->getInvMass()*30 * dir.length();
cameraBody->applyCentralForce(-dir*str);//scale the force by camera mass
cameraBody->applyCentralForce(btVector3(0,96,0)); //we leave gravity in because by contrasting gravity to this force we force the camera slightly higher than the ball when still, but it follows more closer to the level of the ball (though still slightly higher) when it is moving.
counter=0;
2014-11-14 13:17:04 +00:00
}
void Physics::removePositionConstraint(int bodyIndice) //remover function for deleting all pos constraints on one body
{
for(unsigned i = 0; i < allPositionConstraints.size(); i++)
{
if(allPositionConstraints[i].body == bodies[bodyIndice])
{
allPositionConstraints.erase(allPositionConstraints.begin()+i);
}
}
}
void Physics::addPositionConstraint(int bodyIndice, float strength, glm::vec3 position) //function for adding position constraints
{
positionConstraint cons;
cons.body = bodies[bodyIndice];
cons.strength = strength;
cons.position = btVector3(position.x,position.y,position.z);
allPositionConstraints.push_back(cons);
}
//players and objects
void Physics::addPlayer(float friction, float rad, Entity entity, float mass, float dampningL, float dampningA, unsigned indice)
{
if(bodies.size() == indice)
throw std::invalid_argument( "Bodies out of Sync" ); //these error are to ensure that level can always communicate with physics without having to worry about synching errors
btSphereShape* sphere = new btSphereShape(rad); //the first thing we need for a rigid body is the shape
btVector3 inertia(0,0,0);
if(mass != 0.0)
{
sphere->calculateLocalInertia((btScalar)mass,inertia); //from this shape we can then calculate the innertia, as long as the mass != 0 (otherwise inertia = 0)
}
glm::quat glmQuat = glm::quat_cast(entity.getRotation());
btDefaultMotionState* motion = new btDefaultMotionState(btTransform(btQuaternion(glmQuat.x,glmQuat.y,glmQuat.z,glmQuat.w),btVector3(entity.getPosition().x,entity.getPosition().y,entity.getPosition().z))); //next we define the motionstate, wich describes the innital position and rotation
btRigidBody::btRigidBodyConstructionInfo info(mass,motion,sphere,inertia); //next we process all data for the rigid body into info
info.m_friction = friction*2; //here we modify the friction and restitution (bounciness) of the object
info.m_restitution = 0;
playerBall = new btRigidBody(info); //finally we create the rigid body using the info
playerBall->setDamping(dampningL, dampningA); //here we can set the dampning (how much of the motion is lost)
world->addRigidBody(playerBall,COL_OBJECTS,COL_OBJECTS|COL_OBJECTS_NO_TERRAIN|COL_TERRAIN); //then we add the rigid body to the wiorld, allowing it to be simulated
bodies.push_back(playerBall); //next we add the rigid body to our own list (for cleanup and for synchronitaation with level)
//note, while we can always access playerBall through its global name, we add it to this array for synchronization purposes
playerBall->setSleepingThresholds(0,0); //in a final step we make sure that the body never is removed from the active rigid bodies
if(bodies.size() != indice)
throw std::invalid_argument( "Bodies out of Sync" ); //one last check to make sure level and physics are in synch
addCamera(); //now that the player exists add a camera for the player
}
void Physics::addTerrain(int width, int length, float** heightData) //The terrian adding function
2014-11-14 13:23:10 +00:00
{
float* heightfield = new float[width * length];//bullet only accepts data in a one dimensional array, so parse data into appropriate format
2014-11-14 13:23:10 +00:00
int highest = -999999, j = 0, i = 0;
for (i = 0; i < width; i++)
{
for (j = 0; j < length; j++) {
heightfield[i*length+j] = heightData[j][i]; //reverse order because they are loaded backwards
if (heightData[j][i] > highest)
highest = heightData[j][i]; //bullet needs to know the highest point of the heightmap
}
}
2014-11-17 17:54:19 +00:00
highest++;
2014-11-14 13:23:10 +00:00
btHeightfieldTerrainShape* terrianShape = new btHeightfieldTerrainShape(length,width,heightfield,highest,1,true,false);
btRigidBody::btRigidBodyConstructionInfo info(0,new btDefaultMotionState(),terrianShape,btVector3(0,0,0)); //next we process all data for the rigid body into info
info.m_restitution = 0;
btRigidBody* tBody = new btRigidBody(info);
2014-11-14 13:23:10 +00:00
tBody->getWorldTransform().setOrigin(btVector3(0,((float)highest)/2,0)); //we have to move the origin of our rigid body down, because bullet sets the origin (0,0,0) at (width/2, height/2, length/2) in the map the x and z are correct in our level, but y needs to be addapted
2014-11-14 13:23:10 +00:00
terrainBody = tBody;
world->addRigidBody(terrainBody, COL_TERRAIN, COL_TERRAIN | COL_OBJECTS); //COL_XXXX are collision masks, allowing us to ignore collisions between certain object groups (required for buttons)
2014-11-14 13:23:10 +00:00
}
void Physics::addTriangleMeshBody(Entity entity, std::string path, float mass, float dampningL, float dampningA,unsigned indice,float scaling, bool rotate)
2014-11-14 13:17:04 +00:00
{
if(bodies.size() == indice)
throw std::invalid_argument( "Bodies out of Sync" );
std::vector< unsigned int > vertexIndices; //temp lists for data sets
std::vector< btVector3 > temp_vertices;
path = "../Levels/Geometry/" + path;
FILE * file = fopen(path.c_str(), "r");
if( file == NULL ){
throw std::invalid_argument( "Impossible to open the file" ); //create correct filepath and report error if cannot open
}
while( 1 ){
char lineHeader[128];
// read the first word of the line
int res = fscanf(file, "%s", lineHeader);
if (res == EOF)
break; // while not at end do loop
if ( strcmp( lineHeader, "v" ) == 0 ){ //if a vertex
glm::vec3 vertex;
fscanf(file, "%f %f %f\n", &vertex.x, &vertex.y, &vertex.z );
temp_vertices.push_back(btVector3(vertex.x,vertex.y,vertex.z)); //save vertex in array
}
else if ( strcmp( lineHeader, "f" ) == 0 ){ //if face (index for 3 vertexes for a triangle)
std::string vertex1, vertex2, vertex3;
unsigned int vertexIndex[3], uvIndex[3], normalIndex[3];
int matches = fscanf(file, "%d/%d/%d %d/%d/%d %d/%d/%d\n", &vertexIndex[0], &uvIndex[0], &normalIndex[0], &vertexIndex[1], &uvIndex[1], &normalIndex[1], &vertexIndex[2], &uvIndex[2], &normalIndex[2] );
vertexIndices.push_back(vertexIndex[0]);
vertexIndices.push_back(vertexIndex[1]);
vertexIndices.push_back(vertexIndex[2]); //save 3 indexes in array
}
}
//finally start making body
btTriangleMesh* triMesh = new btTriangleMesh();
2014-12-19 14:20:05 +00:00
for(unsigned i = 2; i < vertexIndices.size();i+=3)
{
triMesh->addTriangle(temp_vertices[vertexIndices[i]],temp_vertices[vertexIndices[i-1]],temp_vertices[vertexIndices[i-2]]); // for every face (3 elements in vertexIndices) create triangle use the indices to find correct vertexes to make the triangle
}
btBvhTriangleMeshShape* shape = new btBvhTriangleMeshShape(triMesh,true);
shape->setLocalScaling(btVector3(scaling,scaling,scaling)); //we need to add a scaling here because the objects seem to have diffrent sizes when loaded (no clue why, see composition.xml for exact scaling factors)
glm::quat glmQuat = glm::quat_cast(entity.getRotation());
btDefaultMotionState* motion = new btDefaultMotionState(btTransform(btQuaternion(glmQuat.x,glmQuat.y,glmQuat.z,glmQuat.w),btVector3(entity.getPosition().x,entity.getPosition().y,entity.getPosition().z)));
btVector3 inertia(0,0,0);
if(mass != 0.0 && rotate) //&& rotate lets certain objects get inertia (0,0,0) (not rotateable)
{
shape->calculateLocalInertia((btScalar)mass,inertia);
}
btRigidBody::btRigidBodyConstructionInfo info(mass,motion,shape,inertia);
btRigidBody* body = new btRigidBody(info);
body->setDamping(dampningL,dampningA);
bodies.push_back(body);
2015-01-19 14:24:44 +00:00
world->addRigidBody(body,COL_OBJECTS, objectsPhysicsCollision);
if(bodies.size() != indice)
throw std::invalid_argument( "Bodies out of Sync" );
}
2015-01-23 12:31:19 +00:00
void Physics::addButton(float width, float height, float length, Entity entity, float mass, float dampningL, float dampningA, unsigned indice,bool rotate)
{
if(bodies.size() == indice)
throw std::invalid_argument( "Bodies out of Sync" );
2015-01-23 11:05:43 +00:00
btBoxShape* box = new btBoxShape(btVector3(width/2,height/2,length/2));
glm::quat glmQuat = glm::quat_cast(entity.getRotation());
btDefaultMotionState* motion = new btDefaultMotionState(btTransform(btQuaternion(glmQuat.x,glmQuat.y,glmQuat.z,glmQuat.w),btVector3(entity.getPosition().x,entity.getPosition().y,entity.getPosition().z)));
btVector3 inertia(0,0,0);
if(mass != 0.0 && rotate) //&& rotate lets certain objects get inertia (0,0,0) (not rotateable)
{
box->calculateLocalInertia((btScalar)mass,inertia);
}
2015-01-23 11:05:43 +00:00
btRigidBody::btRigidBodyConstructionInfo info(mass,motion,box,inertia);
btRigidBody* body = new btRigidBody(info);
body->setDamping(dampningL, dampningA);
world->addRigidBody(body,COL_OBJECTS_NO_TERRAIN, specialPhysicsCollision); //the specialPhysicsCollision allows these objects to not collide with the terrain
bodies.push_back(body);
if(bodies.size() != indice)
throw std::invalid_argument( "Bodies out of Sync" );
}
2015-01-23 12:31:19 +00:00
void Physics::addBox(float width, float height, float length, Entity entity, float mass, float dampningL, float dampningA, unsigned indice,bool rotate)
{
//similar to other constructors
2014-11-21 15:22:36 +00:00
if(bodies.size() == indice)
throw std::invalid_argument( "Bodies out of Sync" );
glm::quat glmQuat = glm::quat_cast(entity.getRotation());
2014-11-21 11:50:11 +00:00
btBoxShape* box = new btBoxShape(btVector3(width/2,height/2,length/2));
btDefaultMotionState* motion = new btDefaultMotionState(btTransform(btQuaternion(glmQuat.x,glmQuat.y,glmQuat.z,glmQuat.w),btVector3(entity.getPosition().x,entity.getPosition().y,entity.getPosition().z)));
2014-11-21 11:50:11 +00:00
btVector3 inertia(0,0,0);
if(mass != 0.0)
{
box->calculateLocalInertia((btScalar)mass,inertia);
}
btRigidBody::btRigidBodyConstructionInfo info(mass,motion,box,inertia);
btRigidBody* body = new btRigidBody(info);
body->setDamping(dampningL, dampningA);
2014-11-21 11:50:11 +00:00
world->addRigidBody(body,COL_OBJECTS, objectsPhysicsCollision);
2014-11-21 11:50:11 +00:00
bodies.push_back(body);
2014-11-21 11:50:11 +00:00
if(bodies.size() != indice)
throw std::invalid_argument( "Bodies out of Sync" );
}
2015-01-23 12:31:19 +00:00
void Physics::addSphere(float rad, Entity entity, float mass, float dampningL, float dampningA, unsigned indice,bool rotate)
{
2014-11-21 15:22:36 +00:00
if(bodies.size() == indice)
throw std::invalid_argument( "Bodies out of Sync" );
btSphereShape* sphere = new btSphereShape(rad);
btVector3 inertia(0,0,0);
if(mass != 0.0)
2014-11-14 13:17:04 +00:00
{
sphere->calculateLocalInertia((btScalar)mass,inertia);
}
glm::quat glmQuat = glm::quat_cast(entity.getRotation());
2014-11-14 13:17:04 +00:00
btDefaultMotionState* motion = new btDefaultMotionState(btTransform(btQuaternion(glmQuat.x,glmQuat.y,glmQuat.z,glmQuat.w),btVector3(entity.getPosition().x,entity.getPosition().y,entity.getPosition().z)));
2014-11-14 13:17:04 +00:00
btRigidBody::btRigidBodyConstructionInfo info(mass,motion,sphere,inertia);
2014-11-14 13:17:04 +00:00
btRigidBody* body = new btRigidBody(info);
2014-11-21 11:50:11 +00:00
body->setDamping(dampningL, dampningA);
2014-11-14 13:17:04 +00:00
world->addRigidBody(body,COL_OBJECTS, objectsPhysicsCollision);
2014-11-14 13:17:04 +00:00
bodies.push_back(body);
body->setSleepingThresholds(0,0);
2014-11-21 15:22:36 +00:00
if(bodies.size() != indice)
throw std::invalid_argument( "Bodies out of Sync" );
2014-11-14 13:17:04 +00:00
}
void Physics::addCamera() //Camera Creator automatically called when player is created
{
btSphereShape* sphere = new btSphereShape(0.5f);//we use this to make a more interesting camera, that does not interpenetrate with the terrain/objects
btVector3 inertia(0,0,0); //rotation handled elsewhere (as it always has to look at the player)
btVector3 direction(1,1,1);
direction.normalize();
direction*=5; //create a offset of lenth 5 so we have a stable camera at the beginning
btDefaultMotionState* motion = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),playerBall->getCenterOfMassPosition()+direction));
btRigidBody::btRigidBodyConstructionInfo info(1,motion,sphere,inertia);
cameraBody = new btRigidBody(info);
cameraBody->setDamping(0.9,0.5); //this damping factor leaves a relativly smoothe system
2015-01-19 14:24:44 +00:00
world->addRigidBody(cameraBody,COL_OBJECTS, objectsPhysicsCollision);
cameraBody->setSleepingThresholds(0,0); //very important, otherwise camera may go to sleep, aka not move until next collision
}
//update functions
glm::vec3 Physics::getCameraPosition()
{
btVector3 origin = cameraBody->getCenterOfMassPosition();
glm::vec3 save(origin.getX(),origin.getY(),origin.getZ());
return save;
}
glm::vec3 Physics::getCameraToPlayer()//returns a glm::vec3 the goes from the player to the camera
{
btVector3 origin = playerBall->getCenterOfMassPosition() - cameraBody->getCenterOfMassPosition();
glm::vec3 save(origin.getX(),origin.getY(),origin.getZ());
return save;
}
glm::vec3 Physics::getPos(int i) //this and the next function are used to synchronize the graphics data and the physics data
2014-11-14 13:17:04 +00:00
{
btVector3 origin = bodies[i]->getCenterOfMassPosition();
2014-11-14 13:23:10 +00:00
glm::vec3 save(origin.getX(),origin.getY(),origin.getZ());
return save;
2014-11-14 13:17:04 +00:00
}
glm::mat4 Physics::getRotation(int i)
2014-11-14 13:17:04 +00:00
{
btQuaternion quat = bodies[i]->getOrientation();
glm::mat4 matrix = glm::rotate(
quat.getAngle(),
glm::vec3(quat.getAxis().getX(), quat.getAxis().getY(), quat.getAxis().getZ())
); //somewhat clunky, but basicly creates a rotation matrix out of the angle of the body, and its axis (from the quaterion in bullet)
return matrix;
2014-11-14 13:17:04 +00:00
}
//these are used to apply a force to the camera body according to the movement of the mouse
2015-01-19 15:30:11 +00:00
void Physics::updateCameraPos(glm::vec2 mouseMovement, float strength)
{
//note: in mouseMovement x and y are flipped in contrast to bullet
btVector3 change = playerBall->getCenterOfMassPosition()-cameraBody->getCenterOfMassPosition();
2015-01-19 15:30:11 +00:00
change.setY(0);
change.normalize(); //normalize so that the distance between camera and body does not matter
change *= (mouseMovement.y);//we start with left/right movement because this needs to be calculated via a crossproduct, and the up down value would alter that
2015-01-19 15:30:11 +00:00
change = btCross(btVector3(0,1,0),change);
change.setY(mouseMovement.x/5);//scaleing because otherwise oup/down much stronger then left right
change*= strength;
cameraBody->applyCentralForce(change);
2015-01-19 15:30:11 +00:00
}
//use the crossproduct to correctly apply a torque to the palyer if function called
void Physics::rollForward(glm::vec3 camPos,float strength)
2014-11-14 13:17:04 +00:00
{
btVector3 pos = cameraBody->getCenterOfMassPosition() - playerBall->getCenterOfMassPosition();
pos.setY(0);
pos.normalize();
pos = btCross(pos,btVector3(0,1,0));
pos *= strength;
playerBall->applyTorque(pos);
}
void Physics::rollBack(glm::vec3 camPos,float strength)
{
btVector3 pos = cameraBody->getCenterOfMassPosition() - playerBall->getCenterOfMassPosition();
pos.setY(0);
pos.normalize();
pos = btCross(btVector3(0,1,0),pos);
pos *= strength;
playerBall->applyTorque(pos);
}
void Physics::rollLeft(glm::vec3 camPos,float strength)
{
btVector3 pos = cameraBody->getCenterOfMassPosition() - playerBall->getCenterOfMassPosition();
pos.setY(0);
pos.normalize();
pos *= strength;
playerBall->applyTorque(pos);
}
void Physics::rollRight(glm::vec3 camPos,float strength)
{
btVector3 pos = cameraBody->getCenterOfMassPosition() - playerBall->getCenterOfMassPosition();
pos.setY(0);
pos.normalize();
pos *= strength;
playerBall->applyTorque(-pos);
2014-11-14 13:17:04 +00:00
}
//not used right now
void Physics::addStaticGroundPlane()
{
btCollisionShape* groundShape = new btStaticPlaneShape(btVector3(0, 1, 0), 0);
btDefaultMotionState* groundMotionState = new btDefaultMotionState(btTransform(btQuaternion(0, 0, 0, 1), btVector3(0, 0, 0)));
btRigidBody::btRigidBodyConstructionInfo groundRigidBodyCI(0, groundMotionState, groundShape, btVector3(0, 0, 0));
staticGroundBody = new btRigidBody(groundRigidBodyCI);
world->addRigidBody(staticGroundBody);
}//not needed anymoer, but still good for debugging
void Physics::kill()//delete dynamically allocated memory
2014-11-14 13:17:04 +00:00
{
//btDynamimcWorld*
for(unsigned i = 0; i < bodies.size();i++)
2014-11-14 13:17:04 +00:00
{
world->removeCollisionObject(bodies[i]); //go through the list of bodies in world for each body b, then remove exactly this body b from world
2014-11-14 13:17:04 +00:00
btMotionState* motionState = bodies[i]->getMotionState();
btCollisionShape* shape = bodies[i]->getCollisionShape();
delete shape;
delete motionState;
delete bodies[i];
}
btMotionState* motionState = terrainBody->getMotionState();//delete the rest that are not in the array bodies
btCollisionShape* shape = terrainBody->getCollisionShape();
delete shape;
delete motionState;
delete terrainBody;
motionState = cameraBody->getMotionState();
shape = cameraBody->getCollisionShape();
delete shape;
delete motionState;
delete cameraBody; //note: palyerBall is also in the array bodies so we do not need to clean it up
2014-11-14 13:17:04 +00:00
delete dispatcher; //clean up rest
2014-11-14 13:17:04 +00:00
delete colConfig;
delete solver;
delete broadphase;
delete world;
//feel like a good little programmer because everything is clean
2014-11-14 13:17:04 +00:00
}
2014-11-14 13:17:04 +00:00