From 2d038c8f49144f3f2d45502119bbe56a1d5ef49b Mon Sep 17 00:00:00 2001 From: ejcoumans Date: Fri, 27 Jul 2007 20:23:54 +0000 Subject: [PATCH] added RagdollDemo (needs a bit more work, the initial constraint setup 'flips') --- Demos/Jamfile | 2 + Demos/RagdollDemo/Jamfile | 3 + Demos/RagdollDemo/RagdollDemo.cpp | 381 ++++++++++++++++++++++++++++++ Demos/RagdollDemo/RagdollDemo.h | 43 ++++ 4 files changed, 429 insertions(+) create mode 100644 Demos/RagdollDemo/Jamfile create mode 100644 Demos/RagdollDemo/RagdollDemo.cpp create mode 100644 Demos/RagdollDemo/RagdollDemo.h diff --git a/Demos/Jamfile b/Demos/Jamfile index 91ac95888..7497b0aae 100644 --- a/Demos/Jamfile +++ b/Demos/Jamfile @@ -46,9 +46,11 @@ SubInclude TOP Demos CollisionInterfaceDemo ; SubInclude TOP Demos MovingConcaveDemo ; SubInclude TOP Demos ConcaveDemo ; SubInclude TOP Demos ConstraintDemo ; +SubInclude TOP Demos RagdollDemo ; SubInclude TOP Demos ContinuousConvexCollision ; SubInclude TOP Demos GjkConvexCastDemo ; SubInclude TOP Demos Raytracer ; SubInclude TOP Demos SimplexDemo ; SubInclude TOP Demos DoublePrecisionDemo ; +SubInclude TOP Demos RagdollDemo ; diff --git a/Demos/RagdollDemo/Jamfile b/Demos/RagdollDemo/Jamfile new file mode 100644 index 000000000..76249d3db --- /dev/null +++ b/Demos/RagdollDemo/Jamfile @@ -0,0 +1,3 @@ +SubDir TOP Demos RagdollDemo ; + +BulletDemo RagdollDemo : [ Wildcard *.h *.cpp ] ; diff --git a/Demos/RagdollDemo/RagdollDemo.cpp b/Demos/RagdollDemo/RagdollDemo.cpp new file mode 100644 index 000000000..27698a902 --- /dev/null +++ b/Demos/RagdollDemo/RagdollDemo.cpp @@ -0,0 +1,381 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Ragdoll Demo +Copyright (c) 2007 Starbreeze Studios + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +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. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +Written by: Marten Svanfeldt +*/ + + +#include "btBulletDynamicsCommon.h" +#include "GlutStuff.h" +#include "GL_ShapeDrawer.h" + +#include "RagdollDemo.h" +#define M_PI 3.14159265358979323846 +#define M_PI_2 1.57079632679489661923 +#define M_PI_4 0.785398163397448309616 + + +class RagDoll +{ + enum + { + BODYPART_PELVIS = 0, + BODYPART_SPINE, + BODYPART_HEAD, + + BODYPART_LEFT_UPPER_LEG, + BODYPART_LEFT_LOWER_LEG, + + BODYPART_RIGHT_UPPER_LEG, + BODYPART_RIGHT_LOWER_LEG, + + BODYPART_LEFT_UPPER_ARM, + BODYPART_LEFT_LOWER_ARM, + + BODYPART_RIGHT_UPPER_ARM, + BODYPART_RIGHT_LOWER_ARM, + + BODYPART_COUNT + }; + + enum + { + JOINT_PELVIS_SPINE = 0, + JOINT_SPINE_HEAD, + + JOINT_LEFT_HIP, + JOINT_LEFT_KNEE, + + JOINT_RIGHT_HIP, + JOINT_RIGHT_KNEE, + + JOINT_LEFT_SHOULDER, + JOINT_LEFT_ELBOW, + + JOINT_RIGHT_SHOULDER, + JOINT_RIGHT_ELBOW, + + JOINT_COUNT + }; + + btDynamicsWorld* m_ownerWorld; + btCollisionShape* m_shapes[BODYPART_COUNT]; + btRigidBody* m_bodies[BODYPART_COUNT]; + btTypedConstraint* m_joints[JOINT_COUNT]; + + btRigidBody* localCreateRigidBody (btScalar mass, const btTransform& startTransform, btCollisionShape* shape) + { + bool isDynamic = (mass != 0.f); + + btVector3 localInertia(0,0,0); + if (isDynamic) + shape->calculateLocalInertia(mass,localInertia); + + btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform); + btRigidBody* body = new btRigidBody(mass,myMotionState,shape,localInertia); + + m_ownerWorld->addRigidBody(body); + + return body; + } + +public: + RagDoll (btDynamicsWorld* ownerWorld, const btVector3& positionOffset) + : m_ownerWorld (ownerWorld) + { + // Setup the geometry + m_shapes[BODYPART_PELVIS] = new btCapsuleShape(btScalar(0.15), btScalar(0.20)); + m_shapes[BODYPART_SPINE] = new btCapsuleShape(btScalar(0.15), btScalar(0.28)); + m_shapes[BODYPART_HEAD] = new btCapsuleShape(btScalar(0.10), btScalar(0.05)); + m_shapes[BODYPART_LEFT_UPPER_LEG] = new btCapsuleShape(btScalar(0.07), btScalar(0.45)); + m_shapes[BODYPART_LEFT_LOWER_LEG] = new btCapsuleShape(btScalar(0.05), btScalar(0.37)); + m_shapes[BODYPART_RIGHT_UPPER_LEG] = new btCapsuleShape(btScalar(0.07), btScalar(0.45)); + m_shapes[BODYPART_RIGHT_LOWER_LEG] = new btCapsuleShape(btScalar(0.05), btScalar(0.37)); + m_shapes[BODYPART_LEFT_UPPER_ARM] = new btCapsuleShape(btScalar(0.05), btScalar(0.33)); + m_shapes[BODYPART_LEFT_LOWER_ARM] = new btCapsuleShape(btScalar(0.04), btScalar(0.25)); + m_shapes[BODYPART_RIGHT_UPPER_ARM] = new btCapsuleShape(btScalar(0.05), btScalar(0.33)); + m_shapes[BODYPART_RIGHT_LOWER_ARM] = new btCapsuleShape(btScalar(0.04), btScalar(0.25)); + + // Setup all the rigid bodies + btTransform offset; offset.setIdentity(); + offset.setOrigin(positionOffset); + + btTransform transform; + transform.setIdentity(); + transform.setOrigin(btVector3(btScalar(0.), btScalar(1.), btScalar(0.))); + m_bodies[BODYPART_PELVIS] = localCreateRigidBody(btScalar(1.), offset*transform, m_shapes[BODYPART_PELVIS]); + + transform.setIdentity(); + transform.setOrigin(btVector3(btScalar(0.), btScalar(1.2), btScalar(0.))); + m_bodies[BODYPART_SPINE] = localCreateRigidBody(btScalar(1.), offset*transform, m_shapes[BODYPART_SPINE]); + + transform.setIdentity(); + transform.setOrigin(btVector3(btScalar(0.), btScalar(1.6), btScalar(0.))); + m_bodies[BODYPART_HEAD] = localCreateRigidBody(btScalar(1.), offset*transform, m_shapes[BODYPART_HEAD]); + + transform.setIdentity(); + transform.setOrigin(btVector3(btScalar(-0.18), btScalar(0.65), btScalar(0.))); + m_bodies[BODYPART_LEFT_UPPER_LEG] = localCreateRigidBody(btScalar(1.), offset*transform, m_shapes[BODYPART_LEFT_UPPER_LEG]); + + transform.setIdentity(); + transform.setOrigin(btVector3(btScalar(-0.18), btScalar(0.2), btScalar(0.))); + m_bodies[BODYPART_LEFT_LOWER_LEG] = localCreateRigidBody(btScalar(1.), offset*transform, m_shapes[BODYPART_LEFT_LOWER_LEG]); + + transform.setIdentity(); + transform.setOrigin(btVector3(btScalar(0.18), btScalar(0.65), btScalar(0.))); + m_bodies[BODYPART_RIGHT_UPPER_LEG] = localCreateRigidBody(btScalar(1.), offset*transform, m_shapes[BODYPART_RIGHT_UPPER_LEG]); + + transform.setIdentity(); + transform.setOrigin(btVector3(btScalar(0.18), btScalar(0.2), btScalar(0.))); + m_bodies[BODYPART_RIGHT_LOWER_LEG] = localCreateRigidBody(btScalar(1.), offset*transform, m_shapes[BODYPART_RIGHT_LOWER_LEG]); + + transform.setIdentity(); + transform.setOrigin(btVector3(btScalar(-0.35), btScalar(1.45), btScalar(0.))); + transform.getBasis().setEulerZYX(0,0,M_PI_2); + m_bodies[BODYPART_LEFT_UPPER_ARM] = localCreateRigidBody(btScalar(1.), offset*transform, m_shapes[BODYPART_LEFT_UPPER_ARM]); + + transform.setIdentity(); + transform.setOrigin(btVector3(btScalar(-0.7), btScalar(1.45), btScalar(0.))); + transform.getBasis().setEulerZYX(0,0,M_PI_2); + m_bodies[BODYPART_LEFT_LOWER_ARM] = localCreateRigidBody(btScalar(1.), offset*transform, m_shapes[BODYPART_LEFT_LOWER_ARM]); + + transform.setIdentity(); + transform.setOrigin(btVector3(btScalar(0.35), btScalar(1.45), btScalar(0.))); + transform.getBasis().setEulerZYX(0,0,-M_PI_2); + m_bodies[BODYPART_RIGHT_UPPER_ARM] = localCreateRigidBody(btScalar(1.), offset*transform, m_shapes[BODYPART_RIGHT_UPPER_ARM]); + + transform.setIdentity(); + transform.setOrigin(btVector3(btScalar(0.7), btScalar(1.45), btScalar(0.))); + transform.getBasis().setEulerZYX(0,0,-M_PI_2); + m_bodies[BODYPART_RIGHT_LOWER_ARM] = localCreateRigidBody(btScalar(1.), offset*transform, m_shapes[BODYPART_RIGHT_LOWER_ARM]); + + // Setup some damping on the m_bodies + for (int i = 0; i < BODYPART_COUNT; ++i) + { + m_bodies[i]->setDamping(0.05, 0.85); + m_bodies[i]->setDeactivationTime(0.8); + m_bodies[i]->setSleepingThresholds(1.6, 2.5); + } + + // Now setup the constraints + btHingeConstraint* hingeC; + btConeTwistConstraint* coneC; + + btTransform localA, localB; + + localA.setIdentity(); localB.setIdentity(); + localA.getBasis().setEulerZYX(0,M_PI_2,0); localA.setOrigin(btVector3(btScalar(0.), btScalar(0.15), btScalar(0.))); + localB.getBasis().setEulerZYX(0,M_PI_2,0); localB.setOrigin(btVector3(btScalar(0.), btScalar(-0.15), btScalar(0.))); + hingeC = new btHingeConstraint(*m_bodies[BODYPART_PELVIS], *m_bodies[BODYPART_SPINE], localA, localB); + hingeC->setLimit(btScalar(-M_PI_4), btScalar(M_PI_2)); + m_joints[JOINT_PELVIS_SPINE] = hingeC; + m_ownerWorld->addConstraint(m_joints[JOINT_PELVIS_SPINE], true); + + + localA.setIdentity(); localB.setIdentity(); + localA.getBasis().setEulerZYX(0,0,M_PI_2); localA.setOrigin(btVector3(btScalar(0.), btScalar(0.30), btScalar(0.))); + localB.getBasis().setEulerZYX(0,0,-M_PI_2); localB.setOrigin(btVector3(btScalar(0.), btScalar(-0.14), btScalar(0.))); + coneC = new btConeTwistConstraint(*m_bodies[BODYPART_SPINE], *m_bodies[BODYPART_HEAD], localA, localB); + coneC->setLimit(M_PI_4, M_PI_4, M_PI_2); + m_joints[JOINT_SPINE_HEAD] = coneC; + m_ownerWorld->addConstraint(m_joints[JOINT_SPINE_HEAD], true); + + + localA.setIdentity(); localB.setIdentity(); + localA.getBasis().setEulerZYX(0,0,-M_PI_4*5); localA.setOrigin(btVector3(btScalar(-0.18), btScalar(-0.10), btScalar(0.))); + localB.getBasis().setEulerZYX(0,0,M_PI_4*5); localB.setOrigin(btVector3(btScalar(0.), btScalar(0.225), btScalar(0.))); + coneC = new btConeTwistConstraint(*m_bodies[BODYPART_PELVIS], *m_bodies[BODYPART_LEFT_UPPER_LEG], localA, localB); + coneC->setLimit(M_PI_4, M_PI_4, 0); + m_joints[JOINT_LEFT_HIP] = coneC; + m_ownerWorld->addConstraint(m_joints[JOINT_LEFT_HIP], true); + + localA.setIdentity(); localB.setIdentity(); + localA.getBasis().setEulerZYX(0,M_PI_2,0); localA.setOrigin(btVector3(btScalar(0.), btScalar(-0.225), btScalar(0.))); + localB.getBasis().setEulerZYX(0,M_PI_2,0); localB.setOrigin(btVector3(btScalar(0.), btScalar(0.185), btScalar(0.))); + hingeC = new btHingeConstraint(*m_bodies[BODYPART_LEFT_UPPER_LEG], *m_bodies[BODYPART_LEFT_LOWER_LEG], localA, localB); + hingeC->setLimit(btScalar(0), btScalar(M_PI_2)); + m_joints[JOINT_LEFT_KNEE] = hingeC; + m_ownerWorld->addConstraint(m_joints[JOINT_LEFT_KNEE], true); + + + localA.setIdentity(); localB.setIdentity(); + localA.getBasis().setEulerZYX(0,0,M_PI_4); localA.setOrigin(btVector3(btScalar(0.18), btScalar(-0.10), btScalar(0.))); + localB.getBasis().setEulerZYX(0,0,-M_PI_4); localB.setOrigin(btVector3(btScalar(0.), btScalar(0.225), btScalar(0.))); + coneC = new btConeTwistConstraint(*m_bodies[BODYPART_PELVIS], *m_bodies[BODYPART_RIGHT_UPPER_LEG], localA, localB); + coneC->setLimit(M_PI_4, M_PI_4, 0); + m_joints[JOINT_RIGHT_HIP] = coneC; + m_ownerWorld->addConstraint(m_joints[JOINT_RIGHT_HIP], true); + + localA.setIdentity(); localB.setIdentity(); + localA.getBasis().setEulerZYX(0,M_PI_2,0); localA.setOrigin(btVector3(btScalar(0.), btScalar(-0.225), btScalar(0.))); + localB.getBasis().setEulerZYX(0,M_PI_2,0); localB.setOrigin(btVector3(btScalar(0.), btScalar(0.185), btScalar(0.))); + hingeC = new btHingeConstraint(*m_bodies[BODYPART_RIGHT_UPPER_LEG], *m_bodies[BODYPART_RIGHT_LOWER_LEG], localA, localB); + hingeC->setLimit(btScalar(0), btScalar(M_PI_2)); + m_joints[JOINT_RIGHT_KNEE] = hingeC; + m_ownerWorld->addConstraint(m_joints[JOINT_RIGHT_KNEE], true); + + + localA.setIdentity(); localB.setIdentity(); + localA.getBasis().setEulerZYX(0,0,M_PI); localA.setOrigin(btVector3(btScalar(-0.2), btScalar(0.15), btScalar(0.))); + localB.getBasis().setEulerZYX(0,0,-M_PI_2); localB.setOrigin(btVector3(btScalar(0.), btScalar(-0.18), btScalar(0.))); + coneC = new btConeTwistConstraint(*m_bodies[BODYPART_SPINE], *m_bodies[BODYPART_LEFT_UPPER_ARM], localA, localB); + coneC->setLimit(M_PI_2, M_PI_2, 0); + m_joints[JOINT_LEFT_SHOULDER] = coneC; + m_ownerWorld->addConstraint(m_joints[JOINT_LEFT_SHOULDER], true); + + localA.setIdentity(); localB.setIdentity(); + localA.getBasis().setEulerZYX(0,M_PI_2,0); localA.setOrigin(btVector3(btScalar(0.), btScalar(0.18), btScalar(0.))); + localB.getBasis().setEulerZYX(0,M_PI_2,0); localB.setOrigin(btVector3(btScalar(0.), btScalar(-0.14), btScalar(0.))); + hingeC = new btHingeConstraint(*m_bodies[BODYPART_LEFT_UPPER_ARM], *m_bodies[BODYPART_LEFT_LOWER_ARM], localA, localB); + hingeC->setLimit(btScalar(-M_PI_2), btScalar(0)); + m_joints[JOINT_LEFT_ELBOW] = hingeC; + m_ownerWorld->addConstraint(m_joints[JOINT_LEFT_ELBOW], true); + + + + localA.setIdentity(); localB.setIdentity(); + localA.getBasis().setEulerZYX(0,0,0); localA.setOrigin(btVector3(btScalar(0.2), btScalar(0.15), btScalar(0.))); + localB.getBasis().setEulerZYX(0,0,-M_PI_2); localB.setOrigin(btVector3(btScalar(0.), btScalar(-0.18), btScalar(0.))); + coneC = new btConeTwistConstraint(*m_bodies[BODYPART_SPINE], *m_bodies[BODYPART_RIGHT_UPPER_ARM], localA, localB); + coneC->setLimit(M_PI_2, M_PI_2, 0); + m_joints[JOINT_RIGHT_SHOULDER] = coneC; + m_ownerWorld->addConstraint(m_joints[JOINT_RIGHT_SHOULDER], true); + + localA.setIdentity(); localB.setIdentity(); + localA.getBasis().setEulerZYX(0,M_PI_2,0); localA.setOrigin(btVector3(btScalar(0.), btScalar(0.18), btScalar(0.))); + localB.getBasis().setEulerZYX(0,M_PI_2,0); localB.setOrigin(btVector3(btScalar(0.), btScalar(-0.14), btScalar(0.))); + hingeC = new btHingeConstraint(*m_bodies[BODYPART_RIGHT_UPPER_ARM], *m_bodies[BODYPART_RIGHT_LOWER_ARM], localA, localB); + hingeC->setLimit(btScalar(-M_PI_2), btScalar(0)); + m_joints[JOINT_RIGHT_ELBOW] = hingeC; + m_ownerWorld->addConstraint(m_joints[JOINT_RIGHT_ELBOW], true); + } + + ~RagDoll () + { + // Remove all constraints + for (int i = 0; i < JOINT_COUNT; ++i) + { + m_ownerWorld->removeConstraint(m_joints[i]); + delete m_joints[i]; m_joints[i] = 0; + } + + // Remove all bodies and shapes + for (int i = 0; i < BODYPART_COUNT; ++i) + { + m_ownerWorld->removeRigidBody(m_bodies[i]); + + delete m_bodies[i]->getMotionState(); + + delete m_bodies[i]; m_bodies[i] = 0; + delete m_shapes[i]; m_shapes[i] = 0; + } + } +}; + + + +int main(int argc,char* argv[]) +{ + RagdollDemo demoApp; + + demoApp.initPhysics(); + demoApp.setCameraDistance(btScalar(10.)); + + return glutmain(argc, argv,640,480,"Bullet Physics Demo. http://bullet.sf.net",&demoApp); +} + + +void RagdollDemo::initPhysics() +{ + // Setup the basic world + + btCollisionDispatcher* dispatcher = new btCollisionDispatcher; + + btPoint3 worldAabbMin(-10000,-10000,-10000); + btPoint3 worldAabbMax(10000,10000,10000); + btOverlappingPairCache* overlappingPairCache = new btAxisSweep3 (worldAabbMin, worldAabbMax); + + btConstraintSolver* solver = new btSequentialImpulseConstraintSolver; + + m_dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,overlappingPairCache,solver); + + // Setup a big ground box + { + btCollisionShape* groundShape = new btBoxShape(btVector3(btScalar(200.),btScalar(10.),btScalar(200.))); + btTransform groundTransform; + groundTransform.setIdentity(); + groundTransform.setOrigin(btVector3(0,-10,0)); + localCreateRigidBody(btScalar(0.),groundTransform,groundShape); + } + + // Spawn one ragdoll + spawnRagdoll(); + + clientResetScene(); +} + +void RagdollDemo::spawnRagdoll(bool random) +{ + RagDoll* ragDoll = new RagDoll (m_dynamicsWorld, btVector3 (0,1,0)); + m_ragdolls.push_back(ragDoll); +} + +void RagdollDemo::clientMoveAndDisplay() +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + //simple dynamics world doesn't handle fixed-time-stepping + float ms = m_clock.getTimeMicroseconds(); + m_clock.reset(); + float minFPS = 1000000.f/60.f; + if (ms > minFPS) + ms = minFPS; + + if (m_dynamicsWorld) + m_dynamicsWorld->stepSimulation(ms / 1000000.f); + + renderme(); + + glFlush(); + + glutSwapBuffers(); +} + +void RagdollDemo::displayCallback() +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if (m_dynamicsWorld) + m_dynamicsWorld->updateAabbs(); + + renderme(); + + glFlush(); + glutSwapBuffers(); +} + +void RagdollDemo::keyboardCallback(unsigned char key, int x, int y) +{ + switch (key) + { + case 'e': + spawnRagdoll(true); + break; + default: + DemoApplication::keyboardCallback(key, x, y); + } + + +} diff --git a/Demos/RagdollDemo/RagdollDemo.h b/Demos/RagdollDemo/RagdollDemo.h new file mode 100644 index 000000000..dfc305967 --- /dev/null +++ b/Demos/RagdollDemo/RagdollDemo.h @@ -0,0 +1,43 @@ +/* +Bullet Continuous Collision Detection and Physics Library +RagdollDemo +Copyright (c) 2007 Starbreeze Studios + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +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. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +Written by: Marten Svanfeldt +*/ + +#ifndef RAGDOLLDEMO_H +#define RAGDOLLDEMO_H + +#include "DemoApplication.h" +#include "LinearMath/btAlignedObjectArray.h" + +class RagdollDemo : public DemoApplication +{ + + btAlignedObjectArray m_ragdolls; + +public: + void initPhysics(); + + void spawnRagdoll(bool random = false); + + virtual void clientMoveAndDisplay(); + + virtual void displayCallback(); + + virtual void keyboardCallback(unsigned char key, int x, int y); +}; + + +#endif