diff --git a/Bullet_User_Manual.pdf b/Bullet_User_Manual.pdf index 0fd63fe5b..596241732 100644 Binary files a/Bullet_User_Manual.pdf and b/Bullet_User_Manual.pdf differ diff --git a/Demos/CMakeLists.txt b/Demos/CMakeLists.txt index e016a9cd5..e50595d89 100644 --- a/Demos/CMakeLists.txt +++ b/Demos/CMakeLists.txt @@ -12,7 +12,7 @@ IF (USE_GLUT) SET(SharedDemoSubdirs OpenGL AllBulletDemos ConvexDecompositionDemo CcdPhysicsDemo ConstraintDemo SliderConstraintDemo GenericJointDemo Raytracer - RagdollDemo ForkLiftDemo BasicDemo FractureDemo Box2dDemo BspDemo MovingConcaveDemo VehicleDemo + RagdollDemo ForkLiftDemo BasicDemo VoronoiFractureDemo FractureDemo Box2dDemo BspDemo MovingConcaveDemo VehicleDemo UserCollisionAlgorithm CharacterDemo SoftDemo HeightFieldFluidDemo CollisionInterfaceDemo ConcaveConvexcastDemo SimplexDemo DynamicControlDemo ConvexHullDistance @@ -54,6 +54,7 @@ ELSE (USE_GLUT) SerializeDemo SoftDemo VectorAdd_OpenCL + VoronoiFractureDemo ) ENDIF(WIN32) ENDIF (USE_GLUT) diff --git a/Demos/VoronoiFractureDemo/CMakeLists.txt b/Demos/VoronoiFractureDemo/CMakeLists.txt new file mode 100644 index 000000000..7c06df228 --- /dev/null +++ b/Demos/VoronoiFractureDemo/CMakeLists.txt @@ -0,0 +1,87 @@ +# This is basically the overall name of the project in Visual Studio this is the name of the Solution File + + +# For every executable you have with a main method you should have an add_executable line below. +# For every add executable line you should list every .cpp and .h file you have associated with that executable. + + +# This is the variable for Windows. I use this to define the root of my directory structure. +SET(GLUT_ROOT ${BULLET_PHYSICS_SOURCE_DIR}/Glut) + +# You shouldn't have to modify anything below this line +######################################################## + +INCLUDE_DIRECTORIES( +${BULLET_PHYSICS_SOURCE_DIR}/src ${BULLET_PHYSICS_SOURCE_DIR}/Demos/OpenGL +) + + + +IF (USE_GLUT) + LINK_LIBRARIES( + OpenGLSupport BulletDynamics BulletCollision LinearMath ${GLUT_glut_LIBRARY} ${OPENGL_gl_LIBRARY} ${OPENGL_glu_LIBRARY} + ) + +IF (WIN32) +ADD_EXECUTABLE(AppVoronoiFractureDemo + main.cpp + VoronoiFractureDemo.cpp + VoronoiFractureDemo.h + ${BULLET_PHYSICS_SOURCE_DIR}/msvc/bullet.rc + ) +ELSE() + ADD_EXECUTABLE(AppVoronoiFractureDemo + main.cpp + VoronoiFractureDemo.cpp + VoronoiFractureDemo.h + ) +ENDIF() + + + + + IF (WIN32) + IF (NOT INTERNAL_CREATE_DISTRIBUTABLE_MSVC_PROJECTFILES) + IF (CMAKE_CL_64) + ADD_CUSTOM_COMMAND( + TARGET AppVoronoiFractureDemo + POST_BUILD + COMMAND ${CMAKE_COMMAND} ARGS -E copy_if_different ${BULLET_PHYSICS_SOURCE_DIR}/glut64.dll ${CMAKE_CURRENT_BINARY_DIR} + ) + ELSE(CMAKE_CL_64) + ADD_CUSTOM_COMMAND( + TARGET AppVoronoiFractureDemo + POST_BUILD +# COMMAND copy /Y ${BULLET_PHYSICS_SOURCE_DIR}/GLUT32.DLL ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} ARGS -E copy_if_different ${BULLET_PHYSICS_SOURCE_DIR}/GLUT32.DLL ${CMAKE_CURRENT_BINARY_DIR} + ) + ENDIF(CMAKE_CL_64) + ENDIF (NOT INTERNAL_CREATE_DISTRIBUTABLE_MSVC_PROJECTFILES) + + ENDIF(WIN32) +ELSE (USE_GLUT) + + + + LINK_LIBRARIES( + OpenGLSupport BulletDynamics BulletCollision LinearMath ${OPENGL_gl_LIBRARY} ${OPENGL_glu_LIBRARY} + ) + + + ADD_EXECUTABLE(AppVoronoiFractureDemo + WIN32 + ../OpenGL/Win32AppMain.cpp + Win32VoronoiFractureDemo.cpp + VoronoiFractureDemo.cpp + VoronoiFractureDemo.h + ${BULLET_PHYSICS_SOURCE_DIR}/msvc/bullet.rc + ) + + +ENDIF (USE_GLUT) + +IF (INTERNAL_ADD_POSTFIX_EXECUTABLE_NAMES) + SET_TARGET_PROPERTIES(AppVoronoiFractureDemo PROPERTIES DEBUG_POSTFIX "_Debug") + SET_TARGET_PROPERTIES(AppVoronoiFractureDemo PROPERTIES MINSIZEREL_POSTFIX "_MinsizeRel") + SET_TARGET_PROPERTIES(AppVoronoiFractureDemo PROPERTIES RELWITHDEBINFO_POSTFIX "_RelWithDebugInfo") +ENDIF(INTERNAL_ADD_POSTFIX_EXECUTABLE_NAMES) \ No newline at end of file diff --git a/Demos/VoronoiFractureDemo/Makefile.am b/Demos/VoronoiFractureDemo/Makefile.am new file mode 100644 index 000000000..a73e4fb9e --- /dev/null +++ b/Demos/VoronoiFractureDemo/Makefile.am @@ -0,0 +1,5 @@ +noinst_PROGRAMS=BasicDemo + +BasicDemo_SOURCES=BasicDemo.cpp BasicDemo.h main.cpp +BasicDemo_CXXFLAGS=-I@top_builddir@/src -I@top_builddir@/Demos/OpenGL $(CXXFLAGS) +BasicDemo_LDADD=-L../OpenGL -lbulletopenglsupport -L../../src -lBulletDynamics -lBulletCollision -lLinearMath @opengl_LIBS@ diff --git a/Demos/VoronoiFractureDemo/Makefile.original b/Demos/VoronoiFractureDemo/Makefile.original new file mode 100644 index 000000000..6db49cd8a --- /dev/null +++ b/Demos/VoronoiFractureDemo/Makefile.original @@ -0,0 +1,49 @@ +__ARCH_BITS__ := 32 + +# define macros +RM=rm -f +OUTDIR=. +CELLSDKDIR=/opt/ibm/cell-sdk +ARCHITECTUREFLAG=-m$(__ARCH_BITS__) +USE_CESOF=1 +ifeq "$(__ARCH_BITS__)" "64" + DEFFLAGS= -DUSE_LIBSPE2 -DUSE_ADDR64 -DUSE_PARALLEL_DISPATCHER + GCC=ppu-g++ +else + DEFFLAGS= -DUSE_LIBSPE2 -DUSE_PARALLEL_DISPATCHER + GCC=ppu32-g++ +endif +DEBUGFLAG= +ifeq "$(USE_CESOF)" "1" + CFLAGS= $(ARCHITECTUREFLAG) $(DEBUGFLAG) -DUSE_CESOF -W -Wall -Winline -O3 -mabi=altivec -maltivec -include altivec.h -include stdbool.h -c +else + CFLAGS= $(ARCHITECTUREFLAG) $(DEBUGFLAG) -W -Wall -Winline -O3 -mabi=altivec -maltivec -include altivec.h -include stdbool.h -c +endif +INCLUDEDIR= -I. -I$(CELLSDKDIR)/prototype/sysroot/usr/include -I../../src -I../OpenGL +LFLAGS= $(ARCHITECTUREFLAG) -Wl,-m,elf$(__ARCH_BITS__)ppc +ifeq "$(USE_CESOF)" "1" + LIBRARIES= -lstdc++ -lsupc++ -lgcc -lgcov -lspe2 -lpthread -L../../out/linuxppc/optimize/libs \ + -lbulletmultithreaded -lspu -lbulletdynamics -lbulletcollision -lbulletmath \ + -L$(CELLSDKDIR)/prototype/sysroot/usr/lib$(__ARCH_BITS__) -R$(CELLSDKDIR)/prototype/sysroot/usr/lib +else + LIBRARIES= -lstdc++ -lsupc++ -lgcc -lgcov -lspe2 -lpthread -L../../out/linuxppc/optimize/libs \ + -lbulletmultithreaded -lbulletdynamics -lbulletcollision -lbulletmath \ + -L$(CELLSDKDIR)/prototype/sysroot/usr/lib$(__ARCH_BITS__) -R$(CELLSDKDIR)/prototype/sysroot/usr/lib +endif + +DemoApplication : + $(GCC) $(DEFFLAGS) $(CFLAGS) $(INCLUDEDIR) -o $(OUTDIR)/$@.o ../OpenGL/$@.cpp + + +BasicDemo : DemoApplication + $(GCC) $(DEFFLAGS) $(CFLAGS) $(INCLUDEDIR) -o $(OUTDIR)/$@.o $@.cpp + + + +all : BasicDemo + $(GCC) -o$(OUTDIR)/BasicDemo $(OUTDIR)/BasicDemo.o $(OUTDIR)/DemoApplication.o $(LFLAGS) $(LIBRARIES) + + +clean : + $(RM) $(OUTDIR)/BasicDemo ; $(RM) $(OUTDIR)/BasicDemo.o ; $(RM) $(OUTDIR)/DemoApplication.o + diff --git a/Demos/VoronoiFractureDemo/VoronoiFractureDemo.cpp b/Demos/VoronoiFractureDemo/VoronoiFractureDemo.cpp new file mode 100644 index 000000000..a316477e8 --- /dev/null +++ b/Demos/VoronoiFractureDemo/VoronoiFractureDemo.cpp @@ -0,0 +1,655 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ +/* +Voronoi fracture and shatter code and demo copyright (c) 2011 Alain Ducharme + - Reset scene (press spacebar) to generate new random voronoi shattered cuboids + - Check console for total time required to: compute and mesh all 3D shards, calculate volumes and centers of mass and create rigid bodies + - Modify VORONOIPOINTS define below to change number of potential voronoi shards + - Note that demo's visual cracks between voronoi shards are NOT present in the internally generated voronoi mesh! +*/ + +//Number of random voronoi points to generate for shattering +#define VORONOIPOINTS 100 + +//maximum number of objects (and allow user to shoot additional boxes) +#define MAX_PROXIES (2048) +#define BREAKING_THRESHOLD 2 +#define CONVEX_MARGIN 0.04 + +#include "VoronoiFractureDemo.h" +#include "GlutStuff.h" +///btBulletDynamicsCommon.h is the main Bullet include file, contains most common include files. +#include "btBulletDynamicsCommon.h" + +#include //printf debugging + +#include "GLDebugDrawer.h" +static GLDebugDrawer sDebugDraw; + +void VoronoiFractureDemo::attachFixedConstraints() +{ + btAlignedObjectArray bodies; + + int numManifolds = getDynamicsWorld()->getDispatcher()->getNumManifolds(); + + for (int i=0;igetDispatcher()->getManifoldByIndexInternal(i); + if (!manifold->getNumContacts()) + continue; + + btScalar minDist = 1e30f; + int minIndex = -1; + for (int v=0;vgetNumContacts();v++) + { + if (minDist >manifold->getContactPoint(v).getDistance()) + { + minDist = manifold->getContactPoint(v).getDistance(); + minIndex = v; + } + } + if (minDist>0.) + continue; + + btCollisionObject* colObj0 = (btCollisionObject*)manifold->getBody0(); + btCollisionObject* colObj1 = (btCollisionObject*)manifold->getBody1(); + int tag0 = (colObj0)->getIslandTag(); + int tag1 = (colObj1)->getIslandTag(); + btRigidBody* body0 = btRigidBody::upcast(colObj0); + btRigidBody* body1 = btRigidBody::upcast(colObj1); + if (bodies.findLinearSearch(body0)==bodies.size()) + bodies.push_back(body0); + if (bodies.findLinearSearch(body1)==bodies.size()) + bodies.push_back(body1); + + if (body0 && body1) + { + if (!colObj0->isStaticOrKinematicObject() && !colObj1->isStaticOrKinematicObject()) + { + if (body0->checkCollideWithOverride(body1)) + { + { + btTransform trA,trB; + trA.setIdentity(); + trB.setIdentity(); + btVector3 contactPosWorld = manifold->getContactPoint(minIndex).m_positionWorldOnA; + btTransform globalFrame; + globalFrame.setIdentity(); + globalFrame.setOrigin(contactPosWorld); + + trA = body0->getWorldTransform().inverse()*globalFrame; + trB = body1->getWorldTransform().inverse()*globalFrame; + + btGeneric6DofConstraint* dof6 = new btGeneric6DofConstraint(*body0,*body1,trA,trB,true); + dof6->setOverrideNumSolverIterations(100); + + float totalMass = 1.f/body0->getInvMass() + 1.f/body1->getInvMass(); + + dof6->setBreakingImpulseThreshold(BREAKING_THRESHOLD*totalMass); + + for (int i=0;i<6;i++) + dof6->setLimit(i,0,0); + getDynamicsWorld()->addConstraint(dof6,true); + + } + } + } + } + + } + + for (int i=0;iremoveRigidBody(bodies[i]); + getDynamicsWorld()->addRigidBody(bodies[i]); + } +} + +void VoronoiFractureDemo::keyboardCallback(unsigned char key, int x, int y) +{ + if (key == 'g') + { + attachFixedConstraints(); + }else + { + PlatformDemoApplication::keyboardCallback(key,x,y); + } +} + + +void VoronoiFractureDemo::getVerticesInsidePlanes(const btAlignedObjectArray& planes, btAlignedObjectArray& verticesOut, std::set& planeIndicesOut) +{ + // Based on btGeometryUtil.cpp (Gino van den Bergen / Erwin Coumans) + verticesOut.resize(0); + planeIndicesOut.clear(); + const int numPlanes = planes.size(); + int i, j, k, l; + for (i=0;i btScalar(0.0001)) + { + for (k=j+1;k btScalar(0.0001)) && (n3n1.length2() > btScalar(0.0001) )) + { + btScalar quotient = (N1.dot(n2n3)); + if (btFabs(quotient) > btScalar(0.0001)) + { + btVector3 potentialVertex = (n2n3 * N1[3] + n3n1 * N2[3] + n1n2 * N3[3]) * (btScalar(-1.) / quotient); + for (l=0; l btScalar(0.000001)) + break; + } + if (l == numPlanes) + { + // vertex (three plane intersection) inside all planes + verticesOut.push_back(potentialVertex); + planeIndicesOut.insert(i); + planeIndicesOut.insert(j); + planeIndicesOut.insert(k); + } + } + } + } + } + } + } +} + +static btVector3 curVoronoiPoint; // Here for btAlignedObjectArray.quickSort pointCmp scope + +struct pointCmp +{ + bool operator()(const btVector3& p1, const btVector3& p2) const + { + return ((p1-curVoronoiPoint).length2() < (p2-curVoronoiPoint).length2()); + } +}; + +void VoronoiFractureDemo::voronoiBBShatter(const btAlignedObjectArray& points, const btVector3& bbmin, const btVector3& bbmax, const btQuaternion& bbq, const btVector3& bbt, btScalar matDensity) { + // points define voronoi cells in world space (avoid duplicates) + // bbmin & bbmax = bounding box min and max in local space + // bbq & bbt = bounding box quaternion rotation and translation + // matDensity = Material density for voronoi shard mass calculation + btVector3 bbvx = quatRotate(bbq, btVector3(1.0, 0.0, 0.0)); + btVector3 bbvy = quatRotate(bbq, btVector3(0.0, 1.0, 0.0)); + btVector3 bbvz = quatRotate(bbq, btVector3(0.0, 0.0, 1.0)); + btQuaternion bbiq = bbq.inverse(); + btConvexHullComputer* convexHC = new btConvexHullComputer(); + btAlignedObjectArray vertices; + btVector3 rbb, nrbb; + btScalar nlength, maxDistance, distance; + btAlignedObjectArray sortedVoronoiPoints; + sortedVoronoiPoints.copyFromArray(points); + btVector3 normal, plane; + btAlignedObjectArray planes; + std::set planeIndices; + std::set::iterator planeIndicesIter; + int numplaneIndices; + int cellnum = 0; + int i, j, k; + + int numpoints = points.size(); + for (i=0; i < numpoints ;i++) { + curVoronoiPoint = points[i]; + btVector3 icp = quatRotate(bbiq, curVoronoiPoint - bbt); + rbb = icp - bbmax; + nrbb = bbmin - icp; + planes.resize(6); + planes[0] = bbvx; planes[0][3] = rbb.x(); + planes[1] = bbvy; planes[1][3] = rbb.y(); + planes[2] = bbvz; planes[2][3] = rbb.z(); + planes[3] = -bbvx; planes[3][3] = nrbb.x(); + planes[4] = -bbvy; planes[4][3] = nrbb.y(); + planes[5] = -bbvz; planes[5][3] = nrbb.z(); + maxDistance = SIMD_INFINITY; + sortedVoronoiPoints.quickSort(pointCmp()); + for (j=1; j < numpoints; j++) { + normal = sortedVoronoiPoints[j] - curVoronoiPoint; + nlength = normal.length(); + if (nlength > maxDistance) + break; + plane = normal.normalized(); + plane[3] = -nlength / btScalar(2.); + planes.push_back(plane); + getVerticesInsidePlanes(planes, vertices, planeIndices); + if (vertices.size() == 0) + break; + numplaneIndices = planeIndices.size(); + if (numplaneIndices != planes.size()) { + planeIndicesIter = planeIndices.begin(); + for (k=0; k < numplaneIndices; k++) { + if (k != *planeIndicesIter) + planes[k] = planes[*planeIndicesIter]; + planeIndicesIter++; + } + planes.resize(numplaneIndices); + } + maxDistance = vertices[0].length(); + for (k=1; k < vertices.size(); k++) { + distance = vertices[k].length(); + if (maxDistance < distance) + maxDistance = distance; + } + maxDistance *= btScalar(2.); + } + if (vertices.size() == 0) + continue; + + // Clean-up voronoi convex shard vertices and generate edges & faces + convexHC->compute(&vertices[0].getX(), sizeof(btVector3), vertices.size(),CONVEX_MARGIN,0.0); + + // At this point we have a complete 3D voronoi shard mesh contained in convexHC + + // Calculate volume and center of mass (Stan Melax volume integration) + int numFaces = convexHC->faces.size(); + int v0, v1, v2; // Triangle vertices + btScalar volume = btScalar(0.); + btVector3 com(0., 0., 0.); + for (j=0; j < numFaces; j++) { + const btConvexHullComputer::Edge* edge = &convexHC->edges[convexHC->faces[j]]; + v0 = edge->getSourceVertex(); + v1 = edge->getTargetVertex(); + edge = edge->getNextEdgeOfFace(); + v2 = edge->getTargetVertex(); + while (v2 != v0) { + // Counter-clockwise triangulated voronoi shard mesh faces (v0-v1-v2) and edges here... + btScalar vol = convexHC->vertices[v0].triple(convexHC->vertices[v1], convexHC->vertices[v2]); + volume += vol; + com += vol * (convexHC->vertices[v0] + convexHC->vertices[v1] + convexHC->vertices[v2]); + edge = edge->getNextEdgeOfFace(); + v1 = v2; + v2 = edge->getTargetVertex(); + } + } + com /= volume * btScalar(4.); + volume /= btScalar(6.); + + // Shift all vertices relative to center of mass + int numVerts = convexHC->vertices.size(); + for (j=0; j < numVerts; j++) + { + convexHC->vertices[j] -= com; + } + + // Note: + // At this point convex hulls contained in convexHC should be accurate (line up flush with other pieces, no cracks), + // ...however Bullet Physics rigid bodies demo visualizations appear to produce some visible cracks. + // Use the mesh in convexHC for visual display or to perform boolean operations with. + + // Create Bullet Physics rigid body shards + btCollisionShape* shardShape = new btConvexHullShape(&(convexHC->vertices[0].getX()), convexHC->vertices.size()); + shardShape->setMargin(CONVEX_MARGIN); // for this demo; note convexHC has optional margin parameter for this + m_collisionShapes.push_back(shardShape); + btTransform shardTransform; + shardTransform.setIdentity(); + shardTransform.setOrigin(curVoronoiPoint + com); // Shard's adjusted location + btDefaultMotionState* shardMotionState = new btDefaultMotionState(shardTransform); + btScalar shardMass(volume * matDensity); + btVector3 shardInertia(0.,0.,0.); + shardShape->calculateLocalInertia(shardMass, shardInertia); + btRigidBody::btRigidBodyConstructionInfo shardRBInfo(shardMass, shardMotionState, shardShape, shardInertia); + btRigidBody* shardBody = new btRigidBody(shardRBInfo); + m_dynamicsWorld->addRigidBody(shardBody); + + cellnum ++; + + } + printf("Generated %d voronoi btRigidBody shards\n", cellnum); +} + +void VoronoiFractureDemo::voronoiConvexHullShatter(const btAlignedObjectArray& points, const btAlignedObjectArray& verts, const btQuaternion& bbq, const btVector3& bbt, btScalar matDensity) { + // points define voronoi cells in world space (avoid duplicates) + // verts = source (convex hull) mesh vertices in local space + // bbq & bbt = source (convex hull) mesh quaternion rotation and translation + // matDensity = Material density for voronoi shard mass calculation + btConvexHullComputer* convexHC = new btConvexHullComputer(); + btAlignedObjectArray vertices, chverts; + btVector3 rbb, nrbb; + btScalar nlength, maxDistance, distance; + btAlignedObjectArray sortedVoronoiPoints; + sortedVoronoiPoints.copyFromArray(points); + btVector3 normal, plane; + btAlignedObjectArray planes, convexPlanes; + std::set planeIndices; + std::set::iterator planeIndicesIter; + int numplaneIndices; + int cellnum = 0; + int i, j, k; + + // Convert verts to world space and get convexPlanes + int numverts = verts.size(); + chverts.resize(verts.size()); + for (i=0; i < numverts ;i++) { + chverts[i] = quatRotate(bbq, verts[i]) + bbt; + } + //btGeometryUtil::getPlaneEquationsFromVertices(chverts, convexPlanes); + // Using convexHullComputer faster than getPlaneEquationsFromVertices for large meshes... + convexHC->compute(&chverts[0].getX(), sizeof(btVector3), numverts, 0.0, 0.0); + int numFaces = convexHC->faces.size(); + int v0, v1, v2; // vertices + for (i=0; i < numFaces; i++) { + const btConvexHullComputer::Edge* edge = &convexHC->edges[convexHC->faces[i]]; + v0 = edge->getSourceVertex(); + v1 = edge->getTargetVertex(); + edge = edge->getNextEdgeOfFace(); + v2 = edge->getTargetVertex(); + plane = (convexHC->vertices[v1]-convexHC->vertices[v0]).cross(convexHC->vertices[v2]-convexHC->vertices[v0]).normalize(); + plane[3] = -plane.dot(convexHC->vertices[v0]); + convexPlanes.push_back(plane); + } + const int numconvexPlanes = convexPlanes.size(); + + int numpoints = points.size(); + for (i=0; i < numpoints ;i++) { + curVoronoiPoint = points[i]; + planes.copyFromArray(convexPlanes); + for (j=0; j < numconvexPlanes ;j++) { + planes[j][3] += planes[j].dot(curVoronoiPoint); + } + maxDistance = SIMD_INFINITY; + sortedVoronoiPoints.quickSort(pointCmp()); + for (j=1; j < numpoints; j++) { + normal = sortedVoronoiPoints[j] - curVoronoiPoint; + nlength = normal.length(); + if (nlength > maxDistance) + break; + plane = normal.normalized(); + plane[3] = -nlength / btScalar(2.); + planes.push_back(plane); + getVerticesInsidePlanes(planes, vertices, planeIndices); + if (vertices.size() == 0) + break; + numplaneIndices = planeIndices.size(); + if (numplaneIndices != planes.size()) { + planeIndicesIter = planeIndices.begin(); + for (k=0; k < numplaneIndices; k++) { + if (k != *planeIndicesIter) + planes[k] = planes[*planeIndicesIter]; + planeIndicesIter++; + } + planes.resize(numplaneIndices); + } + maxDistance = vertices[0].length(); + for (k=1; k < vertices.size(); k++) { + distance = vertices[k].length(); + if (maxDistance < distance) + maxDistance = distance; + } + maxDistance *= btScalar(2.); + } + if (vertices.size() == 0) + continue; + + // Clean-up voronoi convex shard vertices and generate edges & faces + convexHC->compute(&vertices[0].getX(), sizeof(btVector3), vertices.size(),0.0,0.0); + + // At this point we have a complete 3D voronoi shard mesh contained in convexHC + + // Calculate volume and center of mass (Stan Melax volume integration) + numFaces = convexHC->faces.size(); + btScalar volume = btScalar(0.); + btVector3 com(0., 0., 0.); + for (j=0; j < numFaces; j++) { + const btConvexHullComputer::Edge* edge = &convexHC->edges[convexHC->faces[j]]; + v0 = edge->getSourceVertex(); + v1 = edge->getTargetVertex(); + edge = edge->getNextEdgeOfFace(); + v2 = edge->getTargetVertex(); + while (v2 != v0) { + // Counter-clockwise triangulated voronoi shard mesh faces (v0-v1-v2) and edges here... + btScalar vol = convexHC->vertices[v0].triple(convexHC->vertices[v1], convexHC->vertices[v2]); + volume += vol; + com += vol * (convexHC->vertices[v0] + convexHC->vertices[v1] + convexHC->vertices[v2]); + edge = edge->getNextEdgeOfFace(); + v1 = v2; + v2 = edge->getTargetVertex(); + } + } + com /= volume * btScalar(4.); + volume /= btScalar(6.); + + // Shift all vertices relative to center of mass + int numVerts = convexHC->vertices.size(); + for (j=0; j < numVerts; j++) + { + convexHC->vertices[j] -= com; + } + + // Note: + // At this point convex hulls contained in convexHC should be accurate (line up flush with other pieces, no cracks), + // ...however Bullet Physics rigid bodies demo visualizations appear to produce some visible cracks. + // Use the mesh in convexHC for visual display or to perform boolean operations with. + + // Create Bullet Physics rigid body shards + btCollisionShape* shardShape = new btConvexHullShape(&(convexHC->vertices[0].getX()), convexHC->vertices.size()); + shardShape->setMargin(CONVEX_MARGIN); // for this demo; note convexHC has optional margin parameter for this + m_collisionShapes.push_back(shardShape); + btTransform shardTransform; + shardTransform.setIdentity(); + shardTransform.setOrigin(curVoronoiPoint + com); // Shard's adjusted location + btDefaultMotionState* shardMotionState = new btDefaultMotionState(shardTransform); + btScalar shardMass(volume * matDensity); + btVector3 shardInertia(0.,0.,0.); + shardShape->calculateLocalInertia(shardMass, shardInertia); + btRigidBody::btRigidBodyConstructionInfo shardRBInfo(shardMass, shardMotionState, shardShape, shardInertia); + btRigidBody* shardBody = new btRigidBody(shardRBInfo); + m_dynamicsWorld->addRigidBody(shardBody); + + cellnum ++; + + } + printf("Generated %d voronoi btRigidBody shards\n", cellnum); +} + +void VoronoiFractureDemo::clientMoveAndDisplay() +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + //simple dynamics world doesn't handle fixed-time-stepping + float ms = getDeltaTimeMicroseconds(); + + ///step the simulation + if (m_dynamicsWorld) + { + m_dynamicsWorld->stepSimulation(ms / 1000000.f); + //optional but useful: debug drawing + m_dynamicsWorld->debugDrawWorld(); + } + + renderme(); + + glFlush(); + + swapBuffers(); +} + + +void VoronoiFractureDemo::displayCallback(void) { + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + renderme(); + + //optional but useful: debug drawing to detect problems + if (m_dynamicsWorld) + m_dynamicsWorld->debugDrawWorld(); + + glFlush(); + swapBuffers(); +} + + +void VoronoiFractureDemo::initPhysics() +{ + setTexturing(true); + setShadows(true); + + setCameraDistance(btScalar(20.)); + + ///collision configuration contains default setup for memory, collision setup + m_collisionConfiguration = new btDefaultCollisionConfiguration(); + //m_collisionConfiguration->setConvexConvexMultipointIterations(); + + ///use the default collision dispatcher. For parallel processing you can use a diffent dispatcher (see Extras/BulletMultiThreaded) + m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration); + + m_broadphase = new btDbvtBroadphase(); + + ///the default constraint solver. For parallel processing you can use a different solver (see Extras/BulletMultiThreaded) + btSequentialImpulseConstraintSolver* sol = new btSequentialImpulseConstraintSolver; + m_solver = sol; + + m_dynamicsWorld = new btDiscreteDynamicsWorld(m_dispatcher,m_broadphase,m_solver,m_collisionConfiguration); + m_dynamicsWorld->getSolverInfo().m_splitImpulse = true; + m_dynamicsWorld->setDebugDrawer(&sDebugDraw); + + m_dynamicsWorld->setGravity(btVector3(0,-10,0)); + + ///create a few basic rigid bodies + btCollisionShape* groundShape = new btBoxShape(btVector3(btScalar(50.),btScalar(50.),btScalar(50.))); +// btCollisionShape* groundShape = new btStaticPlaneShape(btVector3(0,1,0),50); + + m_collisionShapes.push_back(groundShape); + + btTransform groundTransform; + groundTransform.setIdentity(); + groundTransform.setOrigin(btVector3(0,-50,0)); + + //We can also use DemoApplication::localCreateRigidBody, but for clarity it is provided here: + { + btScalar mass(0.); + + //rigidbody is dynamic if and only if mass is non zero, otherwise static + bool isDynamic = (mass != 0.f); + + btVector3 localInertia(0,0,0); + if (isDynamic) + groundShape->calculateLocalInertia(mass,localInertia); + + //using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects + btDefaultMotionState* myMotionState = new btDefaultMotionState(groundTransform); + btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,myMotionState,groundShape,localInertia); + btRigidBody* body = new btRigidBody(rbInfo); + + //add the body to the dynamics world + m_dynamicsWorld->addRigidBody(body); + } + + // ==> Voronoi Shatter Basic Demo: Random Cuboid + + // Random size cuboid (defined by bounding box max and min) + btVector3 bbmax(btScalar(rand() / btScalar(RAND_MAX)) * 6. +0.5, btScalar(rand() / btScalar(RAND_MAX)) * 6. +0.5, btScalar(rand() / btScalar(RAND_MAX)) * 6. +0.5); + btVector3 bbmin = -bbmax; + // Place it 10 units above ground + btVector3 bbt(0,10,0); + // Use an arbitrary material density for shards (should be consitent/relative with/to rest of RBs in world) + btScalar matDensity = 1; + // Using random rotation + btQuaternion bbq(btScalar(rand() / btScalar(RAND_MAX)) * 2. -1.,btScalar(rand() / btScalar(RAND_MAX)) * 2. -1.,btScalar(rand() / btScalar(RAND_MAX)) * 2. -1.,btScalar(rand() / btScalar(RAND_MAX)) * 2. -1.); + bbq.normalize(); + // Generate random points for voronoi cells + btAlignedObjectArray points; + btVector3 point; + btVector3 diff = bbmax - bbmin; + for (int i=0; i < VORONOIPOINTS; i++) { + // Place points within box area (points are in world coordinates) + point = quatRotate(bbq, btVector3(btScalar(rand() / btScalar(RAND_MAX)) * diff.x() -diff.x()/2., btScalar(rand() / btScalar(RAND_MAX)) * diff.y() -diff.y()/2., btScalar(rand() / btScalar(RAND_MAX)) * diff.z() -diff.z()/2.)) + bbt; + points.push_back(point); + } + m_perfmTimer.reset(); + voronoiBBShatter(points, bbmin, bbmax, bbq, bbt, matDensity); + printf("Total Time: %f seconds\n", m_perfmTimer.getTimeMilliseconds()/1000.); + + for (int i=m_dynamicsWorld->getNumCollisionObjects()-1; i>=0 ;i--) + { + btCollisionObject* obj = m_dynamicsWorld->getCollisionObjectArray()[i]; + obj->getCollisionShape()->setMargin(CONVEX_MARGIN+0.01); + } + m_dynamicsWorld->performDiscreteCollisionDetection(); + + for (int i=m_dynamicsWorld->getNumCollisionObjects()-1; i>=0 ;i--) + { + btCollisionObject* obj = m_dynamicsWorld->getCollisionObjectArray()[i]; + obj->getCollisionShape()->setMargin(CONVEX_MARGIN); + } + + attachFixedConstraints(); + +} +void VoronoiFractureDemo::clientResetScene() +{ + exitPhysics(); + initPhysics(); +} + + +void VoronoiFractureDemo::exitPhysics() +{ + + //cleanup in the reverse order of creation/initialization + + int i; + //remove all constraints + for (i=m_dynamicsWorld->getNumConstraints()-1;i>=0;i--) + { + btTypedConstraint* constraint = m_dynamicsWorld->getConstraint(i); + m_dynamicsWorld->removeConstraint(constraint); + delete constraint; + } + + //remove the rigidbodies from the dynamics world and delete them + + for (i=m_dynamicsWorld->getNumCollisionObjects()-1; i>=0 ;i--) + { + btCollisionObject* obj = m_dynamicsWorld->getCollisionObjectArray()[i]; + btRigidBody* body = btRigidBody::upcast(obj); + if (body && body->getMotionState()) + { + delete body->getMotionState(); + } + m_dynamicsWorld->removeCollisionObject( obj ); + delete obj; + } + + //delete collision shapes + for (int j=0;j +#include + +class btBroadphaseInterface; +class btCollisionShape; +class btOverlappingPairCache; +class btCollisionDispatcher; +class btConstraintSolver; +struct btCollisionAlgorithmCreateFunc; +class btDefaultCollisionConfiguration; + +///VoronoiFractureDemo is good starting point for learning the code base and porting. + +class VoronoiFractureDemo : public PlatformDemoApplication +{ + //keep the collision shapes, for deletion/cleanup + btAlignedObjectArray m_collisionShapes; + + btBroadphaseInterface* m_broadphase; + + btCollisionDispatcher* m_dispatcher; + + btConstraintSolver* m_solver; + + btDefaultCollisionConfiguration* m_collisionConfiguration; + + btClock m_perfmTimer; + + public: + + VoronoiFractureDemo() + { + srand((unsigned)time(NULL)); // Seed it... + } + virtual ~VoronoiFractureDemo() + { + exitPhysics(); + } + void initPhysics(); + + void exitPhysics(); + + void getVerticesInsidePlanes(const btAlignedObjectArray& planes, btAlignedObjectArray& verticesOut, std::set& planeIndicesOut); + void voronoiBBShatter(const btAlignedObjectArray& points, const btVector3& bbmin, const btVector3& bbmax, const btQuaternion& bbq, const btVector3& bbt, btScalar matDensity); + void voronoiConvexHullShatter(const btAlignedObjectArray& points, const btAlignedObjectArray& verts, const btQuaternion& bbq, const btVector3& bbt, btScalar matDensity); + + virtual void clientMoveAndDisplay(); + + virtual void displayCallback(); + virtual void clientResetScene(); + + virtual void keyboardCallback(unsigned char key, int x, int y); + + void attachFixedConstraints(); + + + static DemoApplication* Create() + { + VoronoiFractureDemo* demo = new VoronoiFractureDemo; + demo->myinit(); + demo->initPhysics(); + return demo; + } + + +}; + +#endif //BASIC_DEMO_H + diff --git a/Demos/VoronoiFractureDemo/Win32VoronoiFractureDemo.cpp b/Demos/VoronoiFractureDemo/Win32VoronoiFractureDemo.cpp new file mode 100644 index 000000000..a55fb67eb --- /dev/null +++ b/Demos/VoronoiFractureDemo/Win32VoronoiFractureDemo.cpp @@ -0,0 +1,25 @@ +#ifdef _WINDOWS +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.org + +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. +*/ + +#include "BasicDemo.h" + +///The 'createDemo' function is called from Bullet/Demos/OpenGL/Win32AppMain.cpp to instantiate this particular demo +DemoApplication* createDemo() +{ + return new BasicDemo(); +} + +#endif diff --git a/Demos/VoronoiFractureDemo/main.cpp b/Demos/VoronoiFractureDemo/main.cpp new file mode 100644 index 000000000..e39112801 --- /dev/null +++ b/Demos/VoronoiFractureDemo/main.cpp @@ -0,0 +1,51 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ + +#include "VoronoiFractureDemo.h" +#include "GlutStuff.h" +#include "btBulletDynamicsCommon.h" +#include "LinearMath/btHashMap.h" + + + +static bool cmp(const int& p1, const int& p2) { + return p1 bla; + + bla.push_back(8); + bla.push_back(6); + bla.push_back(6); + bla.push_back(4); + bla.push_back(9); + bla.quickSort(cmp); + + VoronoiFractureDemo ccdDemo; + ccdDemo.initPhysics(); + +#ifdef CHECK_MEMORY_LEAKS + ccdDemo.exitPhysics(); +#else + return glutmain(argc, argv,1024,600,"Bullet Physics Demo. http://bulletphysics.org",&ccdDemo); +#endif + + //default glut doesn't return from mainloop + return 0; +} + diff --git a/Demos/premake4.lua b/Demos/premake4.lua index 7f40dc350..4900db675 100644 --- a/Demos/premake4.lua +++ b/Demos/premake4.lua @@ -75,7 +75,8 @@ end "SliderConstraintDemo", "TerrainDemo", "UserCollisionAlgorithm", - "VehicleDemo" + "VehicleDemo", + "VoronoiFractureDemo" } -- the following demos require custom include or link settings diff --git a/UnitTests/BulletUnitTests/TestLinearMath.h b/UnitTests/BulletUnitTests/TestLinearMath.h index 43a6e6e4d..cf88fff79 100644 --- a/UnitTests/BulletUnitTests/TestLinearMath.h +++ b/UnitTests/BulletUnitTests/TestLinearMath.h @@ -55,6 +55,7 @@ static btVector3 sPenetrationDirections[TEST_NUM_UNITSPHERE_POINTS] = }; + // --------------------------------------------------------------------------- class TestLinearMath : public CppUnit::TestFixture @@ -103,6 +104,43 @@ public: CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.0, vec.length2(), 1e-5 ); } + + void testQuicksort() + { + int tests = 0; + int numElems = 100; + btAlignedObjectArray m_unsortedIntegers; + m_unsortedIntegers.resize(numElems); + for (int i=0;igetRigidBodyA(); - const btRigidBody* colObj1 = &constraint->getRigidBodyB(); - - if (((colObj0) && (!(colObj0)->isStaticOrKinematicObject())) && - ((colObj1) && (!(colObj1)->isStaticOrKinematicObject()))) + if (constraint->isEnabled()) { - if (colObj0->isActive() || colObj1->isActive()) - { + const btRigidBody* colObj0 = &constraint->getRigidBodyA(); + const btRigidBody* colObj1 = &constraint->getRigidBodyB(); - getSimulationIslandManager()->getUnionFind().unite((colObj0)->getIslandTag(), - (colObj1)->getIslandTag()); + if (((colObj0) && (!(colObj0)->isStaticOrKinematicObject())) && + ((colObj1) && (!(colObj1)->isStaticOrKinematicObject()))) + { + if (colObj0->isActive() || colObj1->isActive()) + { + + getSimulationIslandManager()->getUnionFind().unite((colObj0)->getIslandTag(), + (colObj1)->getIslandTag()); + } } } } diff --git a/src/BulletDynamics/Dynamics/btRigidBody.cpp b/src/BulletDynamics/Dynamics/btRigidBody.cpp index f0cfd687b..911b50723 100644 --- a/src/BulletDynamics/Dynamics/btRigidBody.cpp +++ b/src/BulletDynamics/Dynamics/btRigidBody.cpp @@ -309,8 +309,9 @@ bool btRigidBody::checkCollideWithOverride(btCollisionObject* co) for (int i = 0; i < m_constraintRefs.size(); ++i) { btTypedConstraint* c = m_constraintRefs[i]; - if (&c->getRigidBodyA() == otherRb || &c->getRigidBodyB() == otherRb) - return false; + if (c->isEnabled()) + if (&c->getRigidBodyA() == otherRb || &c->getRigidBodyB() == otherRb) + return false; } return true; diff --git a/src/LinearMath/btAlignedObjectArray.h b/src/LinearMath/btAlignedObjectArray.h index ab032c13b..0179fd071 100644 --- a/src/LinearMath/btAlignedObjectArray.h +++ b/src/LinearMath/btAlignedObjectArray.h @@ -313,33 +313,46 @@ protected: } }; + template + int partition (const L& CompareFunc, int lo, int hi, int pivotIndex) + { + if (pivotIndex != lo) + swap (lo, pivotIndex); + + pivotIndex = lo; + lo++; + while (lo <= hi) + { + //if (m_data[low] <= m_data[pivotIndex]) -> we don't have a <= so we use !(b void quickSortInternal(const L& CompareFunc,int lo, int hi) { - // lo is the lower index, hi is the upper index - // of the region of array a that is to be sorted - int i=lo, j=hi; - T x=m_data[(lo+hi)/2]; - - // partition - do - { - while (CompareFunc(m_data[i],x)) - i++; - while (CompareFunc(x,m_data[j])) - j--; - if (i<=j) - { - swap(i,j); - i++; j--; - } - } while (i<=j); - - // recursion - if (lo=hi) + return; + int pivotIndex = (lo+hi)/2; + pivotIndex = partition(CompareFunc,lo,hi,pivotIndex); + + // recursion, exclude the pivot + if (lo