From 8f17a3a819400b548033fc0a34c89086cb734aee Mon Sep 17 00:00:00 2001 From: cirdan Date: Wed, 16 Jan 2008 11:45:17 +0000 Subject: [PATCH] initial commit --- Makefile | 16 + Makefile.common | 41 + Versions.txt | 11 + bullet/AUTHORS | 22 + bullet/BulletLicense.txt | 17 + bullet/ChangeLog.txt | 357 + bullet/LICENSE | 19 + bullet/Makefile | 42 + bullet/NEWS | 4 + bullet/README | 7 + bullet/VERSION | 3 + .../BroadphaseCollision/btAxisSweep3.cpp | 680 + .../BroadphaseCollision/btAxisSweep3.h | 153 + .../btBroadphaseInterface.h | 46 + .../BroadphaseCollision/btBroadphaseProxy.cpp | 17 + .../BroadphaseCollision/btBroadphaseProxy.h | 217 + .../btCollisionAlgorithm.cpp | 23 + .../btCollisionAlgorithm.h | 77 + .../BroadphaseCollision/btDispatcher.cpp | 22 + .../BroadphaseCollision/btDispatcher.h | 93 + .../btMultiSapBroadphase.cpp | 186 + .../btMultiSapBroadphase.h | 113 + .../btOverlappingPairCache.cpp | 199 + .../btOverlappingPairCache.h | 129 + .../btSimpleBroadphase.cpp | 314 + .../BroadphaseCollision/btSimpleBroadphase.h | 107 + .../SphereTriangleDetector.cpp | 200 + .../SphereTriangleDetector.h | 49 + .../CollisionDispatch/btCollisionCreateFunc.h | 46 + .../btCollisionDispatcher.cpp | 367 + .../CollisionDispatch/btCollisionDispatcher.h | 135 + .../CollisionDispatch/btCollisionObject.cpp | 61 + .../CollisionDispatch/btCollisionObject.h | 346 + .../CollisionDispatch/btCollisionWorld.cpp | 367 + .../CollisionDispatch/btCollisionWorld.h | 262 + .../btCompoundCollisionAlgorithm.cpp | 140 + .../btCompoundCollisionAlgorithm.h | 64 + .../btConvexConcaveCollisionAlgorithm.cpp | 312 + .../btConvexConcaveCollisionAlgorithm.h | 111 + .../btConvexConvexAlgorithm.cpp | 254 + .../btConvexConvexAlgorithm.h | 76 + .../btEmptyCollisionAlgorithm.cpp | 34 + .../btEmptyCollisionAlgorithm.h | 48 + .../CollisionDispatch/btManifoldResult.cpp | 109 + .../CollisionDispatch/btManifoldResult.h | 77 + .../btSimulationIslandManager.cpp | 357 + .../btSimulationIslandManager.h | 61 + .../btSphereBoxCollisionAlgorithm.cpp | 249 + .../btSphereBoxCollisionAlgorithm.h | 64 + .../btSphereSphereCollisionAlgorithm.cpp | 85 + .../btSphereSphereCollisionAlgorithm.h | 56 + .../btSphereTriangleCollisionAlgorithm.cpp | 76 + .../btSphereTriangleCollisionAlgorithm.h | 59 + .../CollisionDispatch/btUnionFind.cpp | 83 + .../CollisionDispatch/btUnionFind.h | 124 + .../CollisionShapes/btBoxShape.cpp | 57 + .../CollisionShapes/btBoxShape.h | 290 + .../btBvhTriangleMeshShape.cpp | 173 + .../CollisionShapes/btBvhTriangleMeshShape.h | 75 + .../CollisionShapes/btCapsuleShape.cpp | 146 + .../CollisionShapes/btCapsuleShape.h | 60 + .../CollisionShapes/btCollisionMargin.h | 26 + .../CollisionShapes/btCollisionShape.cpp | 85 + .../CollisionShapes/btCollisionShape.h | 94 + .../CollisionShapes/btCompoundShape.cpp | 107 + .../CollisionShapes/btCompoundShape.h | 132 + .../CollisionShapes/btConcaveShape.cpp | 28 + .../CollisionShapes/btConcaveShape.h | 50 + .../CollisionShapes/btConeShape.cpp | 133 + .../CollisionShapes/btConeShape.h | 103 + .../CollisionShapes/btConvexHullShape.cpp | 179 + .../CollisionShapes/btConvexHullShape.h | 76 + .../CollisionShapes/btConvexInternalShape.cpp | 77 + .../CollisionShapes/btConvexInternalShape.h | 99 + .../CollisionShapes/btConvexShape.cpp | 18 + .../CollisionShapes/btConvexShape.h | 74 + .../btConvexTriangleMeshShape.cpp | 206 + .../btConvexTriangleMeshShape.h | 51 + .../CollisionShapes/btCylinderShape.cpp | 206 + .../CollisionShapes/btCylinderShape.h | 138 + .../CollisionShapes/btEmptyShape.cpp | 49 + .../CollisionShapes/btEmptyShape.h | 70 + .../btHeightfieldTerrainShape.cpp | 339 + .../btHeightfieldTerrainShape.h | 88 + .../CollisionShapes/btMinkowskiSumShape.cpp | 57 + .../CollisionShapes/btMinkowskiSumShape.h | 62 + .../CollisionShapes/btMultiSphereShape.cpp | 148 + .../CollisionShapes/btMultiSphereShape.h | 74 + .../CollisionShapes/btOptimizedBvh.cpp | 845 ++ .../CollisionShapes/btOptimizedBvh.h | 330 + .../btPolyhedralConvexShape.cpp | 148 + .../CollisionShapes/btPolyhedralConvexShape.h | 93 + .../CollisionShapes/btSphereShape.cpp | 77 + .../CollisionShapes/btSphereShape.h | 63 + .../CollisionShapes/btStaticPlaneShape.cpp | 105 + .../CollisionShapes/btStaticPlaneShape.h | 61 + .../btStridingMeshInterface.cpp | 125 + .../CollisionShapes/btStridingMeshInterface.h | 89 + .../CollisionShapes/btTetrahedronShape.cpp | 195 + .../CollisionShapes/btTetrahedronShape.h | 75 + .../CollisionShapes/btTriangleBuffer.cpp | 42 + .../CollisionShapes/btTriangleBuffer.h | 61 + .../CollisionShapes/btTriangleCallback.cpp | 28 + .../CollisionShapes/btTriangleCallback.h | 40 + .../btTriangleIndexVertexArray.cpp | 65 + .../btTriangleIndexVertexArray.h | 97 + .../CollisionShapes/btTriangleMesh.cpp | 60 + .../CollisionShapes/btTriangleMesh.h | 75 + .../CollisionShapes/btTriangleMeshShape.cpp | 203 + .../CollisionShapes/btTriangleMeshShape.h | 78 + .../CollisionShapes/btTriangleShape.h | 179 + .../CollisionShapes/btUniformScalingShape.cpp | 114 + .../CollisionShapes/btUniformScalingShape.h | 86 + .../btContinuousConvexCollision.cpp | 210 + .../btContinuousConvexCollision.h | 52 + .../NarrowPhaseCollision/btConvexCast.cpp | 20 + .../NarrowPhaseCollision/btConvexCast.h | 71 + .../btConvexPenetrationDepthSolver.h | 43 + .../btDiscreteCollisionDetectorInterface.h | 88 + .../NarrowPhaseCollision/btGjkConvexCast.cpp | 174 + .../NarrowPhaseCollision/btGjkConvexCast.h | 50 + .../NarrowPhaseCollision/btGjkEpa.cpp | 628 + .../NarrowPhaseCollision/btGjkEpa.h | 53 + .../btGjkEpaPenetrationDepthSolver.cpp | 50 + .../btGjkEpaPenetrationDepthSolver.h | 39 + .../btGjkPairDetector.cpp | 299 + .../NarrowPhaseCollision/btGjkPairDetector.h | 85 + .../NarrowPhaseCollision/btManifoldPoint.h | 99 + .../btMinkowskiPenetrationDepthSolver.cpp | 334 + .../btMinkowskiPenetrationDepthSolver.h | 37 + .../btPersistentManifold.cpp | 246 + .../btPersistentManifold.h | 161 + .../NarrowPhaseCollision/btPointCollector.h | 61 + .../btRaycastCallback.cpp | 101 + .../NarrowPhaseCollision/btRaycastCallback.h | 42 + .../btSimplexSolverInterface.h | 64 + .../btSubSimplexConvexCast.cpp | 139 + .../btSubSimplexConvexCast.h | 50 + .../btVoronoiSimplexSolver.cpp | 607 + .../btVoronoiSimplexSolver.h | 157 + .../btConeTwistConstraint.cpp | 287 + .../ConstraintSolver/btConeTwistConstraint.h | 126 + .../ConstraintSolver/btConstraintSolver.h | 52 + .../ConstraintSolver/btContactConstraint.cpp | 417 + .../ConstraintSolver/btContactConstraint.h | 122 + .../ConstraintSolver/btContactSolverInfo.h | 51 + .../btGeneric6DofConstraint.cpp | 390 + .../btGeneric6DofConstraint.h | 120 + .../ConstraintSolver/btHingeConstraint.cpp | 398 + .../ConstraintSolver/btHingeConstraint.h | 130 + .../ConstraintSolver/btJacobianEntry.h | 156 + .../btPoint2PointConstraint.cpp | 117 + .../btPoint2PointConstraint.h | 80 + .../btSequentialImpulseConstraintSolver.cpp | 1165 ++ .../btSequentialImpulseConstraintSolver.h | 114 + .../btSolve2LinearConstraint.cpp | 255 + .../btSolve2LinearConstraint.h | 107 + .../ConstraintSolver/btSolverBody.h | 71 + .../ConstraintSolver/btSolverConstraint.h | 63 + .../ConstraintSolver/btTypedConstraint.cpp | 56 + .../ConstraintSolver/btTypedConstraint.h | 112 + .../Dynamics/btDiscreteDynamicsWorld.cpp | 960 ++ .../Dynamics/btDiscreteDynamicsWorld.h | 159 + .../BulletDynamics/Dynamics/btDynamicsWorld.h | 80 + .../BulletDynamics/Dynamics/btRigidBody.cpp | 351 + .../src/BulletDynamics/Dynamics/btRigidBody.h | 385 + .../Dynamics/btSimpleDynamicsWorld.cpp | 217 + .../Dynamics/btSimpleDynamicsWorld.h | 84 + .../Vehicle/btRaycastVehicle.cpp | 734 ++ .../BulletDynamics/Vehicle/btRaycastVehicle.h | 201 + .../Vehicle/btVehicleRaycaster.h | 35 + .../BulletDynamics/Vehicle/btWheelInfo.cpp | 56 + .../src/BulletDynamics/Vehicle/btWheelInfo.h | 116 + bullet/src/LinearMath/btAabbUtil2.h | 127 + bullet/src/LinearMath/btAlignedAllocator.cpp | 70 + bullet/src/LinearMath/btAlignedAllocator.h | 80 + bullet/src/LinearMath/btAlignedObjectArray.h | 367 + bullet/src/LinearMath/btDefaultMotionState.h | 38 + bullet/src/LinearMath/btGeometryUtil.cpp | 178 + bullet/src/LinearMath/btGeometryUtil.h | 41 + bullet/src/LinearMath/btIDebugDraw.h | 100 + bullet/src/LinearMath/btList.h | 73 + bullet/src/LinearMath/btMatrix3x3.h | 410 + bullet/src/LinearMath/btMinMax.h | 69 + bullet/src/LinearMath/btMotionState.h | 40 + bullet/src/LinearMath/btPoint3.h | 24 + bullet/src/LinearMath/btQuadWord.h | 130 + bullet/src/LinearMath/btQuaternion.h | 321 + bullet/src/LinearMath/btQuickprof.cpp | 38 + bullet/src/LinearMath/btQuickprof.h | 712 + bullet/src/LinearMath/btRandom.h | 42 + bullet/src/LinearMath/btScalar.h | 192 + bullet/src/LinearMath/btSimdMinMax.h | 76 + bullet/src/LinearMath/btStackAlloc.h | 106 + bullet/src/LinearMath/btTransform.h | 206 + bullet/src/LinearMath/btTransformUtil.h | 138 + bullet/src/LinearMath/btVector3.h | 406 + bullet/src/btBulletCollisionCommon.h | 62 + bullet/src/btBulletDynamicsCommon.h | 42 + corona/Makefile | 16 + corona/src/Convert.cpp | 275 + corona/src/Corona.cpp | 321 + corona/src/Debug.cpp | 52 + corona/src/Debug.h | 70 + corona/src/DefaultFileSystem.cpp | 55 + corona/src/MemoryFile.cpp | 93 + corona/src/MemoryFile.h | 36 + corona/src/Open.h | 16 + corona/src/OpenBMP.cpp | 678 + corona/src/OpenPCX.cpp | 190 + corona/src/OpenPNG.cpp | 255 + corona/src/OpenTGA.cpp | 147 + corona/src/Save.h | 16 + corona/src/SavePNG.cpp | 168 + corona/src/SaveTGA.cpp | 49 + corona/src/SimpleImage.h | 101 + corona/src/Types.h | 27 + corona/src/Utility.h | 127 + corona/src/corona.h | 728 ++ engine/AABB.h | 39 + engine/Camera.cpp | 123 + engine/Camera.h | 55 + engine/FileSystem.h | 57 + engine/FontManager.cpp | 204 + engine/FontManager.h | 70 + engine/FontManagerDefaultFont.h | 4483 +++++++ engine/Makefile | 66 + engine/Math/Matrix.h | 866 ++ engine/Math/Plane.h | 51 + engine/Math/Point.h | 57 + engine/Math/Quaternion.h | 366 + engine/Math/Ray.h | 119 + engine/Math/Rectangle.h | 91 + engine/Math/Scalar.h | 19 + engine/Math/Transformation.h | 51 + engine/Math/Vector.h | 432 + engine/MeshManager.cpp | 419 + engine/MeshManager.h | 94 + engine/ModelManager.cpp | 322 + engine/ModelManager.h | 80 + engine/Node.h | 26 + engine/RenderDevice.cpp | 329 + engine/RenderDevice.h | 58 + engine/RenderWindow.cpp | 176 + engine/RenderWindow.h | 71 + engine/RigidBodySimulation.cpp | 618 + engine/RigidBodySimulation.h | 180 + engine/SceneNode.cpp | 162 + engine/SceneNode.h | 61 + engine/ScriptSystem.cpp | 203 + engine/ScriptSystem.h | 38 + engine/ScriptSystem_Font.cpp | 126 + engine/ScriptSystem_Font.h | 14 + engine/ScriptSystem_Image.cpp | 126 + engine/ScriptSystem_Image.h | 13 + engine/ScriptSystem_Math.cpp | 440 + engine/ScriptSystem_Math.h | 138 + engine/ScriptSystem_RigidBody.cpp | 542 + engine/ScriptSystem_RigidBody.h | 12 + engine/ShaderManager.cpp | 332 + engine/ShaderManager.h | 66 + engine/TextureImage.cpp | 146 + engine/TextureImage.h | 26 + engine/TextureManager.cpp | 444 + engine/TextureManager.h | 92 + engine/Utilities/Activated.h | 87 + engine/Utilities/Buffer.h | 151 + engine/Utilities/CfgParser.cpp | 198 + engine/Utilities/CfgParser.h | 39 + engine/Utilities/IniParser.h | 11 + engine/Utilities/Kernel.cpp | 82 + engine/Utilities/Kernel.h | 83 + engine/Utilities/Log.cpp | 75 + engine/Utilities/Log.h | 60 + engine/Utilities/MersenneTwister.h | 423 + engine/Utilities/Named.h | 64 + engine/Utilities/Referenced.h | 236 + engine/Utilities/StringUtilities.h | 119 + engine/Utilities/format.cpp | 142 + engine/Utilities/format.h | 919 ++ engine/Utilities/sigslot.h | 2567 ++++ engine/data/DejaVuSans.ttf | Bin 0 -> 519412 bytes engine/data/ambient_color_emissive.frag | 10 + engine/data/ambient_color_emissive.prog | 2 + engine/data/ambient_color_emissive.vert | 10 + engine/data/combat.zip | Bin 0 -> 1701759 bytes engine/data/image.png | Bin 0 -> 32003 bytes engine/data/main.nut | 114 + engine/main.cpp | 168 + engine/options.cfg | 5 + engine/todo.txt | 13 + freetype/Makefile | 107 + freetype/include/freetype/config/ftconfig.h | 356 + freetype/include/freetype/config/ftheader.h | 704 + freetype/include/freetype/config/ftmodule.h | 32 + freetype/include/freetype/config/ftoption.h | 603 + freetype/include/freetype/config/ftstdlib.h | 188 + freetype/include/freetype/freetype.h | 3357 +++++ freetype/include/freetype/ftbbox.h | 94 + freetype/include/freetype/ftbdf.h | 200 + freetype/include/freetype/ftbitmap.h | 206 + freetype/include/freetype/ftcache.h | 1000 ++ freetype/include/freetype/ftchapters.h | 97 + freetype/include/freetype/fterrdef.h | 235 + freetype/include/freetype/fterrors.h | 206 + freetype/include/freetype/ftglyph.h | 575 + freetype/include/freetype/ftgxval.h | 358 + freetype/include/freetype/ftgzip.h | 102 + freetype/include/freetype/ftimage.h | 1238 ++ freetype/include/freetype/ftincrem.h | 331 + freetype/include/freetype/ftlist.h | 274 + freetype/include/freetype/ftlzw.h | 99 + freetype/include/freetype/ftmac.h | 222 + freetype/include/freetype/ftmm.h | 378 + freetype/include/freetype/ftmodapi.h | 406 + freetype/include/freetype/ftmoderr.h | 155 + freetype/include/freetype/ftotval.h | 198 + freetype/include/freetype/ftoutln.h | 526 + freetype/include/freetype/ftpfr.h | 172 + freetype/include/freetype/ftrender.h | 229 + freetype/include/freetype/ftsizes.h | 159 + freetype/include/freetype/ftsnames.h | 170 + freetype/include/freetype/ftstroke.h | 716 + freetype/include/freetype/ftsynth.h | 73 + freetype/include/freetype/ftsystem.h | 346 + freetype/include/freetype/fttrigon.h | 350 + freetype/include/freetype/fttypes.h | 582 + freetype/include/freetype/ftwinfnt.h | 263 + freetype/include/freetype/ftxf86.h | 60 + freetype/include/freetype/internal/autohint.h | 205 + freetype/include/freetype/internal/ftcalc.h | 129 + freetype/include/freetype/internal/ftdebug.h | 244 + freetype/include/freetype/internal/ftdriver.h | 252 + freetype/include/freetype/internal/ftgloadr.h | 167 + freetype/include/freetype/internal/ftmemory.h | 333 + freetype/include/freetype/internal/ftobjs.h | 780 ++ freetype/include/freetype/internal/ftrfork.h | 184 + freetype/include/freetype/internal/ftserv.h | 326 + freetype/include/freetype/internal/ftstream.h | 539 + freetype/include/freetype/internal/fttrace.h | 133 + freetype/include/freetype/internal/ftvalid.h | 148 + freetype/include/freetype/internal/internal.h | 50 + freetype/include/freetype/internal/pcftypes.h | 56 + freetype/include/freetype/internal/psaux.h | 809 ++ freetype/include/freetype/internal/pshints.h | 687 + .../freetype/internal/services/svbdf.h | 57 + .../freetype/internal/services/svgldict.h | 60 + .../freetype/internal/services/svgxval.h | 72 + .../freetype/internal/services/svkern.h | 51 + .../include/freetype/internal/services/svmm.h | 79 + .../freetype/internal/services/svotval.h | 55 + .../freetype/internal/services/svpfr.h | 66 + .../freetype/internal/services/svpostnm.h | 58 + .../freetype/internal/services/svpscmap.h | 120 + .../freetype/internal/services/svpsinfo.h | 60 + .../freetype/internal/services/svsfnt.h | 80 + .../freetype/internal/services/svttcmap.h | 77 + .../freetype/internal/services/svtteng.h | 53 + .../freetype/internal/services/svwinfnt.h | 50 + .../freetype/internal/services/svxf86nm.h | 55 + freetype/include/freetype/internal/sfnt.h | 762 ++ freetype/include/freetype/internal/t1types.h | 241 + freetype/include/freetype/internal/tttypes.h | 1532 +++ freetype/include/freetype/t1tables.h | 441 + freetype/include/freetype/ttnameid.h | 1123 ++ freetype/include/freetype/tttables.h | 736 ++ freetype/include/freetype/tttags.h | 99 + freetype/include/freetype/ttunpat.h | 59 + freetype/include/ft2build.h | 39 + freetype/include/ftconfig.h | 335 + freetype/include/ftmodule.h | 20 + freetype/src/autofit/afangles.c | 433 + freetype/src/autofit/afangles.h | 7 + freetype/src/autofit/afcjk.c | 1508 +++ freetype/src/autofit/afcjk.h | 41 + freetype/src/autofit/afdummy.c | 62 + freetype/src/autofit/afdummy.h | 43 + freetype/src/autofit/aferrors.h | 40 + freetype/src/autofit/afglobal.c | 271 + freetype/src/autofit/afglobal.h | 66 + freetype/src/autofit/afhints.c | 1171 ++ freetype/src/autofit/afhints.h | 301 + freetype/src/autofit/aflatin.c | 2032 +++ freetype/src/autofit/aflatin.h | 207 + freetype/src/autofit/afloader.c | 492 + freetype/src/autofit/afloader.h | 73 + freetype/src/autofit/afmodule.c | 89 + freetype/src/autofit/afmodule.h | 37 + freetype/src/autofit/aftypes.h | 339 + freetype/src/autofit/afwarp.c | 313 + freetype/src/autofit/afwarp.h | 65 + freetype/src/autofit/autofit.c | 34 + freetype/src/base/ftapi.c | 121 + freetype/src/base/ftbase.c | 38 + freetype/src/base/ftbbox.c | 659 + freetype/src/base/ftbdf.c | 88 + freetype/src/base/ftbitmap.c | 616 + freetype/src/base/ftcalc.c | 667 + freetype/src/base/ftdbgmem.c | 996 ++ freetype/src/base/ftdebug.c | 246 + freetype/src/base/ftgloadr.c | 371 + freetype/src/base/ftglyph.c | 682 + freetype/src/base/ftgxval.c | 129 + freetype/src/base/ftinit.c | 161 + freetype/src/base/ftmac.c | 1432 ++ freetype/src/base/ftmm.c | 202 + freetype/src/base/ftnames.c | 94 + freetype/src/base/ftobjs.c | 3895 ++++++ freetype/src/base/ftotval.c | 83 + freetype/src/base/ftoutln.c | 1023 ++ freetype/src/base/ftpfr.c | 132 + freetype/src/base/ftrfork.c | 728 ++ freetype/src/base/ftstream.c | 842 ++ freetype/src/base/ftstroke.c | 2010 +++ freetype/src/base/ftsynth.c | 156 + freetype/src/base/ftsystem.c | 301 + freetype/src/base/fttrigon.c | 546 + freetype/src/base/fttype1.c | 94 + freetype/src/base/ftutil.c | 449 + freetype/src/base/ftwinfnt.c | 51 + freetype/src/base/ftxf86.c | 40 + freetype/src/bdf/README | 148 + freetype/src/bdf/bdf.c | 34 + freetype/src/bdf/bdf.h | 295 + freetype/src/bdf/bdfdrivr.c | 856 ++ freetype/src/bdf/bdfdrivr.h | 76 + freetype/src/bdf/bdferror.h | 44 + freetype/src/bdf/bdflib.c | 2440 ++++ freetype/src/cache/ftcache.c | 31 + freetype/src/cache/ftcbasic.c | 700 + freetype/src/cache/ftccache.c | 586 + freetype/src/cache/ftccache.h | 316 + freetype/src/cache/ftccback.h | 90 + freetype/src/cache/ftccmap.c | 410 + freetype/src/cache/ftcerror.h | 40 + freetype/src/cache/ftcglyph.c | 211 + freetype/src/cache/ftcglyph.h | 319 + freetype/src/cache/ftcimage.c | 163 + freetype/src/cache/ftcimage.h | 107 + freetype/src/cache/ftcmanag.c | 732 ++ freetype/src/cache/ftcmanag.h | 175 + freetype/src/cache/ftcmru.c | 357 + freetype/src/cache/ftcmru.h | 247 + freetype/src/cache/ftcsbits.c | 401 + freetype/src/cache/ftcsbits.h | 98 + freetype/src/cff/cff.c | 29 + freetype/src/cff/cffcmap.c | 207 + freetype/src/cff/cffcmap.h | 69 + freetype/src/cff/cffdrivr.c | 457 + freetype/src/cff/cffdrivr.h | 39 + freetype/src/cff/cfferrs.h | 41 + freetype/src/cff/cffgload.c | 2607 ++++ freetype/src/cff/cffgload.h | 208 + freetype/src/cff/cffload.c | 2315 ++++ freetype/src/cff/cffload.h | 74 + freetype/src/cff/cffobjs.c | 789 ++ freetype/src/cff/cffobjs.h | 165 + freetype/src/cff/cffparse.c | 688 + freetype/src/cff/cffparse.h | 69 + freetype/src/cff/cfftoken.h | 97 + freetype/src/cff/cfftypes.h | 256 + freetype/src/cid/ciderrs.h | 40 + freetype/src/cid/cidgload.c | 413 + freetype/src/cid/cidgload.h | 51 + freetype/src/cid/cidload.c | 644 + freetype/src/cid/cidload.h | 53 + freetype/src/cid/cidobjs.c | 480 + freetype/src/cid/cidobjs.h | 154 + freetype/src/cid/cidparse.c | 205 + freetype/src/cid/cidparse.h | 123 + freetype/src/cid/cidriver.c | 163 + freetype/src/cid/cidriver.h | 39 + freetype/src/cid/cidtoken.h | 103 + freetype/src/cid/type1cid.c | 29 + freetype/src/gxvalid/README | 532 + freetype/src/gxvalid/gxvalid.c | 46 + freetype/src/gxvalid/gxvalid.h | 107 + freetype/src/gxvalid/gxvbsln.c | 333 + freetype/src/gxvalid/gxvcommn.c | 1758 +++ freetype/src/gxvalid/gxvcommn.h | 560 + freetype/src/gxvalid/gxverror.h | 51 + freetype/src/gxvalid/gxvfeat.c | 343 + freetype/src/gxvalid/gxvfeat.h | 172 + freetype/src/gxvalid/gxvfgen.c | 482 + freetype/src/gxvalid/gxvjust.c | 630 + freetype/src/gxvalid/gxvkern.c | 839 ++ freetype/src/gxvalid/gxvlcar.c | 223 + freetype/src/gxvalid/gxvmod.c | 281 + freetype/src/gxvalid/gxvmod.h | 46 + freetype/src/gxvalid/gxvmort.c | 285 + freetype/src/gxvalid/gxvmort.h | 93 + freetype/src/gxvalid/gxvmort0.c | 137 + freetype/src/gxvalid/gxvmort1.c | 258 + freetype/src/gxvalid/gxvmort2.c | 282 + freetype/src/gxvalid/gxvmort4.c | 125 + freetype/src/gxvalid/gxvmort5.c | 226 + freetype/src/gxvalid/gxvmorx.c | 183 + freetype/src/gxvalid/gxvmorx.h | 67 + freetype/src/gxvalid/gxvmorx0.c | 103 + freetype/src/gxvalid/gxvmorx1.c | 274 + freetype/src/gxvalid/gxvmorx2.c | 285 + freetype/src/gxvalid/gxvmorx4.c | 55 + freetype/src/gxvalid/gxvmorx5.c | 217 + freetype/src/gxvalid/gxvopbd.c | 217 + freetype/src/gxvalid/gxvprop.c | 301 + freetype/src/gxvalid/gxvtrak.c | 277 + freetype/src/gzip/adler32.c | 48 + freetype/src/gzip/ftgzip.c | 614 + freetype/src/gzip/infblock.c | 387 + freetype/src/gzip/infblock.h | 36 + freetype/src/gzip/infcodes.c | 250 + freetype/src/gzip/infcodes.h | 31 + freetype/src/gzip/inffixed.h | 151 + freetype/src/gzip/inflate.c | 273 + freetype/src/gzip/inftrees.c | 462 + freetype/src/gzip/inftrees.h | 63 + freetype/src/gzip/infutil.c | 86 + freetype/src/gzip/infutil.h | 98 + freetype/src/gzip/zconf.h | 278 + freetype/src/gzip/zlib.h | 830 ++ freetype/src/gzip/zutil.c | 181 + freetype/src/gzip/zutil.h | 215 + freetype/src/lzw/ftlzw.c | 413 + freetype/src/lzw/ftzopen.c | 384 + freetype/src/lzw/ftzopen.h | 173 + freetype/src/otvalid/otvalid.c | 30 + freetype/src/otvalid/otvalid.h | 72 + freetype/src/otvalid/otvbase.c | 318 + freetype/src/otvalid/otvcommn.c | 1055 ++ freetype/src/otvalid/otvcommn.h | 436 + freetype/src/otvalid/otverror.h | 43 + freetype/src/otvalid/otvgdef.c | 219 + freetype/src/otvalid/otvgpos.c | 1013 ++ freetype/src/otvalid/otvgpos.h | 36 + freetype/src/otvalid/otvgsub.c | 584 + freetype/src/otvalid/otvjstf.c | 258 + freetype/src/otvalid/otvmod.c | 238 + freetype/src/otvalid/otvmod.h | 39 + freetype/src/pcf/README | 114 + freetype/src/pcf/pcf.c | 36 + freetype/src/pcf/pcf.h | 237 + freetype/src/pcf/pcfdrivr.c | 661 + freetype/src/pcf/pcfdrivr.h | 44 + freetype/src/pcf/pcferror.h | 40 + freetype/src/pcf/pcfread.c | 1183 ++ freetype/src/pcf/pcfread.h | 45 + freetype/src/pcf/pcfutil.c | 100 + freetype/src/pcf/pcfutil.h | 55 + freetype/src/pfr/pfr.c | 29 + freetype/src/pfr/pfrcmap.c | 158 + freetype/src/pfr/pfrcmap.h | 46 + freetype/src/pfr/pfrdrivr.c | 207 + freetype/src/pfr/pfrdrivr.h | 39 + freetype/src/pfr/pfrerror.h | 40 + freetype/src/pfr/pfrgload.c | 803 ++ freetype/src/pfr/pfrgload.h | 49 + freetype/src/pfr/pfrload.c | 938 ++ freetype/src/pfr/pfrload.h | 118 + freetype/src/pfr/pfrobjs.c | 552 + freetype/src/pfr/pfrobjs.h | 96 + freetype/src/pfr/pfrsbit.c | 680 + freetype/src/pfr/pfrsbit.h | 36 + freetype/src/pfr/pfrtypes.h | 360 + freetype/src/psaux/afmparse.c | 965 ++ freetype/src/psaux/afmparse.h | 87 + freetype/src/psaux/psaux.c | 34 + freetype/src/psaux/psauxerr.h | 41 + freetype/src/psaux/psauxmod.c | 139 + freetype/src/psaux/psauxmod.h | 38 + freetype/src/psaux/psconv.c | 399 + freetype/src/psaux/psconv.h | 107 + freetype/src/psaux/psobjs.c | 1495 +++ freetype/src/psaux/psobjs.h | 212 + freetype/src/psaux/t1cmap.c | 332 + freetype/src/psaux/t1cmap.h | 105 + freetype/src/psaux/t1decode.c | 1166 ++ freetype/src/psaux/t1decode.h | 64 + freetype/src/pshinter/pshalgo.c | 2092 +++ freetype/src/pshinter/pshalgo.h | 255 + freetype/src/pshinter/pshglob.c | 750 ++ freetype/src/pshinter/pshglob.h | 196 + freetype/src/pshinter/pshinter.c | 28 + freetype/src/pshinter/pshmod.c | 120 + freetype/src/pshinter/pshmod.h | 39 + freetype/src/pshinter/pshnterr.h | 40 + freetype/src/pshinter/pshrec.c | 1215 ++ freetype/src/pshinter/pshrec.h | 176 + freetype/src/psnames/psmodule.c | 454 + freetype/src/psnames/psmodule.h | 38 + freetype/src/psnames/psnamerr.h | 41 + freetype/src/psnames/psnames.c | 25 + freetype/src/psnames/pstables.h | 4090 ++++++ freetype/src/raster/ftmisc.h | 83 + freetype/src/raster/ftraster.c | 3341 +++++ freetype/src/raster/ftraster.h | 46 + freetype/src/raster/ftrend1.c | 273 + freetype/src/raster/ftrend1.h | 44 + freetype/src/raster/raster.c | 26 + freetype/src/raster/rasterrs.h | 41 + freetype/src/sfnt/sfdriver.c | 627 + freetype/src/sfnt/sfdriver.h | 38 + freetype/src/sfnt/sferrors.h | 41 + freetype/src/sfnt/sfnt.c | 41 + freetype/src/sfnt/sfobjs.c | 1069 ++ freetype/src/sfnt/sfobjs.h | 54 + freetype/src/sfnt/ttbdf.c | 250 + freetype/src/sfnt/ttbdf.h | 46 + freetype/src/sfnt/ttcmap.c | 2342 ++++ freetype/src/sfnt/ttcmap.h | 85 + freetype/src/sfnt/ttkern.c | 494 + freetype/src/sfnt/ttkern.h | 56 + freetype/src/sfnt/ttload.c | 1168 ++ freetype/src/sfnt/ttload.h | 112 + freetype/src/sfnt/ttmtx.c | 440 + freetype/src/sfnt/ttmtx.h | 55 + freetype/src/sfnt/ttpost.c | 521 + freetype/src/sfnt/ttpost.h | 46 + freetype/src/sfnt/ttsbit.c | 1501 +++ freetype/src/sfnt/ttsbit.h | 79 + freetype/src/sfnt/ttsbit0.c | 964 ++ freetype/src/sfnt/ttsbit0.h | 7 + freetype/src/smooth/ftgrays.c | 2159 ++++ freetype/src/smooth/ftgrays.h | 57 + freetype/src/smooth/ftsmerrs.h | 41 + freetype/src/smooth/ftsmooth.c | 374 + freetype/src/smooth/ftsmooth.h | 49 + freetype/src/smooth/smooth.c | 26 + freetype/src/truetype/rules.mk | 72 + freetype/src/truetype/truetype.c | 36 + freetype/src/truetype/ttdriver.c | 406 + freetype/src/truetype/ttdriver.h | 38 + freetype/src/truetype/tterrors.h | 40 + freetype/src/truetype/ttgload.c | 1884 +++ freetype/src/truetype/ttgload.h | 49 + freetype/src/truetype/ttgxvar.c | 1536 +++ freetype/src/truetype/ttgxvar.h | 182 + freetype/src/truetype/ttinterp.c | 7690 +++++++++++ freetype/src/truetype/ttinterp.h | 312 + freetype/src/truetype/ttobjs.c | 855 ++ freetype/src/truetype/ttobjs.h | 453 + freetype/src/truetype/ttpload.c | 748 ++ freetype/src/truetype/ttpload.h | 75 + freetype/src/type1/rules.mk | 73 + freetype/src/type1/t1afm.c | 384 + freetype/src/type1/t1afm.h | 54 + freetype/src/type1/t1driver.c | 328 + freetype/src/type1/t1driver.h | 38 + freetype/src/type1/t1errors.h | 40 + freetype/src/type1/t1gload.c | 393 + freetype/src/type1/t1gload.h | 46 + freetype/src/type1/t1load.c | 2124 +++ freetype/src/type1/t1load.h | 93 + freetype/src/type1/t1objs.c | 558 + freetype/src/type1/t1objs.h | 171 + freetype/src/type1/t1parse.c | 479 + freetype/src/type1/t1parse.h | 135 + freetype/src/type1/t1tokens.h | 84 + freetype/src/type1/type1.c | 33 + freetype/src/type42/t42drivr.c | 247 + freetype/src/type42/t42drivr.h | 38 + freetype/src/type42/t42error.h | 40 + freetype/src/type42/t42objs.c | 642 + freetype/src/type42/t42objs.h | 124 + freetype/src/type42/t42parse.c | 1139 ++ freetype/src/type42/t42parse.h | 90 + freetype/src/type42/t42types.h | 54 + freetype/src/type42/type42.c | 25 + freetype/src/winfonts/fnterrs.h | 41 + freetype/src/winfonts/winfnt.c | 823 ++ freetype/src/winfonts/winfnt.h | 107 + ftgl/FTBBox.h | 124 + ftgl/FTBitmapGlyph.cpp | 65 + ftgl/FTBitmapGlyph.h | 76 + ftgl/FTCharToGlyphIndexMap.h | 130 + ftgl/FTCharmap.cpp | 62 + ftgl/FTCharmap.h | 136 + ftgl/FTContour.cpp | 147 + ftgl/FTContour.h | 88 + ftgl/FTExtrdGlyph.cpp | 173 + ftgl/FTExtrdGlyph.h | 69 + ftgl/FTFace.cpp | 143 + ftgl/FTFace.h | 147 + ftgl/FTFont.cpp | 297 + ftgl/FTFont.h | 276 + ftgl/FTGL.h | 86 + ftgl/FTGLBitmapFont.cpp | 66 + ftgl/FTGLBitmapFont.h | 65 + ftgl/FTGLExtrdFont.cpp | 35 + ftgl/FTGLExtrdFont.h | 63 + ftgl/FTGLOutlineFont.cpp | 66 + ftgl/FTGLOutlineFont.h | 64 + ftgl/FTGLPixmapFont.cpp | 83 + ftgl/FTGLPixmapFont.h | 68 + ftgl/FTGLPolygonFont.cpp | 33 + ftgl/FTGLPolygonFont.h | 53 + ftgl/FTGLTextureFont.cpp | 183 + ftgl/FTGLTextureFont.h | 152 + ftgl/FTGlyph.cpp | 17 + ftgl/FTGlyph.h | 101 + ftgl/FTGlyphContainer.cpp | 91 + ftgl/FTGlyphContainer.h | 127 + ftgl/FTLibrary.cpp | 64 + ftgl/FTLibrary.h | 97 + ftgl/FTList.h | 112 + ftgl/FTOutlineGlyph.cpp | 66 + ftgl/FTOutlineGlyph.h | 57 + ftgl/FTPixmapGlyph.cpp | 73 + ftgl/FTPixmapGlyph.h | 68 + ftgl/FTPoint.cpp | 19 + ftgl/FTPoint.h | 162 + ftgl/FTPolyGlyph.cpp | 77 + ftgl/FTPolyGlyph.h | 59 + ftgl/FTSize.cpp | 104 + ftgl/FTSize.h | 138 + ftgl/FTTextureGlyph.cpp | 84 + ftgl/FTTextureGlyph.h | 94 + ftgl/FTVector.h | 190 + ftgl/FTVectoriser.cpp | 225 + ftgl/FTVectoriser.h | 275 + ftgl/Makefile | 17 + glew/Makefile | 17 + glew/include/GL/glew.h | 10718 +++++++++++++++ glew/include/GL/glxew.h | 1188 ++ glew/include/GL/wglew.h | 998 ++ glew/src/glew.c | 10750 ++++++++++++++++ glfw/Makefile | 28 + glfw/include/GL/glfw.h | 488 + glfw/lib/enable.c | 275 + glfw/lib/fullscreen.c | 97 + glfw/lib/glext.c | 203 + glfw/lib/image.c | 631 + glfw/lib/init.c | 110 + glfw/lib/input.c | 282 + glfw/lib/internal.h | 212 + glfw/lib/joystick.c | 103 + glfw/lib/macosx/macosx_enable.c | 44 + glfw/lib/macosx/macosx_fullscreen.c | 128 + glfw/lib/macosx/macosx_glext.c | 54 + glfw/lib/macosx/macosx_init.c | 170 + glfw/lib/macosx/macosx_joystick.c | 52 + glfw/lib/macosx/macosx_thread.c | 416 + glfw/lib/macosx/macosx_time.c | 114 + glfw/lib/macosx/macosx_window.c | 1245 ++ glfw/lib/macosx/platform.h | 348 + glfw/lib/stream.c | 196 + glfw/lib/tga.c | 407 + glfw/lib/thread.c | 342 + glfw/lib/time.c | 85 + glfw/lib/win32/platform.h | 468 + glfw/lib/win32/win32_dllmain.c | 62 + glfw/lib/win32/win32_enable.c | 157 + glfw/lib/win32/win32_fullscreen.c | 319 + glfw/lib/win32/win32_glext.c | 103 + glfw/lib/win32/win32_init.c | 358 + glfw/lib/win32/win32_joystick.c | 236 + glfw/lib/win32/win32_thread.c | 513 + glfw/lib/win32/win32_time.c | 148 + glfw/lib/win32/win32_window.c | 1524 +++ glfw/lib/window.c | 729 ++ glfw/lib/x11/platform.h | 417 + glfw/lib/x11/x11_enable.c | 53 + glfw/lib/x11/x11_fullscreen.c | 526 + glfw/lib/x11/x11_glext.c | 71 + glfw/lib/x11/x11_init.c | 277 + glfw/lib/x11/x11_joystick.c | 373 + glfw/lib/x11/x11_keysym2unicode.c | 885 ++ glfw/lib/x11/x11_thread.c | 509 + glfw/lib/x11/x11_time.c | 156 + glfw/lib/x11/x11_window.c | 1752 +++ glfw/license.txt | 21 + glfw/readme.html | 922 ++ libpng/Makefile | 17 + libpng/png.c | 828 ++ libpng/png.h | 3419 +++++ libpng/pngconf.h | 1437 +++ libpng/pngerror.c | 295 + libpng/pngget.c | 934 ++ libpng/pngmem.c | 595 + libpng/pngpread.c | 1573 +++ libpng/pngread.c | 1456 +++ libpng/pngrio.c | 161 + libpng/pngrtran.c | 4177 ++++++ libpng/pngrutil.c | 3124 +++++ libpng/pngset.c | 1219 ++ libpng/pngtrans.c | 650 + libpng/pngwio.c | 228 + libpng/pngwrite.c | 1464 +++ libpng/pngwtran.c | 563 + libpng/pngwutil.c | 2730 ++++ ode/CHANGELOG.txt | 467 + ode/INSTALL.txt | 118 + ode/LICENSE-BSD.TXT | 34 + ode/Makefile | 23 + ode/OPCODE/Ice/IceAABB.cpp | 405 + ode/OPCODE/Ice/IceAABB.h | 505 + ode/OPCODE/Ice/IceAxes.h | 54 + ode/OPCODE/Ice/IceBoundingSphere.h | 142 + ode/OPCODE/Ice/IceContainer.cpp | 345 + ode/OPCODE/Ice/IceContainer.h | 212 + ode/OPCODE/Ice/IceFPU.h | 337 + ode/OPCODE/Ice/IceHPoint.cpp | 70 + ode/OPCODE/Ice/IceHPoint.h | 160 + ode/OPCODE/Ice/IceIndexedTriangle.cpp | 548 + ode/OPCODE/Ice/IceIndexedTriangle.h | 72 + ode/OPCODE/Ice/IceLSS.h | 75 + ode/OPCODE/Ice/IceMatrix3x3.cpp | 48 + ode/OPCODE/Ice/IceMatrix3x3.h | 496 + ode/OPCODE/Ice/IceMatrix4x4.cpp | 135 + ode/OPCODE/Ice/IceMatrix4x4.h | 455 + ode/OPCODE/Ice/IceMemoryMacros.h | 109 + ode/OPCODE/Ice/IceOBB.cpp | 323 + ode/OPCODE/Ice/IceOBB.h | 177 + ode/OPCODE/Ice/IcePairs.h | 45 + ode/OPCODE/Ice/IcePlane.cpp | 45 + ode/OPCODE/Ice/IcePlane.h | 113 + ode/OPCODE/Ice/IcePoint.cpp | 193 + ode/OPCODE/Ice/IcePoint.h | 528 + ode/OPCODE/Ice/IcePreprocessor.h | 132 + ode/OPCODE/Ice/IceRandom.cpp | 35 + ode/OPCODE/Ice/IceRandom.h | 42 + ode/OPCODE/Ice/IceRay.cpp | 84 + ode/OPCODE/Ice/IceRay.h | 98 + ode/OPCODE/Ice/IceRevisitedRadix.cpp | 520 + ode/OPCODE/Ice/IceRevisitedRadix.h | 65 + ode/OPCODE/Ice/IceSegment.cpp | 57 + ode/OPCODE/Ice/IceSegment.h | 55 + ode/OPCODE/Ice/IceTriList.h | 61 + ode/OPCODE/Ice/IceTriangle.cpp | 286 + ode/OPCODE/Ice/IceTriangle.h | 68 + ode/OPCODE/Ice/IceTypes.h | 171 + ode/OPCODE/Ice/IceUtils.cpp | 39 + ode/OPCODE/Ice/IceUtils.h | 256 + ode/OPCODE/OPC_AABBCollider.cpp | 696 + ode/OPCODE/OPC_AABBCollider.h | 97 + ode/OPCODE/OPC_AABBTree.cpp | 573 + ode/OPCODE/OPC_AABBTree.h | 137 + ode/OPCODE/OPC_BaseModel.cpp | 138 + ode/OPCODE/OPC_BaseModel.h | 175 + ode/OPCODE/OPC_BoxBoxOverlap.h | 122 + ode/OPCODE/OPC_BoxPruning.cpp | 367 + ode/OPCODE/OPC_BoxPruning.h | 31 + ode/OPCODE/OPC_Collider.cpp | 54 + ode/OPCODE/OPC_Collider.h | 176 + ode/OPCODE/OPC_Common.cpp | 48 + ode/OPCODE/OPC_Common.h | 101 + ode/OPCODE/OPC_HybridModel.cpp | 466 + ode/OPCODE/OPC_HybridModel.h | 106 + ode/OPCODE/OPC_IceHook.h | 70 + ode/OPCODE/OPC_LSSAABBOverlap.h | 523 + ode/OPCODE/OPC_LSSCollider.cpp | 725 ++ ode/OPCODE/OPC_LSSCollider.h | 99 + ode/OPCODE/OPC_LSSTriOverlap.h | 679 + ode/OPCODE/OPC_MeshInterface.cpp | 303 + ode/OPCODE/OPC_MeshInterface.h | 199 + ode/OPCODE/OPC_Model.cpp | 222 + ode/OPCODE/OPC_Model.h | 65 + ode/OPCODE/OPC_OBBCollider.cpp | 767 ++ ode/OPCODE/OPC_OBBCollider.h | 142 + ode/OPCODE/OPC_OptimizedTree.cpp | 782 ++ ode/OPCODE/OPC_OptimizedTree.h | 206 + ode/OPCODE/OPC_Picking.cpp | 182 + ode/OPCODE/OPC_Picking.h | 45 + ode/OPCODE/OPC_PlanesAABBOverlap.h | 50 + ode/OPCODE/OPC_PlanesCollider.cpp | 653 + ode/OPCODE/OPC_PlanesCollider.h | 121 + ode/OPCODE/OPC_PlanesTriOverlap.h | 40 + ode/OPCODE/OPC_RayAABBOverlap.h | 63 + ode/OPCODE/OPC_RayCollider.cpp | 762 ++ ode/OPCODE/OPC_RayCollider.h | 225 + ode/OPCODE/OPC_RayTriOverlap.h | 89 + ode/OPCODE/OPC_Settings.h | 49 + ode/OPCODE/OPC_SphereAABBOverlap.h | 128 + ode/OPCODE/OPC_SphereCollider.cpp | 739 ++ ode/OPCODE/OPC_SphereCollider.h | 96 + ode/OPCODE/OPC_SphereTriOverlap.h | 187 + ode/OPCODE/OPC_SweepAndPrune.cpp | 664 + ode/OPCODE/OPC_SweepAndPrune.h | 86 + ode/OPCODE/OPC_TreeBuilders.cpp | 255 + ode/OPCODE/OPC_TreeBuilders.h | 173 + ode/OPCODE/OPC_TreeCollider.cpp | 943 ++ ode/OPCODE/OPC_TreeCollider.h | 246 + ode/OPCODE/OPC_TriBoxOverlap.h | 339 + ode/OPCODE/OPC_TriTriOverlap.h | 279 + ode/OPCODE/OPC_VolumeCollider.cpp | 103 + ode/OPCODE/OPC_VolumeCollider.h | 138 + ode/OPCODE/Opcode.cpp | 65 + ode/OPCODE/Opcode.dsp | 470 + ode/OPCODE/Opcode.dsw | 29 + ode/OPCODE/Opcode.h | 113 + ode/OPCODE/README-ODE.txt | 17 + ode/OPCODE/ReadMe.txt | 171 + ode/OPCODE/StdAfx.cpp | 10 + ode/OPCODE/Stdafx.h | 24 + ode/OPCODE/TemporalCoherence.txt | 32 + ode/README.txt | 32 + ode/include/ode/README | 18 + ode/include/ode/collision.h | 1379 ++ ode/include/ode/collision_space.h | 76 + ode/include/ode/collision_trimesh.h | 205 + ode/include/ode/common.h | 374 + ode/include/ode/compatibility.h | 40 + ode/include/ode/config.h | 168 + ode/include/ode/contact.h | 103 + ode/include/ode/error.h | 63 + ode/include/ode/export-dif.h | 32 + ode/include/ode/mass.h | 123 + ode/include/ode/matrix.h | 194 + ode/include/ode/memory.h | 59 + ode/include/ode/misc.h | 85 + ode/include/ode/objects.h | 1941 +++ ode/include/ode/ode.h | 47 + ode/include/ode/odecpp.h | 702 + ode/include/ode/odecpp_collision.h | 346 + ode/include/ode/odecpp_old.h | 316 + ode/include/ode/odemath.h | 308 + ode/include/ode/rotation.h | 70 + ode/include/ode/timer.h | 76 + ode/src/array.cpp | 80 + ode/src/array.h | 135 + ode/src/box.cpp | 830 ++ ode/src/capsule.cpp | 361 + ode/src/collision_cylinder_box.cpp | 986 ++ ode/src/collision_cylinder_plane.cpp | 293 + ode/src/collision_cylinder_sphere.cpp | 261 + ode/src/collision_cylinder_trimesh.cpp | 1125 ++ ode/src/collision_kernel.cpp | 1091 ++ ode/src/collision_kernel.h | 214 + ode/src/collision_quadtreespace.cpp | 584 + ode/src/collision_space.cpp | 790 ++ ode/src/collision_space_internal.h | 84 + ode/src/collision_std.h | 172 + ode/src/collision_transform.cpp | 232 + ode/src/collision_transform.h | 40 + ode/src/collision_trimesh_box.cpp | 1423 ++ ode/src/collision_trimesh_ccylinder.cpp | 1161 ++ ode/src/collision_trimesh_distance.cpp | 1255 ++ ode/src/collision_trimesh_gimpact.cpp | 521 + ode/src/collision_trimesh_internal.h | 489 + ode/src/collision_trimesh_opcode.cpp | 829 ++ ode/src/collision_trimesh_plane.cpp | 212 + ode/src/collision_trimesh_ray.cpp | 192 + ode/src/collision_trimesh_sphere.cpp | 560 + ode/src/collision_trimesh_trimesh.cpp | 2020 +++ ode/src/collision_util.cpp | 612 + ode/src/collision_util.h | 339 + ode/src/convex.cpp | 1287 ++ ode/src/cylinder.cpp | 100 + ode/src/error.cpp | 172 + ode/src/export-dif.cpp | 564 + ode/src/fastdot.c | 30 + ode/src/fastldlt.c | 381 + ode/src/fastlsolve.c | 298 + ode/src/fastltsolve.c | 199 + ode/src/heightfield.cpp | 1734 +++ ode/src/heightfield.h | 207 + ode/src/joint.cpp | 3986 ++++++ ode/src/joint.h | 339 + ode/src/lcp.cpp | 2007 +++ ode/src/lcp.h | 58 + ode/src/mass.cpp | 517 + ode/src/mat.cpp | 230 + ode/src/mat.h | 71 + ode/src/matrix.cpp | 358 + ode/src/memory.cpp | 87 + ode/src/misc.cpp | 169 + ode/src/objects.h | 138 + ode/src/obstack.cpp | 130 + ode/src/obstack.h | 68 + ode/src/ode.cpp | 1732 +++ ode/src/odemath.cpp | 165 + ode/src/plane.cpp | 145 + ode/src/quickstep.cpp | 880 ++ ode/src/quickstep.h | 33 + ode/src/ray.cpp | 677 + ode/src/rotation.cpp | 316 + ode/src/sphere.cpp | 235 + ode/src/step.cpp | 1795 +++ ode/src/step.h | 36 + ode/src/stepfast.cpp | 1139 ++ ode/src/timer.cpp | 421 + ode/src/util.cpp | 374 + ode/src/util.h | 38 + physfs/CHANGELOG.txt | 602 + physfs/CREDITS.txt | 95 + physfs/LICENSE.txt | 29 + physfs/Makefile | 29 + physfs/TODO.txt | 45 + physfs/archivers/dir.c | 283 + physfs/archivers/zip.c | 1441 +++ physfs/physfs.c | 2220 ++++ physfs/physfs.h | 2392 ++++ physfs/physfs_byteorder.c | 324 + physfs/physfs_casefolding.h | 2013 +++ physfs/physfs_internal.h | 1779 +++ physfs/physfs_platforms.h | 37 + physfs/physfs_unicode.c | 459 + physfs/platform/macosx.c | 396 + physfs/platform/posix.c | 404 + physfs/platform/unix.c | 395 + physfs/platform/windows.c | 1395 ++ squirrel/Makefile | 17 + squirrel/include/squirrel.h | 424 + squirrel/squirrel/sqapi.cpp | 1218 ++ squirrel/squirrel/sqarray.h | 79 + squirrel/squirrel/sqbaselib.cpp | 871 ++ squirrel/squirrel/sqclass.cpp | 192 + squirrel/squirrel/sqclass.h | 142 + squirrel/squirrel/sqclosure.h | 120 + squirrel/squirrel/sqcompiler.cpp | 1219 ++ squirrel/squirrel/sqcompiler.h | 75 + squirrel/squirrel/sqdebug.cpp | 99 + squirrel/squirrel/sqfuncproto.h | 155 + squirrel/squirrel/sqfuncstate.cpp | 556 + squirrel/squirrel/sqfuncstate.h | 80 + squirrel/squirrel/sqlexer.cpp | 446 + squirrel/squirrel/sqlexer.h | 45 + squirrel/squirrel/sqmem.cpp | 9 + squirrel/squirrel/sqobject.cpp | 538 + squirrel/squirrel/sqobject.h | 331 + squirrel/squirrel/sqopcodes.h | 114 + squirrel/squirrel/sqpcheader.h | 19 + squirrel/squirrel/sqstate.cpp | 573 + squirrel/squirrel/sqstate.h | 142 + squirrel/squirrel/sqstring.h | 31 + squirrel/squirrel/sqtable.cpp | 184 + squirrel/squirrel/sqtable.h | 96 + squirrel/squirrel/squserdata.h | 38 + squirrel/squirrel/squtils.h | 104 + squirrel/squirrel/sqvm.cpp | 1482 +++ squirrel/squirrel/sqvm.h | 203 + tinyxml/Makefile | 16 + tinyxml/tinyxml.cpp | 1592 +++ tinyxml/tinyxml.h | 1427 ++ tinyxml/tinyxmlerror.cpp | 51 + tinyxml/tinyxmlparser.cpp | 1508 +++ trimeshloader/Changelog.txt | 43 + trimeshloader/Makefile | 17 + trimeshloader/Readme.txt | 21 + trimeshloader/ToDo.txt | 10 + trimeshloader/include/tl3ds.h | 167 + trimeshloader/include/tlobj.h | 158 + trimeshloader/include/trimeshloader.h | 199 + trimeshloader/src/tl3ds.c | 1302 ++ trimeshloader/src/tlobj.c | 1553 +++ trimeshloader/src/trimeshloader.c | 396 + zlib/Makefile | 16 + zlib/adler32.c | 149 + zlib/compress.c | 79 + zlib/crc32.c | 423 + zlib/crc32.h | 441 + zlib/deflate.c | 1736 +++ zlib/deflate.h | 331 + zlib/gzio.c | 1026 ++ zlib/infback.c | 623 + zlib/inffast.c | 318 + zlib/inffast.h | 11 + zlib/inffixed.h | 94 + zlib/inflate.c | 1368 ++ zlib/inflate.h | 115 + zlib/inftrees.c | 329 + zlib/inftrees.h | 55 + zlib/trees.c | 1219 ++ zlib/trees.h | 128 + zlib/uncompr.c | 61 + zlib/zconf.h | 332 + zlib/zlib.h | 1357 ++ zlib/zutil.c | 318 + zlib/zutil.h | 269 + 1068 files changed, 384278 insertions(+) create mode 100644 Makefile create mode 100644 Makefile.common create mode 100644 Versions.txt create mode 100644 bullet/AUTHORS create mode 100644 bullet/BulletLicense.txt create mode 100644 bullet/ChangeLog.txt create mode 100644 bullet/LICENSE create mode 100644 bullet/Makefile create mode 100644 bullet/NEWS create mode 100644 bullet/README create mode 100644 bullet/VERSION create mode 100644 bullet/src/BulletCollision/BroadphaseCollision/btAxisSweep3.cpp create mode 100644 bullet/src/BulletCollision/BroadphaseCollision/btAxisSweep3.h create mode 100644 bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseInterface.h create mode 100644 bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseProxy.cpp create mode 100644 bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseProxy.h create mode 100644 bullet/src/BulletCollision/BroadphaseCollision/btCollisionAlgorithm.cpp create mode 100644 bullet/src/BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h create mode 100644 bullet/src/BulletCollision/BroadphaseCollision/btDispatcher.cpp create mode 100644 bullet/src/BulletCollision/BroadphaseCollision/btDispatcher.h create mode 100644 bullet/src/BulletCollision/BroadphaseCollision/btMultiSapBroadphase.cpp create mode 100644 bullet/src/BulletCollision/BroadphaseCollision/btMultiSapBroadphase.h create mode 100644 bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp create mode 100644 bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.h create mode 100644 bullet/src/BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp create mode 100644 bullet/src/BulletCollision/BroadphaseCollision/btSimpleBroadphase.h create mode 100644 bullet/src/BulletCollision/CollisionDispatch/SphereTriangleDetector.cpp create mode 100644 bullet/src/BulletCollision/CollisionDispatch/SphereTriangleDetector.h create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btCollisionCreateFunc.h create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btCollisionDispatcher.h create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.cpp create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.h create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.h create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.h create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.h create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.cpp create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.h create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btManifoldResult.cpp create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btManifoldResult.h create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btSimulationIslandManager.cpp create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btSimulationIslandManager.h create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.cpp create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.h create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.cpp create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.h create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.cpp create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.h create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btUnionFind.cpp create mode 100644 bullet/src/BulletCollision/CollisionDispatch/btUnionFind.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btBoxShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btBoxShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btCapsuleShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btCapsuleShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btCollisionMargin.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btCollisionShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btCollisionShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btCompoundShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btCompoundShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btConcaveShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btConcaveShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btConeShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btConeShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btConvexInternalShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btConvexInternalShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btConvexShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btConvexShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btConvexTriangleMeshShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btConvexTriangleMeshShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btCylinderShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btCylinderShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btEmptyShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btEmptyShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btMinkowskiSumShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btMinkowskiSumShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btMultiSphereShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btMultiSphereShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btOptimizedBvh.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btOptimizedBvh.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btSphereShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btSphereShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btStaticPlaneShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btStaticPlaneShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btStridingMeshInterface.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btStridingMeshInterface.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btTetrahedronShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btTetrahedronShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btTriangleBuffer.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btTriangleBuffer.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btTriangleCallback.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btTriangleCallback.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btTriangleMeshShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btTriangleMeshShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btTriangleShape.h create mode 100644 bullet/src/BulletCollision/CollisionShapes/btUniformScalingShape.cpp create mode 100644 bullet/src/BulletCollision/CollisionShapes/btUniformScalingShape.h create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.cpp create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.h create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btConvexCast.cpp create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btConvexCast.h create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btGjkConvexCast.cpp create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btGjkConvexCast.h create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpa.cpp create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpa.h create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btManifoldPoint.h create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.cpp create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btPersistentManifold.cpp create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btPersistentManifold.h create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btPointCollector.h create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.cpp create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btSimplexSolverInterface.h create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.cpp create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.cpp create mode 100644 bullet/src/BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h create mode 100644 bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp create mode 100644 bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.h create mode 100644 bullet/src/BulletDynamics/ConstraintSolver/btConstraintSolver.h create mode 100644 bullet/src/BulletDynamics/ConstraintSolver/btContactConstraint.cpp create mode 100644 bullet/src/BulletDynamics/ConstraintSolver/btContactConstraint.h create mode 100644 bullet/src/BulletDynamics/ConstraintSolver/btContactSolverInfo.h create mode 100644 bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp create mode 100644 bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h create mode 100644 bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.cpp create mode 100644 bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.h create mode 100644 bullet/src/BulletDynamics/ConstraintSolver/btJacobianEntry.h create mode 100644 bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.cpp create mode 100644 bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h create mode 100644 bullet/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp create mode 100644 bullet/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h create mode 100644 bullet/src/BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.cpp create mode 100644 bullet/src/BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.h create mode 100644 bullet/src/BulletDynamics/ConstraintSolver/btSolverBody.h create mode 100644 bullet/src/BulletDynamics/ConstraintSolver/btSolverConstraint.h create mode 100644 bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.cpp create mode 100644 bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.h create mode 100644 bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp create mode 100644 bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h create mode 100644 bullet/src/BulletDynamics/Dynamics/btDynamicsWorld.h create mode 100644 bullet/src/BulletDynamics/Dynamics/btRigidBody.cpp create mode 100644 bullet/src/BulletDynamics/Dynamics/btRigidBody.h create mode 100644 bullet/src/BulletDynamics/Dynamics/btSimpleDynamicsWorld.cpp create mode 100644 bullet/src/BulletDynamics/Dynamics/btSimpleDynamicsWorld.h create mode 100644 bullet/src/BulletDynamics/Vehicle/btRaycastVehicle.cpp create mode 100644 bullet/src/BulletDynamics/Vehicle/btRaycastVehicle.h create mode 100644 bullet/src/BulletDynamics/Vehicle/btVehicleRaycaster.h create mode 100644 bullet/src/BulletDynamics/Vehicle/btWheelInfo.cpp create mode 100644 bullet/src/BulletDynamics/Vehicle/btWheelInfo.h create mode 100644 bullet/src/LinearMath/btAabbUtil2.h create mode 100644 bullet/src/LinearMath/btAlignedAllocator.cpp create mode 100644 bullet/src/LinearMath/btAlignedAllocator.h create mode 100644 bullet/src/LinearMath/btAlignedObjectArray.h create mode 100644 bullet/src/LinearMath/btDefaultMotionState.h create mode 100644 bullet/src/LinearMath/btGeometryUtil.cpp create mode 100644 bullet/src/LinearMath/btGeometryUtil.h create mode 100644 bullet/src/LinearMath/btIDebugDraw.h create mode 100644 bullet/src/LinearMath/btList.h create mode 100644 bullet/src/LinearMath/btMatrix3x3.h create mode 100644 bullet/src/LinearMath/btMinMax.h create mode 100644 bullet/src/LinearMath/btMotionState.h create mode 100644 bullet/src/LinearMath/btPoint3.h create mode 100644 bullet/src/LinearMath/btQuadWord.h create mode 100644 bullet/src/LinearMath/btQuaternion.h create mode 100644 bullet/src/LinearMath/btQuickprof.cpp create mode 100644 bullet/src/LinearMath/btQuickprof.h create mode 100644 bullet/src/LinearMath/btRandom.h create mode 100644 bullet/src/LinearMath/btScalar.h create mode 100644 bullet/src/LinearMath/btSimdMinMax.h create mode 100644 bullet/src/LinearMath/btStackAlloc.h create mode 100644 bullet/src/LinearMath/btTransform.h create mode 100644 bullet/src/LinearMath/btTransformUtil.h create mode 100644 bullet/src/LinearMath/btVector3.h create mode 100644 bullet/src/btBulletCollisionCommon.h create mode 100644 bullet/src/btBulletDynamicsCommon.h create mode 100644 corona/Makefile create mode 100644 corona/src/Convert.cpp create mode 100644 corona/src/Corona.cpp create mode 100644 corona/src/Debug.cpp create mode 100644 corona/src/Debug.h create mode 100644 corona/src/DefaultFileSystem.cpp create mode 100644 corona/src/MemoryFile.cpp create mode 100644 corona/src/MemoryFile.h create mode 100644 corona/src/Open.h create mode 100644 corona/src/OpenBMP.cpp create mode 100644 corona/src/OpenPCX.cpp create mode 100644 corona/src/OpenPNG.cpp create mode 100644 corona/src/OpenTGA.cpp create mode 100644 corona/src/Save.h create mode 100644 corona/src/SavePNG.cpp create mode 100644 corona/src/SaveTGA.cpp create mode 100644 corona/src/SimpleImage.h create mode 100644 corona/src/Types.h create mode 100644 corona/src/Utility.h create mode 100644 corona/src/corona.h create mode 100644 engine/AABB.h create mode 100644 engine/Camera.cpp create mode 100644 engine/Camera.h create mode 100644 engine/FileSystem.h create mode 100644 engine/FontManager.cpp create mode 100644 engine/FontManager.h create mode 100644 engine/FontManagerDefaultFont.h create mode 100644 engine/Makefile create mode 100644 engine/Math/Matrix.h create mode 100644 engine/Math/Plane.h create mode 100644 engine/Math/Point.h create mode 100644 engine/Math/Quaternion.h create mode 100644 engine/Math/Ray.h create mode 100644 engine/Math/Rectangle.h create mode 100644 engine/Math/Scalar.h create mode 100644 engine/Math/Transformation.h create mode 100644 engine/Math/Vector.h create mode 100644 engine/MeshManager.cpp create mode 100644 engine/MeshManager.h create mode 100644 engine/ModelManager.cpp create mode 100644 engine/ModelManager.h create mode 100644 engine/Node.h create mode 100644 engine/RenderDevice.cpp create mode 100644 engine/RenderDevice.h create mode 100644 engine/RenderWindow.cpp create mode 100644 engine/RenderWindow.h create mode 100644 engine/RigidBodySimulation.cpp create mode 100644 engine/RigidBodySimulation.h create mode 100644 engine/SceneNode.cpp create mode 100644 engine/SceneNode.h create mode 100644 engine/ScriptSystem.cpp create mode 100644 engine/ScriptSystem.h create mode 100644 engine/ScriptSystem_Font.cpp create mode 100644 engine/ScriptSystem_Font.h create mode 100644 engine/ScriptSystem_Image.cpp create mode 100644 engine/ScriptSystem_Image.h create mode 100644 engine/ScriptSystem_Math.cpp create mode 100644 engine/ScriptSystem_Math.h create mode 100644 engine/ScriptSystem_RigidBody.cpp create mode 100644 engine/ScriptSystem_RigidBody.h create mode 100644 engine/ShaderManager.cpp create mode 100644 engine/ShaderManager.h create mode 100644 engine/TextureImage.cpp create mode 100644 engine/TextureImage.h create mode 100644 engine/TextureManager.cpp create mode 100644 engine/TextureManager.h create mode 100644 engine/Utilities/Activated.h create mode 100644 engine/Utilities/Buffer.h create mode 100644 engine/Utilities/CfgParser.cpp create mode 100644 engine/Utilities/CfgParser.h create mode 100644 engine/Utilities/IniParser.h create mode 100644 engine/Utilities/Kernel.cpp create mode 100644 engine/Utilities/Kernel.h create mode 100644 engine/Utilities/Log.cpp create mode 100644 engine/Utilities/Log.h create mode 100644 engine/Utilities/MersenneTwister.h create mode 100644 engine/Utilities/Named.h create mode 100644 engine/Utilities/Referenced.h create mode 100644 engine/Utilities/StringUtilities.h create mode 100644 engine/Utilities/format.cpp create mode 100644 engine/Utilities/format.h create mode 100644 engine/Utilities/sigslot.h create mode 100644 engine/data/DejaVuSans.ttf create mode 100644 engine/data/ambient_color_emissive.frag create mode 100644 engine/data/ambient_color_emissive.prog create mode 100644 engine/data/ambient_color_emissive.vert create mode 100644 engine/data/combat.zip create mode 100644 engine/data/image.png create mode 100644 engine/data/main.nut create mode 100644 engine/main.cpp create mode 100644 engine/options.cfg create mode 100644 engine/todo.txt create mode 100644 freetype/Makefile create mode 100644 freetype/include/freetype/config/ftconfig.h create mode 100644 freetype/include/freetype/config/ftheader.h create mode 100644 freetype/include/freetype/config/ftmodule.h create mode 100644 freetype/include/freetype/config/ftoption.h create mode 100644 freetype/include/freetype/config/ftstdlib.h create mode 100644 freetype/include/freetype/freetype.h create mode 100644 freetype/include/freetype/ftbbox.h create mode 100644 freetype/include/freetype/ftbdf.h create mode 100644 freetype/include/freetype/ftbitmap.h create mode 100644 freetype/include/freetype/ftcache.h create mode 100644 freetype/include/freetype/ftchapters.h create mode 100644 freetype/include/freetype/fterrdef.h create mode 100644 freetype/include/freetype/fterrors.h create mode 100644 freetype/include/freetype/ftglyph.h create mode 100644 freetype/include/freetype/ftgxval.h create mode 100644 freetype/include/freetype/ftgzip.h create mode 100644 freetype/include/freetype/ftimage.h create mode 100644 freetype/include/freetype/ftincrem.h create mode 100644 freetype/include/freetype/ftlist.h create mode 100644 freetype/include/freetype/ftlzw.h create mode 100644 freetype/include/freetype/ftmac.h create mode 100644 freetype/include/freetype/ftmm.h create mode 100644 freetype/include/freetype/ftmodapi.h create mode 100644 freetype/include/freetype/ftmoderr.h create mode 100644 freetype/include/freetype/ftotval.h create mode 100644 freetype/include/freetype/ftoutln.h create mode 100644 freetype/include/freetype/ftpfr.h create mode 100644 freetype/include/freetype/ftrender.h create mode 100644 freetype/include/freetype/ftsizes.h create mode 100644 freetype/include/freetype/ftsnames.h create mode 100644 freetype/include/freetype/ftstroke.h create mode 100644 freetype/include/freetype/ftsynth.h create mode 100644 freetype/include/freetype/ftsystem.h create mode 100644 freetype/include/freetype/fttrigon.h create mode 100644 freetype/include/freetype/fttypes.h create mode 100644 freetype/include/freetype/ftwinfnt.h create mode 100644 freetype/include/freetype/ftxf86.h create mode 100644 freetype/include/freetype/internal/autohint.h create mode 100644 freetype/include/freetype/internal/ftcalc.h create mode 100644 freetype/include/freetype/internal/ftdebug.h create mode 100644 freetype/include/freetype/internal/ftdriver.h create mode 100644 freetype/include/freetype/internal/ftgloadr.h create mode 100644 freetype/include/freetype/internal/ftmemory.h create mode 100644 freetype/include/freetype/internal/ftobjs.h create mode 100644 freetype/include/freetype/internal/ftrfork.h create mode 100644 freetype/include/freetype/internal/ftserv.h create mode 100644 freetype/include/freetype/internal/ftstream.h create mode 100644 freetype/include/freetype/internal/fttrace.h create mode 100644 freetype/include/freetype/internal/ftvalid.h create mode 100644 freetype/include/freetype/internal/internal.h create mode 100644 freetype/include/freetype/internal/pcftypes.h create mode 100644 freetype/include/freetype/internal/psaux.h create mode 100644 freetype/include/freetype/internal/pshints.h create mode 100644 freetype/include/freetype/internal/services/svbdf.h create mode 100644 freetype/include/freetype/internal/services/svgldict.h create mode 100644 freetype/include/freetype/internal/services/svgxval.h create mode 100644 freetype/include/freetype/internal/services/svkern.h create mode 100644 freetype/include/freetype/internal/services/svmm.h create mode 100644 freetype/include/freetype/internal/services/svotval.h create mode 100644 freetype/include/freetype/internal/services/svpfr.h create mode 100644 freetype/include/freetype/internal/services/svpostnm.h create mode 100644 freetype/include/freetype/internal/services/svpscmap.h create mode 100644 freetype/include/freetype/internal/services/svpsinfo.h create mode 100644 freetype/include/freetype/internal/services/svsfnt.h create mode 100644 freetype/include/freetype/internal/services/svttcmap.h create mode 100644 freetype/include/freetype/internal/services/svtteng.h create mode 100644 freetype/include/freetype/internal/services/svwinfnt.h create mode 100644 freetype/include/freetype/internal/services/svxf86nm.h create mode 100644 freetype/include/freetype/internal/sfnt.h create mode 100644 freetype/include/freetype/internal/t1types.h create mode 100644 freetype/include/freetype/internal/tttypes.h create mode 100644 freetype/include/freetype/t1tables.h create mode 100644 freetype/include/freetype/ttnameid.h create mode 100644 freetype/include/freetype/tttables.h create mode 100644 freetype/include/freetype/tttags.h create mode 100644 freetype/include/freetype/ttunpat.h create mode 100644 freetype/include/ft2build.h create mode 100644 freetype/include/ftconfig.h create mode 100644 freetype/include/ftmodule.h create mode 100644 freetype/src/autofit/afangles.c create mode 100644 freetype/src/autofit/afangles.h create mode 100644 freetype/src/autofit/afcjk.c create mode 100644 freetype/src/autofit/afcjk.h create mode 100644 freetype/src/autofit/afdummy.c create mode 100644 freetype/src/autofit/afdummy.h create mode 100644 freetype/src/autofit/aferrors.h create mode 100644 freetype/src/autofit/afglobal.c create mode 100644 freetype/src/autofit/afglobal.h create mode 100644 freetype/src/autofit/afhints.c create mode 100644 freetype/src/autofit/afhints.h create mode 100644 freetype/src/autofit/aflatin.c create mode 100644 freetype/src/autofit/aflatin.h create mode 100644 freetype/src/autofit/afloader.c create mode 100644 freetype/src/autofit/afloader.h create mode 100644 freetype/src/autofit/afmodule.c create mode 100644 freetype/src/autofit/afmodule.h create mode 100644 freetype/src/autofit/aftypes.h create mode 100644 freetype/src/autofit/afwarp.c create mode 100644 freetype/src/autofit/afwarp.h create mode 100644 freetype/src/autofit/autofit.c create mode 100644 freetype/src/base/ftapi.c create mode 100644 freetype/src/base/ftbase.c create mode 100644 freetype/src/base/ftbbox.c create mode 100644 freetype/src/base/ftbdf.c create mode 100644 freetype/src/base/ftbitmap.c create mode 100644 freetype/src/base/ftcalc.c create mode 100644 freetype/src/base/ftdbgmem.c create mode 100644 freetype/src/base/ftdebug.c create mode 100644 freetype/src/base/ftgloadr.c create mode 100644 freetype/src/base/ftglyph.c create mode 100644 freetype/src/base/ftgxval.c create mode 100644 freetype/src/base/ftinit.c create mode 100644 freetype/src/base/ftmac.c create mode 100644 freetype/src/base/ftmm.c create mode 100644 freetype/src/base/ftnames.c create mode 100644 freetype/src/base/ftobjs.c create mode 100644 freetype/src/base/ftotval.c create mode 100644 freetype/src/base/ftoutln.c create mode 100644 freetype/src/base/ftpfr.c create mode 100644 freetype/src/base/ftrfork.c create mode 100644 freetype/src/base/ftstream.c create mode 100644 freetype/src/base/ftstroke.c create mode 100644 freetype/src/base/ftsynth.c create mode 100644 freetype/src/base/ftsystem.c create mode 100644 freetype/src/base/fttrigon.c create mode 100644 freetype/src/base/fttype1.c create mode 100644 freetype/src/base/ftutil.c create mode 100644 freetype/src/base/ftwinfnt.c create mode 100644 freetype/src/base/ftxf86.c create mode 100644 freetype/src/bdf/README create mode 100644 freetype/src/bdf/bdf.c create mode 100644 freetype/src/bdf/bdf.h create mode 100644 freetype/src/bdf/bdfdrivr.c create mode 100644 freetype/src/bdf/bdfdrivr.h create mode 100644 freetype/src/bdf/bdferror.h create mode 100644 freetype/src/bdf/bdflib.c create mode 100644 freetype/src/cache/ftcache.c create mode 100644 freetype/src/cache/ftcbasic.c create mode 100644 freetype/src/cache/ftccache.c create mode 100644 freetype/src/cache/ftccache.h create mode 100644 freetype/src/cache/ftccback.h create mode 100644 freetype/src/cache/ftccmap.c create mode 100644 freetype/src/cache/ftcerror.h create mode 100644 freetype/src/cache/ftcglyph.c create mode 100644 freetype/src/cache/ftcglyph.h create mode 100644 freetype/src/cache/ftcimage.c create mode 100644 freetype/src/cache/ftcimage.h create mode 100644 freetype/src/cache/ftcmanag.c create mode 100644 freetype/src/cache/ftcmanag.h create mode 100644 freetype/src/cache/ftcmru.c create mode 100644 freetype/src/cache/ftcmru.h create mode 100644 freetype/src/cache/ftcsbits.c create mode 100644 freetype/src/cache/ftcsbits.h create mode 100644 freetype/src/cff/cff.c create mode 100644 freetype/src/cff/cffcmap.c create mode 100644 freetype/src/cff/cffcmap.h create mode 100644 freetype/src/cff/cffdrivr.c create mode 100644 freetype/src/cff/cffdrivr.h create mode 100644 freetype/src/cff/cfferrs.h create mode 100644 freetype/src/cff/cffgload.c create mode 100644 freetype/src/cff/cffgload.h create mode 100644 freetype/src/cff/cffload.c create mode 100644 freetype/src/cff/cffload.h create mode 100644 freetype/src/cff/cffobjs.c create mode 100644 freetype/src/cff/cffobjs.h create mode 100644 freetype/src/cff/cffparse.c create mode 100644 freetype/src/cff/cffparse.h create mode 100644 freetype/src/cff/cfftoken.h create mode 100644 freetype/src/cff/cfftypes.h create mode 100644 freetype/src/cid/ciderrs.h create mode 100644 freetype/src/cid/cidgload.c create mode 100644 freetype/src/cid/cidgload.h create mode 100644 freetype/src/cid/cidload.c create mode 100644 freetype/src/cid/cidload.h create mode 100644 freetype/src/cid/cidobjs.c create mode 100644 freetype/src/cid/cidobjs.h create mode 100644 freetype/src/cid/cidparse.c create mode 100644 freetype/src/cid/cidparse.h create mode 100644 freetype/src/cid/cidriver.c create mode 100644 freetype/src/cid/cidriver.h create mode 100644 freetype/src/cid/cidtoken.h create mode 100644 freetype/src/cid/type1cid.c create mode 100644 freetype/src/gxvalid/README create mode 100644 freetype/src/gxvalid/gxvalid.c create mode 100644 freetype/src/gxvalid/gxvalid.h create mode 100644 freetype/src/gxvalid/gxvbsln.c create mode 100644 freetype/src/gxvalid/gxvcommn.c create mode 100644 freetype/src/gxvalid/gxvcommn.h create mode 100644 freetype/src/gxvalid/gxverror.h create mode 100644 freetype/src/gxvalid/gxvfeat.c create mode 100644 freetype/src/gxvalid/gxvfeat.h create mode 100644 freetype/src/gxvalid/gxvfgen.c create mode 100644 freetype/src/gxvalid/gxvjust.c create mode 100644 freetype/src/gxvalid/gxvkern.c create mode 100644 freetype/src/gxvalid/gxvlcar.c create mode 100644 freetype/src/gxvalid/gxvmod.c create mode 100644 freetype/src/gxvalid/gxvmod.h create mode 100644 freetype/src/gxvalid/gxvmort.c create mode 100644 freetype/src/gxvalid/gxvmort.h create mode 100644 freetype/src/gxvalid/gxvmort0.c create mode 100644 freetype/src/gxvalid/gxvmort1.c create mode 100644 freetype/src/gxvalid/gxvmort2.c create mode 100644 freetype/src/gxvalid/gxvmort4.c create mode 100644 freetype/src/gxvalid/gxvmort5.c create mode 100644 freetype/src/gxvalid/gxvmorx.c create mode 100644 freetype/src/gxvalid/gxvmorx.h create mode 100644 freetype/src/gxvalid/gxvmorx0.c create mode 100644 freetype/src/gxvalid/gxvmorx1.c create mode 100644 freetype/src/gxvalid/gxvmorx2.c create mode 100644 freetype/src/gxvalid/gxvmorx4.c create mode 100644 freetype/src/gxvalid/gxvmorx5.c create mode 100644 freetype/src/gxvalid/gxvopbd.c create mode 100644 freetype/src/gxvalid/gxvprop.c create mode 100644 freetype/src/gxvalid/gxvtrak.c create mode 100644 freetype/src/gzip/adler32.c create mode 100644 freetype/src/gzip/ftgzip.c create mode 100644 freetype/src/gzip/infblock.c create mode 100644 freetype/src/gzip/infblock.h create mode 100644 freetype/src/gzip/infcodes.c create mode 100644 freetype/src/gzip/infcodes.h create mode 100644 freetype/src/gzip/inffixed.h create mode 100644 freetype/src/gzip/inflate.c create mode 100644 freetype/src/gzip/inftrees.c create mode 100644 freetype/src/gzip/inftrees.h create mode 100644 freetype/src/gzip/infutil.c create mode 100644 freetype/src/gzip/infutil.h create mode 100644 freetype/src/gzip/zconf.h create mode 100644 freetype/src/gzip/zlib.h create mode 100644 freetype/src/gzip/zutil.c create mode 100644 freetype/src/gzip/zutil.h create mode 100644 freetype/src/lzw/ftlzw.c create mode 100644 freetype/src/lzw/ftzopen.c create mode 100644 freetype/src/lzw/ftzopen.h create mode 100644 freetype/src/otvalid/otvalid.c create mode 100644 freetype/src/otvalid/otvalid.h create mode 100644 freetype/src/otvalid/otvbase.c create mode 100644 freetype/src/otvalid/otvcommn.c create mode 100644 freetype/src/otvalid/otvcommn.h create mode 100644 freetype/src/otvalid/otverror.h create mode 100644 freetype/src/otvalid/otvgdef.c create mode 100644 freetype/src/otvalid/otvgpos.c create mode 100644 freetype/src/otvalid/otvgpos.h create mode 100644 freetype/src/otvalid/otvgsub.c create mode 100644 freetype/src/otvalid/otvjstf.c create mode 100644 freetype/src/otvalid/otvmod.c create mode 100644 freetype/src/otvalid/otvmod.h create mode 100644 freetype/src/pcf/README create mode 100644 freetype/src/pcf/pcf.c create mode 100644 freetype/src/pcf/pcf.h create mode 100644 freetype/src/pcf/pcfdrivr.c create mode 100644 freetype/src/pcf/pcfdrivr.h create mode 100644 freetype/src/pcf/pcferror.h create mode 100644 freetype/src/pcf/pcfread.c create mode 100644 freetype/src/pcf/pcfread.h create mode 100644 freetype/src/pcf/pcfutil.c create mode 100644 freetype/src/pcf/pcfutil.h create mode 100644 freetype/src/pfr/pfr.c create mode 100644 freetype/src/pfr/pfrcmap.c create mode 100644 freetype/src/pfr/pfrcmap.h create mode 100644 freetype/src/pfr/pfrdrivr.c create mode 100644 freetype/src/pfr/pfrdrivr.h create mode 100644 freetype/src/pfr/pfrerror.h create mode 100644 freetype/src/pfr/pfrgload.c create mode 100644 freetype/src/pfr/pfrgload.h create mode 100644 freetype/src/pfr/pfrload.c create mode 100644 freetype/src/pfr/pfrload.h create mode 100644 freetype/src/pfr/pfrobjs.c create mode 100644 freetype/src/pfr/pfrobjs.h create mode 100644 freetype/src/pfr/pfrsbit.c create mode 100644 freetype/src/pfr/pfrsbit.h create mode 100644 freetype/src/pfr/pfrtypes.h create mode 100644 freetype/src/psaux/afmparse.c create mode 100644 freetype/src/psaux/afmparse.h create mode 100644 freetype/src/psaux/psaux.c create mode 100644 freetype/src/psaux/psauxerr.h create mode 100644 freetype/src/psaux/psauxmod.c create mode 100644 freetype/src/psaux/psauxmod.h create mode 100644 freetype/src/psaux/psconv.c create mode 100644 freetype/src/psaux/psconv.h create mode 100644 freetype/src/psaux/psobjs.c create mode 100644 freetype/src/psaux/psobjs.h create mode 100644 freetype/src/psaux/t1cmap.c create mode 100644 freetype/src/psaux/t1cmap.h create mode 100644 freetype/src/psaux/t1decode.c create mode 100644 freetype/src/psaux/t1decode.h create mode 100644 freetype/src/pshinter/pshalgo.c create mode 100644 freetype/src/pshinter/pshalgo.h create mode 100644 freetype/src/pshinter/pshglob.c create mode 100644 freetype/src/pshinter/pshglob.h create mode 100644 freetype/src/pshinter/pshinter.c create mode 100644 freetype/src/pshinter/pshmod.c create mode 100644 freetype/src/pshinter/pshmod.h create mode 100644 freetype/src/pshinter/pshnterr.h create mode 100644 freetype/src/pshinter/pshrec.c create mode 100644 freetype/src/pshinter/pshrec.h create mode 100644 freetype/src/psnames/psmodule.c create mode 100644 freetype/src/psnames/psmodule.h create mode 100644 freetype/src/psnames/psnamerr.h create mode 100644 freetype/src/psnames/psnames.c create mode 100644 freetype/src/psnames/pstables.h create mode 100644 freetype/src/raster/ftmisc.h create mode 100644 freetype/src/raster/ftraster.c create mode 100644 freetype/src/raster/ftraster.h create mode 100644 freetype/src/raster/ftrend1.c create mode 100644 freetype/src/raster/ftrend1.h create mode 100644 freetype/src/raster/raster.c create mode 100644 freetype/src/raster/rasterrs.h create mode 100644 freetype/src/sfnt/sfdriver.c create mode 100644 freetype/src/sfnt/sfdriver.h create mode 100644 freetype/src/sfnt/sferrors.h create mode 100644 freetype/src/sfnt/sfnt.c create mode 100644 freetype/src/sfnt/sfobjs.c create mode 100644 freetype/src/sfnt/sfobjs.h create mode 100644 freetype/src/sfnt/ttbdf.c create mode 100644 freetype/src/sfnt/ttbdf.h create mode 100644 freetype/src/sfnt/ttcmap.c create mode 100644 freetype/src/sfnt/ttcmap.h create mode 100644 freetype/src/sfnt/ttkern.c create mode 100644 freetype/src/sfnt/ttkern.h create mode 100644 freetype/src/sfnt/ttload.c create mode 100644 freetype/src/sfnt/ttload.h create mode 100644 freetype/src/sfnt/ttmtx.c create mode 100644 freetype/src/sfnt/ttmtx.h create mode 100644 freetype/src/sfnt/ttpost.c create mode 100644 freetype/src/sfnt/ttpost.h create mode 100644 freetype/src/sfnt/ttsbit.c create mode 100644 freetype/src/sfnt/ttsbit.h create mode 100644 freetype/src/sfnt/ttsbit0.c create mode 100644 freetype/src/sfnt/ttsbit0.h create mode 100644 freetype/src/smooth/ftgrays.c create mode 100644 freetype/src/smooth/ftgrays.h create mode 100644 freetype/src/smooth/ftsmerrs.h create mode 100644 freetype/src/smooth/ftsmooth.c create mode 100644 freetype/src/smooth/ftsmooth.h create mode 100644 freetype/src/smooth/smooth.c create mode 100644 freetype/src/truetype/rules.mk create mode 100644 freetype/src/truetype/truetype.c create mode 100644 freetype/src/truetype/ttdriver.c create mode 100644 freetype/src/truetype/ttdriver.h create mode 100644 freetype/src/truetype/tterrors.h create mode 100644 freetype/src/truetype/ttgload.c create mode 100644 freetype/src/truetype/ttgload.h create mode 100644 freetype/src/truetype/ttgxvar.c create mode 100644 freetype/src/truetype/ttgxvar.h create mode 100644 freetype/src/truetype/ttinterp.c create mode 100644 freetype/src/truetype/ttinterp.h create mode 100644 freetype/src/truetype/ttobjs.c create mode 100644 freetype/src/truetype/ttobjs.h create mode 100644 freetype/src/truetype/ttpload.c create mode 100644 freetype/src/truetype/ttpload.h create mode 100644 freetype/src/type1/rules.mk create mode 100644 freetype/src/type1/t1afm.c create mode 100644 freetype/src/type1/t1afm.h create mode 100644 freetype/src/type1/t1driver.c create mode 100644 freetype/src/type1/t1driver.h create mode 100644 freetype/src/type1/t1errors.h create mode 100644 freetype/src/type1/t1gload.c create mode 100644 freetype/src/type1/t1gload.h create mode 100644 freetype/src/type1/t1load.c create mode 100644 freetype/src/type1/t1load.h create mode 100644 freetype/src/type1/t1objs.c create mode 100644 freetype/src/type1/t1objs.h create mode 100644 freetype/src/type1/t1parse.c create mode 100644 freetype/src/type1/t1parse.h create mode 100644 freetype/src/type1/t1tokens.h create mode 100644 freetype/src/type1/type1.c create mode 100644 freetype/src/type42/t42drivr.c create mode 100644 freetype/src/type42/t42drivr.h create mode 100644 freetype/src/type42/t42error.h create mode 100644 freetype/src/type42/t42objs.c create mode 100644 freetype/src/type42/t42objs.h create mode 100644 freetype/src/type42/t42parse.c create mode 100644 freetype/src/type42/t42parse.h create mode 100644 freetype/src/type42/t42types.h create mode 100644 freetype/src/type42/type42.c create mode 100644 freetype/src/winfonts/fnterrs.h create mode 100644 freetype/src/winfonts/winfnt.c create mode 100644 freetype/src/winfonts/winfnt.h create mode 100644 ftgl/FTBBox.h create mode 100644 ftgl/FTBitmapGlyph.cpp create mode 100644 ftgl/FTBitmapGlyph.h create mode 100644 ftgl/FTCharToGlyphIndexMap.h create mode 100644 ftgl/FTCharmap.cpp create mode 100644 ftgl/FTCharmap.h create mode 100644 ftgl/FTContour.cpp create mode 100644 ftgl/FTContour.h create mode 100644 ftgl/FTExtrdGlyph.cpp create mode 100644 ftgl/FTExtrdGlyph.h create mode 100644 ftgl/FTFace.cpp create mode 100644 ftgl/FTFace.h create mode 100644 ftgl/FTFont.cpp create mode 100644 ftgl/FTFont.h create mode 100644 ftgl/FTGL.h create mode 100644 ftgl/FTGLBitmapFont.cpp create mode 100644 ftgl/FTGLBitmapFont.h create mode 100644 ftgl/FTGLExtrdFont.cpp create mode 100644 ftgl/FTGLExtrdFont.h create mode 100644 ftgl/FTGLOutlineFont.cpp create mode 100644 ftgl/FTGLOutlineFont.h create mode 100644 ftgl/FTGLPixmapFont.cpp create mode 100644 ftgl/FTGLPixmapFont.h create mode 100644 ftgl/FTGLPolygonFont.cpp create mode 100644 ftgl/FTGLPolygonFont.h create mode 100644 ftgl/FTGLTextureFont.cpp create mode 100644 ftgl/FTGLTextureFont.h create mode 100644 ftgl/FTGlyph.cpp create mode 100644 ftgl/FTGlyph.h create mode 100644 ftgl/FTGlyphContainer.cpp create mode 100644 ftgl/FTGlyphContainer.h create mode 100644 ftgl/FTLibrary.cpp create mode 100644 ftgl/FTLibrary.h create mode 100644 ftgl/FTList.h create mode 100644 ftgl/FTOutlineGlyph.cpp create mode 100644 ftgl/FTOutlineGlyph.h create mode 100644 ftgl/FTPixmapGlyph.cpp create mode 100644 ftgl/FTPixmapGlyph.h create mode 100644 ftgl/FTPoint.cpp create mode 100644 ftgl/FTPoint.h create mode 100644 ftgl/FTPolyGlyph.cpp create mode 100644 ftgl/FTPolyGlyph.h create mode 100644 ftgl/FTSize.cpp create mode 100644 ftgl/FTSize.h create mode 100644 ftgl/FTTextureGlyph.cpp create mode 100644 ftgl/FTTextureGlyph.h create mode 100644 ftgl/FTVector.h create mode 100644 ftgl/FTVectoriser.cpp create mode 100644 ftgl/FTVectoriser.h create mode 100644 ftgl/Makefile create mode 100644 glew/Makefile create mode 100644 glew/include/GL/glew.h create mode 100644 glew/include/GL/glxew.h create mode 100644 glew/include/GL/wglew.h create mode 100644 glew/src/glew.c create mode 100644 glfw/Makefile create mode 100644 glfw/include/GL/glfw.h create mode 100644 glfw/lib/enable.c create mode 100644 glfw/lib/fullscreen.c create mode 100644 glfw/lib/glext.c create mode 100644 glfw/lib/image.c create mode 100644 glfw/lib/init.c create mode 100644 glfw/lib/input.c create mode 100644 glfw/lib/internal.h create mode 100644 glfw/lib/joystick.c create mode 100644 glfw/lib/macosx/macosx_enable.c create mode 100644 glfw/lib/macosx/macosx_fullscreen.c create mode 100644 glfw/lib/macosx/macosx_glext.c create mode 100644 glfw/lib/macosx/macosx_init.c create mode 100644 glfw/lib/macosx/macosx_joystick.c create mode 100644 glfw/lib/macosx/macosx_thread.c create mode 100644 glfw/lib/macosx/macosx_time.c create mode 100644 glfw/lib/macosx/macosx_window.c create mode 100644 glfw/lib/macosx/platform.h create mode 100644 glfw/lib/stream.c create mode 100644 glfw/lib/tga.c create mode 100644 glfw/lib/thread.c create mode 100644 glfw/lib/time.c create mode 100644 glfw/lib/win32/platform.h create mode 100644 glfw/lib/win32/win32_dllmain.c create mode 100644 glfw/lib/win32/win32_enable.c create mode 100644 glfw/lib/win32/win32_fullscreen.c create mode 100644 glfw/lib/win32/win32_glext.c create mode 100644 glfw/lib/win32/win32_init.c create mode 100644 glfw/lib/win32/win32_joystick.c create mode 100644 glfw/lib/win32/win32_thread.c create mode 100644 glfw/lib/win32/win32_time.c create mode 100644 glfw/lib/win32/win32_window.c create mode 100644 glfw/lib/window.c create mode 100644 glfw/lib/x11/platform.h create mode 100644 glfw/lib/x11/x11_enable.c create mode 100644 glfw/lib/x11/x11_fullscreen.c create mode 100644 glfw/lib/x11/x11_glext.c create mode 100644 glfw/lib/x11/x11_init.c create mode 100644 glfw/lib/x11/x11_joystick.c create mode 100644 glfw/lib/x11/x11_keysym2unicode.c create mode 100644 glfw/lib/x11/x11_thread.c create mode 100644 glfw/lib/x11/x11_time.c create mode 100644 glfw/lib/x11/x11_window.c create mode 100644 glfw/license.txt create mode 100644 glfw/readme.html create mode 100644 libpng/Makefile create mode 100644 libpng/png.c create mode 100644 libpng/png.h create mode 100644 libpng/pngconf.h create mode 100644 libpng/pngerror.c create mode 100644 libpng/pngget.c create mode 100644 libpng/pngmem.c create mode 100644 libpng/pngpread.c create mode 100644 libpng/pngread.c create mode 100644 libpng/pngrio.c create mode 100644 libpng/pngrtran.c create mode 100644 libpng/pngrutil.c create mode 100644 libpng/pngset.c create mode 100644 libpng/pngtrans.c create mode 100644 libpng/pngwio.c create mode 100644 libpng/pngwrite.c create mode 100644 libpng/pngwtran.c create mode 100644 libpng/pngwutil.c create mode 100644 ode/CHANGELOG.txt create mode 100644 ode/INSTALL.txt create mode 100644 ode/LICENSE-BSD.TXT create mode 100644 ode/Makefile create mode 100644 ode/OPCODE/Ice/IceAABB.cpp create mode 100644 ode/OPCODE/Ice/IceAABB.h create mode 100644 ode/OPCODE/Ice/IceAxes.h create mode 100644 ode/OPCODE/Ice/IceBoundingSphere.h create mode 100644 ode/OPCODE/Ice/IceContainer.cpp create mode 100644 ode/OPCODE/Ice/IceContainer.h create mode 100644 ode/OPCODE/Ice/IceFPU.h create mode 100644 ode/OPCODE/Ice/IceHPoint.cpp create mode 100644 ode/OPCODE/Ice/IceHPoint.h create mode 100644 ode/OPCODE/Ice/IceIndexedTriangle.cpp create mode 100644 ode/OPCODE/Ice/IceIndexedTriangle.h create mode 100644 ode/OPCODE/Ice/IceLSS.h create mode 100644 ode/OPCODE/Ice/IceMatrix3x3.cpp create mode 100644 ode/OPCODE/Ice/IceMatrix3x3.h create mode 100644 ode/OPCODE/Ice/IceMatrix4x4.cpp create mode 100644 ode/OPCODE/Ice/IceMatrix4x4.h create mode 100644 ode/OPCODE/Ice/IceMemoryMacros.h create mode 100644 ode/OPCODE/Ice/IceOBB.cpp create mode 100644 ode/OPCODE/Ice/IceOBB.h create mode 100644 ode/OPCODE/Ice/IcePairs.h create mode 100644 ode/OPCODE/Ice/IcePlane.cpp create mode 100644 ode/OPCODE/Ice/IcePlane.h create mode 100644 ode/OPCODE/Ice/IcePoint.cpp create mode 100644 ode/OPCODE/Ice/IcePoint.h create mode 100644 ode/OPCODE/Ice/IcePreprocessor.h create mode 100644 ode/OPCODE/Ice/IceRandom.cpp create mode 100644 ode/OPCODE/Ice/IceRandom.h create mode 100644 ode/OPCODE/Ice/IceRay.cpp create mode 100644 ode/OPCODE/Ice/IceRay.h create mode 100644 ode/OPCODE/Ice/IceRevisitedRadix.cpp create mode 100644 ode/OPCODE/Ice/IceRevisitedRadix.h create mode 100644 ode/OPCODE/Ice/IceSegment.cpp create mode 100644 ode/OPCODE/Ice/IceSegment.h create mode 100644 ode/OPCODE/Ice/IceTriList.h create mode 100644 ode/OPCODE/Ice/IceTriangle.cpp create mode 100644 ode/OPCODE/Ice/IceTriangle.h create mode 100644 ode/OPCODE/Ice/IceTypes.h create mode 100644 ode/OPCODE/Ice/IceUtils.cpp create mode 100644 ode/OPCODE/Ice/IceUtils.h create mode 100644 ode/OPCODE/OPC_AABBCollider.cpp create mode 100644 ode/OPCODE/OPC_AABBCollider.h create mode 100644 ode/OPCODE/OPC_AABBTree.cpp create mode 100644 ode/OPCODE/OPC_AABBTree.h create mode 100644 ode/OPCODE/OPC_BaseModel.cpp create mode 100644 ode/OPCODE/OPC_BaseModel.h create mode 100644 ode/OPCODE/OPC_BoxBoxOverlap.h create mode 100644 ode/OPCODE/OPC_BoxPruning.cpp create mode 100644 ode/OPCODE/OPC_BoxPruning.h create mode 100644 ode/OPCODE/OPC_Collider.cpp create mode 100644 ode/OPCODE/OPC_Collider.h create mode 100644 ode/OPCODE/OPC_Common.cpp create mode 100644 ode/OPCODE/OPC_Common.h create mode 100644 ode/OPCODE/OPC_HybridModel.cpp create mode 100644 ode/OPCODE/OPC_HybridModel.h create mode 100644 ode/OPCODE/OPC_IceHook.h create mode 100644 ode/OPCODE/OPC_LSSAABBOverlap.h create mode 100644 ode/OPCODE/OPC_LSSCollider.cpp create mode 100644 ode/OPCODE/OPC_LSSCollider.h create mode 100644 ode/OPCODE/OPC_LSSTriOverlap.h create mode 100644 ode/OPCODE/OPC_MeshInterface.cpp create mode 100644 ode/OPCODE/OPC_MeshInterface.h create mode 100644 ode/OPCODE/OPC_Model.cpp create mode 100644 ode/OPCODE/OPC_Model.h create mode 100644 ode/OPCODE/OPC_OBBCollider.cpp create mode 100644 ode/OPCODE/OPC_OBBCollider.h create mode 100644 ode/OPCODE/OPC_OptimizedTree.cpp create mode 100644 ode/OPCODE/OPC_OptimizedTree.h create mode 100644 ode/OPCODE/OPC_Picking.cpp create mode 100644 ode/OPCODE/OPC_Picking.h create mode 100644 ode/OPCODE/OPC_PlanesAABBOverlap.h create mode 100644 ode/OPCODE/OPC_PlanesCollider.cpp create mode 100644 ode/OPCODE/OPC_PlanesCollider.h create mode 100644 ode/OPCODE/OPC_PlanesTriOverlap.h create mode 100644 ode/OPCODE/OPC_RayAABBOverlap.h create mode 100644 ode/OPCODE/OPC_RayCollider.cpp create mode 100644 ode/OPCODE/OPC_RayCollider.h create mode 100644 ode/OPCODE/OPC_RayTriOverlap.h create mode 100644 ode/OPCODE/OPC_Settings.h create mode 100644 ode/OPCODE/OPC_SphereAABBOverlap.h create mode 100644 ode/OPCODE/OPC_SphereCollider.cpp create mode 100644 ode/OPCODE/OPC_SphereCollider.h create mode 100644 ode/OPCODE/OPC_SphereTriOverlap.h create mode 100644 ode/OPCODE/OPC_SweepAndPrune.cpp create mode 100644 ode/OPCODE/OPC_SweepAndPrune.h create mode 100644 ode/OPCODE/OPC_TreeBuilders.cpp create mode 100644 ode/OPCODE/OPC_TreeBuilders.h create mode 100644 ode/OPCODE/OPC_TreeCollider.cpp create mode 100644 ode/OPCODE/OPC_TreeCollider.h create mode 100644 ode/OPCODE/OPC_TriBoxOverlap.h create mode 100644 ode/OPCODE/OPC_TriTriOverlap.h create mode 100644 ode/OPCODE/OPC_VolumeCollider.cpp create mode 100644 ode/OPCODE/OPC_VolumeCollider.h create mode 100644 ode/OPCODE/Opcode.cpp create mode 100644 ode/OPCODE/Opcode.dsp create mode 100644 ode/OPCODE/Opcode.dsw create mode 100644 ode/OPCODE/Opcode.h create mode 100644 ode/OPCODE/README-ODE.txt create mode 100644 ode/OPCODE/ReadMe.txt create mode 100644 ode/OPCODE/StdAfx.cpp create mode 100644 ode/OPCODE/Stdafx.h create mode 100644 ode/OPCODE/TemporalCoherence.txt create mode 100644 ode/README.txt create mode 100644 ode/include/ode/README create mode 100644 ode/include/ode/collision.h create mode 100644 ode/include/ode/collision_space.h create mode 100644 ode/include/ode/collision_trimesh.h create mode 100644 ode/include/ode/common.h create mode 100644 ode/include/ode/compatibility.h create mode 100644 ode/include/ode/config.h create mode 100644 ode/include/ode/contact.h create mode 100644 ode/include/ode/error.h create mode 100644 ode/include/ode/export-dif.h create mode 100644 ode/include/ode/mass.h create mode 100644 ode/include/ode/matrix.h create mode 100644 ode/include/ode/memory.h create mode 100644 ode/include/ode/misc.h create mode 100644 ode/include/ode/objects.h create mode 100644 ode/include/ode/ode.h create mode 100644 ode/include/ode/odecpp.h create mode 100644 ode/include/ode/odecpp_collision.h create mode 100644 ode/include/ode/odecpp_old.h create mode 100644 ode/include/ode/odemath.h create mode 100644 ode/include/ode/rotation.h create mode 100644 ode/include/ode/timer.h create mode 100644 ode/src/array.cpp create mode 100644 ode/src/array.h create mode 100644 ode/src/box.cpp create mode 100644 ode/src/capsule.cpp create mode 100644 ode/src/collision_cylinder_box.cpp create mode 100644 ode/src/collision_cylinder_plane.cpp create mode 100644 ode/src/collision_cylinder_sphere.cpp create mode 100644 ode/src/collision_cylinder_trimesh.cpp create mode 100644 ode/src/collision_kernel.cpp create mode 100644 ode/src/collision_kernel.h create mode 100644 ode/src/collision_quadtreespace.cpp create mode 100644 ode/src/collision_space.cpp create mode 100644 ode/src/collision_space_internal.h create mode 100644 ode/src/collision_std.h create mode 100644 ode/src/collision_transform.cpp create mode 100644 ode/src/collision_transform.h create mode 100644 ode/src/collision_trimesh_box.cpp create mode 100644 ode/src/collision_trimesh_ccylinder.cpp create mode 100644 ode/src/collision_trimesh_distance.cpp create mode 100644 ode/src/collision_trimesh_gimpact.cpp create mode 100644 ode/src/collision_trimesh_internal.h create mode 100644 ode/src/collision_trimesh_opcode.cpp create mode 100644 ode/src/collision_trimesh_plane.cpp create mode 100644 ode/src/collision_trimesh_ray.cpp create mode 100644 ode/src/collision_trimesh_sphere.cpp create mode 100644 ode/src/collision_trimesh_trimesh.cpp create mode 100644 ode/src/collision_util.cpp create mode 100644 ode/src/collision_util.h create mode 100644 ode/src/convex.cpp create mode 100644 ode/src/cylinder.cpp create mode 100644 ode/src/error.cpp create mode 100644 ode/src/export-dif.cpp create mode 100644 ode/src/fastdot.c create mode 100644 ode/src/fastldlt.c create mode 100644 ode/src/fastlsolve.c create mode 100644 ode/src/fastltsolve.c create mode 100644 ode/src/heightfield.cpp create mode 100644 ode/src/heightfield.h create mode 100644 ode/src/joint.cpp create mode 100644 ode/src/joint.h create mode 100644 ode/src/lcp.cpp create mode 100644 ode/src/lcp.h create mode 100644 ode/src/mass.cpp create mode 100644 ode/src/mat.cpp create mode 100644 ode/src/mat.h create mode 100644 ode/src/matrix.cpp create mode 100644 ode/src/memory.cpp create mode 100644 ode/src/misc.cpp create mode 100644 ode/src/objects.h create mode 100644 ode/src/obstack.cpp create mode 100644 ode/src/obstack.h create mode 100644 ode/src/ode.cpp create mode 100644 ode/src/odemath.cpp create mode 100644 ode/src/plane.cpp create mode 100644 ode/src/quickstep.cpp create mode 100644 ode/src/quickstep.h create mode 100644 ode/src/ray.cpp create mode 100644 ode/src/rotation.cpp create mode 100644 ode/src/sphere.cpp create mode 100644 ode/src/step.cpp create mode 100644 ode/src/step.h create mode 100644 ode/src/stepfast.cpp create mode 100644 ode/src/timer.cpp create mode 100644 ode/src/util.cpp create mode 100644 ode/src/util.h create mode 100644 physfs/CHANGELOG.txt create mode 100644 physfs/CREDITS.txt create mode 100644 physfs/LICENSE.txt create mode 100644 physfs/Makefile create mode 100644 physfs/TODO.txt create mode 100644 physfs/archivers/dir.c create mode 100644 physfs/archivers/zip.c create mode 100644 physfs/physfs.c create mode 100644 physfs/physfs.h create mode 100644 physfs/physfs_byteorder.c create mode 100644 physfs/physfs_casefolding.h create mode 100644 physfs/physfs_internal.h create mode 100644 physfs/physfs_platforms.h create mode 100644 physfs/physfs_unicode.c create mode 100644 physfs/platform/macosx.c create mode 100644 physfs/platform/posix.c create mode 100644 physfs/platform/unix.c create mode 100644 physfs/platform/windows.c create mode 100644 squirrel/Makefile create mode 100644 squirrel/include/squirrel.h create mode 100644 squirrel/squirrel/sqapi.cpp create mode 100644 squirrel/squirrel/sqarray.h create mode 100644 squirrel/squirrel/sqbaselib.cpp create mode 100644 squirrel/squirrel/sqclass.cpp create mode 100644 squirrel/squirrel/sqclass.h create mode 100644 squirrel/squirrel/sqclosure.h create mode 100644 squirrel/squirrel/sqcompiler.cpp create mode 100644 squirrel/squirrel/sqcompiler.h create mode 100644 squirrel/squirrel/sqdebug.cpp create mode 100644 squirrel/squirrel/sqfuncproto.h create mode 100644 squirrel/squirrel/sqfuncstate.cpp create mode 100644 squirrel/squirrel/sqfuncstate.h create mode 100644 squirrel/squirrel/sqlexer.cpp create mode 100644 squirrel/squirrel/sqlexer.h create mode 100644 squirrel/squirrel/sqmem.cpp create mode 100644 squirrel/squirrel/sqobject.cpp create mode 100644 squirrel/squirrel/sqobject.h create mode 100644 squirrel/squirrel/sqopcodes.h create mode 100644 squirrel/squirrel/sqpcheader.h create mode 100644 squirrel/squirrel/sqstate.cpp create mode 100644 squirrel/squirrel/sqstate.h create mode 100644 squirrel/squirrel/sqstring.h create mode 100644 squirrel/squirrel/sqtable.cpp create mode 100644 squirrel/squirrel/sqtable.h create mode 100644 squirrel/squirrel/squserdata.h create mode 100644 squirrel/squirrel/squtils.h create mode 100644 squirrel/squirrel/sqvm.cpp create mode 100644 squirrel/squirrel/sqvm.h create mode 100644 tinyxml/Makefile create mode 100644 tinyxml/tinyxml.cpp create mode 100644 tinyxml/tinyxml.h create mode 100644 tinyxml/tinyxmlerror.cpp create mode 100644 tinyxml/tinyxmlparser.cpp create mode 100644 trimeshloader/Changelog.txt create mode 100644 trimeshloader/Makefile create mode 100644 trimeshloader/Readme.txt create mode 100644 trimeshloader/ToDo.txt create mode 100644 trimeshloader/include/tl3ds.h create mode 100644 trimeshloader/include/tlobj.h create mode 100644 trimeshloader/include/trimeshloader.h create mode 100644 trimeshloader/src/tl3ds.c create mode 100644 trimeshloader/src/tlobj.c create mode 100644 trimeshloader/src/trimeshloader.c create mode 100644 zlib/Makefile create mode 100644 zlib/adler32.c create mode 100644 zlib/compress.c create mode 100644 zlib/crc32.c create mode 100644 zlib/crc32.h create mode 100644 zlib/deflate.c create mode 100644 zlib/deflate.h create mode 100644 zlib/gzio.c create mode 100644 zlib/infback.c create mode 100644 zlib/inffast.c create mode 100644 zlib/inffast.h create mode 100644 zlib/inffixed.h create mode 100644 zlib/inflate.c create mode 100644 zlib/inflate.h create mode 100644 zlib/inftrees.c create mode 100644 zlib/inftrees.h create mode 100644 zlib/trees.c create mode 100644 zlib/trees.h create mode 100644 zlib/uncompr.c create mode 100644 zlib/zconf.h create mode 100644 zlib/zlib.h create mode 100644 zlib/zutil.c create mode 100644 zlib/zutil.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b707a64 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +all clean: + @$(MAKE) -s -C zlib $@ + @$(MAKE) -s -C libpng $@ + @$(MAKE) -s -C glfw $@ + @$(MAKE) -s -C trimeshloader $@ + @$(MAKE) -s -C physfs $@ + @$(MAKE) -s -C freetype $@ + @$(MAKE) -s -C ftgl $@ + @$(MAKE) -s -C bullet $@ + @$(MAKE) -s -C corona $@ + @$(MAKE) -s -C glew $@ + @$(MAKE) -s -C squirrel $@ + @$(MAKE) -s -C ode $@ + @$(MAKE) -s -C tinyxml $@ + @$(MAKE) -s -C engine $@ + @echo done diff --git a/Makefile.common b/Makefile.common new file mode 100644 index 0000000..540c555 --- /dev/null +++ b/Makefile.common @@ -0,0 +1,41 @@ +ifdef RELEASE + # RELEASE CFLAGS + #export CFLAGS := -fmessage-length=0 -O3 -fomit-frame-pointer -pipe -march=pentium-m -msse2 -msse -mmmx + export CFLAGS := -O2 -fomit-frame-pointer -pipe -march=i686 + export CXXFLAGS := $(CFLAGS) + export LDFLAGS := -s +else + # DEBUG CFLAGS + export CFLAGS := -g3 -fmessage-length=0 -Wall -DDEBUG -pipe + export CXXFLAGS := $(CFLAGS) + export LDFLAGS := +endif + +ifeq ($(MAKE),mingw32-make) + # Windows + export OS = WIN32 + export RM = del /F + export RMSUB = del /F /s /Q + export EXT = .exe + export SLASH = \\ + export CC = gcc + export CP = copy + export MKDIR = mkdir +else + # Linux + export OS = LINUX + export RM = rm -f + export RMSUB = rm -Rf + export EXT = + export SLASH = / + export CP = cp + export MKDIR = mkdir -p +endif + +%.o: %.cpp + @echo Compiling $@ ... + @$(CXX) -c $(CXXFLAGS) -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o $@ $< + +%.o: %.c + @echo Compiling $@ ... + @$(CC) -c $(CFLAGS) -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o $@ $< diff --git a/Versions.txt b/Versions.txt new file mode 100644 index 0000000..17c67c1 --- /dev/null +++ b/Versions.txt @@ -0,0 +1,11 @@ +libpng: 1.2.8 +zlib: 1.2.3 +glfw: 2.6.beta1 +trimeshloader: 0.0.12 +physfs: 1.1.1 +freetype: 2.2.1 +ftgl: 2.1.2 +bullet: 2.56 +corona: 1.0.2 +glew: 1.4.0 +tinyxml: 2.3.4 diff --git a/bullet/AUTHORS b/bullet/AUTHORS new file mode 100644 index 0000000..f2cc86d --- /dev/null +++ b/bullet/AUTHORS @@ -0,0 +1,22 @@ + +Bullet Physics Library is an open source project with help from the community at the Physics Forum +See the forum at http://bulletphysics.com + +The project was started by Erwin Coumans + +Following people contributed to Bullet +(random order, please let us know on the forum if your name should be in this list) + +Gino van den Bergen: LinearMath classes +Christer Ericson: parts of the voronoi simplex solver +Simon Hobbs: 3d axis sweep and prune, Extras/SATCollision, separating axis theorem + SIMD code +Dirk Gregorius: generic D6 constraint +Erin Catto: accumulated impulse in sequential impulse +Nathanael Presson: EPA penetration depth calculation +Francisco Leon: GIMPACT Concave Concave collision +Joerg Henrichs: make buildsystem (work in progress) +Eric Sunshine: jam + msvcgen buildsystem +Steve Baker: GPU physics and general implementation improvements +Jay Lee: Double precision support +KleMiX, aka Vsevolod Klementjev, managed version, rewritten in C# for XNA +Erwin Coumans: most other source code diff --git a/bullet/BulletLicense.txt b/bullet/BulletLicense.txt new file mode 100644 index 0000000..d428fab --- /dev/null +++ b/bullet/BulletLicense.txt @@ -0,0 +1,17 @@ +/* +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. +*/ + + +Free for commercial use, but please mail bullet@erwincoumans.com to report projects, and join the forum at +www.continuousphysics.com/Bullet/phpBB2 diff --git a/bullet/ChangeLog.txt b/bullet/ChangeLog.txt new file mode 100644 index 0000000..1fd86ad --- /dev/null +++ b/bullet/ChangeLog.txt @@ -0,0 +1,357 @@ +Bullet Continuous Collision Detection and Physics Library +Primary author and maintainer: Erwin Coumans + +2007 Aug 15 + - fixed bug in Extras/GIMPACT 0.2 related to moving triangle meshes + Thanks Thomas, http://www.continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=1368 + +2007 Aug 14 + - added parallel constraint solver. Works on Playstation 3 Cell SPU and multi core (Win Threads on PC and XBox 360). + See Extras/BulletMultiThreaded for SpuSolverTask subfolder and SpuParallelSolver.cpp + Thanks Marten Svanfeldt (Starbreeze Studios) + - fixed some bugs related to parallel collision detection (Extras/BulletMultiThreaded) + Thanks Marten Svanfeldt (Starbreeze Studios) + +2007 Aug 2 + - added compound and concave-convex (swapped) case for BulletMultiThreaded collision detection, thanks to Marten Svanfeldt + - refactored broadphase and overlapping pair cache. This allows performance improvement by combining multiple broadphases. This helps add/remove of large batches of objects and large worlds. See also Pierre Terdiman forum topic: + http://www.continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=1329 + + +2007 July 27 + - added Ragdoll Demo + Thanks to Marten Svanfeldt (Starbreeze Studios) + + - added Vector Math library for SIMD 3D graphics linear algebra (vector, matrix, quaternion) + See Bullet/Extras/vectormathlibrary + Supports SIMD SSE, PowerPC PPU and Cell SPU (including PS3 Linux and CellBlade), as well as generic portable scalar version + Will be used to improve BulletMultiThreaded performance + Open Sourced by Sony Computer Entertainment Inc. under the new BSD license + - added SIMD math library + 4-way SIMD for common math functions like atan2f4, cosf4, floorf4, fabsf4, rsqrtf4 etc. Used by Vector Math library under PPU and SPU. + Supports PowerPC (PPU) and Cell SPU, including PS3 Linux and CellBlade. + See Bullet/Extras/simdmathlibrary + Open sourced by Sony Computer Entertainment Inc. under the new BSD license + + +2007 July 25 + - added several patches: per-rigidbody sleeping threshold. added Assert to prevent deletion of rigidbody while constraints are still pointing at it + Thanks to Marten Svanfeldt (Starbreeze Studios) + +2007 July 13 + - fixed relative #include paths again. We can't use "../" relative paths: some compilers choke on it (it causes extreme long paths) + Within the libraries, we always need to start with "BulletCollision/" or "BulletDynamics/ or "LinearMath/" + +2007 July 10 + - Updated Bullet User Manual + +2007 July 5 + - added btConeTwistConstraint, especially useful for ragdolls. See Demos/RagdollDemo + Thanks to Marten Svanfeldt (Starbreeze Studios) + +2007 June 29 + - btHeightfieldTerrainShape: Added heightfield support, with customizations + - Upgraded to GIMPACT 0.2, see Extras/GIMPACT and MovingConcaveDemo + - Several patches from Marten Svanfeldt (Starbreeze Studios) + Improved collision filtering (in broadphase and rigidbody) + Improved debug rendering + Allow to set collision filter group/mask in addRigidBody + + +2007 June 15 + - Changed btAlignedObjectArray to call copy constructor/replacement new for duplication, rather then assignment operator (operator=). + +2007 June 11 + - Added multi-threading. Originally for Playstation 3 Cell SPU, but the same code can run using Win32 Threads using fake DMA transfers (memcpy) + Libspe2 support for Cell Blade / PS3 Linux is upcoming + See Extras/BulletMultiThreaded. Usage: replace btCollisionDispatcher by btSpuGatheringCollisionDispatcher + + - Added managed Bullet library, entirely rewritten in C# for Windows and XBox 360 XNA + See Extras/BulletX + Thanks to KleMiX, aka Vsevolod Klementjev + +2007 May 31 + - sign-bit went wrong in case of 32-bit broadphase, causing quantization problems. + Thanks DevO for reporting. + +2007 May 23 + - Fixed quantization problem for planar triangle meshes in btOptimizedBvh + Thanks Phil Knight for reporting and helping to fix this bug. + +2007 May 20 + - btAxisSweep3: Fixed a bug in btAxisSweep3 (sweep and prune) related to object removal. Only showed up when at least one btStaticPlaneShape was inserted. + Thanks tbp for more details on reproducing case. + - btAxisSweep3: Fixed issue with full 32bit precision btAxisSweep3 (define BP_USE_FIXEDPOINT_INT_32), it used only 0xffff/65536 for quantization instead of full integer space (0xffffffff) + - btRaycastVehicle: Added 'getForwardVector' and getCurrentSpeedKmHour utility functions + - Fixed local scaling issues (btConvexTriangleMeshShape, btBvhTriangleMeshShape, removed scaling from btMatrix3x3). + Thanks Volker for reporting! + - Added second filename search, so that starting BspDemo and ConvexDecompositionDemo from within Visual Studio (without setting the starting path) still works + +2007 April 22 + - Added braking functionality to btRaycastVehicle + - Removed tons of warnings, under MSVC 2005 compilation in -W4 + +2007 March 21 + - Fixed issues: comma at end of enum causes errors for some compilers + - Fixed initialization bug in LocalRayResult ( m_localShapeInfo(localShapeInfo) ) + +2007 March 20 + - Added refit tree to quantized stackless tree, and updated ConcaveDemo as example. + +2007 March 17 + - Added constraint solver optimizations, avoiding cross products during iterations, and gather rigidbody/constraint info in contiguous memory (btSolverBody/btSolverConstraint) + - These optimizations don't give large benefit yet, but it has good potential. Turned on by default. Can be switched off using solver->setSolverMode(SOLVER_RANDMIZE_ORDER). + - Enabled anti-jitter for rigid bodies. This is experimental, and can be switched off by setting a global (it is experimental so no proper interface) gJitterVelocityDampingFactor = 1.0; + - Fixed bug in islandmanifold.heapSort(btPersistentManifoldSortPredicate()); , thanks Noehrgel for reporting this (affected Sun Solaris) + +2007 March 12 + - Added compile-time toggle between on 16-bit and 32-bit fixed-point SAP broadphase. + This allows the number of bodies to exceed 32767 + - Enable useQuantizedAabbCompression on btTriangleMesh, see ColladaDemo + +2007 March 8 + - Fixed bug in constraint/island sorting (caused by replacing STL by dedicated btAlignedObjectArray with heapSort) + Thanks Clemens Unterkofler for pointing this out! + +2007 March 6 + - removed STL from the Bullet library: replace std::vector by btAlignedObjectArray. Also removed the std::set for overlapping pair set, and turned it into an overlapping pair array. The SAP only adds objects, never removed. Removal is postponed for during traversal of overlapping pairs (duplicates and non-overlapping pairs are removed during that traversal). + - added heap sort and binary search/linear search to btAlignedObjectArray + - fixed wrong cast, thanks Hamstray, http://www.continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=1015 + + +2007 Feb 25 + - Improved performance of convex collision shapes, cache local AABB instead of recomputation. This fixes issue with very slow performance in larger .bsp levels + +2007 Feb 24 + - Added compressed/quantized AABB tree, 16 bytes per node, while supporting 32-bit (triangle) indices. + Should be faster and smaller then original version (quantized aabb check is done in integer space) + Original aabb tree nodes are still supported. They are 44 bytes, with full floating point precision and additional subPart index. + - added meter-unit scaling support in ColladaConverter.cpp + +2007 Feb 21 + - Build system: updated bullet.pc.in library names + - Updated EPA comparison integration (missing parameter) + +2007 Jan 04 + - fixed optimized AABB tree building: in some cases the tree building fails due to unbalanced trees, which generated stack overflow + +2006 Dec 15 + - added contribution to allow double precision collision detection/dynamics. Define BT_USE_DOUBLE_PRECISION in your project and libraries that include Bullet + +2006 Dec 14 + - merged contact and non-contact constraint solving into one loop, will improve stability of jointed bodies during collisions + - added first draft for hingeConstraint motor + +2006 Dec 8, Erwin Coumans + - preparation for SIMD: added btAlignedAllocator and btAlignedObjectArray, to replace stl std::vector, same interface, but compatible with 16 byte alignment + - cleaned up dependencies in autogenerated msvc projectfiles + - aligned btVector3 on 16 bytes boundary, under win32. see if developers will come up with problems + +2006 Dec 04, Erwin Coumans + Added btNearCallback. This is similar to Open Dynamics Engine (ODE) dNearCallback, but important differences: + - contact points are persistent (lifetime more then one frame, for warmstarting/incremental contact point management) + - continuous collision detection, time of impact + Added btRigidBody::isInWorld(), returns true if btRigidBody is inside a btCollisionWorld/btDynamicsWorld derived class + Added angularFactor to btRigidbody, this helps some character control (no angular impulse applied) + + +2006 Nov 28 + Moved StackAlloc from EPA into LinearMath/btStackAlloc + renamed internal class ConcaveShape into btConcaveShape + added btHeightfieldTerrainShape (not completed yet) + +2006 Nov 15 Nathanael Presson + Added EPA penetration depth algorithm, Expanding Polytope Algorithm + Added Pierre Terdiman penetration depth comparison/test DEMO + Fixed Bullet's Minkowski sampling penetration depth solver + Contributed by Nathanael Presson + +2006 Nov 11 Francisco León Nájera + Added GIMPACT trimesh collision detection: concave versus concave, + Contributed by Francisco León Nájera + +2006 Nov 2 + Minor refactoring: btCollisionObject changes from struct into class, added accessor methods + Force use of btMotionState to synchronize graphics transform, disabled old btRigidBody constructor that accepts btTransform + Renamed treshold into threshold throughout the code + +2006 Oct 30 + Enable decoupling of physics and graphics framerate using interpolation and internal fixed timestep, based on btMotionState + Enabled raycast vehicle demo (still needs tuning) + Refresh contact points, even when they are already persistent. + Fixed debugDraw colors (thanks pc0de for reporting) + Use Dispatcher in ConcaveConvexCollisionAlgorithm (so it uses the registered collision algorithm, not hardcoded convexconcave) + Improved performance of constraint solver by precalculating the cross product/impulse arm + Added collision comparison code: ODE box-box, also sphere-triangle + Added safety check into GJK, and an assert for AABB's that are very large + Fixed kinematic support (deriving velocities for animated objects) + Updated comparison/optional quickstep solver in Extras + UserCollisionAlgorithm demonstrates btTriangleMesh usage (easier trimesh compared to index array version) + Removed scaling from btTransform (we only want to deal with rigid transforms) + +2006 Oct 4 + Fixed minor leak in btOptimizeBVH + Cleanup of btRigidBody construction + added getW() in btQuaternion + assert when setLinearVelocity is called on btRigidBody + renamed projectfile library from collada-dom to colladadom (to make VC6 happy) + +2006 Sept 27 + Big Refactoring: renamed and moved files, create a replacement for CcdPhysicsEnvironment/CcdPhysicsController. + All Bullet classes in LinearMath, BulletCollision and BulletDynamics start with bt, and methods start with lowercase. + Moved classes into src folder, which is the only include folder needed. + Added 2 headerfiles in src: btBulletCollisionCommon.h and btBulletDynamicsCommon.h + +2006 Sept 23 + Fixed 2 bugs, causing crashes when removing objects. Should do better unit-testing. UnionFind and 3D SAP were involved. + +2006 Sept 19 + Allow programmable friction and contact solver model. User can register their own functions for several interaction types. + Improved performance, and removed hardcoded maximum overlaps (switched from C-array to stl::set) + +2006 Sept 16 + Added Bullet 2.0 User Manual + Allow registration of custom user collision algorithms + +2006 Sept 10 + Started cleaning up demos + +2006 Sept 4 + Fixed concave collision bug (caused instability/missing collisions in meshes/compounds) + Fixed memoryleak in OptimizedBvh, added RayTestSingle to CollisionWorld + Prepared for VehicleDemo + Increased Performance (island generation for sleeping objects took too much time) + Better COLLADA 1.4.1 physics conformance in ColladaDemo + +2006 August 11 + Added Quake BspDemo + Improved CCD for compound and non-convex objects + +2006 August 10 + Added per-triangle material (friction/restitution) support for non-convex meshes. See ConcaveDemo for usage. + +2006 August 9 + Added CMake support (see http://cmake.org) + This can autogenerate makefiles, projectfiles cross platform (including MacOS X Xcode ) + Just run cmake . in the root folder and it will autogenerate build files + +2006 July 26 Erwin Coumans + Upgraded to COLLADA-DOM 1.4.1, latest SVN version + ColladaDemo can export snapshots to .dae + +2006 July 24 Erwin Coumans + Added Compound CollisionShape support + (this is still low performance -> requires stackless tree-versus-tree traversal for better performance) + +2006 July 15 Erwin Coumans + Added initial support for Parallel execution (collision detection, constraint solving) + See ParallelPhysicsEnvironment in Extras\PhysicsInterface\CcdPhysics + +2006 July 10 Erwin Coumans + Added MacOS X support (some build issues mainly) + +2006 July 5 Erwin Coumans + Improved COLLADA 1.4 physics import, both COLLADA-DOM and FCollada + +2006 June 29 Erwin Coumans + Refactoring of the broadphase + Moved some optional files to Extras: Algebraic ccd and EPA, quickstep + Moved the limits on bodies/overlap to 32k and 65k + +2006 June 25 Erwin Coumans + Added basic Collision Filtering, during broadphase + Allow adding meshes to the TriangleIndexVertexArray, + (input for TriangleMeshShape) + Preparation for CompoundShape + +2006 June 19 Erwin Coumans + Added support for COLLADA Physics Import. + Both jam and Visual Studio can compile ColladaDemo + +2006 June 18 Dirk Gregorius + Started implementing Generic6DOF joint and setup basic interface + + +2006 June 17 Frank Richter + Bumped version in configure.ac to 1.5.6 (assuming that "1.5f" is + the next version released). + Updated files in mk/autoconf and mk/jam with copies from CS; fixes a + GLU detection issue on MinGW. + Set msvc/bullet_ico.ico as the default application icon. + Disabled exceptions for gcc builds. + Applied a patch from Michael D. Adams to fix a warning with gcc. +2006 jUNE 16 Erwin Coumans + Constraints now merge simulation islands. + +2006 May 24 + Improved GJK accuracy, fixed GjkConvexCast issue, thanks to ~MyXa~ for reporting + +2006 May 19 + Added restitution support + Moved out Friction and Dynamics info from ManifoldPoint (removed logical dependency) + Added a void* m_userPersistentData in ManifoldPoint. + Added a ContactDestroyedCallback, to allow user to handle destruction of m_userPersistentData + +2006 May 13 + Fixed some bugs in friction / jacobian calculations. Reported by Dirk Gregorius. Thanks! + +2006 May 9 + Fixed raycasting filtering + Moved repository to SVN at https://svn.sourceforge.net/svnroot/bullet + +2006 April 27 + Moved raycasting to CollisionWorld, to make it more generic + Added basic CCD option in the CcdCollisionDemo + Fixed 'noResponse' mode, for triggering rigidbodies (useful for Artificial Intelligence queries) + Improved Bullet/ODE sample (in Extras) + +2006 April 10 + Separating Axis Test (SAT) convex hull collision detector, contribution by Simon Hobbs + Added SIMD SSE Math classes (for above SAT) + Added Mouse picking in CcdPhysicsDemo + Improved penetration depth estimation in MinkowskiPenetrationDepthSolver, both accuracy and performance + Added Hinge constraint + Added quickprof profiling (see http://sourceforge.net/projects/quickprof ) + +2006 March 21 Frank Richter + Removed VC manifest files. + Removed superfluous "grpplugins" projects. + +2006 March 20 Erwin Coumans + Clamped the acculumated impulse rather then intermediate impulse (within the iteration) + Use the persistent contacts for reusing the impulse + Separated friction and normal solving for better stability + Decreased the default number of iterations of the constraint solver from 10 to 4 + +2006 March 19 Frank Richter + Removed a couple of CSisms from the VC projects. + Fixed VC include & lib paths to go to the Addtional* options + instead the command line arguments. + Added pkgconfig support. + +2006 March 14 Frank Richter + Added support for shipped GLUT on MinGW. + Fixed GLUT support on MinGW. + +2006 March 13 Frank Richter + Bolted on Jam-based build system. + Generated VC project files. + Fixed GCC warnings. + Fixed Linux build issues. + +2006 March 13 +Added 3D Sweep and Prune Broadphase Collision Detection, Contribution from Simon Hobbs. + +2006 March 2 + Minor change in license to ZLib/LibPNG + This makes it legally a bit easier to deploy on Playstation 3 + Prepared for more generic constraints, added ConstraintsDemo + +2006 Feb 23 + Rearranged files and dependencies to allow for easier standalone Collision Detection without Bullet Dynamics. + See Demos/CollisionInterfaceDemo and Extras/ode/ode/test/test_BulletGjk.cpp for examples how to use. + + +... todo: add history + +2003 Initial version (continuous collision detection) diff --git a/bullet/LICENSE b/bullet/LICENSE new file mode 100644 index 0000000..ba24a53 --- /dev/null +++ b/bullet/LICENSE @@ -0,0 +1,19 @@ +/* +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. +*/ + +All files in the Bullet/src folder are under this Zlib license. +Optional Extras/GIMPACT and Extras/GIMPACTBullet is also under ZLib license. Other optional external libraries in Extras/Demos have own license,see respective files. + +This means Bullet can freely be used in any software, including commercial and console software. A Playstation 3 optimized version is available through Sony. diff --git a/bullet/Makefile b/bullet/Makefile new file mode 100644 index 0000000..158bbe6 --- /dev/null +++ b/bullet/Makefile @@ -0,0 +1,42 @@ +include ../Makefile.common + +OBJ += $(patsubst %.cpp,%.o,$(wildcard src/*.cpp)) $(patsubst %.cpp,%.o,$(wildcard src/*/*.cpp)) $(patsubst %.cpp,%.o,$(wildcard src/*/*/*.cpp)) +LIBNAME = libbullet.a +CXXFLAGS = -Isrc + +all: $(LIBNAME) + +$(LIBNAME): $(OBJ) + @echo Creating archive $@ + @ar -crsu $@ $(OBJ) + @echo + +clean: + -@$(RM) src$(SLASH)*.o + -@$(RM) src$(SLASH)*.d + + -@$(RM) src$(SLASH)LinearMath$(SLASH)*.o + -@$(RM) src$(SLASH)LinearMath$(SLASH)*.d + + -@$(RM) src$(SLASH)BulletDynamics$(SLASH)ConstraintSolver$(SLASH)*.o + -@$(RM) src$(SLASH)BulletDynamics$(SLASH)ConstraintSolver$(SLASH)*.d + + -@$(RM) src$(SLASH)BulletDynamics$(SLASH)Dynamics$(SLASH)*.o + -@$(RM) src$(SLASH)BulletDynamics$(SLASH)Dynamics$(SLASH)*.d + + -@$(RM) src$(SLASH)BulletDynamics$(SLASH)Vehicle$(SLASH)*.o + -@$(RM) src$(SLASH)BulletDynamics$(SLASH)Vehicle$(SLASH)*.d + + -@$(RM) src$(SLASH)BulletCollision$(SLASH)BroadphaseCollision$(SLASH)*.o + -@$(RM) src$(SLASH)BulletCollision$(SLASH)BroadphaseCollision$(SLASH)*.d + + -@$(RM) src$(SLASH)BulletCollision$(SLASH)CollisionDispatch$(SLASH)*.o + -@$(RM) src$(SLASH)BulletCollision$(SLASH)CollisionDispatch$(SLASH)*.d + + -@$(RM) src$(SLASH)BulletCollision$(SLASH)CollisionShapes$(SLASH)*.o + -@$(RM) src$(SLASH)BulletCollision$(SLASH)CollisionShapes$(SLASH)*.d + + -@$(RM) src$(SLASH)BulletCollision$(SLASH)NarrowPhaseCollision$(SLASH)*.o + -@$(RM) src$(SLASH)BulletCollision$(SLASH)NarrowPhaseCollision$(SLASH)*.d + + -@$(RM) $(LIBNAME) diff --git a/bullet/NEWS b/bullet/NEWS new file mode 100644 index 0000000..d976ae4 --- /dev/null +++ b/bullet/NEWS @@ -0,0 +1,4 @@ + +For news, visit the Bullet Physics Forum at +http://www.continuousphysics.com/Bullet/phpBB2/viewforum.php?f=9 + diff --git a/bullet/README b/bullet/README new file mode 100644 index 0000000..8f37053 --- /dev/null +++ b/bullet/README @@ -0,0 +1,7 @@ + +Bullet is a 3D Collision Detection and Rigid Body Dynamics Library for games and animation. +Free for commercial use, including Playstation 3, open source under the ZLib License. +Discrete and continuous collision detection, integrated into Blender 3D, and COLLADA 1.4 Physics import. + +See the Bullet_User_Manual.pdf for more info and visit the Bullet Physics Forum at +http://bulletphysics.com diff --git a/bullet/VERSION b/bullet/VERSION new file mode 100644 index 0000000..9899922 --- /dev/null +++ b/bullet/VERSION @@ -0,0 +1,3 @@ +Bullet Collision Detection and Physics Library version 2.56 +http://bullet.sourceforge.net + diff --git a/bullet/src/BulletCollision/BroadphaseCollision/btAxisSweep3.cpp b/bullet/src/BulletCollision/BroadphaseCollision/btAxisSweep3.cpp new file mode 100644 index 0000000..ec1ab18 --- /dev/null +++ b/bullet/src/BulletCollision/BroadphaseCollision/btAxisSweep3.cpp @@ -0,0 +1,680 @@ + +//Bullet Continuous Collision Detection and Physics Library +//Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + + +// +// btAxisSweep3 +// +// Copyright (c) 2006 Simon Hobbs +// +// 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 "btAxisSweep3.h" + +#include + +#ifdef DEBUG_BROADPHASE +#include +void btAxisSweep3::debugPrintAxis(int axis, bool checkCardinality) +{ + int numEdges = m_pHandles[0].m_maxEdges[axis]; + printf("SAP Axis %d, numEdges=%d\n",axis,numEdges); + + int i; + for (i=0;im_handle); + int handleIndex = pEdge->IsMax()? pHandlePrev->m_maxEdges[axis] : pHandlePrev->m_minEdges[axis]; + char beginOrEnd; + beginOrEnd=pEdge->IsMax()?'E':'B'; + printf(" [%c,h=%d,p=%x,i=%d]\n",beginOrEnd,pEdge->m_handle,pEdge->m_pos,handleIndex); + } + + if (checkCardinality) + assert(numEdges == m_numHandles*2+1); +} +#endif //DEBUG_BROADPHASE + + +btBroadphaseProxy* btAxisSweep3::createProxy( const btVector3& aabbMin, const btVector3& aabbMax,int shapeType,void* userPtr,short int collisionFilterGroup,short int collisionFilterMask) +{ + (void)shapeType; + BP_FP_INT_TYPE handleId = addHandle(aabbMin,aabbMax, userPtr,collisionFilterGroup,collisionFilterMask); + + Handle* handle = getHandle(handleId); + + return handle; +} + +void btAxisSweep3::destroyProxy(btBroadphaseProxy* proxy) +{ + Handle* handle = static_cast(proxy); + removeHandle(handle->m_handleId); +} + +void btAxisSweep3::setAabb(btBroadphaseProxy* proxy,const btVector3& aabbMin,const btVector3& aabbMax) +{ + Handle* handle = static_cast(proxy); + updateHandle(handle->m_handleId,aabbMin,aabbMax); + +} + + + + + + +btAxisSweep3::btAxisSweep3(const btPoint3& worldAabbMin,const btPoint3& worldAabbMax, int maxHandles, btOverlappingPairCache* pairCache) +:m_invalidPair(0), +m_pairCache(pairCache), +m_ownsPairCache(false) +{ + if (!m_pairCache) + { + m_pairCache = new btOverlappingPairCache(); + m_ownsPairCache = true; + } + + //assert(bounds.HasVolume()); + + // 1 handle is reserved as sentinel + btAssert(maxHandles > 1 && maxHandles < BP_MAX_HANDLES); + + // init bounds + m_worldAabbMin = worldAabbMin; + m_worldAabbMax = worldAabbMax; + + btVector3 aabbSize = m_worldAabbMax - m_worldAabbMin; + + BP_FP_INT_TYPE maxInt = BP_HANDLE_SENTINEL; + + m_quantize = btVector3(btScalar(maxInt),btScalar(maxInt),btScalar(maxInt)) / aabbSize; + + // allocate handles buffer and put all handles on free list + m_pHandles = new Handle[maxHandles]; + m_maxHandles = maxHandles; + m_numHandles = 0; + + // handle 0 is reserved as the null index, and is also used as the sentinel + m_firstFreeHandle = 1; + { + for (BP_FP_INT_TYPE i = m_firstFreeHandle; i < maxHandles; i++) + m_pHandles[i].SetNextFree(i + 1); + m_pHandles[maxHandles - 1].SetNextFree(0); + } + + { + // allocate edge buffers + for (int i = 0; i < 3; i++) + m_pEdges[i] = new Edge[maxHandles * 2]; + } + //removed overlap management + + // make boundary sentinels + + m_pHandles[0].m_clientObject = 0; + + for (int axis = 0; axis < 3; axis++) + { + m_pHandles[0].m_minEdges[axis] = 0; + m_pHandles[0].m_maxEdges[axis] = 1; + + m_pEdges[axis][0].m_pos = 0; + m_pEdges[axis][0].m_handle = 0; + m_pEdges[axis][1].m_pos = BP_HANDLE_SENTINEL; + m_pEdges[axis][1].m_handle = 0; +#ifdef DEBUG_BROADPHASE + debugPrintAxis(axis); +#endif //DEBUG_BROADPHASE + + } + +} + +btAxisSweep3::~btAxisSweep3() +{ + + for (int i = 2; i >= 0; i--) + delete[] m_pEdges[i]; + delete[] m_pHandles; + + if (m_ownsPairCache) + { + delete m_pairCache; + } +} + +void btAxisSweep3::quantize(BP_FP_INT_TYPE* out, const btPoint3& point, int isMax) const +{ + btPoint3 clampedPoint(point); + + + + clampedPoint.setMax(m_worldAabbMin); + clampedPoint.setMin(m_worldAabbMax); + + btVector3 v = (clampedPoint - m_worldAabbMin) * m_quantize; + out[0] = (BP_FP_INT_TYPE)(((BP_FP_INT_TYPE)v.getX() & BP_HANDLE_MASK) | isMax); + out[1] = (BP_FP_INT_TYPE)(((BP_FP_INT_TYPE)v.getY() & BP_HANDLE_MASK) | isMax); + out[2] = (BP_FP_INT_TYPE)(((BP_FP_INT_TYPE)v.getZ() & BP_HANDLE_MASK) | isMax); + +} + + + +BP_FP_INT_TYPE btAxisSweep3::allocHandle() +{ + assert(m_firstFreeHandle); + + BP_FP_INT_TYPE handle = m_firstFreeHandle; + m_firstFreeHandle = getHandle(handle)->GetNextFree(); + m_numHandles++; + + return handle; +} + +void btAxisSweep3::freeHandle(BP_FP_INT_TYPE handle) +{ + assert(handle > 0 && handle < m_maxHandles); + + getHandle(handle)->SetNextFree(m_firstFreeHandle); + m_firstFreeHandle = handle; + + m_numHandles--; +} + + + +BP_FP_INT_TYPE btAxisSweep3::addHandle(const btPoint3& aabbMin,const btPoint3& aabbMax, void* pOwner,short int collisionFilterGroup,short int collisionFilterMask) +{ + // quantize the bounds + BP_FP_INT_TYPE min[3], max[3]; + quantize(min, aabbMin, 0); + quantize(max, aabbMax, 1); + + // allocate a handle + BP_FP_INT_TYPE handle = allocHandle(); + assert(handle!= 0xcdcd); + + Handle* pHandle = getHandle(handle); + + pHandle->m_handleId = handle; + //pHandle->m_pOverlaps = 0; + pHandle->m_clientObject = pOwner; + pHandle->m_collisionFilterGroup = collisionFilterGroup; + pHandle->m_collisionFilterMask = collisionFilterMask; + + // compute current limit of edge arrays + BP_FP_INT_TYPE limit = m_numHandles * 2; + + + // insert new edges just inside the max boundary edge + for (BP_FP_INT_TYPE axis = 0; axis < 3; axis++) + { + + m_pHandles[0].m_maxEdges[axis] += 2; + + m_pEdges[axis][limit + 1] = m_pEdges[axis][limit - 1]; + + m_pEdges[axis][limit - 1].m_pos = min[axis]; + m_pEdges[axis][limit - 1].m_handle = handle; + + m_pEdges[axis][limit].m_pos = max[axis]; + m_pEdges[axis][limit].m_handle = handle; + + pHandle->m_minEdges[axis] = limit - 1; + pHandle->m_maxEdges[axis] = limit; + } + + // now sort the new edges to their correct position + sortMinDown(0, pHandle->m_minEdges[0], false); + sortMaxDown(0, pHandle->m_maxEdges[0], false); + sortMinDown(1, pHandle->m_minEdges[1], false); + sortMaxDown(1, pHandle->m_maxEdges[1], false); + sortMinDown(2, pHandle->m_minEdges[2], true); + sortMaxDown(2, pHandle->m_maxEdges[2], true); + + + return handle; +} + + +void btAxisSweep3::removeHandle(BP_FP_INT_TYPE handle) +{ + + Handle* pHandle = getHandle(handle); + + //explicitly remove the pairs containing the proxy + //we could do it also in the sortMinUp (passing true) + //todo: compare performance + m_pairCache->removeOverlappingPairsContainingProxy(pHandle); + + + // compute current limit of edge arrays + int limit = m_numHandles * 2; + + int axis; + + for (axis = 0;axis<3;axis++) + { + m_pHandles[0].m_maxEdges[axis] -= 2; + } + + // remove the edges by sorting them up to the end of the list + for ( axis = 0; axis < 3; axis++) + { + Edge* pEdges = m_pEdges[axis]; + BP_FP_INT_TYPE max = pHandle->m_maxEdges[axis]; + pEdges[max].m_pos = BP_HANDLE_SENTINEL; + + sortMaxUp(axis,max,false); + + + BP_FP_INT_TYPE i = pHandle->m_minEdges[axis]; + pEdges[i].m_pos = BP_HANDLE_SENTINEL; + + + sortMinUp(axis,i,false); + + pEdges[limit-1].m_handle = 0; + pEdges[limit-1].m_pos = BP_HANDLE_SENTINEL; + +#ifdef DEBUG_BROADPHASE + debugPrintAxis(axis,false); +#endif //DEBUG_BROADPHASE + + + } + + + // free the handle + freeHandle(handle); + + +} + +extern int gOverlappingPairs; + + +void btAxisSweep3::calculateOverlappingPairs() +{ + + if (m_ownsPairCache) + { + btBroadphasePairArray& overlappingPairArray = m_pairCache->getOverlappingPairArray(); + + //perform a sort, to find duplicates and to sort 'invalid' pairs to the end + overlappingPairArray.heapSort(btBroadphasePairSortPredicate()); + + overlappingPairArray.resize(overlappingPairArray.size() - m_invalidPair); + m_invalidPair = 0; + + + int i; + + btBroadphasePair previousPair; + previousPair.m_pProxy0 = 0; + previousPair.m_pProxy1 = 0; + previousPair.m_algorithm = 0; + + + for (i=0;iprocessOverlap(pair); + } else + { + needsRemoval = true; + } + } else + { + //remove duplicate + needsRemoval = true; + //should have no algorithm + btAssert(!pair.m_algorithm); + } + + if (needsRemoval) + { + m_pairCache->cleanOverlappingPair(pair); + + // m_overlappingPairArray.swap(i,m_overlappingPairArray.size()-1); + // m_overlappingPairArray.pop_back(); + pair.m_pProxy0 = 0; + pair.m_pProxy1 = 0; + m_invalidPair++; + gOverlappingPairs--; + } + + } + + ///if you don't like to skip the invalid pairs in the array, execute following code: + #define CLEAN_INVALID_PAIRS 1 + #ifdef CLEAN_INVALID_PAIRS + + //perform a sort, to sort 'invalid' pairs to the end + overlappingPairArray.heapSort(btBroadphasePairSortPredicate()); + + overlappingPairArray.resize(overlappingPairArray.size() - m_invalidPair); + m_invalidPair = 0; + #endif//CLEAN_INVALID_PAIRS + + } + +} + + + +bool btAxisSweep3::testAabbOverlap(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) +{ + const Handle* pHandleA = static_cast(proxy0); + const Handle* pHandleB = static_cast(proxy1); + + //optimization 1: check the array index (memory address), instead of the m_pos + + for (int axis = 0; axis < 3; axis++) + { + if (pHandleA->m_maxEdges[axis] < pHandleB->m_minEdges[axis] || + pHandleB->m_maxEdges[axis] < pHandleA->m_minEdges[axis]) + { + return false; + } + } + return true; +} + +bool btAxisSweep3::testOverlap(int ignoreAxis,const Handle* pHandleA, const Handle* pHandleB) +{ + //optimization 1: check the array index (memory address), instead of the m_pos + + for (int axis = 0; axis < 3; axis++) + { + if (axis != ignoreAxis) + { + if (pHandleA->m_maxEdges[axis] < pHandleB->m_minEdges[axis] || + pHandleB->m_maxEdges[axis] < pHandleA->m_minEdges[axis]) + { + return false; + } + } + } + + //optimization 2: only 2 axis need to be tested (conflicts with 'delayed removal' optimization) + + /*for (int axis = 0; axis < 3; axis++) + { + if (m_pEdges[axis][pHandleA->m_maxEdges[axis]].m_pos < m_pEdges[axis][pHandleB->m_minEdges[axis]].m_pos || + m_pEdges[axis][pHandleB->m_maxEdges[axis]].m_pos < m_pEdges[axis][pHandleA->m_minEdges[axis]].m_pos) + { + return false; + } + } + */ + + return true; +} + +void btAxisSweep3::updateHandle(BP_FP_INT_TYPE handle, const btPoint3& aabbMin,const btPoint3& aabbMax) +{ +// assert(bounds.IsFinite()); + //assert(bounds.HasVolume()); + + Handle* pHandle = getHandle(handle); + + // quantize the new bounds + BP_FP_INT_TYPE min[3], max[3]; + quantize(min, aabbMin, 0); + quantize(max, aabbMax, 1); + + // update changed edges + for (int axis = 0; axis < 3; axis++) + { + BP_FP_INT_TYPE emin = pHandle->m_minEdges[axis]; + BP_FP_INT_TYPE emax = pHandle->m_maxEdges[axis]; + + int dmin = (int)min[axis] - (int)m_pEdges[axis][emin].m_pos; + int dmax = (int)max[axis] - (int)m_pEdges[axis][emax].m_pos; + + m_pEdges[axis][emin].m_pos = min[axis]; + m_pEdges[axis][emax].m_pos = max[axis]; + + // expand (only adds overlaps) + if (dmin < 0) + sortMinDown(axis, emin); + + if (dmax > 0) + sortMaxUp(axis, emax); + + // shrink (only removes overlaps) + if (dmin > 0) + sortMinUp(axis, emin); + + if (dmax < 0) + sortMaxDown(axis, emax); + +#ifdef DEBUG_BROADPHASE + debugPrintAxis(axis); +#endif //DEBUG_BROADPHASE + } + + +} + + + + +// sorting a min edge downwards can only ever *add* overlaps +void btAxisSweep3::sortMinDown(int axis, BP_FP_INT_TYPE edge, bool updateOverlaps) +{ + + Edge* pEdge = m_pEdges[axis] + edge; + Edge* pPrev = pEdge - 1; + Handle* pHandleEdge = getHandle(pEdge->m_handle); + + while (pEdge->m_pos < pPrev->m_pos) + { + Handle* pHandlePrev = getHandle(pPrev->m_handle); + + if (pPrev->IsMax()) + { + // if previous edge is a maximum check the bounds and add an overlap if necessary + if (updateOverlaps && testOverlap(axis,pHandleEdge, pHandlePrev)) + { + m_pairCache->addOverlappingPair(pHandleEdge,pHandlePrev); + + //AddOverlap(pEdge->m_handle, pPrev->m_handle); + + } + + // update edge reference in other handle + pHandlePrev->m_maxEdges[axis]++; + } + else + pHandlePrev->m_minEdges[axis]++; + + pHandleEdge->m_minEdges[axis]--; + + // swap the edges + Edge swap = *pEdge; + *pEdge = *pPrev; + *pPrev = swap; + + // decrement + pEdge--; + pPrev--; + } + +#ifdef DEBUG_BROADPHASE + debugPrintAxis(axis); +#endif //DEBUG_BROADPHASE + +} + +// sorting a min edge upwards can only ever *remove* overlaps +void btAxisSweep3::sortMinUp(int axis, BP_FP_INT_TYPE edge, bool updateOverlaps) +{ + Edge* pEdge = m_pEdges[axis] + edge; + Edge* pNext = pEdge + 1; + Handle* pHandleEdge = getHandle(pEdge->m_handle); + + while (pNext->m_handle && (pEdge->m_pos >= pNext->m_pos)) + { + Handle* pHandleNext = getHandle(pNext->m_handle); + + if (pNext->IsMax()) + { + // if next edge is maximum remove any overlap between the two handles + if (updateOverlaps) + { + /* + Handle* handle0 = getHandle(pEdge->m_handle); + Handle* handle1 = getHandle(pNext->m_handle); + btBroadphasePair tmpPair(*handle0,*handle1); + removeOverlappingPair(tmpPair); + */ + + } + + // update edge reference in other handle + pHandleNext->m_maxEdges[axis]--; + } + else + pHandleNext->m_minEdges[axis]--; + + pHandleEdge->m_minEdges[axis]++; + + // swap the edges + Edge swap = *pEdge; + *pEdge = *pNext; + *pNext = swap; + + // increment + pEdge++; + pNext++; + } + + +} + +// sorting a max edge downwards can only ever *remove* overlaps +void btAxisSweep3::sortMaxDown(int axis, BP_FP_INT_TYPE edge, bool updateOverlaps) +{ + + Edge* pEdge = m_pEdges[axis] + edge; + Edge* pPrev = pEdge - 1; + Handle* pHandleEdge = getHandle(pEdge->m_handle); + + while (pEdge->m_pos < pPrev->m_pos) + { + Handle* pHandlePrev = getHandle(pPrev->m_handle); + + if (!pPrev->IsMax()) + { + // if previous edge was a minimum remove any overlap between the two handles + if (updateOverlaps) + { + //this is done during the overlappingpairarray iteration/narrowphase collision + /* + Handle* handle0 = getHandle(pEdge->m_handle); + Handle* handle1 = getHandle(pPrev->m_handle); + btBroadphasePair* pair = findPair(handle0,handle1); + //assert(pair); + + if (pair) + { + removeOverlappingPair(*pair); + } + */ + + } + + // update edge reference in other handle + pHandlePrev->m_minEdges[axis]++;; + } + else + pHandlePrev->m_maxEdges[axis]++; + + pHandleEdge->m_maxEdges[axis]--; + + // swap the edges + Edge swap = *pEdge; + *pEdge = *pPrev; + *pPrev = swap; + + // decrement + pEdge--; + pPrev--; + } + + +#ifdef DEBUG_BROADPHASE + debugPrintAxis(axis); +#endif //DEBUG_BROADPHASE + +} + +// sorting a max edge upwards can only ever *add* overlaps +void btAxisSweep3::sortMaxUp(int axis, BP_FP_INT_TYPE edge, bool updateOverlaps) +{ + Edge* pEdge = m_pEdges[axis] + edge; + Edge* pNext = pEdge + 1; + Handle* pHandleEdge = getHandle(pEdge->m_handle); + + while (pNext->m_handle && (pEdge->m_pos >= pNext->m_pos)) + { + Handle* pHandleNext = getHandle(pNext->m_handle); + + if (!pNext->IsMax()) + { + // if next edge is a minimum check the bounds and add an overlap if necessary + if (updateOverlaps && testOverlap(axis, pHandleEdge, pHandleNext)) + { + Handle* handle0 = getHandle(pEdge->m_handle); + Handle* handle1 = getHandle(pNext->m_handle); + m_pairCache->addOverlappingPair(handle0,handle1); + } + + // update edge reference in other handle + pHandleNext->m_minEdges[axis]--; + } + else + pHandleNext->m_maxEdges[axis]--; + + pHandleEdge->m_maxEdges[axis]++; + + // swap the edges + Edge swap = *pEdge; + *pEdge = *pNext; + *pNext = swap; + + // increment + pEdge++; + pNext++; + } + +} + + diff --git a/bullet/src/BulletCollision/BroadphaseCollision/btAxisSweep3.h b/bullet/src/BulletCollision/BroadphaseCollision/btAxisSweep3.h new file mode 100644 index 0000000..61aad5d --- /dev/null +++ b/bullet/src/BulletCollision/BroadphaseCollision/btAxisSweep3.h @@ -0,0 +1,153 @@ +//Bullet Continuous Collision Detection and Physics Library +//Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +// +// btAxisSweep3.h +// +// Copyright (c) 2006 Simon Hobbs +// +// 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. + +#ifndef AXIS_SWEEP_3_H +#define AXIS_SWEEP_3_H + +#include "LinearMath/btPoint3.h" +#include "LinearMath/btVector3.h" +#include "btOverlappingPairCache.h" +#include "btBroadphaseInterface.h" +#include "btBroadphaseProxy.h" + + +//Enable BP_USE_FIXEDPOINT_INT_32 if you need more then 32767 objects +//#define BP_USE_FIXEDPOINT_INT_32 1 + +#ifdef BP_USE_FIXEDPOINT_INT_32 + #define BP_FP_INT_TYPE unsigned int + #define BP_MAX_HANDLES 1500000 //arbitrary maximum number of handles + #define BP_HANDLE_SENTINEL 0x7fffffff + #define BP_HANDLE_MASK 0xfffffffe +#else + #define BP_FP_INT_TYPE unsigned short int + #define BP_MAX_HANDLES 32767 + #define BP_HANDLE_SENTINEL 0xffff + #define BP_HANDLE_MASK 0xfffe +#endif //BP_USE_FIXEDPOINT_INT_32 + +//#define DEBUG_BROADPHASE 1 + +/// btAxisSweep3 is an efficient implementation of the 3d axis sweep and prune broadphase. +/// It uses arrays rather then lists for storage of the 3 axis. Also it operates using integer coordinates instead of floats. +/// The testOverlap check is optimized to check the array index, rather then the actual AABB coordinates/pos +class btAxisSweep3 : public btBroadphaseInterface +{ + +public: + + + class Edge + { + public: + BP_FP_INT_TYPE m_pos; // low bit is min/max + BP_FP_INT_TYPE m_handle; + + BP_FP_INT_TYPE IsMax() const {return m_pos & 1;} + }; + +public: + class Handle : public btBroadphaseProxy + { + public: + + // indexes into the edge arrays + BP_FP_INT_TYPE m_minEdges[3], m_maxEdges[3]; // 6 * 2 = 12 + BP_FP_INT_TYPE m_handleId; + BP_FP_INT_TYPE m_pad; + + //void* m_pOwner; this is now in btBroadphaseProxy.m_clientObject + + inline void SetNextFree(BP_FP_INT_TYPE next) {m_minEdges[0] = next;} + inline BP_FP_INT_TYPE GetNextFree() const {return m_minEdges[0];} + }; // 24 bytes + 24 for Edge structures = 44 bytes total per entry + + +protected: + btPoint3 m_worldAabbMin; // overall system bounds + btPoint3 m_worldAabbMax; // overall system bounds + + btVector3 m_quantize; // scaling factor for quantization + + BP_FP_INT_TYPE m_numHandles; // number of active handles + int m_maxHandles; // max number of handles + Handle* m_pHandles; // handles pool + BP_FP_INT_TYPE m_firstFreeHandle; // free handles list + + Edge* m_pEdges[3]; // edge arrays for the 3 axes (each array has m_maxHandles * 2 + 2 sentinel entries) + + btOverlappingPairCache* m_pairCache; + bool m_ownsPairCache; + + int m_invalidPair; + + // allocation/deallocation + BP_FP_INT_TYPE allocHandle(); + void freeHandle(BP_FP_INT_TYPE handle); + + + bool testOverlap(int ignoreAxis,const Handle* pHandleA, const Handle* pHandleB); + +#ifdef DEBUG_BROADPHASE + void debugPrintAxis(int axis,bool checkCardinality=true); +#endif //DEBUG_BROADPHASE + + //Overlap* AddOverlap(BP_FP_INT_TYPE handleA, BP_FP_INT_TYPE handleB); + //void RemoveOverlap(BP_FP_INT_TYPE handleA, BP_FP_INT_TYPE handleB); + + void quantize(BP_FP_INT_TYPE* out, const btPoint3& point, int isMax) const; + + void sortMinDown(int axis, BP_FP_INT_TYPE edge, bool updateOverlaps = true); + void sortMinUp(int axis, BP_FP_INT_TYPE edge, bool updateOverlaps = true); + void sortMaxDown(int axis, BP_FP_INT_TYPE edge, bool updateOverlaps = true); + void sortMaxUp(int axis, BP_FP_INT_TYPE edge, bool updateOverlaps = true); + +public: + btAxisSweep3(const btPoint3& worldAabbMin,const btPoint3& worldAabbMax, int maxHandles = 16384, btOverlappingPairCache* pairCache=0); + virtual ~btAxisSweep3(); + + virtual void calculateOverlappingPairs(); + + BP_FP_INT_TYPE addHandle(const btPoint3& aabbMin,const btPoint3& aabbMax, void* pOwner,short int collisionFilterGroup,short int collisionFilterMask); + void removeHandle(BP_FP_INT_TYPE handle); + void updateHandle(BP_FP_INT_TYPE handle, const btPoint3& aabbMin,const btPoint3& aabbMax); + inline Handle* getHandle(BP_FP_INT_TYPE index) const {return m_pHandles + index;} + + void processAllOverlappingPairs(btOverlapCallback* callback); + + //Broadphase Interface + virtual btBroadphaseProxy* createProxy( const btVector3& aabbMin, const btVector3& aabbMax,int shapeType,void* userPtr ,short int collisionFilterGroup,short int collisionFilterMask); + virtual void destroyProxy(btBroadphaseProxy* proxy); + virtual void setAabb(btBroadphaseProxy* proxy,const btVector3& aabbMin,const btVector3& aabbMax); + + bool testAabbOverlap(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1); + + btOverlappingPairCache* getOverlappingPairCache() + { + return m_pairCache; + } + const btOverlappingPairCache* getOverlappingPairCache() const + { + return m_pairCache; + } + + +}; + +#endif + diff --git a/bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseInterface.h b/bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseInterface.h new file mode 100644 index 0000000..3162f81 --- /dev/null +++ b/bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseInterface.h @@ -0,0 +1,46 @@ +/* +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. +*/ + +#ifndef BROADPHASE_INTERFACE_H +#define BROADPHASE_INTERFACE_H + + + +struct btDispatcherInfo; +class btDispatcher; +#include "btBroadphaseProxy.h" +class btOverlappingPairCache; + +#include "LinearMath/btVector3.h" + +///BroadphaseInterface for aabb-overlapping object pairs +class btBroadphaseInterface +{ +public: + virtual ~btBroadphaseInterface() {} + + virtual btBroadphaseProxy* createProxy( const btVector3& aabbMin, const btVector3& aabbMax,int shapeType,void* userPtr, short int collisionFilterGroup,short int collisionFilterMask) =0; + virtual void destroyProxy(btBroadphaseProxy* proxy)=0; + virtual void setAabb(btBroadphaseProxy* proxy,const btVector3& aabbMin,const btVector3& aabbMax)=0; + + ///calculateOverlappingPairs is optional: incremental algorithms (sweep and prune) might do it during the set aabb + virtual void calculateOverlappingPairs()=0; + + virtual btOverlappingPairCache* getOverlappingPairCache()=0; + virtual const btOverlappingPairCache* getOverlappingPairCache() const =0; + +}; + +#endif //BROADPHASE_INTERFACE_H diff --git a/bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseProxy.cpp b/bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseProxy.cpp new file mode 100644 index 0000000..63e97f2 --- /dev/null +++ b/bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseProxy.cpp @@ -0,0 +1,17 @@ +/* +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. +*/ + +#include "btBroadphaseProxy.h" + diff --git a/bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseProxy.h b/bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseProxy.h new file mode 100644 index 0000000..553e59d --- /dev/null +++ b/bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseProxy.h @@ -0,0 +1,217 @@ +/* +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. +*/ + +#ifndef BROADPHASE_PROXY_H +#define BROADPHASE_PROXY_H + +#include "LinearMath/btScalar.h" //for SIMD_FORCE_INLINE + + +/// btDispatcher uses these types +/// IMPORTANT NOTE:The types are ordered polyhedral, implicit convex and concave +/// to facilitate type checking +enum BroadphaseNativeTypes +{ +// polyhedral convex shapes + BOX_SHAPE_PROXYTYPE, + TRIANGLE_SHAPE_PROXYTYPE, + TETRAHEDRAL_SHAPE_PROXYTYPE, + CONVEX_TRIANGLEMESH_SHAPE_PROXYTYPE, + CONVEX_HULL_SHAPE_PROXYTYPE, +//implicit convex shapes +IMPLICIT_CONVEX_SHAPES_START_HERE, + SPHERE_SHAPE_PROXYTYPE, + MULTI_SPHERE_SHAPE_PROXYTYPE, + CAPSULE_SHAPE_PROXYTYPE, + CONE_SHAPE_PROXYTYPE, + CONVEX_SHAPE_PROXYTYPE, + CYLINDER_SHAPE_PROXYTYPE, + UNIFORM_SCALING_SHAPE_PROXYTYPE, + MINKOWSKI_SUM_SHAPE_PROXYTYPE, + MINKOWSKI_DIFFERENCE_SHAPE_PROXYTYPE, +//concave shapes +CONCAVE_SHAPES_START_HERE, + //keep all the convex shapetype below here, for the check IsConvexShape in broadphase proxy! + TRIANGLE_MESH_SHAPE_PROXYTYPE, + ///used for demo integration FAST/Swift collision library and Bullet + FAST_CONCAVE_MESH_PROXYTYPE, + //terrain + TERRAIN_SHAPE_PROXYTYPE, +///Used for GIMPACT Trimesh integration + GIMPACT_SHAPE_PROXYTYPE, + + EMPTY_SHAPE_PROXYTYPE, + STATIC_PLANE_PROXYTYPE, +CONCAVE_SHAPES_END_HERE, + + COMPOUND_SHAPE_PROXYTYPE, + + MAX_BROADPHASE_COLLISION_TYPES +}; + + +///btBroadphaseProxy +struct btBroadphaseProxy +{ + + ///optional filtering to cull potential collisions + enum CollisionFilterGroups + { + DefaultFilter = 1, + StaticFilter = 2, + KinematicFilter = 4, + DebrisFilter = 8, + SensorTrigger = 16, + AllFilter = DefaultFilter | StaticFilter | KinematicFilter | DebrisFilter | SensorTrigger + }; + + //Usually the client btCollisionObject or Rigidbody class + void* m_clientObject; + + ///in the case of btMultiSapBroadphase, we store the collifionFilterGroup/Mask in the m_multiSapParentProxy + union + { + struct + { + short int m_collisionFilterGroup; + short int m_collisionFilterMask; + }; + + void* m_multiSapParentProxy; + + }; + + //used for memory pools + btBroadphaseProxy() :m_clientObject(0){} + + btBroadphaseProxy(void* userPtr,short int collisionFilterGroup, short int collisionFilterMask) + :m_clientObject(userPtr), + m_collisionFilterGroup(collisionFilterGroup), + m_collisionFilterMask(collisionFilterMask) + { + } + + static inline bool isPolyhedral(int proxyType) + { + return (proxyType < IMPLICIT_CONVEX_SHAPES_START_HERE); + } + + static inline bool isConvex(int proxyType) + { + return (proxyType < CONCAVE_SHAPES_START_HERE); + } + + static inline bool isConcave(int proxyType) + { + return ((proxyType > CONCAVE_SHAPES_START_HERE) && + (proxyType < CONCAVE_SHAPES_END_HERE)); + } + static inline bool isCompound(int proxyType) + { + return (proxyType == COMPOUND_SHAPE_PROXYTYPE); + } + static inline bool isInfinite(int proxyType) + { + return (proxyType == STATIC_PLANE_PROXYTYPE); + } + +} +; + +class btCollisionAlgorithm; + +struct btBroadphaseProxy; + + + +/// contains a pair of aabb-overlapping objects +struct btBroadphasePair +{ + btBroadphasePair () + : + m_pProxy0(0), + m_pProxy1(0), + m_algorithm(0), + m_userInfo(0) + { + } + + btBroadphasePair(const btBroadphasePair& other) + : m_pProxy0(other.m_pProxy0), + m_pProxy1(other.m_pProxy1), + m_algorithm(other.m_algorithm), + m_userInfo(other.m_userInfo) + { + } + btBroadphasePair(btBroadphaseProxy& proxy0,btBroadphaseProxy& proxy1) + { + + //keep them sorted, so the std::set operations work + if (&proxy0 < &proxy1) + { + m_pProxy0 = &proxy0; + m_pProxy1 = &proxy1; + } + else + { + m_pProxy0 = &proxy1; + m_pProxy1 = &proxy0; + } + + m_algorithm = 0; + m_userInfo = 0; + + } + + btBroadphaseProxy* m_pProxy0; + btBroadphaseProxy* m_pProxy1; + + mutable btCollisionAlgorithm* m_algorithm; + mutable void* m_userInfo; + +}; + +/* +//comparison for set operation, see Solid DT_Encounter +SIMD_FORCE_INLINE bool operator<(const btBroadphasePair& a, const btBroadphasePair& b) +{ + return a.m_pProxy0 < b.m_pProxy0 || + (a.m_pProxy0 == b.m_pProxy0 && a.m_pProxy1 < b.m_pProxy1); +} +*/ + + + +class btBroadphasePairSortPredicate +{ + public: + + bool operator() ( const btBroadphasePair& a, const btBroadphasePair& b ) + { + return a.m_pProxy0 > b.m_pProxy0 || + (a.m_pProxy0 == b.m_pProxy0 && a.m_pProxy1 > b.m_pProxy1) || + (a.m_pProxy0 == b.m_pProxy0 && a.m_pProxy1 == b.m_pProxy1 && a.m_algorithm > b.m_algorithm); + } +}; + + +SIMD_FORCE_INLINE bool operator==(const btBroadphasePair& a, const btBroadphasePair& b) +{ + return (a.m_pProxy0 == b.m_pProxy0) && (a.m_pProxy1 == b.m_pProxy1); +} + + +#endif //BROADPHASE_PROXY_H + diff --git a/bullet/src/BulletCollision/BroadphaseCollision/btCollisionAlgorithm.cpp b/bullet/src/BulletCollision/BroadphaseCollision/btCollisionAlgorithm.cpp new file mode 100644 index 0000000..2257511 --- /dev/null +++ b/bullet/src/BulletCollision/BroadphaseCollision/btCollisionAlgorithm.cpp @@ -0,0 +1,23 @@ +/* +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. +*/ + +#include "btCollisionAlgorithm.h" +#include "btDispatcher.h" + +btCollisionAlgorithm::btCollisionAlgorithm(const btCollisionAlgorithmConstructionInfo& ci) +{ + m_dispatcher = ci.m_dispatcher; +} + diff --git a/bullet/src/BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h b/bullet/src/BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h new file mode 100644 index 0000000..61ae35a --- /dev/null +++ b/bullet/src/BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h @@ -0,0 +1,77 @@ +/* +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. +*/ + +#ifndef COLLISION_ALGORITHM_H +#define COLLISION_ALGORITHM_H + +#include "LinearMath/btScalar.h" + +struct btBroadphaseProxy; +class btDispatcher; +class btManifoldResult; +class btCollisionObject; +struct btDispatcherInfo; +class btPersistentManifold; + + +struct btCollisionAlgorithmConstructionInfo +{ + btCollisionAlgorithmConstructionInfo() + :m_dispatcher(0), + m_manifold(0) + { + } + btCollisionAlgorithmConstructionInfo(btDispatcher* dispatcher,int temp) + :m_dispatcher(dispatcher) + { + (void)temp; + } + + btDispatcher* m_dispatcher; + btPersistentManifold* m_manifold; + + int getDispatcherId(); + +}; + + +///btCollisionAlgorithm is an collision interface that is compatible with the Broadphase and btDispatcher. +///It is persistent over frames +class btCollisionAlgorithm +{ + +protected: + + btDispatcher* m_dispatcher; + +protected: + int getDispatcherId(); + +public: + + btCollisionAlgorithm() {}; + + btCollisionAlgorithm(const btCollisionAlgorithmConstructionInfo& ci); + + virtual ~btCollisionAlgorithm() {}; + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) = 0; + + virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) = 0; + +}; + + +#endif //COLLISION_ALGORITHM_H diff --git a/bullet/src/BulletCollision/BroadphaseCollision/btDispatcher.cpp b/bullet/src/BulletCollision/BroadphaseCollision/btDispatcher.cpp new file mode 100644 index 0000000..e702232 --- /dev/null +++ b/bullet/src/BulletCollision/BroadphaseCollision/btDispatcher.cpp @@ -0,0 +1,22 @@ +/* +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. +*/ + +#include "btDispatcher.h" + +btDispatcher::~btDispatcher() +{ + +} + diff --git a/bullet/src/BulletCollision/BroadphaseCollision/btDispatcher.h b/bullet/src/BulletCollision/BroadphaseCollision/btDispatcher.h new file mode 100644 index 0000000..51cc154 --- /dev/null +++ b/bullet/src/BulletCollision/BroadphaseCollision/btDispatcher.h @@ -0,0 +1,93 @@ +/* +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. +*/ + +#ifndef _DISPATCHER_H +#define _DISPATCHER_H + +#include "LinearMath/btScalar.h" + +class btCollisionAlgorithm; +struct btBroadphaseProxy; +class btRigidBody; +class btCollisionObject; +class btOverlappingPairCache; + + +class btPersistentManifold; +class btStackAlloc; + +struct btDispatcherInfo +{ + enum DispatchFunc + { + DISPATCH_DISCRETE = 1, + DISPATCH_CONTINUOUS + }; + btDispatcherInfo() + :m_timeStep(btScalar(0.)), + m_stepCount(0), + m_dispatchFunc(DISPATCH_DISCRETE), + m_timeOfImpact(btScalar(1.)), + m_useContinuous(false), + m_debugDraw(0), + m_enableSatConvex(false), + m_enableSPU(false), + m_stackAllocator(0) + { + + } + btScalar m_timeStep; + int m_stepCount; + int m_dispatchFunc; + btScalar m_timeOfImpact; + bool m_useContinuous; + class btIDebugDraw* m_debugDraw; + bool m_enableSatConvex; + bool m_enableSPU; + btStackAlloc* m_stackAllocator; + +}; + +/// btDispatcher can be used in combination with broadphase to dispatch overlapping pairs. +/// For example for pairwise collision detection or user callbacks (game logic). +class btDispatcher +{ + + +public: + virtual ~btDispatcher() ; + + virtual btCollisionAlgorithm* findAlgorithm(btCollisionObject* body0,btCollisionObject* body1,btPersistentManifold* sharedManifold=0) = 0; + + virtual btPersistentManifold* getNewManifold(void* body0,void* body1)=0; + + virtual void releaseManifold(btPersistentManifold* manifold)=0; + + virtual void clearManifold(btPersistentManifold* manifold)=0; + + virtual bool needsCollision(btCollisionObject* body0,btCollisionObject* body1) = 0; + + virtual bool needsResponse(btCollisionObject* body0,btCollisionObject* body1)=0; + + virtual void dispatchAllCollisionPairs(btOverlappingPairCache* pairCache,btDispatcherInfo& dispatchInfo)=0; + + virtual int getNumManifolds() const = 0; + + virtual btPersistentManifold* getManifoldByIndexInternal(int index) = 0; + +}; + + +#endif //_DISPATCHER_H diff --git a/bullet/src/BulletCollision/BroadphaseCollision/btMultiSapBroadphase.cpp b/bullet/src/BulletCollision/BroadphaseCollision/btMultiSapBroadphase.cpp new file mode 100644 index 0000000..a94fa86 --- /dev/null +++ b/bullet/src/BulletCollision/BroadphaseCollision/btMultiSapBroadphase.cpp @@ -0,0 +1,186 @@ +/* +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. +*/ + +#include "btMultiSapBroadphase.h" + +#include "btSimpleBroadphase.h" +#include "LinearMath/btAabbUtil2.h" + +/// btSapBroadphaseArray m_sapBroadphases; + +/// btOverlappingPairCache* m_overlappingPairs; +extern int gOverlappingPairs; + +btMultiSapBroadphase::btMultiSapBroadphase(int maxProxies) +:m_invalidPair(0) +{ + m_overlappingPairs = new btOverlappingPairCache(); + + struct btMultiSapOverlapFilterCallback : public btOverlapFilterCallback + { + virtual ~btMultiSapOverlapFilterCallback() + {} + // return true when pairs need collision + virtual bool needBroadphaseCollision(btBroadphaseProxy* childProxy0,btBroadphaseProxy* childProxy1) const + { + btMultiSapProxy* multiSapProxy0 = (btMultiSapProxy*)childProxy0->m_multiSapParentProxy; + btMultiSapProxy* multiSapProxy1 = (btMultiSapProxy*)childProxy1->m_multiSapParentProxy; + + bool collides = (multiSapProxy0->m_collisionFilterGroup & multiSapProxy1->m_collisionFilterMask) != 0; + collides = collides && (multiSapProxy1->m_collisionFilterGroup & multiSapProxy0->m_collisionFilterMask); + + return collides; + } + }; + + m_filterCallback = new btMultiSapOverlapFilterCallback(); + + m_overlappingPairs->setOverlapFilterCallback(m_filterCallback); + + m_simpleBroadphase = new btSimpleBroadphase(maxProxies,m_overlappingPairs); +} + +btMultiSapBroadphase::~btMultiSapBroadphase() +{ +} + +btBroadphaseProxy* btMultiSapBroadphase::createProxy( const btVector3& aabbMin, const btVector3& aabbMax,int shapeType,void* userPtr, short int collisionFilterGroup,short int collisionFilterMask) +{ + btMultiSapProxy* proxy = new btMultiSapProxy(aabbMin, aabbMax,shapeType,userPtr, collisionFilterGroup,collisionFilterMask); + m_multiSapProxies.push_back(proxy); + + ///we don't pass the userPtr but our multisap proxy. We need to patch this, before processing an actual collision + ///this is needed to be able to calculate the aabb overlap + btBroadphaseProxy* simpleProxy = m_simpleBroadphase->createProxy(aabbMin,aabbMax,shapeType,userPtr,collisionFilterGroup,collisionFilterMask); + simpleProxy->m_multiSapParentProxy = proxy; + + btChildProxy* childProxyRef = new btChildProxy(); + childProxyRef->m_proxy = simpleProxy; + childProxyRef->m_childBroadphase = m_simpleBroadphase; + proxy->m_childProxies.push_back(childProxyRef); + + ///this should deal with inserting/removal into child broadphases + setAabb(proxy,aabbMin,aabbMax); + return proxy; +} + +void btMultiSapBroadphase::destroyProxy(btBroadphaseProxy* proxy) +{ + ///not yet + btAssert(0); + +} +void btMultiSapBroadphase::setAabb(btBroadphaseProxy* proxy,const btVector3& aabbMin,const btVector3& aabbMax) +{ + btMultiSapProxy* multiProxy = static_cast(proxy); + multiProxy->m_aabbMin = aabbMin; + multiProxy->m_aabbMax = aabbMax; + + for (int i=0;im_childProxies.size();i++) + { + btChildProxy* childProxyRef = multiProxy->m_childProxies[i]; + childProxyRef->m_childBroadphase->setAabb(childProxyRef->m_proxy,aabbMin,aabbMax); + } + +} + + ///calculateOverlappingPairs is optional: incremental algorithms (sweep and prune) might do it during the set aabb +void btMultiSapBroadphase::calculateOverlappingPairs() +{ + m_simpleBroadphase->calculateOverlappingPairs(); + + btBroadphasePairArray& overlappingPairArray = m_overlappingPairs->getOverlappingPairArray(); + + //perform a sort, to find duplicates and to sort 'invalid' pairs to the end + overlappingPairArray.heapSort(btBroadphasePairSortPredicate()); + + overlappingPairArray.resize(overlappingPairArray.size() - m_invalidPair); + m_invalidPair = 0; + + + btBroadphasePair previousPair; + previousPair.m_pProxy0 = 0; + previousPair.m_pProxy1 = 0; + previousPair.m_algorithm = 0; + + int i; + + for (i=0;iprocessOverlap(pair); + } else + { + needsRemoval = true; + } + } else + { + //remove duplicate + needsRemoval = true; + //should have no algorithm + btAssert(!pair.m_algorithm); + } + + if (needsRemoval) + { + m_overlappingPairs->cleanOverlappingPair(pair); + + // m_overlappingPairArray.swap(i,m_overlappingPairArray.size()-1); + // m_overlappingPairArray.pop_back(); + pair.m_pProxy0 = 0; + pair.m_pProxy1 = 0; + m_invalidPair++; + gOverlappingPairs--; + } + + } + +///if you don't like to skip the invalid pairs in the array, execute following code: +#define CLEAN_INVALID_PAIRS 1 +#ifdef CLEAN_INVALID_PAIRS + + //perform a sort, to sort 'invalid' pairs to the end + overlappingPairArray.heapSort(btBroadphasePairSortPredicate()); + + overlappingPairArray.resize(overlappingPairArray.size() - m_invalidPair); + m_invalidPair = 0; +#endif//CLEAN_INVALID_PAIRS + +} + + +bool btMultiSapBroadphase::testAabbOverlap(btBroadphaseProxy* childProxy0,btBroadphaseProxy* childProxy1) +{ + btMultiSapProxy* multiSapProxy0 = (btMultiSapProxy*)childProxy0->m_multiSapParentProxy; + btMultiSapProxy* multiSapProxy1 = (btMultiSapProxy*)childProxy1->m_multiSapParentProxy; + + return TestAabbAgainstAabb2(multiSapProxy0->m_aabbMin,multiSapProxy0->m_aabbMax, + multiSapProxy1->m_aabbMin,multiSapProxy1->m_aabbMax); + +} diff --git a/bullet/src/BulletCollision/BroadphaseCollision/btMultiSapBroadphase.h b/bullet/src/BulletCollision/BroadphaseCollision/btMultiSapBroadphase.h new file mode 100644 index 0000000..f719924 --- /dev/null +++ b/bullet/src/BulletCollision/BroadphaseCollision/btMultiSapBroadphase.h @@ -0,0 +1,113 @@ +/* +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. +*/ +#ifndef BT_MULTI_SAP_BROADPHASE +#define BT_MULTI_SAP_BROADPHASE + +#include "btBroadphaseInterface.h" +#include "LinearMath/btAlignedObjectArray.h" +#include "btOverlappingPairCache.h" + +class btAxisSweep3; +class btSimpleBroadphase; + + +typedef btAlignedObjectArray btSapBroadphaseArray; + +///multi SAP broadphase +///See http://www.continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=328 +///and http://www.continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=1329 +class btMultiSapBroadphase :public btBroadphaseInterface +{ + btSapBroadphaseArray m_sapBroadphases; + + btSimpleBroadphase* m_simpleBroadphase; + + btOverlappingPairCache* m_overlappingPairs; + + btOverlapFilterCallback* m_filterCallback; + + int m_invalidPair; + + struct btChildProxy + { + btBroadphaseProxy* m_proxy; + btBroadphaseInterface* m_childBroadphase; + }; + + struct btMultiSapProxy : public btBroadphaseProxy + { + + ///array with all the entries that this proxy belongs to + btAlignedObjectArray m_childProxies; + btVector3 m_aabbMin; + btVector3 m_aabbMax; + + int m_shapeType; + void* m_userPtr; + short int m_collisionFilterGroup; + short int m_collisionFilterMask; + + btMultiSapProxy(const btVector3& aabbMin, const btVector3& aabbMax,int shapeType,void* userPtr, short int collisionFilterGroup,short int collisionFilterMask) + :m_aabbMin(aabbMin), + m_aabbMax(aabbMax), + m_shapeType(shapeType), + m_userPtr(userPtr), + m_collisionFilterGroup(collisionFilterGroup), + m_collisionFilterMask(collisionFilterMask) + { + + } + + + }; + + btAlignedObjectArray m_multiSapProxies; + +public: + + btMultiSapBroadphase(int maxProxies = 16384); + + btSapBroadphaseArray getBroadphaseArray() + { + return m_sapBroadphases; + } + + const btSapBroadphaseArray getBroadphaseArray() const + { + return m_sapBroadphases; + } + + virtual ~btMultiSapBroadphase(); + + virtual btBroadphaseProxy* createProxy( const btVector3& aabbMin, const btVector3& aabbMax,int shapeType,void* userPtr, short int collisionFilterGroup,short int collisionFilterMask); + virtual void destroyProxy(btBroadphaseProxy* proxy); + virtual void setAabb(btBroadphaseProxy* proxy,const btVector3& aabbMin,const btVector3& aabbMax); + + ///calculateOverlappingPairs is optional: incremental algorithms (sweep and prune) might do it during the set aabb + virtual void calculateOverlappingPairs(); + + bool testAabbOverlap(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1); + + virtual btOverlappingPairCache* getOverlappingPairCache() + { + return m_overlappingPairs; + } + virtual const btOverlappingPairCache* getOverlappingPairCache() const + { + return m_overlappingPairs; + } +}; + +#endif //BT_MULTI_SAP_BROADPHASE diff --git a/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp b/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp new file mode 100644 index 0000000..a2813a8 --- /dev/null +++ b/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp @@ -0,0 +1,199 @@ + +/* +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. +*/ + + + +#include "btOverlappingPairCache.h" + +#include "btDispatcher.h" +#include "btCollisionAlgorithm.h" + +int gOverlappingPairs = 0; + +btOverlappingPairCache::btOverlappingPairCache(): +m_blockedForChanges(false), +m_overlapFilterCallback(0) +//m_NumOverlapBroadphasePair(0) +{ +} + + +btOverlappingPairCache::~btOverlappingPairCache() +{ + //todo/test: show we erase/delete data, or is it automatic +} + + +void btOverlappingPairCache::removeOverlappingPair(btBroadphasePair& findPair) +{ + + int findIndex = m_overlappingPairArray.findLinearSearch(findPair); + if (findIndex < m_overlappingPairArray.size()) + { + gOverlappingPairs--; + btBroadphasePair& pair = m_overlappingPairArray[findIndex]; + cleanOverlappingPair(pair); + + m_overlappingPairArray.swap(findIndex,m_overlappingPairArray.size()-1); + m_overlappingPairArray.pop_back(); + } +} + + +void btOverlappingPairCache::cleanOverlappingPair(btBroadphasePair& pair) +{ + if (pair.m_algorithm) + { + { + delete pair.m_algorithm;; + pair.m_algorithm=0; + } + } +} + + + + + +void btOverlappingPairCache::addOverlappingPair(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) +{ + //don't add overlap with own + assert(proxy0 != proxy1); + + if (!needsBroadphaseCollision(proxy0,proxy1)) + return; + + + btBroadphasePair pair(*proxy0,*proxy1); + + m_overlappingPairArray.push_back(pair); + gOverlappingPairs++; + +} + +///this findPair becomes really slow. Either sort the list to speedup the query, or +///use a different solution. It is mainly used for Removing overlapping pairs. Removal could be delayed. +///we could keep a linked list in each proxy, and store pair in one of the proxies (with lowest memory address) +///Also we can use a 2D bitmap, which can be useful for a future GPU implementation + btBroadphasePair* btOverlappingPairCache::findPair(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) +{ + if (!needsBroadphaseCollision(proxy0,proxy1)) + return 0; + + btBroadphasePair tmpPair(*proxy0,*proxy1); + int findIndex = m_overlappingPairArray.findLinearSearch(tmpPair); + + if (findIndex < m_overlappingPairArray.size()) + { + //assert(it != m_overlappingPairSet.end()); + btBroadphasePair* pair = &m_overlappingPairArray[findIndex]; + return pair; + } + return 0; +} + + + + + +void btOverlappingPairCache::cleanProxyFromPairs(btBroadphaseProxy* proxy) +{ + + class CleanPairCallback : public btOverlapCallback + { + btBroadphaseProxy* m_cleanProxy; + btOverlappingPairCache* m_pairCache; + + public: + CleanPairCallback(btBroadphaseProxy* cleanProxy,btOverlappingPairCache* pairCache) + :m_cleanProxy(cleanProxy), + m_pairCache(pairCache) + { + } + virtual bool processOverlap(btBroadphasePair& pair) + { + if ((pair.m_pProxy0 == m_cleanProxy) || + (pair.m_pProxy1 == m_cleanProxy)) + { + m_pairCache->cleanOverlappingPair(pair); + } + return false; + } + + }; + + CleanPairCallback cleanPairs(proxy,this); + + processAllOverlappingPairs(&cleanPairs); + +} + + + +void btOverlappingPairCache::removeOverlappingPairsContainingProxy(btBroadphaseProxy* proxy) +{ + + class RemovePairCallback : public btOverlapCallback + { + btBroadphaseProxy* m_obsoleteProxy; + + public: + RemovePairCallback(btBroadphaseProxy* obsoleteProxy) + :m_obsoleteProxy(obsoleteProxy) + { + } + virtual bool processOverlap(btBroadphasePair& pair) + { + return ((pair.m_pProxy0 == m_obsoleteProxy) || + (pair.m_pProxy1 == m_obsoleteProxy)); + } + + }; + + + RemovePairCallback removeCallback(proxy); + + processAllOverlappingPairs(&removeCallback); +} + + + +void btOverlappingPairCache::processAllOverlappingPairs(btOverlapCallback* callback) +{ + + int i; + + for (i=0;iprocessOverlap(*pair)) + { + cleanOverlappingPair(*pair); + + m_overlappingPairArray.swap(i,m_overlappingPairArray.size()-1); + m_overlappingPairArray.pop_back(); + gOverlappingPairs--; + } else + { + i++; + } + } +} + + + + diff --git a/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.h b/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.h new file mode 100644 index 0000000..f207db9 --- /dev/null +++ b/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.h @@ -0,0 +1,129 @@ + +/* +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. +*/ + +#ifndef OVERLAPPING_PAIR_CACHE_H +#define OVERLAPPING_PAIR_CACHE_H + + +#include "btBroadphaseInterface.h" +#include "btBroadphaseProxy.h" +#include "LinearMath/btPoint3.h" +#include "LinearMath/btAlignedObjectArray.h" + + +struct btOverlapCallback +{ + virtual ~btOverlapCallback() + {} + //return true for deletion of the pair + virtual bool processOverlap(btBroadphasePair& pair) = 0; +}; + +struct btOverlapFilterCallback +{ + virtual ~btOverlapFilterCallback() + {} + // return true when pairs need collision + virtual bool needBroadphaseCollision(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) const = 0; +}; + +typedef btAlignedObjectArray btBroadphasePairArray; + +///btOverlappingPairCache maintains the objects with overlapping AABB +///Typically managed by the Broadphase, Axis3Sweep or btSimpleBroadphase +class btOverlappingPairCache +{ + protected: + //avoid brute-force finding all the time + btBroadphasePairArray m_overlappingPairArray; + + //during the dispatch, check that user doesn't destroy/create proxy + bool m_blockedForChanges; + + //if set, use the callback instead of the built in filter in needBroadphaseCollision + btOverlapFilterCallback* m_overlapFilterCallback; + + public: + + btOverlappingPairCache(); + virtual ~btOverlappingPairCache(); + + virtual void processAllOverlappingPairs(btOverlapCallback*); + + void removeOverlappingPair(btBroadphasePair& pair); + + void cleanOverlappingPair(btBroadphasePair& pair); + + void addOverlappingPair(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1); + + btBroadphasePair* findPair(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1); + + + void cleanProxyFromPairs(btBroadphaseProxy* proxy); + + void removeOverlappingPairsContainingProxy(btBroadphaseProxy* proxy); + + + inline bool needsBroadphaseCollision(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) const + { + if (m_overlapFilterCallback) + return m_overlapFilterCallback->needBroadphaseCollision(proxy0,proxy1); + + bool collides = (proxy0->m_collisionFilterGroup & proxy1->m_collisionFilterMask) != 0; + collides = collides && (proxy1->m_collisionFilterGroup & proxy0->m_collisionFilterMask); + + return collides; + } + + btBroadphasePairArray& getOverlappingPairArray() + { + return m_overlappingPairArray; + } + + const btBroadphasePairArray& getOverlappingPairArray() const + { + return m_overlappingPairArray; + } + + btBroadphasePair* getOverlappingPairArrayPtr() + { + return &m_overlappingPairArray[0]; + } + + const btBroadphasePair* getOverlappingPairArrayPtr() const + { + return &m_overlappingPairArray[0]; + } + + int getNumOverlappingPairs() const + { + return m_overlappingPairArray.size(); + } + + btOverlapFilterCallback* getOverlapFilterCallback() + { + return m_overlapFilterCallback; + } + + void setOverlapFilterCallback(btOverlapFilterCallback* callback) + { + m_overlapFilterCallback = callback; + } + +}; +#endif //OVERLAPPING_PAIR_CACHE_H + + diff --git a/bullet/src/BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp b/bullet/src/BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp new file mode 100644 index 0000000..1e6c903 --- /dev/null +++ b/bullet/src/BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp @@ -0,0 +1,314 @@ +/* +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. +*/ + +#include "btSimpleBroadphase.h" +#include +#include + +#include "LinearMath/btVector3.h" +#include "LinearMath/btTransform.h" +#include "LinearMath/btMatrix3x3.h" +#include + +extern int gOverlappingPairs; + +void btSimpleBroadphase::validate() +{ + for (int i=0;i=0;i--) + { + BP_Proxy* proxy = m_pProxies[i]; + destroyProxy(proxy); + } + */ + + if (m_ownsPairCache) + { + delete m_pairCache; + } +} + + +btBroadphaseProxy* btSimpleBroadphase::createProxy( const btVector3& aabbMin, const btVector3& aabbMax,int shapeType,void* userPtr ,short int collisionFilterGroup,short int collisionFilterMask) +{ + if (m_numProxies >= m_maxProxies) + { + assert(0); + return 0; //should never happen, but don't let the game crash ;-) + } + assert(aabbMin[0]<= aabbMax[0] && aabbMin[1]<= aabbMax[1] && aabbMin[2]<= aabbMax[2]); + + int freeIndex= m_freeProxies[m_firstFreeProxy]; + btSimpleBroadphaseProxy* proxy = new (&m_proxies[freeIndex])btSimpleBroadphaseProxy(aabbMin,aabbMax,shapeType,userPtr,collisionFilterGroup,collisionFilterMask); + m_firstFreeProxy++; + + btSimpleBroadphaseProxy* proxy1 = &m_proxies[0]; + + int index = int(proxy - proxy1); + btAssert(index == freeIndex); + + m_pProxies[m_numProxies] = proxy; + m_numProxies++; + //validate(); + + return proxy; +} + +class RemovingOverlapCallback : public btOverlapCallback +{ +protected: + virtual bool processOverlap(btBroadphasePair& pair) + { + (void)pair; + btAssert(0); + return false; + } +}; + +class RemovePairContainingProxy +{ + + btBroadphaseProxy* m_targetProxy; + public: + virtual ~RemovePairContainingProxy() + { + } +protected: + virtual bool processOverlap(btBroadphasePair& pair) + { + btSimpleBroadphaseProxy* proxy0 = static_cast(pair.m_pProxy0); + btSimpleBroadphaseProxy* proxy1 = static_cast(pair.m_pProxy1); + + return ((m_targetProxy == proxy0 || m_targetProxy == proxy1)); + }; +}; + +void btSimpleBroadphase::destroyProxy(btBroadphaseProxy* proxyOrg) +{ + + int i; + + btSimpleBroadphaseProxy* proxy0 = static_cast(proxyOrg); + btSimpleBroadphaseProxy* proxy1 = &m_proxies[0]; + + int index = int(proxy0 - proxy1); + btAssert (index < m_maxProxies); + m_freeProxies[--m_firstFreeProxy] = index; + + m_pairCache->removeOverlappingPairsContainingProxy(proxyOrg); + + for (i=0;im_min = aabbMin; + sbp->m_max = aabbMax; +} + + + + + + + + + +bool btSimpleBroadphase::aabbOverlap(btSimpleBroadphaseProxy* proxy0,btSimpleBroadphaseProxy* proxy1) +{ + return proxy0->m_min[0] <= proxy1->m_max[0] && proxy1->m_min[0] <= proxy0->m_max[0] && + proxy0->m_min[1] <= proxy1->m_max[1] && proxy1->m_min[1] <= proxy0->m_max[1] && + proxy0->m_min[2] <= proxy1->m_max[2] && proxy1->m_min[2] <= proxy0->m_max[2]; + +} + + + +//then remove non-overlapping ones +class CheckOverlapCallback : public btOverlapCallback +{ +public: + virtual bool processOverlap(btBroadphasePair& pair) + { + return (!btSimpleBroadphase::aabbOverlap(static_cast(pair.m_pProxy0),static_cast(pair.m_pProxy1))); + } +}; + +void btSimpleBroadphase::calculateOverlappingPairs() +{ + //first check for new overlapping pairs + int i,j; + + for (i=0;ifindPair(proxy0,proxy1)) + { + m_pairCache->addOverlappingPair(proxy0,proxy1); + } + } + + } + } + + if (m_ownsPairCache) + { + + btBroadphasePairArray& overlappingPairArray = m_pairCache->getOverlappingPairArray(); + + //perform a sort, to find duplicates and to sort 'invalid' pairs to the end + overlappingPairArray.heapSort(btBroadphasePairSortPredicate()); + + overlappingPairArray.resize(overlappingPairArray.size() - m_invalidPair); + m_invalidPair = 0; + + + btBroadphasePair previousPair; + previousPair.m_pProxy0 = 0; + previousPair.m_pProxy1 = 0; + previousPair.m_algorithm = 0; + + + for (i=0;iprocessOverlap(pair); + } else + { + needsRemoval = true; + } + } else + { + //remove duplicate + needsRemoval = true; + //should have no algorithm + btAssert(!pair.m_algorithm); + } + + if (needsRemoval) + { + m_pairCache->cleanOverlappingPair(pair); + + // m_overlappingPairArray.swap(i,m_overlappingPairArray.size()-1); + // m_overlappingPairArray.pop_back(); + pair.m_pProxy0 = 0; + pair.m_pProxy1 = 0; + m_invalidPair++; + gOverlappingPairs--; + } + + } + + ///if you don't like to skip the invalid pairs in the array, execute following code: + #define CLEAN_INVALID_PAIRS 1 + #ifdef CLEAN_INVALID_PAIRS + + //perform a sort, to sort 'invalid' pairs to the end + overlappingPairArray.heapSort(btBroadphasePairSortPredicate()); + + overlappingPairArray.resize(overlappingPairArray.size() - m_invalidPair); + m_invalidPair = 0; + #endif//CLEAN_INVALID_PAIRS + + } +} + + +bool btSimpleBroadphase::testAabbOverlap(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) +{ + btSimpleBroadphaseProxy* p0 = getSimpleProxyFromProxy(proxy0); + btSimpleBroadphaseProxy* p1 = getSimpleProxyFromProxy(proxy1); + return aabbOverlap(p0,p1); +} + + + diff --git a/bullet/src/BulletCollision/BroadphaseCollision/btSimpleBroadphase.h b/bullet/src/BulletCollision/BroadphaseCollision/btSimpleBroadphase.h new file mode 100644 index 0000000..bfb279a --- /dev/null +++ b/bullet/src/BulletCollision/BroadphaseCollision/btSimpleBroadphase.h @@ -0,0 +1,107 @@ +/* +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. +*/ + +#ifndef SIMPLE_BROADPHASE_H +#define SIMPLE_BROADPHASE_H + + +#include "btOverlappingPairCache.h" + + +struct btSimpleBroadphaseProxy : public btBroadphaseProxy +{ + btVector3 m_min; + btVector3 m_max; + + btSimpleBroadphaseProxy() {}; + + btSimpleBroadphaseProxy(const btPoint3& minpt,const btPoint3& maxpt,int shapeType,void* userPtr,short int collisionFilterGroup,short int collisionFilterMask) + :btBroadphaseProxy(userPtr,collisionFilterGroup,collisionFilterMask), + m_min(minpt),m_max(maxpt) + { + (void)shapeType; + } + + +}; + +///SimpleBroadphase is a brute force aabb culling broadphase based on O(n^2) aabb checks +class btSimpleBroadphase : public btBroadphaseInterface +{ + +protected: + + btSimpleBroadphaseProxy* m_proxies; + int* m_freeProxies; + int m_firstFreeProxy; + + btSimpleBroadphaseProxy** m_pProxies; + int m_numProxies; + + btOverlappingPairCache* m_pairCache; + bool m_ownsPairCache; + + int m_invalidPair; + + int m_maxProxies; + + + inline btSimpleBroadphaseProxy* getSimpleProxyFromProxy(btBroadphaseProxy* proxy) + { + btSimpleBroadphaseProxy* proxy0 = static_cast(proxy); + return proxy0; + } + + + void validate(); + +protected: + + + + +public: + btSimpleBroadphase(int maxProxies=16384,btOverlappingPairCache* overlappingPairCache=0); + virtual ~btSimpleBroadphase(); + + + static bool aabbOverlap(btSimpleBroadphaseProxy* proxy0,btSimpleBroadphaseProxy* proxy1); + + + virtual btBroadphaseProxy* createProxy( const btVector3& aabbMin, const btVector3& aabbMax,int shapeType,void* userPtr ,short int collisionFilterGroup,short int collisionFilterMask); + + virtual void calculateOverlappingPairs(); + + virtual void destroyProxy(btBroadphaseProxy* proxy); + virtual void setAabb(btBroadphaseProxy* proxy,const btVector3& aabbMin,const btVector3& aabbMax); + + btOverlappingPairCache* getOverlappingPairCache() + { + return m_pairCache; + } + const btOverlappingPairCache* getOverlappingPairCache() const + { + return m_pairCache; + } + + bool testAabbOverlap(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1); + + +}; + + + +#endif //SIMPLE_BROADPHASE_H + diff --git a/bullet/src/BulletCollision/CollisionDispatch/SphereTriangleDetector.cpp b/bullet/src/BulletCollision/CollisionDispatch/SphereTriangleDetector.cpp new file mode 100644 index 0000000..b322c9f --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/SphereTriangleDetector.cpp @@ -0,0 +1,200 @@ +/* +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. +*/ + +#include "LinearMath/btScalar.h" +#include "SphereTriangleDetector.h" +#include "BulletCollision/CollisionShapes/btTriangleShape.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" + + +SphereTriangleDetector::SphereTriangleDetector(btSphereShape* sphere,btTriangleShape* triangle) +:m_sphere(sphere), +m_triangle(triangle) +{ + +} + +void SphereTriangleDetector::getClosestPoints(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw) +{ + + (void)debugDraw; + const btTransform& transformA = input.m_transformA; + const btTransform& transformB = input.m_transformB; + + btVector3 point,normal; + btScalar timeOfImpact = btScalar(1.); + btScalar depth = btScalar(0.); +// output.m_distance = btScalar(1e30); + //move sphere into triangle space + btTransform sphereInTr = transformB.inverseTimes(transformA); + + if (collide(sphereInTr.getOrigin(),point,normal,depth,timeOfImpact)) + { + output.addContactPoint(transformB.getBasis()*normal,transformB*point,depth); + } + +} + +#define MAX_OVERLAP btScalar(0.) + + + +// See also geometrictools.com +// Basic idea: D = |p - (lo + t0*lv)| where t0 = lv . (p - lo) / lv . lv +btScalar SegmentSqrDistance(const btVector3& from, const btVector3& to,const btVector3 &p, btVector3 &nearest) { + btVector3 diff = p - from; + btVector3 v = to - from; + btScalar t = v.dot(diff); + + if (t > 0) { + btScalar dotVV = v.dot(v); + if (t < dotVV) { + t /= dotVV; + diff -= t*v; + } else { + t = 1; + diff -= v; + } + } else + t = 0; + + nearest = from + t*v; + return diff.dot(diff); +} + +bool SphereTriangleDetector::facecontains(const btVector3 &p,const btVector3* vertices,btVector3& normal) { + btVector3 lp(p); + btVector3 lnormal(normal); + + return pointInTriangle(vertices, lnormal, &lp); +} + +///combined discrete/continuous sphere-triangle +bool SphereTriangleDetector::collide(const btVector3& sphereCenter,btVector3 &point, btVector3& resultNormal, btScalar& depth, btScalar &timeOfImpact) +{ + + const btVector3* vertices = &m_triangle->getVertexPtr(0); + const btVector3& c = sphereCenter; + btScalar r = m_sphere->getRadius(); + + btVector3 delta (0,0,0); + + btVector3 normal = (vertices[1]-vertices[0]).cross(vertices[2]-vertices[0]); + normal.normalize(); + btVector3 p1ToCentre = c - vertices[0]; + btScalar distanceFromPlane = p1ToCentre.dot(normal); + + if (distanceFromPlane < btScalar(0.)) + { + //triangle facing the other way + + distanceFromPlane *= btScalar(-1.); + normal *= btScalar(-1.); + } + + ///todo: move this gContactBreakingThreshold into a proper structure + extern btScalar gContactBreakingThreshold; + + btScalar contactMargin = gContactBreakingThreshold; + bool isInsideContactPlane = distanceFromPlane < r + contactMargin; + bool isInsideShellPlane = distanceFromPlane < r; + + btScalar deltaDotNormal = delta.dot(normal); + if (!isInsideShellPlane && deltaDotNormal >= btScalar(0.0)) + return false; + + // Check for contact / intersection + bool hasContact = false; + btVector3 contactPoint; + if (isInsideContactPlane) { + if (facecontains(c,vertices,normal)) { + // Inside the contact wedge - touches a point on the shell plane + hasContact = true; + contactPoint = c - normal*distanceFromPlane; + } else { + // Could be inside one of the contact capsules + btScalar contactCapsuleRadiusSqr = (r + contactMargin) * (r + contactMargin); + btVector3 nearestOnEdge; + for (int i = 0; i < m_triangle->getNumEdges(); i++) { + + btPoint3 pa; + btPoint3 pb; + + m_triangle->getEdge(i,pa,pb); + + btScalar distanceSqr = SegmentSqrDistance(pa,pb,c, nearestOnEdge); + if (distanceSqr < contactCapsuleRadiusSqr) { + // Yep, we're inside a capsule + hasContact = true; + contactPoint = nearestOnEdge; + } + + } + } + } + + if (hasContact) { + btVector3 contactToCentre = c - contactPoint; + btScalar distanceSqr = contactToCentre.length2(); + if (distanceSqr < (r - MAX_OVERLAP)*(r - MAX_OVERLAP)) { + btScalar distance = btSqrt(distanceSqr); + resultNormal = contactToCentre; + resultNormal.normalize(); + point = contactPoint; + depth = -(r-distance); + return true; + } + + if (delta.dot(contactToCentre) >= btScalar(0.0)) + return false; + + // Moving towards the contact point -> collision + point = contactPoint; + timeOfImpact = btScalar(0.0); + return true; + } + + return false; +} + + +bool SphereTriangleDetector::pointInTriangle(const btVector3 vertices[], const btVector3 &normal, btVector3 *p ) +{ + const btVector3* p1 = &vertices[0]; + const btVector3* p2 = &vertices[1]; + const btVector3* p3 = &vertices[2]; + + btVector3 edge1( *p2 - *p1 ); + btVector3 edge2( *p3 - *p2 ); + btVector3 edge3( *p1 - *p3 ); + + btVector3 p1_to_p( *p - *p1 ); + btVector3 p2_to_p( *p - *p2 ); + btVector3 p3_to_p( *p - *p3 ); + + btVector3 edge1_normal( edge1.cross(normal)); + btVector3 edge2_normal( edge2.cross(normal)); + btVector3 edge3_normal( edge3.cross(normal)); + + btScalar r1, r2, r3; + r1 = edge1_normal.dot( p1_to_p ); + r2 = edge2_normal.dot( p2_to_p ); + r3 = edge3_normal.dot( p3_to_p ); + if ( ( r1 > 0 && r2 > 0 && r3 > 0 ) || + ( r1 <= 0 && r2 <= 0 && r3 <= 0 ) ) + return true; + return false; + +} diff --git a/bullet/src/BulletCollision/CollisionDispatch/SphereTriangleDetector.h b/bullet/src/BulletCollision/CollisionDispatch/SphereTriangleDetector.h new file mode 100644 index 0000000..db13789 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/SphereTriangleDetector.h @@ -0,0 +1,49 @@ +/* +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. +*/ + +#ifndef SPHERE_TRIANGLE_DETECTOR_H +#define SPHERE_TRIANGLE_DETECTOR_H + +#include "BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h" +#include "LinearMath/btPoint3.h" + + +class btSphereShape; +class btTriangleShape; + + + +/// sphere-triangle to match the btDiscreteCollisionDetectorInterface +struct SphereTriangleDetector : public btDiscreteCollisionDetectorInterface +{ + virtual void getClosestPoints(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw); + + SphereTriangleDetector(btSphereShape* sphere,btTriangleShape* triangle); + + virtual ~SphereTriangleDetector() {}; + +private: + + bool collide(const btVector3& sphereCenter,btVector3 &point, btVector3& resultNormal, btScalar& depth, btScalar &timeOfImpact); + bool pointInTriangle(const btVector3 vertices[], const btVector3 &normal, btVector3 *p ); + bool facecontains(const btVector3 &p,const btVector3* vertices,btVector3& normal); + + btSphereShape* m_sphere; + btTriangleShape* m_triangle; + + +}; +#endif //SPHERE_TRIANGLE_DETECTOR_H + diff --git a/bullet/src/BulletCollision/CollisionDispatch/btCollisionCreateFunc.h b/bullet/src/BulletCollision/CollisionDispatch/btCollisionCreateFunc.h new file mode 100644 index 0000000..f6c4a25 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btCollisionCreateFunc.h @@ -0,0 +1,46 @@ +/* +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. +*/ + +#ifndef COLLISION_CREATE_FUNC +#define COLLISION_CREATE_FUNC + +#include "LinearMath/btAlignedObjectArray.h" +typedef btAlignedObjectArray btCollisionObjectArray; +class btCollisionAlgorithm; +class btCollisionObject; + +struct btCollisionAlgorithmConstructionInfo; + +///Used by the btCollisionDispatcher to register and create instances for btCollisionAlgorithm +struct btCollisionAlgorithmCreateFunc +{ + bool m_swapped; + + btCollisionAlgorithmCreateFunc() + :m_swapped(false) + { + } + virtual ~btCollisionAlgorithmCreateFunc(){}; + + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& , btCollisionObject* body0,btCollisionObject* body1) + { + + (void)body0; + (void)body1; + return 0; + } +}; +#endif //COLLISION_CREATE_FUNC + diff --git a/bullet/src/BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp b/bullet/src/BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp new file mode 100644 index 0000000..3291ad4 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp @@ -0,0 +1,367 @@ +/* +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. +*/ + + + +#include "btCollisionDispatcher.h" + + +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h" +#include "BulletCollision/CollisionShapes/btCollisionShape.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h" + +int gNumManifold = 0; + +#include + + +btCollisionDispatcher::btCollisionDispatcher(bool noDefaultAlgorithms): +m_count(0), +m_useIslands(true), +m_convexConvexCreateFunc(0), +m_convexConcaveCreateFunc(0), +m_swappedConvexConcaveCreateFunc(0), +m_compoundCreateFunc(0), +m_swappedCompoundCreateFunc(0), +m_emptyCreateFunc(0) +{ + (void)noDefaultAlgorithms; + int i; + + setNearCallback(defaultNearCallback); + + m_emptyCreateFunc = new btEmptyAlgorithm::CreateFunc; + for (i=0;iclearManifold(); +} + + +void btCollisionDispatcher::releaseManifold(btPersistentManifold* manifold) +{ + + gNumManifold--; + + //printf("releaseManifold: gNumManifold %d\n",gNumManifold); + clearManifold(manifold); + + ///todo: this can be improved a lot, linear search might be slow part! + int findIndex = m_manifoldsPtr.findLinearSearch(manifold); + if (findIndex < m_manifoldsPtr.size()) + { + m_manifoldsPtr.swap(findIndex,m_manifoldsPtr.size()-1); + m_manifoldsPtr.pop_back(); + delete manifold; + } + +} + + + +btCollisionAlgorithm* btCollisionDispatcher::findAlgorithm(btCollisionObject* body0,btCollisionObject* body1,btPersistentManifold* sharedManifold) +{ + +#ifdef USE_DISPATCH_REGISTRY_ARRAY + + btCollisionAlgorithmConstructionInfo ci; + ci.m_dispatcher = this; + ci.m_manifold = sharedManifold; + btCollisionAlgorithm* algo = m_doubleDispatch[body0->getCollisionShape()->getShapeType()][body1->getCollisionShape()->getShapeType()] + ->CreateCollisionAlgorithm(ci,body0,body1); +#else + btCollisionAlgorithm* algo = internalFindAlgorithm(body0,body1); +#endif //USE_DISPATCH_REGISTRY_ARRAY + return algo; +} + + +#ifndef BT_EXCLUDE_DEFAULT_COLLISIONALGORITHM_REGISTRATION + +btCollisionAlgorithmCreateFunc* btCollisionDispatcher::internalFindCreateFunc(int proxyType0,int proxyType1) +{ + + if (btBroadphaseProxy::isConvex(proxyType0) && btBroadphaseProxy::isConvex(proxyType1)) + { + return m_convexConvexCreateFunc; + } + + if (btBroadphaseProxy::isConvex(proxyType0) && btBroadphaseProxy::isConcave(proxyType1)) + { + return m_convexConcaveCreateFunc; + } + + if (btBroadphaseProxy::isConvex(proxyType1) && btBroadphaseProxy::isConcave(proxyType0)) + { + return m_swappedConvexConcaveCreateFunc; + } + + if (btBroadphaseProxy::isCompound(proxyType0)) + { + return m_compoundCreateFunc; + } else + { + if (btBroadphaseProxy::isCompound(proxyType1)) + { + return m_swappedCompoundCreateFunc; + } + } + + //failed to find an algorithm + return m_emptyCreateFunc; +} + +#endif //BT_EXCLUDE_DEFAULT_COLLISIONALGORITHM_REGISTRATION + + +#ifndef USE_DISPATCH_REGISTRY_ARRAY + +btCollisionAlgorithm* btCollisionDispatcher::internalFindAlgorithm(btCollisionObject* body0,btCollisionObject* body1,btPersistentManifold* sharedManifold) +{ + m_count++; + + btCollisionAlgorithmConstructionInfo ci; + ci.m_dispatcher = this; + + if (body0->getCollisionShape()->isConvex() && body1->getCollisionShape()->isConvex() ) + { + return new btConvexConvexAlgorithm(sharedManifold,ci,body0,body1); + } + + if (body0->getCollisionShape()->isConvex() && body1->getCollisionShape()->isConcave()) + { + return new btConvexConcaveCollisionAlgorithm(ci,body0,body1,false); + } + + if (body1->getCollisionShape()->isConvex() && body0->getCollisionShape()->isConcave()) + { + return new btConvexConcaveCollisionAlgorithm(ci,body0,body1,true); + } + + if (body0->getCollisionShape()->isCompound()) + { + return new btCompoundCollisionAlgorithm(ci,body0,body1,false); + } else + { + if (body1->getCollisionShape()->isCompound()) + { + return new btCompoundCollisionAlgorithm(ci,body0,body1,true); + } + } + + //failed to find an algorithm + return new btEmptyAlgorithm(ci); + +} +#endif //USE_DISPATCH_REGISTRY_ARRAY + +bool btCollisionDispatcher::needsResponse(btCollisionObject* body0,btCollisionObject* body1) +{ + //here you can do filtering + bool hasResponse = + (body0->hasContactResponse() && body1->hasContactResponse()); + //no response between two static/kinematic bodies: + hasResponse = hasResponse && + ((!body0->isStaticOrKinematicObject()) ||(! body1->isStaticOrKinematicObject())); + return hasResponse; +} + +bool btCollisionDispatcher::needsCollision(btCollisionObject* body0,btCollisionObject* body1) +{ + assert(body0); + assert(body1); + + bool needsCollision = true; + + //broadphase filtering already deals with this + if ((body0->isStaticObject() || body0->isKinematicObject()) && + (body1->isStaticObject() || body1->isKinematicObject())) + { + printf("warning btCollisionDispatcher::needsCollision: static-static collision!\n"); + } + + if ((!body0->isActive()) && (!body1->isActive())) + needsCollision = false; + else if (!body0->checkCollideWith(body1)) + needsCollision = false; + + return needsCollision ; + +} + + + +///interface for iterating all overlapping collision pairs, no matter how those pairs are stored (array, set, map etc) +///this is useful for the collision dispatcher. +class btCollisionPairCallback : public btOverlapCallback +{ + btDispatcherInfo& m_dispatchInfo; + btCollisionDispatcher* m_dispatcher; + +public: + + btCollisionPairCallback(btDispatcherInfo& dispatchInfo,btCollisionDispatcher* dispatcher) + :m_dispatchInfo(dispatchInfo), + m_dispatcher(dispatcher) + { + } + + btCollisionPairCallback& operator=(btCollisionPairCallback& other) + { + m_dispatchInfo = other.m_dispatchInfo; + m_dispatcher = other.m_dispatcher; + return *this; + } + + virtual ~btCollisionPairCallback() {} + + + virtual bool processOverlap(btBroadphasePair& pair) + { + (*m_dispatcher->getNearCallback())(pair,*m_dispatcher,m_dispatchInfo); + + return false; + } +}; + + +void btCollisionDispatcher::dispatchAllCollisionPairs(btOverlappingPairCache* pairCache,btDispatcherInfo& dispatchInfo) +{ + //m_blockedForChanges = true; + + btCollisionPairCallback collisionCallback(dispatchInfo,this); + + pairCache->processAllOverlappingPairs(&collisionCallback); + + //m_blockedForChanges = false; + +} + + + + +//by default, Bullet will use this near callback +void btCollisionDispatcher::defaultNearCallback(btBroadphasePair& collisionPair, btCollisionDispatcher& dispatcher, btDispatcherInfo& dispatchInfo) +{ + btCollisionObject* colObj0 = (btCollisionObject*)collisionPair.m_pProxy0->m_clientObject; + btCollisionObject* colObj1 = (btCollisionObject*)collisionPair.m_pProxy1->m_clientObject; + + if (dispatcher.needsCollision(colObj0,colObj1)) + { + //dispatcher will keep algorithms persistent in the collision pair + if (!collisionPair.m_algorithm) + { + collisionPair.m_algorithm = dispatcher.findAlgorithm(colObj0,colObj1); + } + + if (collisionPair.m_algorithm) + { + btManifoldResult contactPointResult(colObj0,colObj1); + + if (dispatchInfo.m_dispatchFunc == btDispatcherInfo::DISPATCH_DISCRETE) + { + //discrete collision detection query + collisionPair.m_algorithm->processCollision(colObj0,colObj1,dispatchInfo,&contactPointResult); + } else + { + //continuous collision detection query, time of impact (toi) + btScalar toi = collisionPair.m_algorithm->calculateTimeOfImpact(colObj0,colObj1,dispatchInfo,&contactPointResult); + if (dispatchInfo.m_timeOfImpact > toi) + dispatchInfo.m_timeOfImpact = toi; + + } + } + } + +} diff --git a/bullet/src/BulletCollision/CollisionDispatch/btCollisionDispatcher.h b/bullet/src/BulletCollision/CollisionDispatch/btCollisionDispatcher.h new file mode 100644 index 0000000..617dced --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btCollisionDispatcher.h @@ -0,0 +1,135 @@ +/* +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. +*/ + +#ifndef COLLISION__DISPATCHER_H +#define COLLISION__DISPATCHER_H + +#include "BulletCollision/BroadphaseCollision/btDispatcher.h" +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" + +#include "BulletCollision/CollisionDispatch/btManifoldResult.h" + +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "LinearMath/btAlignedObjectArray.h" + +class btIDebugDraw; +class btOverlappingPairCache; + + +#include "btCollisionCreateFunc.h" + +#define USE_DISPATCH_REGISTRY_ARRAY 1 + +class btCollisionDispatcher; +///user can override this nearcallback for collision filtering and more finegrained control over collision detection +typedef void (*btNearCallback)(btBroadphasePair& collisionPair, btCollisionDispatcher& dispatcher, btDispatcherInfo& dispatchInfo); + + +///btCollisionDispatcher supports algorithms that handle ConvexConvex and ConvexConcave collision pairs. +///Time of Impact, Closest Points and Penetration Depth. +class btCollisionDispatcher : public btDispatcher +{ + int m_count; + + btAlignedObjectArray m_manifoldsPtr; + + bool m_useIslands; + + btManifoldResult m_defaultManifoldResult; + + btNearCallback m_nearCallback; + + btCollisionAlgorithmCreateFunc* m_doubleDispatch[MAX_BROADPHASE_COLLISION_TYPES][MAX_BROADPHASE_COLLISION_TYPES]; + + btCollisionAlgorithmCreateFunc* internalFindCreateFunc(int proxyType0,int proxyType1); + + //default CreationFunctions, filling the m_doubleDispatch table + btCollisionAlgorithmCreateFunc* m_convexConvexCreateFunc; + btCollisionAlgorithmCreateFunc* m_convexConcaveCreateFunc; + btCollisionAlgorithmCreateFunc* m_swappedConvexConcaveCreateFunc; + btCollisionAlgorithmCreateFunc* m_compoundCreateFunc; + btCollisionAlgorithmCreateFunc* m_swappedCompoundCreateFunc; + btCollisionAlgorithmCreateFunc* m_emptyCreateFunc; + +#ifndef USE_DISPATCH_REGISTRY_ARRAY + btCollisionAlgorithm* internalFindAlgorithm(btCollisionObject* body0,btCollisionObject* body1,btPersistentManifold* sharedManifold = 0); +#endif //USE_DISPATCH_REGISTRY_ARRAY + +public: + + ///registerCollisionCreateFunc allows registration of custom/alternative collision create functions + void registerCollisionCreateFunc(int proxyType0,int proxyType1, btCollisionAlgorithmCreateFunc* createFunc); + + int getNumManifolds() const + { + return int( m_manifoldsPtr.size()); + } + + btPersistentManifold** getInternalManifoldPointer() + { + return &m_manifoldsPtr[0]; + } + + btPersistentManifold* getManifoldByIndexInternal(int index) + { + return m_manifoldsPtr[index]; + } + + const btPersistentManifold* getManifoldByIndexInternal(int index) const + { + return m_manifoldsPtr[index]; + } + + ///the default constructor creates/register default collision algorithms, for convex, compound and concave shape support + btCollisionDispatcher (); + + ///a special constructor that doesn't create/register the default collision algorithms + btCollisionDispatcher(bool noDefaultAlgorithms); + + virtual ~btCollisionDispatcher(); + + virtual btPersistentManifold* getNewManifold(void* b0,void* b1); + + virtual void releaseManifold(btPersistentManifold* manifold); + + + virtual void clearManifold(btPersistentManifold* manifold); + + + btCollisionAlgorithm* findAlgorithm(btCollisionObject* body0,btCollisionObject* body1,btPersistentManifold* sharedManifold = 0); + + virtual bool needsCollision(btCollisionObject* body0,btCollisionObject* body1); + + virtual bool needsResponse(btCollisionObject* body0,btCollisionObject* body1); + + virtual void dispatchAllCollisionPairs(btOverlappingPairCache* pairCache,btDispatcherInfo& dispatchInfo); + + void setNearCallback(btNearCallback nearCallback) + { + m_nearCallback = nearCallback; + } + + btNearCallback getNearCallback() const + { + return m_nearCallback; + } + + //by default, Bullet will use this near callback + static void defaultNearCallback(btBroadphasePair& collisionPair, btCollisionDispatcher& dispatcher, btDispatcherInfo& dispatchInfo); + +}; + +#endif //COLLISION__DISPATCHER_H + diff --git a/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.cpp b/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.cpp new file mode 100644 index 0000000..f20a677 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.cpp @@ -0,0 +1,61 @@ +/* +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. +*/ + +#include "btCollisionObject.h" + +btCollisionObject::btCollisionObject() + : m_broadphaseHandle(0), + m_collisionShape(0), + m_collisionFlags(0), + m_activationState1(1), + m_deactivationTime(btScalar(0.)), + m_userObjectPointer(0), + m_internalOwner(0), + m_hitFraction(btScalar(1.)), + m_ccdSweptSphereRadius(btScalar(0.)), + m_ccdSquareMotionThreshold(btScalar(0.)), + m_checkCollideWith(false), + m_islandTag1(-1), + m_companionId(-1) +{ + +} + +btCollisionObject::~btCollisionObject() +{ +} + +void btCollisionObject::setActivationState(int newState) +{ + if ( (m_activationState1 != DISABLE_DEACTIVATION) && (m_activationState1 != DISABLE_SIMULATION)) + m_activationState1 = newState; +} + +void btCollisionObject::forceActivationState(int newState) +{ + m_activationState1 = newState; +} + +void btCollisionObject::activate(bool forceActivation) +{ + if (forceActivation || !(m_collisionFlags & (CF_STATIC_OBJECT|CF_KINEMATIC_OBJECT))) + { + setActivationState(ACTIVE_TAG); + m_deactivationTime = btScalar(0.); + } +} + + + diff --git a/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.h b/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.h new file mode 100644 index 0000000..b69ec38 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.h @@ -0,0 +1,346 @@ +/* +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. +*/ + +#ifndef COLLISION_OBJECT_H +#define COLLISION_OBJECT_H + +#include "LinearMath/btTransform.h" + +//island management, m_activationState1 +#define ACTIVE_TAG 1 +#define ISLAND_SLEEPING 2 +#define WANTS_DEACTIVATION 3 +#define DISABLE_DEACTIVATION 4 +#define DISABLE_SIMULATION 5 + +struct btBroadphaseProxy; +class btCollisionShape; +#include "LinearMath/btMotionState.h" + + + +/// btCollisionObject can be used to manage collision detection objects. +/// btCollisionObject maintains all information that is needed for a collision detection: Shape, Transform and AABB proxy. +/// They can be added to the btCollisionWorld. +ATTRIBUTE_ALIGNED16(class) btCollisionObject +{ + +protected: + + btTransform m_worldTransform; + + ///m_interpolationWorldTransform is used for CCD and interpolation + ///it can be either previous or future (predicted) transform + btTransform m_interpolationWorldTransform; + //those two are experimental: just added for bullet time effect, so you can still apply impulses (directly modifying velocities) + //without destroying the continuous interpolated motion (which uses this interpolation velocities) + btVector3 m_interpolationLinearVelocity; + btVector3 m_interpolationAngularVelocity; + btBroadphaseProxy* m_broadphaseHandle; + btCollisionShape* m_collisionShape; + + int m_collisionFlags; + + int m_islandTag1; + int m_companionId; + + int m_activationState1; + btScalar m_deactivationTime; + + btScalar m_friction; + btScalar m_restitution; + + ///users can point to their objects, m_userPointer is not used by Bullet, see setUserPointer/getUserPointer + void* m_userObjectPointer; + + ///m_internalOwner is reserved to point to Bullet's btRigidBody. Don't use this, use m_userObjectPointer instead. + void* m_internalOwner; + + ///time of impact calculation + btScalar m_hitFraction; + + ///Swept sphere radius (0.0 by default), see btConvexConvexAlgorithm:: + btScalar m_ccdSweptSphereRadius; + + /// Don't do continuous collision detection if square motion (in one step) is less then m_ccdSquareMotionThreshold + btScalar m_ccdSquareMotionThreshold; + + /// If some object should have elaborate collision filtering by sub-classes + bool m_checkCollideWith; + + char m_pad[7]; + + virtual bool checkCollideWithOverride(btCollisionObject* co) + { + return true; + } + +public: + + enum CollisionFlags + { + CF_STATIC_OBJECT= 1, + CF_KINEMATIC_OBJECT= 2, + CF_NO_CONTACT_RESPONSE = 4, + CF_CUSTOM_MATERIAL_CALLBACK = 8//this allows per-triangle material (friction/restitution) + }; + + + inline bool mergesSimulationIslands() const + { + ///static objects, kinematic and object without contact response don't merge islands + return ((m_collisionFlags & (CF_STATIC_OBJECT | CF_KINEMATIC_OBJECT | CF_NO_CONTACT_RESPONSE) )==0); + } + + + inline bool isStaticObject() const { + return (m_collisionFlags & CF_STATIC_OBJECT) != 0; + } + + inline bool isKinematicObject() const + { + return (m_collisionFlags & CF_KINEMATIC_OBJECT) != 0; + } + + inline bool isStaticOrKinematicObject() const + { + return (m_collisionFlags & (CF_KINEMATIC_OBJECT | CF_STATIC_OBJECT)) != 0 ; + } + + inline bool hasContactResponse() const { + return (m_collisionFlags & CF_NO_CONTACT_RESPONSE)==0; + } + + + btCollisionObject(); + + virtual ~btCollisionObject(); + + void setCollisionShape(btCollisionShape* collisionShape) + { + m_collisionShape = collisionShape; + } + + const btCollisionShape* getCollisionShape() const + { + return m_collisionShape; + } + + btCollisionShape* getCollisionShape() + { + return m_collisionShape; + } + + + + + int getActivationState() const { return m_activationState1;} + + void setActivationState(int newState); + + void setDeactivationTime(btScalar time) + { + m_deactivationTime = time; + } + btScalar getDeactivationTime() const + { + return m_deactivationTime; + } + + void forceActivationState(int newState); + + void activate(bool forceActivation = false); + + inline bool isActive() const + { + return ((getActivationState() != ISLAND_SLEEPING) && (getActivationState() != DISABLE_SIMULATION)); + } + + void setRestitution(btScalar rest) + { + m_restitution = rest; + } + btScalar getRestitution() const + { + return m_restitution; + } + void setFriction(btScalar frict) + { + m_friction = frict; + } + btScalar getFriction() const + { + return m_friction; + } + + ///reserved for Bullet internal usage + void* getInternalOwner() + { + return m_internalOwner; + } + + const void* getInternalOwner() const + { + return m_internalOwner; + } + + btTransform& getWorldTransform() + { + return m_worldTransform; + } + + const btTransform& getWorldTransform() const + { + return m_worldTransform; + } + + void setWorldTransform(const btTransform& worldTrans) + { + m_worldTransform = worldTrans; + } + + + btBroadphaseProxy* getBroadphaseHandle() + { + return m_broadphaseHandle; + } + + const btBroadphaseProxy* getBroadphaseHandle() const + { + return m_broadphaseHandle; + } + + void setBroadphaseHandle(btBroadphaseProxy* handle) + { + m_broadphaseHandle = handle; + } + + + const btTransform& getInterpolationWorldTransform() const + { + return m_interpolationWorldTransform; + } + + btTransform& getInterpolationWorldTransform() + { + return m_interpolationWorldTransform; + } + + void setInterpolationWorldTransform(const btTransform& trans) + { + m_interpolationWorldTransform = trans; + } + + + const btVector3& getInterpolationLinearVelocity() const + { + return m_interpolationLinearVelocity; + } + + const btVector3& getInterpolationAngularVelocity() const + { + return m_interpolationAngularVelocity; + } + + const int getIslandTag() const + { + return m_islandTag1; + } + + void setIslandTag(int tag) + { + m_islandTag1 = tag; + } + + const int getCompanionId() const + { + return m_companionId; + } + + void setCompanionId(int id) + { + m_companionId = id; + } + + const btScalar getHitFraction() const + { + return m_hitFraction; + } + + void setHitFraction(btScalar hitFraction) + { + m_hitFraction = hitFraction; + } + + + const int getCollisionFlags() const + { + return m_collisionFlags; + } + + void setCollisionFlags(int flags) + { + m_collisionFlags = flags; + } + + ///Swept sphere radius (0.0 by default), see btConvexConvexAlgorithm:: + btScalar getCcdSweptSphereRadius() const + { + return m_ccdSweptSphereRadius; + } + + ///Swept sphere radius (0.0 by default), see btConvexConvexAlgorithm:: + void setCcdSweptSphereRadius(btScalar radius) + { + m_ccdSweptSphereRadius = radius; + } + + btScalar getCcdSquareMotionThreshold() const + { + return m_ccdSquareMotionThreshold; + } + + + /// Don't do continuous collision detection if square motion (in one step) is less then m_ccdSquareMotionThreshold + void setCcdSquareMotionThreshold(btScalar ccdSquareMotionThreshold) + { + m_ccdSquareMotionThreshold = ccdSquareMotionThreshold; + } + + ///users can point to their objects, userPointer is not used by Bullet + void* getUserPointer() const + { + return m_userObjectPointer; + } + + ///users can point to their objects, userPointer is not used by Bullet + void setUserPointer(void* userPointer) + { + m_userObjectPointer = userPointer; + } + + inline bool checkCollideWith(btCollisionObject* co) + { + if (m_checkCollideWith) + return checkCollideWithOverride(co); + + return true; + } + + +} +; + +#endif //COLLISION_OBJECT_H diff --git a/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp b/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp new file mode 100644 index 0000000..de91e75 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp @@ -0,0 +1,367 @@ +/* +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. +*/ + +#include "btCollisionWorld.h" +#include "btCollisionDispatcher.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/CollisionShapes/btCollisionShape.h" +#include "BulletCollision/CollisionShapes/btConvexShape.h" + +#include "BulletCollision/CollisionShapes/btSphereShape.h" //for raycasting +#include "BulletCollision/CollisionShapes/btTriangleMeshShape.h" //for raycasting +#include "BulletCollision/NarrowPhaseCollision/btRaycastCallback.h" +#include "BulletCollision/CollisionShapes/btCompoundShape.h" +#include "BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkConvexCast.h" +#include "BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.h" + +#include "BulletCollision/BroadphaseCollision/btBroadphaseInterface.h" +#include "LinearMath/btAabbUtil2.h" +#include "LinearMath/btQuickprof.h" +#include "LinearMath/btStackAlloc.h" + +//When the user doesn't provide dispatcher or broadphase, create basic versions (and delete them in destructor) +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/BroadphaseCollision/btSimpleBroadphase.h" + + + +btCollisionWorld::btCollisionWorld(btDispatcher* dispatcher,btBroadphaseInterface* pairCache, int stackSize) +:m_dispatcher1(dispatcher), +m_broadphasePairCache(pairCache), +m_ownsDispatcher(false), +m_ownsBroadphasePairCache(false) +{ + m_stackAlloc = new btStackAlloc(stackSize); + m_dispatchInfo.m_stackAllocator = m_stackAlloc; +} + + +btCollisionWorld::~btCollisionWorld() +{ + m_stackAlloc->destroy(); + delete m_stackAlloc; + + //clean up remaining objects + int i; + for (i=0;igetBroadphaseHandle(); + if (bp) + { + // + // only clear the cached algorithms + // + getBroadphase()->getOverlappingPairCache()->cleanProxyFromPairs(bp); + getBroadphase()->destroyProxy(bp); + } + } + + if (m_ownsDispatcher) + delete m_dispatcher1; + if (m_ownsBroadphasePairCache) + delete m_broadphasePairCache; + +} + + + + + + + + + + +void btCollisionWorld::addCollisionObject(btCollisionObject* collisionObject,short int collisionFilterGroup,short int collisionFilterMask) +{ + + //check that the object isn't already added + btAssert( m_collisionObjects.findLinearSearch(collisionObject) == m_collisionObjects.size()); + + m_collisionObjects.push_back(collisionObject); + + //calculate new AABB + btTransform trans = collisionObject->getWorldTransform(); + + btVector3 minAabb; + btVector3 maxAabb; + collisionObject->getCollisionShape()->getAabb(trans,minAabb,maxAabb); + + int type = collisionObject->getCollisionShape()->getShapeType(); + collisionObject->setBroadphaseHandle( getBroadphase()->createProxy( + minAabb, + maxAabb, + type, + collisionObject, + collisionFilterGroup, + collisionFilterMask + )) ; + + + + + +} + + + + +void btCollisionWorld::performDiscreteCollisionDetection() +{ + btDispatcherInfo& dispatchInfo = getDispatchInfo(); + + BEGIN_PROFILE("perform Broadphase Collision Detection"); + + + //update aabb (of all moved objects) + + btVector3 aabbMin,aabbMax; + for (int i=0;igetCollisionShape()->getAabb(m_collisionObjects[i]->getWorldTransform(),aabbMin,aabbMax); + m_broadphasePairCache->setAabb(m_collisionObjects[i]->getBroadphaseHandle(),aabbMin,aabbMax); + } + + m_broadphasePairCache->calculateOverlappingPairs(); + + END_PROFILE("perform Broadphase Collision Detection"); + + BEGIN_PROFILE("performDiscreteCollisionDetection"); + + btDispatcher* dispatcher = getDispatcher(); + if (dispatcher) + dispatcher->dispatchAllCollisionPairs(m_broadphasePairCache->getOverlappingPairCache(),dispatchInfo); + + END_PROFILE("performDiscreteCollisionDetection"); + +} + + +void btCollisionWorld::removeCollisionObject(btCollisionObject* collisionObject) +{ + + + //bool removeFromBroadphase = false; + + { + + btBroadphaseProxy* bp = collisionObject->getBroadphaseHandle(); + if (bp) + { + // + // only clear the cached algorithms + // + getBroadphase()->getOverlappingPairCache()->cleanProxyFromPairs(bp); + getBroadphase()->destroyProxy(bp); + collisionObject->setBroadphaseHandle(0); + } + } + + + //swapremove + m_collisionObjects.remove(collisionObject); + +} + + + +void btCollisionWorld::rayTestSingle(const btTransform& rayFromTrans,const btTransform& rayToTrans, + btCollisionObject* collisionObject, + const btCollisionShape* collisionShape, + const btTransform& colObjWorldTransform, + RayResultCallback& resultCallback,short int collisionFilterMask) +{ + + btSphereShape pointShape(btScalar(0.0)); + pointShape.setMargin(0.f); + + objectQuerySingle(&pointShape,rayFromTrans,rayToTrans, + collisionObject, + collisionShape, + colObjWorldTransform, + resultCallback,collisionFilterMask); +} + +void btCollisionWorld::objectQuerySingle(const btConvexShape* castShape,const btTransform& rayFromTrans,const btTransform& rayToTrans, + btCollisionObject* collisionObject, + const btCollisionShape* collisionShape, + const btTransform& colObjWorldTransform, + RayResultCallback& resultCallback,short int collisionFilterMask) +{ + + + if (collisionShape->isConvex()) + { + btConvexCast::CastResult castResult; + castResult.m_fraction = btScalar(1.);//?? + + btConvexShape* convexShape = (btConvexShape*) collisionShape; + btVoronoiSimplexSolver simplexSolver; + btSubsimplexConvexCast convexCaster(castShape,convexShape,&simplexSolver); + //btGjkConvexCast convexCaster(castShape,convexShape,&simplexSolver); + //btContinuousConvexCollision convexCaster(castShape,convexShape,&simplexSolver,0); + + if (convexCaster.calcTimeOfImpact(rayFromTrans,rayToTrans,colObjWorldTransform,colObjWorldTransform,castResult)) + { + //add hit + if (castResult.m_normal.length2() > btScalar(0.0001)) + { + castResult.m_normal.normalize(); + if (castResult.m_fraction < resultCallback.m_closestHitFraction) + { + + btCollisionWorld::LocalRayResult localRayResult + ( + collisionObject, + 0, + castResult.m_normal, + castResult.m_fraction + ); + + bool normalInWorldSpace = true; + resultCallback.AddSingleResult(localRayResult, normalInWorldSpace); + + } + } + } + } + else + { + + if (collisionShape->isConcave()) + { + + btTriangleMeshShape* triangleMesh = (btTriangleMeshShape*)collisionShape; + + btTransform worldTocollisionObject = colObjWorldTransform.inverse(); + + btVector3 rayFromLocal = worldTocollisionObject * rayFromTrans.getOrigin(); + btVector3 rayToLocal = worldTocollisionObject * rayToTrans.getOrigin(); + + //ConvexCast::CastResult + + struct BridgeTriangleRaycastCallback : public btTriangleRaycastCallback + { + btCollisionWorld::RayResultCallback* m_resultCallback; + btCollisionObject* m_collisionObject; + btTriangleMeshShape* m_triangleMesh; + + BridgeTriangleRaycastCallback( const btVector3& from,const btVector3& to, + btCollisionWorld::RayResultCallback* resultCallback, btCollisionObject* collisionObject,btTriangleMeshShape* triangleMesh): + btTriangleRaycastCallback(from,to), + m_resultCallback(resultCallback), + m_collisionObject(collisionObject), + m_triangleMesh(triangleMesh) + { + } + + + virtual btScalar reportHit(const btVector3& hitNormalLocal, btScalar hitFraction, int partId, int triangleIndex ) + { + btCollisionWorld::LocalShapeInfo shapeInfo; + shapeInfo.m_shapePart = partId; + shapeInfo.m_triangleIndex = triangleIndex; + + btCollisionWorld::LocalRayResult rayResult + (m_collisionObject, + &shapeInfo, + hitNormalLocal, + hitFraction); + + bool normalInWorldSpace = false; + return m_resultCallback->AddSingleResult(rayResult,normalInWorldSpace); + + + } + + }; + + + BridgeTriangleRaycastCallback rcb(rayFromLocal,rayToLocal,&resultCallback,collisionObject,triangleMesh); + rcb.m_hitFraction = resultCallback.m_closestHitFraction; + + btVector3 rayAabbMinLocal = rayFromLocal; + rayAabbMinLocal.setMin(rayToLocal); + btVector3 rayAabbMaxLocal = rayFromLocal; + rayAabbMaxLocal.setMax(rayToLocal); + + triangleMesh->processAllTriangles(&rcb,rayAabbMinLocal,rayAabbMaxLocal); + + } else + { + //todo: use AABB tree or other BVH acceleration structure! + if (collisionShape->isCompound()) + { + const btCompoundShape* compoundShape = static_cast(collisionShape); + int i=0; + for (i=0;igetNumChildShapes();i++) + { + btTransform childTrans = compoundShape->getChildTransform(i); + const btCollisionShape* childCollisionShape = compoundShape->getChildShape(i); + btTransform childWorldTrans = colObjWorldTransform * childTrans; + objectQuerySingle(castShape, rayFromTrans,rayToTrans, + collisionObject, + childCollisionShape, + childWorldTrans, + resultCallback, collisionFilterMask); + + } + + + } + } + } +} + +void btCollisionWorld::rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, RayResultCallback& resultCallback,short int collisionFilterMask) +{ + + + btTransform rayFromTrans,rayToTrans; + rayFromTrans.setIdentity(); + rayFromTrans.setOrigin(rayFromWorld); + rayToTrans.setIdentity(); + + rayToTrans.setOrigin(rayToWorld); + + /// go over all objects, and if the ray intersects their aabb, do a ray-shape query using convexCaster (CCD) + + int i; + for (i=0;igetBroadphaseHandle()->m_collisionFilterGroup & collisionFilterMask) { + //RigidcollisionObject* collisionObject = ctrl->GetRigidcollisionObject(); + btVector3 collisionObjectAabbMin,collisionObjectAabbMax; + collisionObject->getCollisionShape()->getAabb(collisionObject->getWorldTransform(),collisionObjectAabbMin,collisionObjectAabbMax); + + btScalar hitLambda = btScalar(1.); //could use resultCallback.m_closestHitFraction, but needs testing + btVector3 hitNormal; + if (btRayAabb(rayFromWorld,rayToWorld,collisionObjectAabbMin,collisionObjectAabbMax,hitLambda,hitNormal)) + { + rayTestSingle(rayFromTrans,rayToTrans, + collisionObject, + collisionObject->getCollisionShape(), + collisionObject->getWorldTransform(), + resultCallback); + } + } + } + +} diff --git a/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.h b/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.h new file mode 100644 index 0000000..aaf4527 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.h @@ -0,0 +1,262 @@ +/* +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. +*/ + + +/** + * @mainpage Bullet Documentation + * + * @section intro_sec Introduction + * Bullet Collision Detection & Physics SDK + * + * Bullet is a Collision Detection and Rigid Body Dynamics Library. The Library is Open Source and free for commercial use, under the ZLib license ( http://opensource.org/licenses/zlib-license.php ). + * + * There is the Physics Forum for Feedback and bteral Collision Detection and Physics discussions. + * Please visit http://www.continuousphysics.com/Bullet/phpBB2/index.php + * + * @section install_sec Installation + * + * @subsection step1 Step 1: Download + * You can download the Bullet Physics Library from our website: http://www.continuousphysics.com/Bullet/ + * @subsection step2 Step 2: Building + * Bullet comes with autogenerated Project Files for Microsoft Visual Studio 6, 7, 7.1 and 8. + * The main Workspace/Solution is located in Bullet/msvc/8/wksbullet.sln (replace 8 with your version). + * + * Under other platforms, like Linux or Mac OS-X, Bullet can be build using either using cmake, http://www.cmake.org, or jam, http://www.perforce.com/jam/jam.html . cmake can autogenerate Xcode, KDevelop, MSVC and other build systems. just run cmake . in the root of Bullet. + * Jam is a build system that can build the library, demos and also autogenerate the MSVC Project Files. + * So if you are not using MSVC, you can run configure and jam . + * If you don't have jam installed, you can make jam from the included jam-2.5 sources, or download jam from ftp://ftp.perforce.com/pub/jam/ + * + * @subsection step3 Step 3: Testing demos + * Try to run and experiment with CcdPhysicsDemo executable as a starting point. + * Bullet can be used in several ways, as Full Rigid Body simulation, as Collision Detector Library or Low Level / Snippets like the GJK Closest Point calculation. + * The Dependencies can be seen in this documentation under Directories + * + * @subsection step4 Step 4: Integrating in your application, Full Rigid Body Simulation + * Check out CcdPhysicsDemo how to create a btDynamicsWorld, btRigidBody and btCollisionShape, Stepping the simulation and synchronizing your graphics object transform. + * PLEASE NOTE THE CcdPhysicsEnvironment and CcdPhysicsController is obsolete and will be removed. It has been replaced by classes derived frmo btDynamicsWorld and btRididBody + * @subsection step5 Step 5 : Integrate the Collision Detection Library (without Dynamics and other Extras) + * Bullet Collision Detection can also be used without the Dynamics/Extras. + * Check out btCollisionWorld and btCollisionObject, and the CollisionInterfaceDemo. Also in Extras/test_BulletOde.cpp there is a sample Collision Detection integration with Open Dynamics Engine, ODE, http://www.ode.org + * @subsection step6 Step 6 : Use Snippets like the GJK Closest Point calculation. + * Bullet has been designed in a modular way keeping dependencies to a minimum. The ConvexHullDistance demo demonstrates direct use of btGjkPairDetector. + * + * @section copyright Copyright + * Copyright (C) 2005-2007 Erwin Coumans, some contributions Copyright Gino van den Bergen, Christer Ericson, Simon Hobbs, Ricardo Padrela, F Richter(res), Stephane Redon + * Special thanks to all visitors of the Bullet Physics forum, and in particular above contributors, Dave Eberle, Dirk Gregorius, Erin Catto, Dave Eberle, Adam Moravanszky, + * Pierre Terdiman, Kenny Erleben, Russell Smith, Oliver Strunk, Jan Paul van Waveren, Marten Svanfeldt. + * + */ + + + +#ifndef COLLISION_WORLD_H +#define COLLISION_WORLD_H + +class btStackAlloc; +class btCollisionShape; +class btConvexShape; +class btBroadphaseInterface; +#include "LinearMath/btVector3.h" +#include "LinearMath/btTransform.h" +#include "btCollisionObject.h" +#include "btCollisionDispatcher.h" //for definition of btCollisionObjectArray +#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h" +#include "LinearMath/btAlignedObjectArray.h" + +///CollisionWorld is interface and container for the collision detection +class btCollisionWorld +{ + + +protected: + + btAlignedObjectArray m_collisionObjects; + + btDispatcher* m_dispatcher1; + + btDispatcherInfo m_dispatchInfo; + + btStackAlloc* m_stackAlloc; + + btBroadphaseInterface* m_broadphasePairCache; + + bool m_ownsDispatcher; + bool m_ownsBroadphasePairCache; + +public: + + //this constructor doesn't own the dispatcher and paircache/broadphase + btCollisionWorld(btDispatcher* dispatcher,btBroadphaseInterface* broadphasePairCache, int stackSize = 2*1024*1024); + + virtual ~btCollisionWorld(); + + + btBroadphaseInterface* getBroadphase() + { + return m_broadphasePairCache; + } + + btOverlappingPairCache* getPairCache() + { + return m_broadphasePairCache->getOverlappingPairCache(); + } + + + btDispatcher* getDispatcher() + { + return m_dispatcher1; + } + + ///LocalShapeInfo gives extra information for complex shapes + ///Currently, only btTriangleMeshShape is available, so it just contains triangleIndex and subpart + struct LocalShapeInfo + { + int m_shapePart; + int m_triangleIndex; + + //const btCollisionShape* m_shapeTemp; + //const btTransform* m_shapeLocalTransform; + }; + + struct LocalRayResult + { + LocalRayResult(btCollisionObject* collisionObject, + LocalShapeInfo* localShapeInfo, + const btVector3& hitNormalLocal, + btScalar hitFraction) + :m_collisionObject(collisionObject), + m_localShapeInfo(localShapeInfo), + m_hitNormalLocal(hitNormalLocal), + m_hitFraction(hitFraction) + { + } + + btCollisionObject* m_collisionObject; + LocalShapeInfo* m_localShapeInfo; + btVector3 m_hitNormalLocal; + btScalar m_hitFraction; + + }; + + ///RayResultCallback is used to report new raycast results + struct RayResultCallback + { + virtual ~RayResultCallback() + { + } + btScalar m_closestHitFraction; + bool HasHit() + { + return (m_closestHitFraction < btScalar(1.)); + } + + RayResultCallback() + :m_closestHitFraction(btScalar(1.)) + { + } + virtual btScalar AddSingleResult(LocalRayResult& rayResult,bool normalInWorldSpace) = 0; + }; + + struct ClosestRayResultCallback : public RayResultCallback + { + ClosestRayResultCallback(const btVector3& rayFromWorld,const btVector3& rayToWorld) + :m_rayFromWorld(rayFromWorld), + m_rayToWorld(rayToWorld), + m_collisionObject(0) + { + } + + btVector3 m_rayFromWorld;//used to calculate hitPointWorld from hitFraction + btVector3 m_rayToWorld; + + btVector3 m_hitNormalWorld; + btVector3 m_hitPointWorld; + btCollisionObject* m_collisionObject; + + virtual btScalar AddSingleResult(LocalRayResult& rayResult,bool normalInWorldSpace) + { + +//caller already does the filter on the m_closestHitFraction + assert(rayResult.m_hitFraction <= m_closestHitFraction); + + m_closestHitFraction = rayResult.m_hitFraction; + m_collisionObject = rayResult.m_collisionObject; + if (normalInWorldSpace) + { + m_hitNormalWorld = rayResult.m_hitNormalLocal; + } else + { + ///need to transform normal into worldspace + m_hitNormalWorld = m_collisionObject->getWorldTransform().getBasis()*rayResult.m_hitNormalLocal; + } + m_hitPointWorld.setInterpolate3(m_rayFromWorld,m_rayToWorld,rayResult.m_hitFraction); + return rayResult.m_hitFraction; + } + }; + + + + + int getNumCollisionObjects() const + { + return int(m_collisionObjects.size()); + } + + /// rayTest performs a raycast on all objects in the btCollisionWorld, and calls the resultCallback + /// This allows for several queries: first hit, all hits, any hit, dependent on the value returned by the callback. + void rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, RayResultCallback& resultCallback, short int collisionFilterMask=-1); + + /// rayTestSingle performs a raycast call and calls the resultCallback. It is used internally by rayTest. + /// In a future implementation, we consider moving the ray test as a virtual method in btCollisionShape. + /// This allows more customization. + static void rayTestSingle(const btTransform& rayFromTrans,const btTransform& rayToTrans, + btCollisionObject* collisionObject, + const btCollisionShape* collisionShape, + const btTransform& colObjWorldTransform, + RayResultCallback& resultCallback, short int collisionFilterMask=-1); + + /// objectQuerySingle performs a collision detection query and calls the resultCallback. It is used internally by rayTest. + static void objectQuerySingle(const btConvexShape* castShape, const btTransform& rayFromTrans,const btTransform& rayToTrans, + btCollisionObject* collisionObject, + const btCollisionShape* collisionShape, + const btTransform& colObjWorldTransform, + RayResultCallback& resultCallback, short int collisionFilterMask=-1); + + void addCollisionObject(btCollisionObject* collisionObject,short int collisionFilterGroup=1,short int collisionFilterMask=1); + + btCollisionObjectArray& getCollisionObjectArray() + { + return m_collisionObjects; + } + + const btCollisionObjectArray& getCollisionObjectArray() const + { + return m_collisionObjects; + } + + + void removeCollisionObject(btCollisionObject* collisionObject); + + virtual void performDiscreteCollisionDetection(); + + btDispatcherInfo& getDispatchInfo() + { + return m_dispatchInfo; + } + +}; + + +#endif //COLLISION_WORLD_H diff --git a/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp b/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp new file mode 100644 index 0000000..3c9dece --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp @@ -0,0 +1,140 @@ +/* +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. +*/ + +#include "BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/CollisionShapes/btCompoundShape.h" + + +btCompoundCollisionAlgorithm::btCompoundCollisionAlgorithm( const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1,bool isSwapped) +:m_isSwapped(isSwapped) +{ + btCollisionObject* colObj = m_isSwapped? body1 : body0; + btCollisionObject* otherObj = m_isSwapped? body0 : body1; + assert (colObj->getCollisionShape()->isCompound()); + + btCompoundShape* compoundShape = static_cast(colObj->getCollisionShape()); + int numChildren = compoundShape->getNumChildShapes(); + int i; + + m_childCollisionAlgorithms.resize(numChildren); + for (i=0;igetChildShape(i); + btCollisionShape* orgShape = colObj->getCollisionShape(); + colObj->setCollisionShape( childShape ); + m_childCollisionAlgorithms[i] = ci.m_dispatcher->findAlgorithm(colObj,otherObj); + colObj->setCollisionShape( orgShape ); + } +} + + +btCompoundCollisionAlgorithm::~btCompoundCollisionAlgorithm() +{ + int numChildren = m_childCollisionAlgorithms.size(); + int i; + for (i=0;igetCollisionShape()->isCompound()); + btCompoundShape* compoundShape = static_cast(colObj->getCollisionShape()); + + //We will use the OptimizedBVH, AABB tree to cull potential child-overlaps + //If both proxies are Compound, we will deal with that directly, by performing sequential/parallel tree traversals + //given Proxy0 and Proxy1, if both have a tree, Tree0 and Tree1, this means: + //determine overlapping nodes of Proxy1 using Proxy0 AABB against Tree1 + //then use each overlapping node AABB against Tree0 + //and vise versa. + + int numChildren = m_childCollisionAlgorithms.size(); + int i; + for (i=0;igetChildShape(i); + + //backup + btTransform orgTrans = colObj->getWorldTransform(); + btCollisionShape* orgShape = colObj->getCollisionShape(); + + const btTransform& childTrans = compoundShape->getChildTransform(i); + //btTransform newChildWorldTrans = orgTrans*childTrans ; + colObj->setWorldTransform( orgTrans*childTrans ); + //the contactpoint is still projected back using the original inverted worldtrans + colObj->setCollisionShape( childShape ); + m_childCollisionAlgorithms[i]->processCollision(colObj,otherObj,dispatchInfo,resultOut); + //revert back + colObj->setCollisionShape( orgShape); + colObj->setWorldTransform( orgTrans ); + } +} + +btScalar btCompoundCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + + btCollisionObject* colObj = m_isSwapped? body1 : body0; + btCollisionObject* otherObj = m_isSwapped? body0 : body1; + + assert (colObj->getCollisionShape()->isCompound()); + + btCompoundShape* compoundShape = static_cast(colObj->getCollisionShape()); + + //We will use the OptimizedBVH, AABB tree to cull potential child-overlaps + //If both proxies are Compound, we will deal with that directly, by performing sequential/parallel tree traversals + //given Proxy0 and Proxy1, if both have a tree, Tree0 and Tree1, this means: + //determine overlapping nodes of Proxy1 using Proxy0 AABB against Tree1 + //then use each overlapping node AABB against Tree0 + //and vise versa. + + btScalar hitFraction = btScalar(1.); + + int numChildren = m_childCollisionAlgorithms.size(); + int i; + for (i=0;igetChildShape(i); + + //backup + btTransform orgTrans = colObj->getWorldTransform(); + btCollisionShape* orgShape = colObj->getCollisionShape(); + + const btTransform& childTrans = compoundShape->getChildTransform(i); + //btTransform newChildWorldTrans = orgTrans*childTrans ; + colObj->setWorldTransform( orgTrans*childTrans ); + + colObj->setCollisionShape( childShape ); + btScalar frac = m_childCollisionAlgorithms[i]->calculateTimeOfImpact(colObj,otherObj,dispatchInfo,resultOut); + if (fracsetCollisionShape( orgShape); + colObj->setWorldTransform( orgTrans); + } + return hitFraction; + +} + + diff --git a/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h b/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h new file mode 100644 index 0000000..4f68830 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h @@ -0,0 +1,64 @@ +/* +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. +*/ + +#ifndef COMPOUND_COLLISION_ALGORITHM_H +#define COMPOUND_COLLISION_ALGORITHM_H + +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" +#include "BulletCollision/BroadphaseCollision/btDispatcher.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseInterface.h" + +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" +class btDispatcher; +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "btCollisionCreateFunc.h" +#include "LinearMath/btAlignedObjectArray.h" + +/// btCompoundCollisionAlgorithm supports collision between CompoundCollisionShapes and other collision shapes +/// Place holder, not fully implemented yet +class btCompoundCollisionAlgorithm : public btCollisionAlgorithm +{ + btAlignedObjectArray m_childCollisionAlgorithms; + bool m_isSwapped; + +public: + + btCompoundCollisionAlgorithm( const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1,bool isSwapped); + + virtual ~btCompoundCollisionAlgorithm(); + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + return new btCompoundCollisionAlgorithm(ci,body0,body1,false); + } + }; + + struct SwappedCreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + return new btCompoundCollisionAlgorithm(ci,body0,body1,true); + } + }; + +}; + +#endif //COMPOUND_COLLISION_ALGORITHM_H diff --git a/bullet/src/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp b/bullet/src/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp new file mode 100644 index 0000000..5853402 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp @@ -0,0 +1,312 @@ +/* +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. +*/ + + +#include "btConvexConcaveCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/CollisionShapes/btMultiSphereShape.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/CollisionShapes/btConcaveShape.h" +#include "BulletCollision/CollisionDispatch/btManifoldResult.h" +#include "BulletCollision/NarrowPhaseCollision/btRaycastCallback.h" +#include "BulletCollision/CollisionShapes/btTriangleShape.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" +#include "LinearMath/btIDebugDraw.h" +#include "BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h" + +btConvexConcaveCollisionAlgorithm::btConvexConcaveCollisionAlgorithm( const btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1,bool isSwapped) +: btCollisionAlgorithm(ci), +m_isSwapped(isSwapped), +m_btConvexTriangleCallback(ci.m_dispatcher,body0,body1,isSwapped) +{ +} + +btConvexConcaveCollisionAlgorithm::~btConvexConcaveCollisionAlgorithm() +{ +} + + + +btConvexTriangleCallback::btConvexTriangleCallback(btDispatcher* dispatcher,btCollisionObject* body0,btCollisionObject* body1,bool isSwapped): + m_dispatcher(dispatcher), + m_dispatchInfoPtr(0) +{ + m_convexBody = isSwapped? body1:body0; + m_triBody = isSwapped? body0:body1; + + // + // create the manifold from the dispatcher 'manifold pool' + // + m_manifoldPtr = m_dispatcher->getNewManifold(m_convexBody,m_triBody); + + clearCache(); +} + +btConvexTriangleCallback::~btConvexTriangleCallback() +{ + clearCache(); + m_dispatcher->releaseManifold( m_manifoldPtr ); + +} + + +void btConvexTriangleCallback::clearCache() +{ + m_dispatcher->clearManifold(m_manifoldPtr); +}; + + + +void btConvexTriangleCallback::processTriangle(btVector3* triangle,int partId, int triangleIndex) +{ + + //just for debugging purposes + //printf("triangle %d",m_triangleCount++); + + + //aabb filter is already applied! + + btCollisionAlgorithmConstructionInfo ci; + ci.m_dispatcher = m_dispatcher; + + btCollisionObject* ob = static_cast(m_triBody); + + + + ///debug drawing of the overlapping triangles + if (m_dispatchInfoPtr && m_dispatchInfoPtr->m_debugDraw && m_dispatchInfoPtr->m_debugDraw->getDebugMode() > 0) + { + btVector3 color(255,255,0); + btTransform& tr = ob->getWorldTransform(); + m_dispatchInfoPtr->m_debugDraw->drawLine(tr(triangle[0]),tr(triangle[1]),color); + m_dispatchInfoPtr->m_debugDraw->drawLine(tr(triangle[1]),tr(triangle[2]),color); + m_dispatchInfoPtr->m_debugDraw->drawLine(tr(triangle[2]),tr(triangle[0]),color); + + //btVector3 center = triangle[0] + triangle[1]+triangle[2]; + //center *= btScalar(0.333333); + //m_dispatchInfoPtr->m_debugDraw->drawLine(tr(triangle[0]),tr(center),color); + //m_dispatchInfoPtr->m_debugDraw->drawLine(tr(triangle[1]),tr(center),color); + //m_dispatchInfoPtr->m_debugDraw->drawLine(tr(triangle[2]),tr(center),color); + + } + + + //btCollisionObject* colObj = static_cast(m_convexProxy->m_clientObject); + + if (m_convexBody->getCollisionShape()->isConvex()) + { + btTriangleShape tm(triangle[0],triangle[1],triangle[2]); + tm.setMargin(m_collisionMarginTriangle); + + + btCollisionShape* tmpShape = ob->getCollisionShape(); + ob->setCollisionShape( &tm ); + + + btCollisionAlgorithm* colAlgo = ci.m_dispatcher->findAlgorithm(m_convexBody,m_triBody,m_manifoldPtr); + ///this should use the btDispatcher, so the actual registered algorithm is used + // btConvexConvexAlgorithm cvxcvxalgo(m_manifoldPtr,ci,m_convexBody,m_triBody); + + m_resultOut->setShapeIdentifiers(-1,-1,partId,triangleIndex); + // cvxcvxalgo.setShapeIdentifiers(-1,-1,partId,triangleIndex); +// cvxcvxalgo.processCollision(m_convexBody,m_triBody,*m_dispatchInfoPtr,m_resultOut); + colAlgo->processCollision(m_convexBody,m_triBody,*m_dispatchInfoPtr,m_resultOut); + delete colAlgo; + ob->setCollisionShape( tmpShape ); + + } + + + +} + + + +void btConvexTriangleCallback::setTimeStepAndCounters(btScalar collisionMarginTriangle,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + m_dispatchInfoPtr = &dispatchInfo; + m_collisionMarginTriangle = collisionMarginTriangle; + m_resultOut = resultOut; + + //recalc aabbs + btTransform convexInTriangleSpace; + convexInTriangleSpace = m_triBody->getWorldTransform().inverse() * m_convexBody->getWorldTransform(); + btCollisionShape* convexShape = static_cast(m_convexBody->getCollisionShape()); + //CollisionShape* triangleShape = static_cast(triBody->m_collisionShape); + convexShape->getAabb(convexInTriangleSpace,m_aabbMin,m_aabbMax); + btScalar extraMargin = collisionMarginTriangle; + btVector3 extra(extraMargin,extraMargin,extraMargin); + + m_aabbMax += extra; + m_aabbMin -= extra; + +} + +void btConvexConcaveCollisionAlgorithm::clearCache() +{ + m_btConvexTriangleCallback.clearCache(); + +} + +void btConvexConcaveCollisionAlgorithm::processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + + + btCollisionObject* convexBody = m_isSwapped ? body1 : body0; + btCollisionObject* triBody = m_isSwapped ? body0 : body1; + + if (triBody->getCollisionShape()->isConcave()) + { + + + btCollisionObject* triOb = triBody; + btConcaveShape* concaveShape = static_cast( triOb->getCollisionShape()); + + if (convexBody->getCollisionShape()->isConvex()) + { + btScalar collisionMarginTriangle = concaveShape->getMargin(); + + resultOut->setPersistentManifold(m_btConvexTriangleCallback.m_manifoldPtr); + m_btConvexTriangleCallback.setTimeStepAndCounters(collisionMarginTriangle,dispatchInfo,resultOut); + + //Disable persistency. previously, some older algorithm calculated all contacts in one go, so you can clear it here. + //m_dispatcher->clearManifold(m_btConvexTriangleCallback.m_manifoldPtr); + + m_btConvexTriangleCallback.m_manifoldPtr->setBodies(convexBody,triBody); + + concaveShape->processAllTriangles( &m_btConvexTriangleCallback,m_btConvexTriangleCallback.getAabbMin(),m_btConvexTriangleCallback.getAabbMax()); + + + } + + } + +} + + +btScalar btConvexConcaveCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + (void)resultOut; + (void)dispatchInfo; + btCollisionObject* convexbody = m_isSwapped ? body1 : body0; + btCollisionObject* triBody = m_isSwapped ? body0 : body1; + + + //quick approximation using raycast, todo: hook up to the continuous collision detection (one of the btConvexCast) + + //only perform CCD above a certain threshold, this prevents blocking on the long run + //because object in a blocked ccd state (hitfraction<1) get their linear velocity halved each frame... + btScalar squareMot0 = (convexbody->getInterpolationWorldTransform().getOrigin() - convexbody->getWorldTransform().getOrigin()).length2(); + if (squareMot0 < convexbody->getCcdSquareMotionThreshold()) + { + return btScalar(1.); + } + + //const btVector3& from = convexbody->m_worldTransform.getOrigin(); + //btVector3 to = convexbody->m_interpolationWorldTransform.getOrigin(); + //todo: only do if the motion exceeds the 'radius' + + btTransform triInv = triBody->getWorldTransform().inverse(); + btTransform convexFromLocal = triInv * convexbody->getWorldTransform(); + btTransform convexToLocal = triInv * convexbody->getInterpolationWorldTransform(); + + struct LocalTriangleSphereCastCallback : public btTriangleCallback + { + btTransform m_ccdSphereFromTrans; + btTransform m_ccdSphereToTrans; + btTransform m_meshTransform; + + btScalar m_ccdSphereRadius; + btScalar m_hitFraction; + + + LocalTriangleSphereCastCallback(const btTransform& from,const btTransform& to,btScalar ccdSphereRadius,btScalar hitFraction) + :m_ccdSphereFromTrans(from), + m_ccdSphereToTrans(to), + m_ccdSphereRadius(ccdSphereRadius), + m_hitFraction(hitFraction) + { + } + + + virtual void processTriangle(btVector3* triangle, int partId, int triangleIndex) + { + (void)partId; + (void)triangleIndex; + //do a swept sphere for now + btTransform ident; + ident.setIdentity(); + btConvexCast::CastResult castResult; + castResult.m_fraction = m_hitFraction; + btSphereShape pointShape(m_ccdSphereRadius); + btTriangleShape triShape(triangle[0],triangle[1],triangle[2]); + btVoronoiSimplexSolver simplexSolver; + btSubsimplexConvexCast convexCaster(&pointShape,&triShape,&simplexSolver); + //GjkConvexCast convexCaster(&pointShape,convexShape,&simplexSolver); + //ContinuousConvexCollision convexCaster(&pointShape,convexShape,&simplexSolver,0); + //local space? + + if (convexCaster.calcTimeOfImpact(m_ccdSphereFromTrans,m_ccdSphereToTrans, + ident,ident,castResult)) + { + if (m_hitFraction > castResult.m_fraction) + m_hitFraction = castResult.m_fraction; + } + + } + + }; + + + + + + if (triBody->getCollisionShape()->isConcave()) + { + btVector3 rayAabbMin = convexFromLocal.getOrigin(); + rayAabbMin.setMin(convexToLocal.getOrigin()); + btVector3 rayAabbMax = convexFromLocal.getOrigin(); + rayAabbMax.setMax(convexToLocal.getOrigin()); + btScalar ccdRadius0 = convexbody->getCcdSweptSphereRadius(); + rayAabbMin -= btVector3(ccdRadius0,ccdRadius0,ccdRadius0); + rayAabbMax += btVector3(ccdRadius0,ccdRadius0,ccdRadius0); + + btScalar curHitFraction = btScalar(1.); //is this available? + LocalTriangleSphereCastCallback raycastCallback(convexFromLocal,convexToLocal, + convexbody->getCcdSweptSphereRadius(),curHitFraction); + + raycastCallback.m_hitFraction = convexbody->getHitFraction(); + + btCollisionObject* concavebody = triBody; + + btConcaveShape* triangleMesh = (btConcaveShape*) concavebody->getCollisionShape(); + + if (triangleMesh) + { + triangleMesh->processAllTriangles(&raycastCallback,rayAabbMin,rayAabbMax); + } + + + + if (raycastCallback.m_hitFraction < convexbody->getHitFraction()) + { + convexbody->setHitFraction( raycastCallback.m_hitFraction); + return raycastCallback.m_hitFraction; + } + } + + return btScalar(1.); + +} diff --git a/bullet/src/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.h b/bullet/src/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.h new file mode 100644 index 0000000..3a97c92 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.h @@ -0,0 +1,111 @@ +/* +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. +*/ + +#ifndef CONVEX_CONCAVE_COLLISION_ALGORITHM_H +#define CONVEX_CONCAVE_COLLISION_ALGORITHM_H + +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" +#include "BulletCollision/BroadphaseCollision/btDispatcher.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseInterface.h" +#include "BulletCollision/CollisionShapes/btTriangleCallback.h" +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" +class btDispatcher; +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "btCollisionCreateFunc.h" + +///For each triangle in the concave mesh that overlaps with the AABB of a convex (m_convexProxy), processTriangle is called. +class btConvexTriangleCallback : public btTriangleCallback +{ + btCollisionObject* m_convexBody; + btCollisionObject* m_triBody; + + btVector3 m_aabbMin; + btVector3 m_aabbMax ; + + btManifoldResult* m_resultOut; + + btDispatcher* m_dispatcher; + const btDispatcherInfo* m_dispatchInfoPtr; + btScalar m_collisionMarginTriangle; + +public: +int m_triangleCount; + + btPersistentManifold* m_manifoldPtr; + + btConvexTriangleCallback(btDispatcher* dispatcher,btCollisionObject* body0,btCollisionObject* body1,bool isSwapped); + + void setTimeStepAndCounters(btScalar collisionMarginTriangle,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual ~btConvexTriangleCallback(); + + virtual void processTriangle(btVector3* triangle, int partId, int triangleIndex); + + void clearCache(); + + inline const btVector3& getAabbMin() const + { + return m_aabbMin; + } + inline const btVector3& getAabbMax() const + { + return m_aabbMax; + } + +}; + + + + +/// btConvexConcaveCollisionAlgorithm supports collision between convex shapes and (concave) trianges meshes. +class btConvexConcaveCollisionAlgorithm : public btCollisionAlgorithm +{ + + bool m_isSwapped; + + btConvexTriangleCallback m_btConvexTriangleCallback; + + +public: + + btConvexConcaveCollisionAlgorithm( const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1,bool isSwapped); + + virtual ~btConvexConcaveCollisionAlgorithm(); + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + void clearCache(); + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + return new btConvexConcaveCollisionAlgorithm(ci,body0,body1,false); + } + }; + + struct SwappedCreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + return new btConvexConcaveCollisionAlgorithm(ci,body0,body1,true); + } + }; + +}; + +#endif //CONVEX_CONCAVE_COLLISION_ALGORITHM_H diff --git a/bullet/src/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp b/bullet/src/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp new file mode 100644 index 0000000..dd07f81 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp @@ -0,0 +1,254 @@ +/* +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. +*/ + +#include "btConvexConvexAlgorithm.h" + +#include +#include "BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseInterface.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/CollisionShapes/btConvexShape.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/CollisionShapes/btBoxShape.h" +#include "BulletCollision/CollisionDispatch/btManifoldResult.h" + +#include "BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h" +#include "BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.h" +#include "BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkConvexCast.h" + + + +#include "BulletCollision/CollisionShapes/btMinkowskiSumShape.h" +#include "BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" + +#include "BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h" + +#include "BulletCollision/NarrowPhaseCollision/btGjkEpa.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h" + + + + + + + +btConvexConvexAlgorithm::CreateFunc::CreateFunc() +{ + m_ownsSolvers = true; + m_simplexSolver = new btVoronoiSimplexSolver(); + m_pdSolver = new btGjkEpaPenetrationDepthSolver; +} + +btConvexConvexAlgorithm::CreateFunc::CreateFunc(btSimplexSolverInterface* simplexSolver, btConvexPenetrationDepthSolver* pdSolver) +{ + m_ownsSolvers = false; + m_simplexSolver = simplexSolver; + m_pdSolver = pdSolver; +} + +btConvexConvexAlgorithm::CreateFunc::~CreateFunc() +{ + if (m_ownsSolvers){ + delete m_simplexSolver; + delete m_pdSolver; + } +} + +btConvexConvexAlgorithm::btConvexConvexAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1,btSimplexSolverInterface* simplexSolver, btConvexPenetrationDepthSolver* pdSolver) +: btCollisionAlgorithm(ci), +m_gjkPairDetector(0,0,simplexSolver,pdSolver), +m_ownManifold (false), +m_manifoldPtr(mf), +m_lowLevelOfDetail(false) +{ + (void)body0; + (void)body1; + + +} + + + + +btConvexConvexAlgorithm::~btConvexConvexAlgorithm() +{ + if (m_ownManifold) + { + if (m_manifoldPtr) + m_dispatcher->releaseManifold(m_manifoldPtr); + } +} + +void btConvexConvexAlgorithm ::setLowLevelOfDetail(bool useLowLevel) +{ + m_lowLevelOfDetail = useLowLevel; +} + + + + + +// +// Convex-Convex collision algorithm +// +void btConvexConvexAlgorithm ::processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + + if (!m_manifoldPtr) + { + //swapped? + m_manifoldPtr = m_dispatcher->getNewManifold(body0,body1); + m_ownManifold = true; + } + resultOut->setPersistentManifold(m_manifoldPtr); + +#ifdef USE_BT_GJKEPA + btConvexShape* shape0(static_cast(body0->getCollisionShape())); + btConvexShape* shape1(static_cast(body1->getCollisionShape())); + const btScalar radialmargin(0/*shape0->getMargin()+shape1->getMargin()*/); + btGjkEpaSolver::sResults results; + if(btGjkEpaSolver::Collide( shape0,body0->getWorldTransform(), + shape1,body1->getWorldTransform(), + radialmargin,results)) + { + dispatchInfo.m_debugDraw->drawLine(results.witnesses[1],results.witnesses[1]+results.normal,btVector3(255,0,0)); + resultOut->addContactPoint(results.normal,results.witnesses[1],-results.depth); + } +#else + + btConvexShape* min0 = static_cast(body0->getCollisionShape()); + btConvexShape* min1 = static_cast(body1->getCollisionShape()); + + btGjkPairDetector::ClosestPointInput input; + + //TODO: if (dispatchInfo.m_useContinuous) + m_gjkPairDetector.setMinkowskiA(min0); + m_gjkPairDetector.setMinkowskiB(min1); + input.m_maximumDistanceSquared = min0->getMargin() + min1->getMargin() + m_manifoldPtr->getContactBreakingThreshold(); + input.m_maximumDistanceSquared*= input.m_maximumDistanceSquared; + input.m_stackAlloc = dispatchInfo.m_stackAllocator; + +// input.m_maximumDistanceSquared = btScalar(1e30); + + input.m_transformA = body0->getWorldTransform(); + input.m_transformB = body1->getWorldTransform(); + + m_gjkPairDetector.getClosestPoints(input,*resultOut,dispatchInfo.m_debugDraw); +#endif + +} + + + +bool disableCcd = false; +btScalar btConvexConvexAlgorithm::calculateTimeOfImpact(btCollisionObject* col0,btCollisionObject* col1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + (void)resultOut; + (void)dispatchInfo; + ///Rather then checking ALL pairs, only calculate TOI when motion exceeds threshold + + ///Linear motion for one of objects needs to exceed m_ccdSquareMotionThreshold + ///col0->m_worldTransform, + btScalar resultFraction = btScalar(1.); + + + btScalar squareMot0 = (col0->getInterpolationWorldTransform().getOrigin() - col0->getWorldTransform().getOrigin()).length2(); + btScalar squareMot1 = (col1->getInterpolationWorldTransform().getOrigin() - col1->getWorldTransform().getOrigin()).length2(); + + if (squareMot0 < col0->getCcdSquareMotionThreshold() && + squareMot1 < col1->getCcdSquareMotionThreshold()) + return resultFraction; + + if (disableCcd) + return btScalar(1.); + + + //An adhoc way of testing the Continuous Collision Detection algorithms + //One object is approximated as a sphere, to simplify things + //Starting in penetration should report no time of impact + //For proper CCD, better accuracy and handling of 'allowed' penetration should be added + //also the mainloop of the physics should have a kind of toi queue (something like Brian Mirtich's application of Timewarp for Rigidbodies) + + + /// Convex0 against sphere for Convex1 + { + btConvexShape* convex0 = static_cast(col0->getCollisionShape()); + + btSphereShape sphere1(col1->getCcdSweptSphereRadius()); //todo: allow non-zero sphere sizes, for better approximation + btConvexCast::CastResult result; + btVoronoiSimplexSolver voronoiSimplex; + //SubsimplexConvexCast ccd0(&sphere,min0,&voronoiSimplex); + ///Simplification, one object is simplified as a sphere + btGjkConvexCast ccd1( convex0 ,&sphere1,&voronoiSimplex); + //ContinuousConvexCollision ccd(min0,min1,&voronoiSimplex,0); + if (ccd1.calcTimeOfImpact(col0->getWorldTransform(),col0->getInterpolationWorldTransform(), + col1->getWorldTransform(),col1->getInterpolationWorldTransform(),result)) + { + + //store result.m_fraction in both bodies + + if (col0->getHitFraction()> result.m_fraction) + col0->setHitFraction( result.m_fraction ); + + if (col1->getHitFraction() > result.m_fraction) + col1->setHitFraction( result.m_fraction); + + if (resultFraction > result.m_fraction) + resultFraction = result.m_fraction; + + } + + + + + } + + /// Sphere (for convex0) against Convex1 + { + btConvexShape* convex1 = static_cast(col1->getCollisionShape()); + + btSphereShape sphere0(col0->getCcdSweptSphereRadius()); //todo: allow non-zero sphere sizes, for better approximation + btConvexCast::CastResult result; + btVoronoiSimplexSolver voronoiSimplex; + //SubsimplexConvexCast ccd0(&sphere,min0,&voronoiSimplex); + ///Simplification, one object is simplified as a sphere + btGjkConvexCast ccd1(&sphere0,convex1,&voronoiSimplex); + //ContinuousConvexCollision ccd(min0,min1,&voronoiSimplex,0); + if (ccd1.calcTimeOfImpact(col0->getWorldTransform(),col0->getInterpolationWorldTransform(), + col1->getWorldTransform(),col1->getInterpolationWorldTransform(),result)) + { + + //store result.m_fraction in both bodies + + if (col0->getHitFraction() > result.m_fraction) + col0->setHitFraction( result.m_fraction); + + if (col1->getHitFraction() > result.m_fraction) + col1->setHitFraction( result.m_fraction); + + if (resultFraction > result.m_fraction) + resultFraction = result.m_fraction; + + } + } + + return resultFraction; + +} + diff --git a/bullet/src/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.h b/bullet/src/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.h new file mode 100644 index 0000000..83ef3c6 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.h @@ -0,0 +1,76 @@ +/* +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. +*/ + +#ifndef CONVEX_CONVEX_ALGORITHM_H +#define CONVEX_CONVEX_ALGORITHM_H + +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h" +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h" +#include "btCollisionCreateFunc.h" + +class btConvexPenetrationDepthSolver; + +///ConvexConvexAlgorithm collision algorithm implements time of impact, convex closest points and penetration depth calculations. +class btConvexConvexAlgorithm : public btCollisionAlgorithm +{ + btGjkPairDetector m_gjkPairDetector; +public: + + bool m_ownManifold; + btPersistentManifold* m_manifoldPtr; + bool m_lowLevelOfDetail; + + +public: + + btConvexConvexAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1, btSimplexSolverInterface* simplexSolver, btConvexPenetrationDepthSolver* pdSolver); + + virtual ~btConvexConvexAlgorithm(); + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + void setLowLevelOfDetail(bool useLowLevel); + + + const btPersistentManifold* getManifold() + { + return m_manifoldPtr; + } + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + btConvexPenetrationDepthSolver* m_pdSolver; + btSimplexSolverInterface* m_simplexSolver; + bool m_ownsSolvers; + + CreateFunc(btSimplexSolverInterface* simplexSolver, btConvexPenetrationDepthSolver* pdSolver); + CreateFunc(); + virtual ~CreateFunc(); + + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + return new btConvexConvexAlgorithm(ci.m_manifold,ci,body0,body1,m_simplexSolver,m_pdSolver); + } + }; + + +}; + +#endif //CONVEX_CONVEX_ALGORITHM_H diff --git a/bullet/src/BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.cpp b/bullet/src/BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.cpp new file mode 100644 index 0000000..605c4ff --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.cpp @@ -0,0 +1,34 @@ +/* +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. +*/ + +#include "btEmptyCollisionAlgorithm.h" + + + +btEmptyAlgorithm::btEmptyAlgorithm(const btCollisionAlgorithmConstructionInfo& ci) + : btCollisionAlgorithm(ci) +{ +} + +void btEmptyAlgorithm::processCollision (btCollisionObject* ,btCollisionObject* ,const btDispatcherInfo& ,btManifoldResult* ) +{ +} + +btScalar btEmptyAlgorithm::calculateTimeOfImpact(btCollisionObject* ,btCollisionObject* ,const btDispatcherInfo& ,btManifoldResult* ) +{ + return btScalar(1.); +} + + diff --git a/bullet/src/BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.h b/bullet/src/BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.h new file mode 100644 index 0000000..86837ff --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.h @@ -0,0 +1,48 @@ +/* +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. +*/ + +#ifndef EMPTY_ALGORITH +#define EMPTY_ALGORITH +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" +#include "btCollisionCreateFunc.h" + +#define ATTRIBUTE_ALIGNED(a) + +///EmptyAlgorithm is a stub for unsupported collision pairs. +///The dispatcher can dispatch a persistent btEmptyAlgorithm to avoid a search every frame. +class btEmptyAlgorithm : public btCollisionAlgorithm +{ + +public: + + btEmptyAlgorithm(const btCollisionAlgorithmConstructionInfo& ci); + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + (void)body0; + (void)body1; + return new btEmptyAlgorithm(ci); + } + }; + +} ATTRIBUTE_ALIGNED(16); + +#endif //EMPTY_ALGORITH diff --git a/bullet/src/BulletCollision/CollisionDispatch/btManifoldResult.cpp b/bullet/src/BulletCollision/CollisionDispatch/btManifoldResult.cpp new file mode 100644 index 0000000..22acc2d --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btManifoldResult.cpp @@ -0,0 +1,109 @@ +/* +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. +*/ + + +#include "btManifoldResult.h" +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" + + +///This is to allow MaterialCombiner/Custom Friction/Restitution values +ContactAddedCallback gContactAddedCallback=0; + +///User can override this material combiner by implementing gContactAddedCallback and setting body0->m_collisionFlags |= btCollisionObject::customMaterialCallback; +inline btScalar calculateCombinedFriction(const btCollisionObject* body0,const btCollisionObject* body1) +{ + btScalar friction = body0->getFriction() * body1->getFriction(); + + const btScalar MAX_FRICTION = btScalar(10.); + if (friction < -MAX_FRICTION) + friction = -MAX_FRICTION; + if (friction > MAX_FRICTION) + friction = MAX_FRICTION; + return friction; + +} + +inline btScalar calculateCombinedRestitution(const btCollisionObject* body0,const btCollisionObject* body1) +{ + return body0->getRestitution() * body1->getRestitution(); +} + + + +btManifoldResult::btManifoldResult(btCollisionObject* body0,btCollisionObject* body1) + :m_manifoldPtr(0), + m_body0(body0), + m_body1(body1) +{ + m_rootTransA = body0->getWorldTransform(); + m_rootTransB = body1->getWorldTransform(); +} + + +void btManifoldResult::addContactPoint(const btVector3& normalOnBInWorld,const btVector3& pointInWorld,btScalar depth) +{ + assert(m_manifoldPtr); + //order in manifold needs to match + + if (depth > m_manifoldPtr->getContactBreakingThreshold()) + return; + + bool isSwapped = m_manifoldPtr->getBody0() != m_body0; + + btVector3 pointA = pointInWorld + normalOnBInWorld * depth; + + btVector3 localA; + btVector3 localB; + + if (isSwapped) + { + localA = m_rootTransB.invXform(pointA ); + localB = m_rootTransA.invXform(pointInWorld); + } else + { + localA = m_rootTransA.invXform(pointA ); + localB = m_rootTransB.invXform(pointInWorld); + } + + btManifoldPoint newPt(localA,localB,normalOnBInWorld,depth); + + int insertIndex = m_manifoldPtr->getCacheEntry(newPt); + + newPt.m_combinedFriction = calculateCombinedFriction(m_body0,m_body1); + newPt.m_combinedRestitution = calculateCombinedRestitution(m_body0,m_body1); + + //User can override friction and/or restitution + if (gContactAddedCallback && + //and if either of the two bodies requires custom material + ((m_body0->getCollisionFlags() & btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK) || + (m_body1->getCollisionFlags() & btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK))) + { + //experimental feature info, for per-triangle material etc. + btCollisionObject* obj0 = isSwapped? m_body1 : m_body0; + btCollisionObject* obj1 = isSwapped? m_body0 : m_body1; + (*gContactAddedCallback)(newPt,obj0,m_partId0,m_index0,obj1,m_partId1,m_index1); + } + + if (insertIndex >= 0) + { + //const btManifoldPoint& oldPoint = m_manifoldPtr->getContactPoint(insertIndex); + m_manifoldPtr->replaceContactPoint(newPt,insertIndex); + } else + { + m_manifoldPtr->AddManifoldPoint(newPt); + } +} + diff --git a/bullet/src/BulletCollision/CollisionDispatch/btManifoldResult.h b/bullet/src/BulletCollision/CollisionDispatch/btManifoldResult.h new file mode 100644 index 0000000..feedd4c --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btManifoldResult.h @@ -0,0 +1,77 @@ +/* +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. +*/ + + +#ifndef MANIFOLD_RESULT_H +#define MANIFOLD_RESULT_H + +class btCollisionObject; +class btPersistentManifold; +class btManifoldPoint; + +#include "BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h" + +#include "LinearMath/btTransform.h" + +typedef bool (*ContactAddedCallback)(btManifoldPoint& cp, const btCollisionObject* colObj0,int partId0,int index0,const btCollisionObject* colObj1,int partId1,int index1); +extern ContactAddedCallback gContactAddedCallback; + + + +///btManifoldResult is a helper class to manage contact results. +class btManifoldResult : public btDiscreteCollisionDetectorInterface::Result +{ + btPersistentManifold* m_manifoldPtr; + + //we need this for compounds + btTransform m_rootTransA; + btTransform m_rootTransB; + + btCollisionObject* m_body0; + btCollisionObject* m_body1; + int m_partId0; + int m_partId1; + int m_index0; + int m_index1; +public: + + btManifoldResult() + { + } + + btManifoldResult(btCollisionObject* body0,btCollisionObject* body1); + + virtual ~btManifoldResult() {}; + + void setPersistentManifold(btPersistentManifold* manifoldPtr) + { + m_manifoldPtr = manifoldPtr; + } + + virtual void setShapeIdentifiers(int partId0,int index0, int partId1,int index1) + { + m_partId0=partId0; + m_partId1=partId1; + m_index0=index0; + m_index1=index1; + } + + virtual void addContactPoint(const btVector3& normalOnBInWorld,const btVector3& pointInWorld,btScalar depth); + + + +}; + +#endif //MANIFOLD_RESULT_H diff --git a/bullet/src/BulletCollision/CollisionDispatch/btSimulationIslandManager.cpp b/bullet/src/BulletCollision/CollisionDispatch/btSimulationIslandManager.cpp new file mode 100644 index 0000000..4790802 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btSimulationIslandManager.cpp @@ -0,0 +1,357 @@ + + +#include "LinearMath/btScalar.h" +#include "btSimulationIslandManager.h" +#include "BulletCollision/BroadphaseCollision/btDispatcher.h" +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/CollisionDispatch/btCollisionWorld.h" + +#include +#include "LinearMath/btQuickprof.h" + +btSimulationIslandManager::btSimulationIslandManager() +{ +} + +btSimulationIslandManager::~btSimulationIslandManager() +{ +} + + +void btSimulationIslandManager::initUnionFind(int n) +{ + m_unionFind.reset(n); +} + + +void btSimulationIslandManager::findUnions(btDispatcher* dispatcher) +{ + + { + for (int i=0;igetNumManifolds();i++) + { + const btPersistentManifold* manifold = dispatcher->getManifoldByIndexInternal(i); + //static objects (invmass btScalar(0.)) don't merge ! + + const btCollisionObject* colObj0 = static_cast(manifold->getBody0()); + const btCollisionObject* colObj1 = static_cast(manifold->getBody1()); + + if (((colObj0) && ((colObj0)->mergesSimulationIslands())) && + ((colObj1) && ((colObj1)->mergesSimulationIslands()))) + { + + m_unionFind.unite((colObj0)->getIslandTag(), + (colObj1)->getIslandTag()); + } + } + } +} + + +void btSimulationIslandManager::updateActivationState(btCollisionWorld* colWorld,btDispatcher* dispatcher) +{ + + initUnionFind( int (colWorld->getCollisionObjectArray().size())); + + // put the index into m_controllers into m_tag + { + + int index = 0; + int i; + for (i=0;igetCollisionObjectArray().size(); i++) + { + btCollisionObject* collisionObject= colWorld->getCollisionObjectArray()[i]; + collisionObject->setIslandTag(index); + collisionObject->setCompanionId(-1); + collisionObject->setHitFraction(btScalar(1.)); + index++; + + } + } + // do the union find + + findUnions(dispatcher); + + + +} + + + + +void btSimulationIslandManager::storeIslandActivationState(btCollisionWorld* colWorld) +{ + // put the islandId ('find' value) into m_tag + { + + + int index = 0; + int i; + for (i=0;igetCollisionObjectArray().size();i++) + { + btCollisionObject* collisionObject= colWorld->getCollisionObjectArray()[i]; + if (collisionObject->mergesSimulationIslands()) + { + collisionObject->setIslandTag( m_unionFind.find(index) ); + collisionObject->setCompanionId(-1); + } else + { + collisionObject->setIslandTag(-1); + collisionObject->setCompanionId(-2); + } + index++; + } + } +} + +inline int getIslandId(const btPersistentManifold* lhs) +{ + int islandId; + const btCollisionObject* rcolObj0 = static_cast(lhs->getBody0()); + const btCollisionObject* rcolObj1 = static_cast(lhs->getBody1()); + islandId= rcolObj0->getIslandTag()>=0?rcolObj0->getIslandTag():rcolObj1->getIslandTag(); + return islandId; + +} + + + +/// function object that routes calls to operator< +class btPersistentManifoldSortPredicate +{ + public: + + SIMD_FORCE_INLINE bool operator() ( const btPersistentManifold* lhs, const btPersistentManifold* rhs ) + { + return getIslandId(lhs) < getIslandId(rhs); + } +}; + + + + + +// +// todo: this is random access, it can be walked 'cache friendly'! +// +void btSimulationIslandManager::buildAndProcessIslands(btDispatcher* dispatcher,btCollisionObjectArray& collisionObjects, IslandCallback* callback) +{ + + + + /*if (0) + { + int maxNumManifolds = dispatcher->getNumManifolds(); + btCollisionDispatcher* colDis = (btCollisionDispatcher*)dispatcher; + btPersistentManifold** manifold = colDis->getInternalManifoldPointer(); + callback->ProcessIsland(&collisionObjects[0],collisionObjects.size(),manifold,maxNumManifolds, 0); + return; + } + */ + + + BEGIN_PROFILE("islandUnionFindAndHeapSort"); + + //we are going to sort the unionfind array, and store the element id in the size + //afterwards, we clean unionfind, to make sure no-one uses it anymore + + getUnionFind().sortIslands(); + int numElem = getUnionFind().getNumElements(); + + int endIslandIndex=1; + int startIslandIndex; + + + //update the sleeping state for bodies, if all are sleeping + for ( startIslandIndex=0;startIslandIndexgetIslandTag() != islandId) && (colObj0->getIslandTag() != -1)) + { + printf("error in island management\n"); + } + + assert((colObj0->getIslandTag() == islandId) || (colObj0->getIslandTag() == -1)); + if (colObj0->getIslandTag() == islandId) + { + if (colObj0->getActivationState()== ACTIVE_TAG) + { + allSleeping = false; + } + if (colObj0->getActivationState()== DISABLE_DEACTIVATION) + { + allSleeping = false; + } + } + } + + + if (allSleeping) + { + int idx; + for (idx=startIslandIndex;idxgetIslandTag() != islandId) && (colObj0->getIslandTag() != -1)) + { + printf("error in island management\n"); + } + + assert((colObj0->getIslandTag() == islandId) || (colObj0->getIslandTag() == -1)); + + if (colObj0->getIslandTag() == islandId) + { + colObj0->setActivationState( ISLAND_SLEEPING ); + } + } + } else + { + + int idx; + for (idx=startIslandIndex;idxgetIslandTag() != islandId) && (colObj0->getIslandTag() != -1)) + { + printf("error in island management\n"); + } + + assert((colObj0->getIslandTag() == islandId) || (colObj0->getIslandTag() == -1)); + + if (colObj0->getIslandTag() == islandId) + { + if ( colObj0->getActivationState() == ISLAND_SLEEPING) + { + colObj0->setActivationState( WANTS_DEACTIVATION); + } + } + } + } + } + + btAlignedObjectArray islandmanifold; + int i; + int maxNumManifolds = dispatcher->getNumManifolds(); + islandmanifold.reserve(maxNumManifolds); + + for (i=0;igetManifoldByIndexInternal(i); + + btCollisionObject* colObj0 = static_cast(manifold->getBody0()); + btCollisionObject* colObj1 = static_cast(manifold->getBody1()); + + //todo: check sleeping conditions! + if (((colObj0) && colObj0->getActivationState() != ISLAND_SLEEPING) || + ((colObj1) && colObj1->getActivationState() != ISLAND_SLEEPING)) + { + + //kinematic objects don't merge islands, but wake up all connected objects + if (colObj0->isStaticOrKinematicObject() && colObj0->getActivationState() != ISLAND_SLEEPING) + { + colObj1->activate(); + } + if (colObj1->isStaticOrKinematicObject() && colObj1->getActivationState() != ISLAND_SLEEPING) + { + colObj0->activate(); + } + + //filtering for response + if (dispatcher->needsResponse(colObj0,colObj1)) + islandmanifold.push_back(manifold); + } + } + + int numManifolds = int (islandmanifold.size()); + + // Sort manifolds, based on islands + // Sort the vector using predicate and std::sort + //std::sort(islandmanifold.begin(), islandmanifold.end(), btPersistentManifoldSortPredicate); + + //we should do radix sort, it it much faster (O(n) instead of O (n log2(n)) + islandmanifold.heapSort(btPersistentManifoldSortPredicate()); + + //now process all active islands (sets of manifolds for now) + + int startManifoldIndex = 0; + int endManifoldIndex = 1; + + //int islandId; + + END_PROFILE("islandUnionFindAndHeapSort"); + + btAlignedObjectArray islandBodies; + + + //traverse the simulation islands, and call the solver, unless all objects are sleeping/deactivated + for ( startIslandIndex=0;startIslandIndexisActive()) + islandSleeping = true; + } + + + //find the accompanying contact manifold for this islandId + int numIslandManifolds = 0; + btPersistentManifold** startManifold = 0; + + if (startManifoldIndexProcessIsland(&islandBodies[0],islandBodies.size(),startManifold,numIslandManifolds, islandId); + } + + if (numIslandManifolds) + { + startManifoldIndex = endManifoldIndex; + } + + islandBodies.resize(0); + } + + +} diff --git a/bullet/src/BulletCollision/CollisionDispatch/btSimulationIslandManager.h b/bullet/src/BulletCollision/CollisionDispatch/btSimulationIslandManager.h new file mode 100644 index 0000000..7c2bd64 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btSimulationIslandManager.h @@ -0,0 +1,61 @@ +/* +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. +*/ + +#ifndef SIMULATION_ISLAND_MANAGER_H +#define SIMULATION_ISLAND_MANAGER_H + +#include "BulletCollision/CollisionDispatch/btUnionFind.h" +#include "btCollisionCreateFunc.h" + +class btCollisionObject; +class btCollisionWorld; +class btDispatcher; + +///SimulationIslandManager creates and handles simulation islands, using btUnionFind +class btSimulationIslandManager +{ + btUnionFind m_unionFind; + +public: + btSimulationIslandManager(); + virtual ~btSimulationIslandManager(); + + + void initUnionFind(int n); + + + btUnionFind& getUnionFind() { return m_unionFind;} + + virtual void updateActivationState(btCollisionWorld* colWorld,btDispatcher* dispatcher); + virtual void storeIslandActivationState(btCollisionWorld* world); + + + void findUnions(btDispatcher* dispatcher); + + + + struct IslandCallback + { + virtual ~IslandCallback() {}; + + virtual void ProcessIsland(btCollisionObject** bodies,int numBodies,class btPersistentManifold** manifolds,int numManifolds, int islandId) = 0; + }; + + void buildAndProcessIslands(btDispatcher* dispatcher,btCollisionObjectArray& collisionObjects, IslandCallback* callback); + +}; + +#endif //SIMULATION_ISLAND_MANAGER_H + diff --git a/bullet/src/BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.cpp b/bullet/src/BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.cpp new file mode 100644 index 0000000..5c67165 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.cpp @@ -0,0 +1,249 @@ +/* +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. +*/ + +#include "btSphereBoxCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" +#include "BulletCollision/CollisionShapes/btBoxShape.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +//#include + +btSphereBoxCollisionAlgorithm::btSphereBoxCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* col0,btCollisionObject* col1, bool isSwapped) +: btCollisionAlgorithm(ci), +m_ownManifold(false), +m_manifoldPtr(mf), +m_isSwapped(isSwapped) +{ + btCollisionObject* sphereObj = m_isSwapped? col1 : col0; + btCollisionObject* boxObj = m_isSwapped? col0 : col1; + + if (!m_manifoldPtr && m_dispatcher->needsCollision(sphereObj,boxObj)) + { + m_manifoldPtr = m_dispatcher->getNewManifold(sphereObj,boxObj); + m_ownManifold = true; + } +} + + +btSphereBoxCollisionAlgorithm::~btSphereBoxCollisionAlgorithm() +{ + if (m_ownManifold) + { + if (m_manifoldPtr) + m_dispatcher->releaseManifold(m_manifoldPtr); + } +} + + + +void btSphereBoxCollisionAlgorithm::processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + (void)dispatchInfo; + (void)resultOut; + if (!m_manifoldPtr) + return; + + btCollisionObject* sphereObj = m_isSwapped? body1 : body0; + btCollisionObject* boxObj = m_isSwapped? body0 : body1; + + + btSphereShape* sphere0 = (btSphereShape*)sphereObj->getCollisionShape(); + + btVector3 normalOnSurfaceB; + btVector3 pOnBox,pOnSphere; + btVector3 sphereCenter = sphereObj->getWorldTransform().getOrigin(); + btScalar radius = sphere0->getRadius(); + + btScalar dist = getSphereDistance(boxObj,pOnBox,pOnSphere,sphereCenter,radius); + + if (dist < SIMD_EPSILON) + { + btVector3 normalOnSurfaceB = (pOnBox- pOnSphere).normalize(); + + /// report a contact. internally this will be kept persistent, and contact reduction is done + + resultOut->setPersistentManifold(m_manifoldPtr); + resultOut->addContactPoint(normalOnSurfaceB,pOnBox,dist); + + } + + + +} + +btScalar btSphereBoxCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* col0,btCollisionObject* col1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + (void)resultOut; + (void)dispatchInfo; + (void)col0; + (void)col1; + + //not yet + return btScalar(1.); +} + + +btScalar btSphereBoxCollisionAlgorithm::getSphereDistance(btCollisionObject* boxObj, btVector3& pointOnBox, btVector3& v3PointOnSphere, const btVector3& sphereCenter, btScalar fRadius ) +{ + + btScalar margins; + btVector3 bounds[2]; + btBoxShape* boxShape= (btBoxShape*)boxObj->getCollisionShape(); + + bounds[0] = -boxShape->getHalfExtents(); + bounds[1] = boxShape->getHalfExtents(); + + margins = boxShape->getMargin();//also add sphereShape margin? + + const btTransform& m44T = boxObj->getWorldTransform(); + + btVector3 boundsVec[2]; + btScalar fPenetration; + + boundsVec[0] = bounds[0]; + boundsVec[1] = bounds[1]; + + btVector3 marginsVec( margins, margins, margins ); + + // add margins + bounds[0] += marginsVec; + bounds[1] -= marginsVec; + + ///////////////////////////////////////////////// + + btVector3 tmp, prel, n[6], normal, v3P; + btScalar fSep = btScalar(10000000.0), fSepThis; + + n[0].setValue( btScalar(-1.0), btScalar(0.0), btScalar(0.0) ); + n[1].setValue( btScalar(0.0), btScalar(-1.0), btScalar(0.0) ); + n[2].setValue( btScalar(0.0), btScalar(0.0), btScalar(-1.0) ); + n[3].setValue( btScalar(1.0), btScalar(0.0), btScalar(0.0) ); + n[4].setValue( btScalar(0.0), btScalar(1.0), btScalar(0.0) ); + n[5].setValue( btScalar(0.0), btScalar(0.0), btScalar(1.0) ); + + // convert point in local space + prel = m44T.invXform( sphereCenter); + + bool bFound = false; + + v3P = prel; + + for (int i=0;i<6;i++) + { + int j = i<3? 0:1; + if ( (fSepThis = ((v3P-bounds[j]) .dot(n[i]))) > btScalar(0.0) ) + { + v3P = v3P - n[i]*fSepThis; + bFound = true; + } + } + + // + + if ( bFound ) + { + bounds[0] = boundsVec[0]; + bounds[1] = boundsVec[1]; + + normal = (prel - v3P).normalize(); + pointOnBox = v3P + normal*margins; + v3PointOnSphere = prel - normal*fRadius; + + if ( ((v3PointOnSphere - pointOnBox) .dot (normal)) > btScalar(0.0) ) + { + return btScalar(1.0); + } + + // transform back in world space + tmp = m44T( pointOnBox); + pointOnBox = tmp; + tmp = m44T( v3PointOnSphere); + v3PointOnSphere = tmp; + btScalar fSeps2 = (pointOnBox-v3PointOnSphere).length2(); + + //if this fails, fallback into deeper penetration case, below + if (fSeps2 > SIMD_EPSILON) + { + fSep = - btSqrt(fSeps2); + normal = (pointOnBox-v3PointOnSphere); + normal *= btScalar(1.)/fSep; + } + + return fSep; + } + + ////////////////////////////////////////////////// + // Deep penetration case + + fPenetration = getSpherePenetration( boxObj,pointOnBox, v3PointOnSphere, sphereCenter, fRadius,bounds[0],bounds[1] ); + + bounds[0] = boundsVec[0]; + bounds[1] = boundsVec[1]; + + if ( fPenetration <= btScalar(0.0) ) + return (fPenetration-margins); + else + return btScalar(1.0); +} + +btScalar btSphereBoxCollisionAlgorithm::getSpherePenetration( btCollisionObject* boxObj,btVector3& pointOnBox, btVector3& v3PointOnSphere, const btVector3& sphereCenter, btScalar fRadius, const btVector3& aabbMin, const btVector3& aabbMax) +{ + + btVector3 bounds[2]; + + bounds[0] = aabbMin; + bounds[1] = aabbMax; + + btVector3 p0, tmp, prel, n[6], normal; + btScalar fSep = btScalar(-10000000.0), fSepThis; + + n[0].setValue( btScalar(-1.0), btScalar(0.0), btScalar(0.0) ); + n[1].setValue( btScalar(0.0), btScalar(-1.0), btScalar(0.0) ); + n[2].setValue( btScalar(0.0), btScalar(0.0), btScalar(-1.0) ); + n[3].setValue( btScalar(1.0), btScalar(0.0), btScalar(0.0) ); + n[4].setValue( btScalar(0.0), btScalar(1.0), btScalar(0.0) ); + n[5].setValue( btScalar(0.0), btScalar(0.0), btScalar(1.0) ); + + const btTransform& m44T = boxObj->getWorldTransform(); + + // convert point in local space + prel = m44T.invXform( sphereCenter); + + /////////// + + for (int i=0;i<6;i++) + { + int j = i<3 ? 0:1; + if ( (fSepThis = ((prel-bounds[j]) .dot( n[i]))-fRadius) > btScalar(0.0) ) return btScalar(1.0); + if ( fSepThis > fSep ) + { + p0 = bounds[j]; normal = (btVector3&)n[i]; + fSep = fSepThis; + } + } + + pointOnBox = prel - normal*(normal.dot((prel-p0))); + v3PointOnSphere = pointOnBox + normal*fSep; + + // transform back in world space + tmp = m44T( pointOnBox); + pointOnBox = tmp; + tmp = m44T( v3PointOnSphere); v3PointOnSphere = tmp; + normal = (pointOnBox-v3PointOnSphere).normalize(); + + return fSep; + +} + diff --git a/bullet/src/BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.h b/bullet/src/BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.h new file mode 100644 index 0000000..83cf52c --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.h @@ -0,0 +1,64 @@ +/* +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. +*/ + +#ifndef SPHERE_BOX_COLLISION_ALGORITHM_H +#define SPHERE_BOX_COLLISION_ALGORITHM_H + +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h" +class btPersistentManifold; +#include "LinearMath/btVector3.h" + +/// btSphereBoxCollisionAlgorithm provides sphere-box collision detection. +/// Other features are frame-coherency (persistent data) and collision response. +class btSphereBoxCollisionAlgorithm : public btCollisionAlgorithm +{ + bool m_ownManifold; + btPersistentManifold* m_manifoldPtr; + bool m_isSwapped; + +public: + + btSphereBoxCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* col0,btCollisionObject* col1, bool isSwapped); + + virtual ~btSphereBoxCollisionAlgorithm(); + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + btScalar getSphereDistance( btCollisionObject* boxObj,btVector3& v3PointOnBox, btVector3& v3PointOnSphere, const btVector3& v3SphereCenter, btScalar fRadius ); + + btScalar getSpherePenetration( btCollisionObject* boxObj, btVector3& v3PointOnBox, btVector3& v3PointOnSphere, const btVector3& v3SphereCenter, btScalar fRadius, const btVector3& aabbMin, const btVector3& aabbMax); + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + if (!m_swapped) + { + return new btSphereBoxCollisionAlgorithm(0,ci,body0,body1,false); + } else + { + return new btSphereBoxCollisionAlgorithm(0,ci,body0,body1,true); + } + } + }; + +}; + +#endif //SPHERE_BOX_COLLISION_ALGORITHM_H + diff --git a/bullet/src/BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.cpp b/bullet/src/BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.cpp new file mode 100644 index 0000000..087c175 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.cpp @@ -0,0 +1,85 @@ +/* +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. +*/ + +#include "btSphereSphereCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" + +btSphereSphereCollisionAlgorithm::btSphereSphereCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* col0,btCollisionObject* col1) +: btCollisionAlgorithm(ci), +m_ownManifold(false), +m_manifoldPtr(mf) +{ + if (!m_manifoldPtr) + { + m_manifoldPtr = m_dispatcher->getNewManifold(col0,col1); + m_ownManifold = true; + } +} + +btSphereSphereCollisionAlgorithm::~btSphereSphereCollisionAlgorithm() +{ + if (m_ownManifold) + { + if (m_manifoldPtr) + m_dispatcher->releaseManifold(m_manifoldPtr); + } +} + +void btSphereSphereCollisionAlgorithm::processCollision (btCollisionObject* col0,btCollisionObject* col1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + (void)dispatchInfo; + + if (!m_manifoldPtr) + return; + + btSphereShape* sphere0 = (btSphereShape*)col0->getCollisionShape(); + btSphereShape* sphere1 = (btSphereShape*)col1->getCollisionShape(); + + btVector3 diff = col0->getWorldTransform().getOrigin()- col1->getWorldTransform().getOrigin(); + btScalar len = diff.length(); + btScalar radius0 = sphere0->getRadius(); + btScalar radius1 = sphere1->getRadius(); + + ///iff distance positive, don't generate a new contact + if ( len > (radius0+radius1)) + return; + + ///distance (negative means penetration) + btScalar dist = len - (radius0+radius1); + + btVector3 normalOnSurfaceB = diff / len; + ///point on A (worldspace) + btVector3 pos0 = col0->getWorldTransform().getOrigin() - radius0 * normalOnSurfaceB; + ///point on B (worldspace) + btVector3 pos1 = col1->getWorldTransform().getOrigin() + radius1* normalOnSurfaceB; + + /// report a contact. internally this will be kept persistent, and contact reduction is done + resultOut->setPersistentManifold(m_manifoldPtr); + resultOut->addContactPoint(normalOnSurfaceB,pos1,dist); + +} + +btScalar btSphereSphereCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* col0,btCollisionObject* col1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + (void)col0; + (void)col1; + (void)dispatchInfo; + (void)resultOut; + + //not yet + return btScalar(1.); +} diff --git a/bullet/src/BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.h b/bullet/src/BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.h new file mode 100644 index 0000000..488a159 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.h @@ -0,0 +1,56 @@ +/* +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. +*/ + +#ifndef SPHERE_SPHERE_COLLISION_ALGORITHM_H +#define SPHERE_SPHERE_COLLISION_ALGORITHM_H + +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h" +class btPersistentManifold; + +/// btSphereSphereCollisionAlgorithm provides sphere-sphere collision detection. +/// Other features are frame-coherency (persistent data) and collision response. +/// Also provides the most basic sample for custom/user btCollisionAlgorithm +class btSphereSphereCollisionAlgorithm : public btCollisionAlgorithm +{ + bool m_ownManifold; + btPersistentManifold* m_manifoldPtr; + +public: + btSphereSphereCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1); + + btSphereSphereCollisionAlgorithm(const btCollisionAlgorithmConstructionInfo& ci) + : btCollisionAlgorithm(ci) {} + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + + virtual ~btSphereSphereCollisionAlgorithm(); + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + return new btSphereSphereCollisionAlgorithm(0,ci,body0,body1); + } + }; + +}; + +#endif //SPHERE_SPHERE_COLLISION_ALGORITHM_H + diff --git a/bullet/src/BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.cpp b/bullet/src/BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.cpp new file mode 100644 index 0000000..9f7b321 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.cpp @@ -0,0 +1,76 @@ +/* +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. +*/ + + +#include "btSphereTriangleCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "SphereTriangleDetector.h" + + +btSphereTriangleCollisionAlgorithm::btSphereTriangleCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* col0,btCollisionObject* col1,bool swapped) +: btCollisionAlgorithm(ci), +m_ownManifold(false), +m_manifoldPtr(mf), +m_swapped(swapped) +{ + if (!m_manifoldPtr) + { + m_manifoldPtr = m_dispatcher->getNewManifold(col0,col1); + m_ownManifold = true; + } +} + +btSphereTriangleCollisionAlgorithm::~btSphereTriangleCollisionAlgorithm() +{ + if (m_ownManifold) + { + if (m_manifoldPtr) + m_dispatcher->releaseManifold(m_manifoldPtr); + } +} + +void btSphereTriangleCollisionAlgorithm::processCollision (btCollisionObject* col0,btCollisionObject* col1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + if (!m_manifoldPtr) + return; + + btSphereShape* sphere = (btSphereShape*)col0->getCollisionShape(); + btTriangleShape* triangle = (btTriangleShape*)col1->getCollisionShape(); + + /// report a contact. internally this will be kept persistent, and contact reduction is done + resultOut->setPersistentManifold(m_manifoldPtr); + SphereTriangleDetector detector(sphere,triangle); + + btDiscreteCollisionDetectorInterface::ClosestPointInput input; + input.m_maximumDistanceSquared = btScalar(1e30);//todo: tighter bounds + input.m_transformA = col0->getWorldTransform(); + input.m_transformB = col1->getWorldTransform(); + + detector.getClosestPoints(input,*resultOut,dispatchInfo.m_debugDraw); + +} + +btScalar btSphereTriangleCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* col0,btCollisionObject* col1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + (void)resultOut; + (void)dispatchInfo; + (void)col0; + (void)col1; + + //not yet + return btScalar(1.); +} diff --git a/bullet/src/BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.h b/bullet/src/BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.h new file mode 100644 index 0000000..26d9f4d --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.h @@ -0,0 +1,59 @@ +/* +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. +*/ + +#ifndef SPHERE_TRIANGLE_COLLISION_ALGORITHM_H +#define SPHERE_TRIANGLE_COLLISION_ALGORITHM_H + +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h" +class btPersistentManifold; + +/// btSphereSphereCollisionAlgorithm provides sphere-sphere collision detection. +/// Other features are frame-coherency (persistent data) and collision response. +/// Also provides the most basic sample for custom/user btCollisionAlgorithm +class btSphereTriangleCollisionAlgorithm : public btCollisionAlgorithm +{ + bool m_ownManifold; + btPersistentManifold* m_manifoldPtr; + bool m_swapped; + +public: + btSphereTriangleCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1,bool swapped); + + btSphereTriangleCollisionAlgorithm(const btCollisionAlgorithmConstructionInfo& ci) + : btCollisionAlgorithm(ci) {} + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + + virtual ~btSphereTriangleCollisionAlgorithm(); + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + + return new btSphereTriangleCollisionAlgorithm(ci.m_manifold,ci,body0,body1,m_swapped); + } + }; + +}; + +#endif //SPHERE_TRIANGLE_COLLISION_ALGORITHM_H + diff --git a/bullet/src/BulletCollision/CollisionDispatch/btUnionFind.cpp b/bullet/src/BulletCollision/CollisionDispatch/btUnionFind.cpp new file mode 100644 index 0000000..66d6997 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionDispatch/btUnionFind.cpp @@ -0,0 +1,83 @@ +/* +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. +*/ + +#include "btUnionFind.h" +#include + + + +btUnionFind::~btUnionFind() +{ + Free(); + +} + +btUnionFind::btUnionFind() +{ + +} + +void btUnionFind::allocate(int N) +{ + m_elements.resize(N); +} +void btUnionFind::Free() +{ + m_elements.clear(); +} + + +void btUnionFind::reset(int N) +{ + allocate(N); + + for (int i = 0; i < N; i++) + { + m_elements[i].m_id = i; m_elements[i].m_sz = 1; + } +} + + +class btUnionFindElementSortPredicate +{ + public: + + bool operator() ( const btElement& lhs, const btElement& rhs ) + { + return lhs.m_id < rhs.m_id; + } +}; + +///this is a special operation, destroying the content of btUnionFind. +///it sorts the elements, based on island id, in order to make it easy to iterate over islands +void btUnionFind::sortIslands() +{ + + //first store the original body index, and islandId + int numElements = m_elements.size(); + + for (int i=0;i m_elements; + + public: + + btUnionFind(); + ~btUnionFind(); + + + //this is a special operation, destroying the content of btUnionFind. + //it sorts the elements, based on island id, in order to make it easy to iterate over islands + void sortIslands(); + + void reset(int N); + + inline int getNumElements() const + { + return int(m_elements.size()); + } + inline bool isRoot(int x) const + { + return (x == m_elements[x].m_id); + } + + btElement& getElement(int index) + { + return m_elements[index]; + } + const btElement& getElement(int index) const + { + return m_elements[index]; + } + + void allocate(int N); + void Free(); + + + + + int find(int p, int q) + { + return (find(p) == find(q)); + } + + void unite(int p, int q) + { + int i = find(p), j = find(q); + if (i == j) + return; + +#ifndef USE_PATH_COMPRESSION + //weighted quick union, this keeps the 'trees' balanced, and keeps performance of unite O( log(n) ) + if (m_elements[i].m_sz < m_elements[j].m_sz) + { + m_elements[i].m_id = j; m_elements[j].m_sz += m_elements[i].m_sz; + } + else + { + m_elements[j].m_id = i; m_elements[i].m_sz += m_elements[j].m_sz; + } +#else + m_elements[i].m_id = j; m_elements[j].m_sz += m_elements[i].m_sz; +#endif //USE_PATH_COMPRESSION + } + + int find(int x) + { + //assert(x < m_N); + //assert(x >= 0); + + while (x != m_elements[x].m_id) + { + //not really a reason not to use path compression, and it flattens the trees/improves find performance dramatically + + #ifdef USE_PATH_COMPRESSION + // + m_elements[x].m_id = m_elements[m_elements[x].m_id].m_id; + #endif // + x = m_elements[x].m_id; + //assert(x < m_N); + //assert(x >= 0); + + } + return x; + } + + + }; + + +#endif //UNION_FIND_H diff --git a/bullet/src/BulletCollision/CollisionShapes/btBoxShape.cpp b/bullet/src/BulletCollision/CollisionShapes/btBoxShape.cpp new file mode 100644 index 0000000..bc3bff6 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btBoxShape.cpp @@ -0,0 +1,57 @@ +/* +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. +*/ + +#include "btBoxShape.h" + +btVector3 btBoxShape::getHalfExtents() const +{ + return m_implicitShapeDimensions * m_localScaling; +} +//{ + + +void btBoxShape::getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const +{ + btVector3 halfExtents = getHalfExtents(); + + btMatrix3x3 abs_b = t.getBasis().absolute(); + btPoint3 center = t.getOrigin(); + btVector3 extent = btVector3(abs_b[0].dot(halfExtents), + abs_b[1].dot(halfExtents), + abs_b[2].dot(halfExtents)); + extent += btVector3(getMargin(),getMargin(),getMargin()); + + aabbMin = center - extent; + aabbMax = center + extent; + + +} + + +void btBoxShape::calculateLocalInertia(btScalar mass,btVector3& inertia) +{ + //btScalar margin = btScalar(0.); + btVector3 halfExtents = getHalfExtents(); + + btScalar lx=btScalar(2.)*(halfExtents.x()); + btScalar ly=btScalar(2.)*(halfExtents.y()); + btScalar lz=btScalar(2.)*(halfExtents.z()); + + inertia.setValue(mass/(btScalar(12.0)) * (ly*ly + lz*lz), + mass/(btScalar(12.0)) * (lx*lx + lz*lz), + mass/(btScalar(12.0)) * (lx*lx + ly*ly)); + +} + diff --git a/bullet/src/BulletCollision/CollisionShapes/btBoxShape.h b/bullet/src/BulletCollision/CollisionShapes/btBoxShape.h new file mode 100644 index 0000000..5cee12b --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btBoxShape.h @@ -0,0 +1,290 @@ +/* +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. +*/ + +#ifndef OBB_BOX_MINKOWSKI_H +#define OBB_BOX_MINKOWSKI_H + +#include "btPolyhedralConvexShape.h" +#include "btCollisionMargin.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "LinearMath/btPoint3.h" +#include "LinearMath/btSimdMinMax.h" + +///btBoxShape implements both a feature based (vertex/edge/plane) and implicit (getSupportingVertex) Box +class btBoxShape: public btPolyhedralConvexShape +{ + + //btVector3 m_boxHalfExtents1; //use m_implicitShapeDimensions instead + + +public: + + btVector3 getHalfExtents() const; + + virtual int getShapeType() const { return BOX_SHAPE_PROXYTYPE;} + + virtual btVector3 localGetSupportingVertex(const btVector3& vec) const + { + btVector3 halfExtents = getHalfExtents(); + + return btVector3(btFsels(vec.x(), halfExtents.x(), -halfExtents.x()), + btFsels(vec.y(), halfExtents.y(), -halfExtents.y()), + btFsels(vec.z(), halfExtents.z(), -halfExtents.z())); + } + + virtual inline btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const + { + btVector3 halfExtents = getHalfExtents(); + btVector3 margin(getMargin(),getMargin(),getMargin()); + halfExtents -= margin; + + return btVector3(btFsels(vec.x(), halfExtents.x(), -halfExtents.x()), + btFsels(vec.y(), halfExtents.y(), -halfExtents.y()), + btFsels(vec.z(), halfExtents.z(), -halfExtents.z())); + } + + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const + { + btVector3 halfExtents = getHalfExtents(); + btVector3 margin(getMargin(),getMargin(),getMargin()); + halfExtents -= margin; + + + for (int i=0;i>1)) - halfExtents.y() * ((i&2)>>1), + halfExtents.z() * (1-((i&4)>>2)) - halfExtents.z() * ((i&4)>>2)); + } + + + virtual void getPlaneEquation(btVector4& plane,int i) const + { + btVector3 halfExtents = getHalfExtents(); + + switch (i) + { + case 0: + plane.setValue(btScalar(1.),btScalar(0.),btScalar(0.)); + plane[3] = -halfExtents.x(); + break; + case 1: + plane.setValue(btScalar(-1.),btScalar(0.),btScalar(0.)); + plane[3] = -halfExtents.x(); + break; + case 2: + plane.setValue(btScalar(0.),btScalar(1.),btScalar(0.)); + plane[3] = -halfExtents.y(); + break; + case 3: + plane.setValue(btScalar(0.),btScalar(-1.),btScalar(0.)); + plane[3] = -halfExtents.y(); + break; + case 4: + plane.setValue(btScalar(0.),btScalar(0.),btScalar(1.)); + plane[3] = -halfExtents.z(); + break; + case 5: + plane.setValue(btScalar(0.),btScalar(0.),btScalar(-1.)); + plane[3] = -halfExtents.z(); + break; + default: + assert(0); + } + } + + + virtual void getEdge(int i,btPoint3& pa,btPoint3& pb) const + //virtual void getEdge(int i,Edge& edge) const + { + int edgeVert0 = 0; + int edgeVert1 = 0; + + switch (i) + { + case 0: + edgeVert0 = 0; + edgeVert1 = 1; + break; + case 1: + edgeVert0 = 0; + edgeVert1 = 2; + break; + case 2: + edgeVert0 = 1; + edgeVert1 = 3; + + break; + case 3: + edgeVert0 = 2; + edgeVert1 = 3; + break; + case 4: + edgeVert0 = 0; + edgeVert1 = 4; + break; + case 5: + edgeVert0 = 1; + edgeVert1 = 5; + + break; + case 6: + edgeVert0 = 2; + edgeVert1 = 6; + break; + case 7: + edgeVert0 = 3; + edgeVert1 = 7; + break; + case 8: + edgeVert0 = 4; + edgeVert1 = 5; + break; + case 9: + edgeVert0 = 4; + edgeVert1 = 6; + break; + case 10: + edgeVert0 = 5; + edgeVert1 = 7; + break; + case 11: + edgeVert0 = 6; + edgeVert1 = 7; + break; + default: + btAssert(0); + + } + + getVertex(edgeVert0,pa ); + getVertex(edgeVert1,pb ); + } + + + + + + virtual bool isInside(const btPoint3& pt,btScalar tolerance) const + { + btVector3 halfExtents = getHalfExtents(); + + //btScalar minDist = 2*tolerance; + + bool result = (pt.x() <= (halfExtents.x()+tolerance)) && + (pt.x() >= (-halfExtents.x()-tolerance)) && + (pt.y() <= (halfExtents.y()+tolerance)) && + (pt.y() >= (-halfExtents.y()-tolerance)) && + (pt.z() <= (halfExtents.z()+tolerance)) && + (pt.z() >= (-halfExtents.z()-tolerance)); + + return result; + } + + + //debugging + virtual const char* getName()const + { + return "Box"; + } + + virtual int getNumPreferredPenetrationDirections() const + { + return 6; + } + + virtual void getPreferredPenetrationDirection(int index, btVector3& penetrationVector) const + { + switch (index) + { + case 0: + penetrationVector.setValue(btScalar(1.),btScalar(0.),btScalar(0.)); + break; + case 1: + penetrationVector.setValue(btScalar(-1.),btScalar(0.),btScalar(0.)); + break; + case 2: + penetrationVector.setValue(btScalar(0.),btScalar(1.),btScalar(0.)); + break; + case 3: + penetrationVector.setValue(btScalar(0.),btScalar(-1.),btScalar(0.)); + break; + case 4: + penetrationVector.setValue(btScalar(0.),btScalar(0.),btScalar(1.)); + break; + case 5: + penetrationVector.setValue(btScalar(0.),btScalar(0.),btScalar(-1.)); + break; + default: + assert(0); + } + } + +}; + +#endif //OBB_BOX_MINKOWSKI_H + + diff --git a/bullet/src/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.cpp b/bullet/src/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.cpp new file mode 100644 index 0000000..95d9680 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.cpp @@ -0,0 +1,173 @@ +/* +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. +*/ + +//#define DISABLE_BVH + +#include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h" +#include "BulletCollision/CollisionShapes/btOptimizedBvh.h" + + +///Bvh Concave triangle mesh is a static-triangle mesh shape with Bounding Volume Hierarchy optimization. +///Uses an interface to access the triangles to allow for sharing graphics/physics triangles. +btBvhTriangleMeshShape::btBvhTriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression) +:btTriangleMeshShape(meshInterface),m_useQuantizedAabbCompression(useQuantizedAabbCompression) +{ + //construct bvh from meshInterface +#ifndef DISABLE_BVH + + m_bvh = new btOptimizedBvh(); + btVector3 bvhAabbMin,bvhAabbMax; + meshInterface->calculateAabbBruteForce(bvhAabbMin,bvhAabbMax); + m_bvh->build(meshInterface,m_useQuantizedAabbCompression,bvhAabbMin,bvhAabbMax); + +#endif //DISABLE_BVH + +} + +btBvhTriangleMeshShape::btBvhTriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression,const btVector3& bvhAabbMin,const btVector3& bvhAabbMax) +:btTriangleMeshShape(meshInterface),m_useQuantizedAabbCompression(useQuantizedAabbCompression) +{ + //construct bvh from meshInterface +#ifndef DISABLE_BVH + + m_bvh = new btOptimizedBvh(); + m_bvh->build(meshInterface,m_useQuantizedAabbCompression,bvhAabbMin,bvhAabbMax); + +#endif //DISABLE_BVH + +} + +void btBvhTriangleMeshShape::partialRefitTree(const btVector3& aabbMin,const btVector3& aabbMax) +{ + m_bvh->refitPartial( m_meshInterface,aabbMin,aabbMax ); + + m_localAabbMin.setMin(aabbMin); + m_localAabbMax.setMax(aabbMax); +} + + +void btBvhTriangleMeshShape::refitTree() +{ + m_bvh->refit( m_meshInterface ); + + recalcLocalAabb(); +} + +btBvhTriangleMeshShape::~btBvhTriangleMeshShape() +{ + delete m_bvh; +} + +//perform bvh tree traversal and report overlapping triangles to 'callback' +void btBvhTriangleMeshShape::processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const +{ + +#ifdef DISABLE_BVH + //brute force traverse all triangles + btTriangleMeshShape::processAllTriangles(callback,aabbMin,aabbMax); +#else + + //first get all the nodes + + + struct MyNodeOverlapCallback : public btNodeOverlapCallback + { + btStridingMeshInterface* m_meshInterface; + btTriangleCallback* m_callback; + btVector3 m_triangle[3]; + + + MyNodeOverlapCallback(btTriangleCallback* callback,btStridingMeshInterface* meshInterface) + :m_meshInterface(meshInterface), + m_callback(callback) + { + } + + virtual void processNode(int nodeSubPart, int nodeTriangleIndex) + { + const unsigned char *vertexbase; + int numverts; + PHY_ScalarType type; + int stride; + const unsigned char *indexbase; + int indexstride; + int numfaces; + PHY_ScalarType indicestype; + + + m_meshInterface->getLockedReadOnlyVertexIndexBase( + &vertexbase, + numverts, + type, + stride, + &indexbase, + indexstride, + numfaces, + indicestype, + nodeSubPart); + + int* gfxbase = (int*)(indexbase+nodeTriangleIndex*indexstride); + + const btVector3& meshScaling = m_meshInterface->getScaling(); + for (int j=2;j>=0;j--) + { + + int graphicsindex = gfxbase[j]; + + +#ifdef DEBUG_TRIANGLE_MESH + printf("%d ,",graphicsindex); +#endif //DEBUG_TRIANGLE_MESH + btScalar* graphicsbase = (btScalar*)(vertexbase+graphicsindex*stride); + + m_triangle[j] = btVector3( + graphicsbase[0]*meshScaling.getX(), + graphicsbase[1]*meshScaling.getY(), + graphicsbase[2]*meshScaling.getZ()); +#ifdef DEBUG_TRIANGLE_MESH + printf("triangle vertices:%f,%f,%f\n",triangle[j].x(),triangle[j].y(),triangle[j].z()); +#endif //DEBUG_TRIANGLE_MESH + } + + m_callback->processTriangle(m_triangle,nodeSubPart,nodeTriangleIndex); + m_meshInterface->unLockReadOnlyVertexBase(nodeSubPart); + } + + }; + + MyNodeOverlapCallback myNodeCallback(callback,m_meshInterface); + + m_bvh->reportAabbOverlappingNodex(&myNodeCallback,aabbMin,aabbMax); + + +#endif//DISABLE_BVH + + +} + + +void btBvhTriangleMeshShape::setLocalScaling(const btVector3& scaling) +{ + if ((getLocalScaling() -scaling).length2() > SIMD_EPSILON) + { + btTriangleMeshShape::setLocalScaling(scaling); + delete m_bvh; + ///m_localAabbMin/m_localAabbMax is already re-calculated in btTriangleMeshShape. We could just scale aabb, but this needs some more work + m_bvh = new btOptimizedBvh(); + //rebuild the bvh... + m_bvh->build(m_meshInterface,m_useQuantizedAabbCompression,m_localAabbMin,m_localAabbMax); + + } +} diff --git a/bullet/src/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h b/bullet/src/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h new file mode 100644 index 0000000..661c433 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h @@ -0,0 +1,75 @@ +/* +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. +*/ + +#ifndef BVH_TRIANGLE_MESH_SHAPE_H +#define BVH_TRIANGLE_MESH_SHAPE_H + +#include "btTriangleMeshShape.h" +#include "btOptimizedBvh.h" + +///Bvh Concave triangle mesh is a static-triangle mesh shape with Bounding Volume Hierarchy optimization. +///Uses an interface to access the triangles to allow for sharing graphics/physics triangles. +ATTRIBUTE_ALIGNED16(class) btBvhTriangleMeshShape : public btTriangleMeshShape +{ + + btOptimizedBvh* m_bvh; + bool m_useQuantizedAabbCompression; + bool m_pad[12];////need padding due to alignment + +public: + + btBvhTriangleMeshShape() :btTriangleMeshShape(0) {}; + btBvhTriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression); + + ///optionally pass in a larger bvh aabb, used for quantization. This allows for deformations within this aabb + btBvhTriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression,const btVector3& bvhAabbMin,const btVector3& bvhAabbMax); + + virtual ~btBvhTriangleMeshShape(); + + + /* + virtual int getShapeType() const + { + return TRIANGLE_MESH_SHAPE_PROXYTYPE; + } + */ + + + + virtual void processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const; + + void refitTree(); + + ///for a fast incremental refit of parts of the tree. Note: the entire AABB of the tree will become more conservative, it never shrinks + void partialRefitTree(const btVector3& aabbMin,const btVector3& aabbMax); + + //debugging + virtual const char* getName()const {return "BVHTRIANGLEMESH";} + + + virtual void setLocalScaling(const btVector3& scaling); + + btOptimizedBvh* getOptimizedBvh() + { + return m_bvh; + } + bool usesQuantizedAabbCompression() const + { + return m_useQuantizedAabbCompression; + } +} +; + +#endif //BVH_TRIANGLE_MESH_SHAPE_H diff --git a/bullet/src/BulletCollision/CollisionShapes/btCapsuleShape.cpp b/bullet/src/BulletCollision/CollisionShapes/btCapsuleShape.cpp new file mode 100644 index 0000000..cdf168b --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btCapsuleShape.cpp @@ -0,0 +1,146 @@ +/* +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. +*/ + + +#include "btCapsuleShape.h" + +#include "BulletCollision/CollisionShapes/btCollisionMargin.h" +#include "LinearMath/btQuaternion.h" + +btCapsuleShape::btCapsuleShape(btScalar radius, btScalar height) +{ + m_implicitShapeDimensions.setValue(radius,0.5f*height,radius); +} + + + btVector3 btCapsuleShape::localGetSupportingVertexWithoutMargin(const btVector3& vec0)const +{ + + btVector3 supVec(0,0,0); + + btScalar maxDot(btScalar(-1e30)); + + btVector3 vec = vec0; + btScalar lenSqr = vec.length2(); + if (lenSqr < btScalar(0.0001)) + { + vec.setValue(1,0,0); + } else + { + btScalar rlen = btScalar(1.) / btSqrt(lenSqr ); + vec *= rlen; + } + + btVector3 vtx; + btScalar newDot; + + btScalar radius = getRadius(); + + + { + btVector3 pos(0,getHalfHeight(),0); + vtx = pos +vec*m_localScaling*(radius) - vec * getMargin(); + newDot = vec.dot(vtx); + if (newDot > maxDot) + { + maxDot = newDot; + supVec = vtx; + } + } + { + btVector3 pos(0,-getHalfHeight(),0); + vtx = pos +vec*m_localScaling*(radius) - vec * getMargin(); + newDot = vec.dot(vtx); + if (newDot > maxDot) + { + maxDot = newDot; + supVec = vtx; + } + } + + return supVec; + +} + + void btCapsuleShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ + + + btScalar radius = getRadius(); + + for (int j=0;j maxDot) + { + maxDot = newDot; + supportVerticesOut[j] = vtx; + } + } + { + btVector3 pos(0,-getHalfHeight(),0); + vtx = pos +vec*m_localScaling*(radius) - vec * getMargin(); + newDot = vec.dot(vtx); + if (newDot > maxDot) + { + maxDot = newDot; + supportVerticesOut[j] = vtx; + } + } + + } +} + + +void btCapsuleShape::calculateLocalInertia(btScalar mass,btVector3& inertia) +{ + //as an approximation, take the inertia of the box that bounds the spheres + + btTransform ident; + ident.setIdentity(); + + + btScalar radius = getRadius(); + + btVector3 halfExtents(radius,radius+getHalfHeight(),radius); + + btScalar margin = CONVEX_DISTANCE_MARGIN; + + btScalar lx=btScalar(2.)*(halfExtents[0]+margin); + btScalar ly=btScalar(2.)*(halfExtents[1]+margin); + btScalar lz=btScalar(2.)*(halfExtents[2]+margin); + const btScalar x2 = lx*lx; + const btScalar y2 = ly*ly; + const btScalar z2 = lz*lz; + const btScalar scaledmass = mass * btScalar(.08333333); + + inertia[0] = scaledmass * (y2+z2); + inertia[1] = scaledmass * (x2+z2); + inertia[2] = scaledmass * (x2+y2); + +} + + + + + diff --git a/bullet/src/BulletCollision/CollisionShapes/btCapsuleShape.h b/bullet/src/BulletCollision/CollisionShapes/btCapsuleShape.h new file mode 100644 index 0000000..e21de08 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btCapsuleShape.h @@ -0,0 +1,60 @@ +/* +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. +*/ + +#ifndef BT_CAPSULE_SHAPE_H +#define BT_CAPSULE_SHAPE_H + +#include "btConvexInternalShape.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" // for the types + + +///btCapsuleShape represents a capsule around the Y axis +///A more general solution that can represent capsules is the btMultiSphereShape +class btCapsuleShape : public btConvexInternalShape +{ + +public: + btCapsuleShape(btScalar radius,btScalar height); + + ///CollisionShape Interface + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia); + + /// btConvexShape Interface + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const; + + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const; + + virtual int getShapeType() const { return CAPSULE_SHAPE_PROXYTYPE; } + + virtual const char* getName()const + { + return "CapsuleShape"; + } + + btScalar getRadius() const + { + return m_implicitShapeDimensions.getX(); + } + + btScalar getHalfHeight() const + { + return m_implicitShapeDimensions.getY(); + } + +}; + + + +#endif //BT_CAPSULE_SHAPE_H diff --git a/bullet/src/BulletCollision/CollisionShapes/btCollisionMargin.h b/bullet/src/BulletCollision/CollisionShapes/btCollisionMargin.h new file mode 100644 index 0000000..71a9170 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btCollisionMargin.h @@ -0,0 +1,26 @@ +/* +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. +*/ + +#ifndef COLLISION_MARGIN_H +#define COLLISION_MARGIN_H + +//used by Gjk and some other algorithms + +#define CONVEX_DISTANCE_MARGIN btScalar(0.04)// btScalar(0.1)//;//btScalar(0.01) + + + +#endif //COLLISION_MARGIN_H + diff --git a/bullet/src/BulletCollision/CollisionShapes/btCollisionShape.cpp b/bullet/src/BulletCollision/CollisionShapes/btCollisionShape.cpp new file mode 100644 index 0000000..28b79da --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btCollisionShape.cpp @@ -0,0 +1,85 @@ +/* +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. +*/ + +#include "BulletCollision/CollisionShapes/btCollisionShape.h" + + +/* + Make sure this dummy function never changes so that it + can be used by probes that are checking whether the + library is actually installed. +*/ +extern "C" void btBulletCollisionProbe () {} + + + +void btCollisionShape::getBoundingSphere(btVector3& center,btScalar& radius) const +{ + btTransform tr; + tr.setIdentity(); + btVector3 aabbMin,aabbMax; + + getAabb(tr,aabbMin,aabbMax); + + radius = (aabbMax-aabbMin).length()*btScalar(0.5); + center = (aabbMin+aabbMax)*btScalar(0.5); +} + +btScalar btCollisionShape::getAngularMotionDisc() const +{ + btVector3 center; + btScalar disc; + getBoundingSphere(center,disc); + disc += (center).length(); + return disc; +} + +void btCollisionShape::calculateTemporalAabb(const btTransform& curTrans,const btVector3& linvel,const btVector3& angvel,btScalar timeStep, btVector3& temporalAabbMin,btVector3& temporalAabbMax) +{ + //start with static aabb + getAabb(curTrans,temporalAabbMin,temporalAabbMax); + + btScalar temporalAabbMaxx = temporalAabbMax.getX(); + btScalar temporalAabbMaxy = temporalAabbMax.getY(); + btScalar temporalAabbMaxz = temporalAabbMax.getZ(); + btScalar temporalAabbMinx = temporalAabbMin.getX(); + btScalar temporalAabbMiny = temporalAabbMin.getY(); + btScalar temporalAabbMinz = temporalAabbMin.getZ(); + + // add linear motion + btVector3 linMotion = linvel*timeStep; + //todo: simd would have a vector max/min operation, instead of per-element access + if (linMotion.x() > btScalar(0.)) + temporalAabbMaxx += linMotion.x(); + else + temporalAabbMinx += linMotion.x(); + if (linMotion.y() > btScalar(0.)) + temporalAabbMaxy += linMotion.y(); + else + temporalAabbMiny += linMotion.y(); + if (linMotion.z() > btScalar(0.)) + temporalAabbMaxz += linMotion.z(); + else + temporalAabbMinz += linMotion.z(); + + //add conservative angular motion + btScalar angularMotion = angvel.length() * getAngularMotionDisc() * timeStep; + btVector3 angularMotion3d(angularMotion,angularMotion,angularMotion); + temporalAabbMin = btVector3(temporalAabbMinx,temporalAabbMiny,temporalAabbMinz); + temporalAabbMax = btVector3(temporalAabbMaxx,temporalAabbMaxy,temporalAabbMaxz); + + temporalAabbMin -= angularMotion3d; + temporalAabbMax += angularMotion3d; +} diff --git a/bullet/src/BulletCollision/CollisionShapes/btCollisionShape.h b/bullet/src/BulletCollision/CollisionShapes/btCollisionShape.h new file mode 100644 index 0000000..4caf777 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btCollisionShape.h @@ -0,0 +1,94 @@ +/* +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. +*/ + +#ifndef COLLISION_SHAPE_H +#define COLLISION_SHAPE_H + +#include "LinearMath/btTransform.h" +#include "LinearMath/btVector3.h" +#include "LinearMath/btMatrix3x3.h" +#include "LinearMath/btPoint3.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" //for the shape types + +///btCollisionShape provides interface for collision shapes that can be shared among btCollisionObjects. +class btCollisionShape +{ +public: + + btCollisionShape() + { + } + virtual ~btCollisionShape() + { + } + + ///getAabb returns the axis aligned bounding box in the coordinate frame of the given transform t. + virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const =0; + + virtual void getBoundingSphere(btVector3& center,btScalar& radius) const; + + ///getAngularMotionDisc returns the maximus radius needed for Conservative Advancement to handle time-of-impact with rotations. + virtual btScalar getAngularMotionDisc() const; + + + ///calculateTemporalAabb calculates the enclosing aabb for the moving object over interval [0..timeStep) + ///result is conservative + void calculateTemporalAabb(const btTransform& curTrans,const btVector3& linvel,const btVector3& angvel,btScalar timeStep, btVector3& temporalAabbMin,btVector3& temporalAabbMax); + +#ifndef __SPU__ + + inline bool isPolyhedral() const + { + return btBroadphaseProxy::isPolyhedral(getShapeType()); + } + + inline bool isConvex() const + { + return btBroadphaseProxy::isConvex(getShapeType()); + } + inline bool isConcave() const + { + return btBroadphaseProxy::isConcave(getShapeType()); + } + inline bool isCompound() const + { + return btBroadphaseProxy::isCompound(getShapeType()); + } + + ///isInfinite is used to catch simulation error (aabb check) + inline bool isInfinite() const + { + return btBroadphaseProxy::isInfinite(getShapeType()); + } + + virtual int getShapeType() const=0; + virtual void setLocalScaling(const btVector3& scaling) =0; + virtual const btVector3& getLocalScaling() const =0; + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia) = 0; + + +//debugging support + virtual const char* getName()const =0 ; +#endif //__SPU__ + + + + virtual void setMargin(btScalar margin) = 0; + virtual btScalar getMargin() const = 0; + +}; + +#endif //COLLISION_SHAPE_H + diff --git a/bullet/src/BulletCollision/CollisionShapes/btCompoundShape.cpp b/bullet/src/BulletCollision/CollisionShapes/btCompoundShape.cpp new file mode 100644 index 0000000..8f09262 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btCompoundShape.cpp @@ -0,0 +1,107 @@ +/* +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. +*/ + +#include "btCompoundShape.h" + + +#include "btCollisionShape.h" + + +btCompoundShape::btCompoundShape() +:m_localAabbMin(btScalar(1e30),btScalar(1e30),btScalar(1e30)), +m_localAabbMax(btScalar(-1e30),btScalar(-1e30),btScalar(-1e30)), +m_aabbTree(0), +m_collisionMargin(btScalar(0.)), +m_localScaling(btScalar(1.),btScalar(1.),btScalar(1.)) +{ +} + + +btCompoundShape::~btCompoundShape() +{ +} + +void btCompoundShape::addChildShape(const btTransform& localTransform,btCollisionShape* shape) +{ + //m_childTransforms.push_back(localTransform); + //m_childShapes.push_back(shape); + btCompoundShapeChild child; + child.m_transform = localTransform; + child.m_childShape = shape; + child.m_childShapeType = shape->getShapeType(); + child.m_childMargin = shape->getMargin(); + + m_children.push_back(child); + + //extend the local aabbMin/aabbMax + btVector3 localAabbMin,localAabbMax; + shape->getAabb(localTransform,localAabbMin,localAabbMax); + for (int i=0;i<3;i++) + { + if (m_localAabbMin[i] > localAabbMin[i]) + { + m_localAabbMin[i] = localAabbMin[i]; + } + if (m_localAabbMax[i] < localAabbMax[i]) + { + m_localAabbMax[i] = localAabbMax[i]; + } + + } +} + + + + ///getAabb's default implementation is brute force, expected derived classes to implement a fast dedicated version +void btCompoundShape::getAabb(const btTransform& trans,btVector3& aabbMin,btVector3& aabbMax) const +{ + btVector3 localHalfExtents = btScalar(0.5)*(m_localAabbMax-m_localAabbMin); + btVector3 localCenter = btScalar(0.5)*(m_localAabbMax+m_localAabbMin); + + btMatrix3x3 abs_b = trans.getBasis().absolute(); + + btPoint3 center = trans(localCenter); + + btVector3 extent = btVector3(abs_b[0].dot(localHalfExtents), + abs_b[1].dot(localHalfExtents), + abs_b[2].dot(localHalfExtents)); + extent += btVector3(getMargin(),getMargin(),getMargin()); + + aabbMin = center - extent; + aabbMax = center + extent; +} + +void btCompoundShape::calculateLocalInertia(btScalar mass,btVector3& inertia) +{ + //approximation: take the inertia from the aabb for now + btTransform ident; + ident.setIdentity(); + btVector3 aabbMin,aabbMax; + getAabb(ident,aabbMin,aabbMax); + + btVector3 halfExtents = (aabbMax-aabbMin)*btScalar(0.5); + + btScalar lx=btScalar(2.)*(halfExtents.x()); + btScalar ly=btScalar(2.)*(halfExtents.y()); + btScalar lz=btScalar(2.)*(halfExtents.z()); + + inertia[0] = mass/(btScalar(12.0)) * (ly*ly + lz*lz); + inertia[1] = mass/(btScalar(12.0)) * (lx*lx + lz*lz); + inertia[2] = mass/(btScalar(12.0)) * (lx*lx + ly*ly); + +} + + + diff --git a/bullet/src/BulletCollision/CollisionShapes/btCompoundShape.h b/bullet/src/BulletCollision/CollisionShapes/btCompoundShape.h new file mode 100644 index 0000000..767f750 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btCompoundShape.h @@ -0,0 +1,132 @@ +/* +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. +*/ + +#ifndef COMPOUND_SHAPE_H +#define COMPOUND_SHAPE_H + +#include "btCollisionShape.h" + +#include "LinearMath/btVector3.h" +#include "LinearMath/btTransform.h" +#include "LinearMath/btMatrix3x3.h" +#include "btCollisionMargin.h" +#include "LinearMath/btAlignedObjectArray.h" + +class btOptimizedBvh; + +ATTRIBUTE_ALIGNED16(struct) btCompoundShapeChild +{ + btTransform m_transform; + btCollisionShape* m_childShape; + int m_childShapeType; + btScalar m_childMargin; +}; + +/// btCompoundShape allows to store multiple other btCollisionShapes +/// This allows for concave collision objects. This is more general then the Static Concave btTriangleMeshShape. +ATTRIBUTE_ALIGNED16(class) btCompoundShape : public btCollisionShape +{ + //btAlignedObjectArray m_childTransforms; + //btAlignedObjectArray m_childShapes; + btAlignedObjectArray m_children; + btVector3 m_localAabbMin; + btVector3 m_localAabbMax; + + btOptimizedBvh* m_aabbTree; + +public: + btCompoundShape(); + + virtual ~btCompoundShape(); + + void addChildShape(const btTransform& localTransform,btCollisionShape* shape); + + int getNumChildShapes() const + { + return int (m_children.size()); + } + + btCollisionShape* getChildShape(int index) + { + return m_children[index].m_childShape; + } + const btCollisionShape* getChildShape(int index) const + { + return m_children[index].m_childShape; + } + + btTransform getChildTransform(int index) + { + return m_children[index].m_transform; + } + const btTransform getChildTransform(int index) const + { + return m_children[index].m_transform; + } + + + btCompoundShapeChild* getChildList() + { + return &m_children[0]; + } + + ///getAabb's default implementation is brute force, expected derived classes to implement a fast dedicated version + void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const; + + + virtual void setLocalScaling(const btVector3& scaling) + { + m_localScaling = scaling; + } + virtual const btVector3& getLocalScaling() const + { + return m_localScaling; + } + + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia); + + virtual int getShapeType() const { return COMPOUND_SHAPE_PROXYTYPE;} + + virtual void setMargin(btScalar margin) + { + m_collisionMargin = margin; + } + virtual btScalar getMargin() const + { + return m_collisionMargin; + } + virtual const char* getName()const + { + return "Compound"; + } + + //this is optional, but should make collision queries faster, by culling non-overlapping nodes + void createAabbTreeFromChildren(); + + const btOptimizedBvh* getAabbTree() const + { + return m_aabbTree; + } + +private: + btScalar m_collisionMargin; +protected: + btVector3 m_localScaling; + +}; + + + +#endif //COMPOUND_SHAPE_H diff --git a/bullet/src/BulletCollision/CollisionShapes/btConcaveShape.cpp b/bullet/src/BulletCollision/CollisionShapes/btConcaveShape.cpp new file mode 100644 index 0000000..ef9913a --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btConcaveShape.cpp @@ -0,0 +1,28 @@ + +/* +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. +*/ + + +#include "btConcaveShape.h" + +btConcaveShape::btConcaveShape() : m_collisionMargin(btScalar(0.)) +{ + +} + +btConcaveShape::~btConcaveShape() +{ + +} diff --git a/bullet/src/BulletCollision/CollisionShapes/btConcaveShape.h b/bullet/src/BulletCollision/CollisionShapes/btConcaveShape.h new file mode 100644 index 0000000..e3e6ec7 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btConcaveShape.h @@ -0,0 +1,50 @@ +/* +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. +*/ + +#ifndef CONCAVE_SHAPE_H +#define CONCAVE_SHAPE_H + +#include "btCollisionShape.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" // for the types +#include "btTriangleCallback.h" + + +///Concave shape proves an interface concave shapes that can produce triangles that overlapping a given AABB. +///Static triangle mesh, infinite plane, height field/landscapes are example that implement this interface. +class btConcaveShape : public btCollisionShape +{ +protected: + btScalar m_collisionMargin; + +public: + btConcaveShape(); + + virtual ~btConcaveShape(); + + virtual void processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const = 0; + + virtual btScalar getMargin() const { + return m_collisionMargin; + } + virtual void setMargin(btScalar collisionMargin) + { + m_collisionMargin = collisionMargin; + } + + + +}; + +#endif //CONCAVE_SHAPE_H diff --git a/bullet/src/BulletCollision/CollisionShapes/btConeShape.cpp b/bullet/src/BulletCollision/CollisionShapes/btConeShape.cpp new file mode 100644 index 0000000..b6ab958 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btConeShape.cpp @@ -0,0 +1,133 @@ +/* +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. +*/ + +#include "btConeShape.h" +#include "LinearMath/btPoint3.h" + + + +btConeShape::btConeShape (btScalar radius,btScalar height): +m_radius (radius), +m_height(height) +{ + setConeUpIndex(1); + btVector3 halfExtents; + m_sinAngle = (m_radius / btSqrt(m_radius * m_radius + m_height * m_height)); +} + +btConeShapeZ::btConeShapeZ (btScalar radius,btScalar height): +btConeShape(radius,height) +{ + setConeUpIndex(2); +} + +btConeShapeX::btConeShapeX (btScalar radius,btScalar height): +btConeShape(radius,height) +{ + setConeUpIndex(0); +} + +///choose upAxis index +void btConeShape::setConeUpIndex(int upIndex) +{ + switch (upIndex) + { + case 0: + m_coneIndices[0] = 1; + m_coneIndices[1] = 0; + m_coneIndices[2] = 2; + break; + case 1: + m_coneIndices[0] = 0; + m_coneIndices[1] = 1; + m_coneIndices[2] = 2; + break; + case 2: + m_coneIndices[0] = 0; + m_coneIndices[1] = 2; + m_coneIndices[2] = 1; + break; + default: + assert(0); + }; +} + +btVector3 btConeShape::coneLocalSupport(const btVector3& v) const +{ + + btScalar halfHeight = m_height * btScalar(0.5); + + if (v[m_coneIndices[1]] > v.length() * m_sinAngle) + { + btVector3 tmp; + + tmp[m_coneIndices[0]] = btScalar(0.); + tmp[m_coneIndices[1]] = halfHeight; + tmp[m_coneIndices[2]] = btScalar(0.); + return tmp; + } + else { + btScalar s = btSqrt(v[m_coneIndices[0]] * v[m_coneIndices[0]] + v[m_coneIndices[2]] * v[m_coneIndices[2]]); + if (s > SIMD_EPSILON) { + btScalar d = m_radius / s; + btVector3 tmp; + tmp[m_coneIndices[0]] = v[m_coneIndices[0]] * d; + tmp[m_coneIndices[1]] = -halfHeight; + tmp[m_coneIndices[2]] = v[m_coneIndices[2]] * d; + return tmp; + } + else { + btVector3 tmp; + tmp[m_coneIndices[0]] = btScalar(0.); + tmp[m_coneIndices[1]] = -halfHeight; + tmp[m_coneIndices[2]] = btScalar(0.); + return tmp; + } + } + +} + +btVector3 btConeShape::localGetSupportingVertexWithoutMargin(const btVector3& vec) const +{ + return coneLocalSupport(vec); +} + +void btConeShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ + for (int i=0;i maxDot) + { + maxDot = newDot; + supVec = vtx; + } + } + return supVec; +} + +void btConvexHullShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ + btScalar newDot; + //use 'w' component of supportVerticesOut? + { + for (int i=0;i supportVerticesOut[j][3]) + { + //WARNING: don't swap next lines, the w component would get overwritten! + supportVerticesOut[j] = vtx; + supportVerticesOut[j][3] = newDot; + } + } + } + + + +} + + + +btVector3 btConvexHullShape::localGetSupportingVertex(const btVector3& vec)const +{ + btVector3 supVertex = localGetSupportingVertexWithoutMargin(vec); + + if ( getMargin()!=btScalar(0.) ) + { + btVector3 vecnorm = vec; + if (vecnorm .length2() < (SIMD_EPSILON*SIMD_EPSILON)) + { + vecnorm.setValue(btScalar(-1.),btScalar(-1.),btScalar(-1.)); + } + vecnorm.normalize(); + supVertex+= getMargin() * vecnorm; + } + return supVertex; +} + + + + + + + + + +//currently just for debugging (drawing), perhaps future support for algebraic continuous collision detection +//Please note that you can debug-draw btConvexHullShape with the Raytracer Demo +int btConvexHullShape::getNumVertices() const +{ + return m_points.size(); +} + +int btConvexHullShape::getNumEdges() const +{ + return m_points.size(); +} + +void btConvexHullShape::getEdge(int i,btPoint3& pa,btPoint3& pb) const +{ + + int index0 = i%m_points.size(); + int index1 = (i+1)%m_points.size(); + pa = m_points[index0]*m_localScaling; + pb = m_points[index1]*m_localScaling; +} + +void btConvexHullShape::getVertex(int i,btPoint3& vtx) const +{ + vtx = m_points[i]*m_localScaling; +} + +int btConvexHullShape::getNumPlanes() const +{ + return 0; +} + +void btConvexHullShape::getPlane(btVector3& ,btPoint3& ,int ) const +{ + + btAssert(0); +} + +//not yet +bool btConvexHullShape::isInside(const btPoint3& ,btScalar ) const +{ + assert(0); + return false; +} + diff --git a/bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.h b/bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.h new file mode 100644 index 0000000..3b45e0c --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.h @@ -0,0 +1,76 @@ +/* +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. +*/ + +#ifndef CONVEX_HULL_SHAPE_H +#define CONVEX_HULL_SHAPE_H + +#include "btPolyhedralConvexShape.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" // for the types +#include "LinearMath/btAlignedObjectArray.h" + +///ConvexHullShape implements an implicit (getSupportingVertex) Convex Hull of a Point Cloud (vertices) +///No connectivity is needed. localGetSupportingVertex iterates linearly though all vertices. +///on modern hardware, due to cache coherency this isn't that bad. Complex algorithms tend to trash the cash. +///(memory is much slower then the cpu) +ATTRIBUTE_ALIGNED16(class) btConvexHullShape : public btPolyhedralConvexShape +{ + btAlignedObjectArray m_points; + +public: + + + ///this constructor optionally takes in a pointer to points. Each point is assumed to be 3 consecutive btScalar (x,y,z), the striding defines the number of bytes between each point, in memory. + ///It is easier to not pass any points in the constructor, and just add one point at a time, using addPoint. + ///btConvexHullShape make an internal copy of the points. + btConvexHullShape(const btScalar* points=0,int numPoints=0, int stride=sizeof(btPoint3)); + + void addPoint(const btPoint3& point); + + btPoint3* getPoints() + { + return &m_points[0]; + } + + int getNumPoints() + { + return m_points.size(); + } + + virtual btVector3 localGetSupportingVertex(const btVector3& vec)const; + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const; + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const; + + + virtual int getShapeType()const { return CONVEX_HULL_SHAPE_PROXYTYPE; } + + //debugging + virtual const char* getName()const {return "Convex";} + + + virtual int getNumVertices() const; + virtual int getNumEdges() const; + virtual void getEdge(int i,btPoint3& pa,btPoint3& pb) const; + virtual void getVertex(int i,btPoint3& vtx) const; + virtual int getNumPlanes() const; + virtual void getPlane(btVector3& planeNormal,btPoint3& planeSupport,int i ) const; + virtual bool isInside(const btPoint3& pt,btScalar tolerance) const; + + + +}; + + +#endif //CONVEX_HULL_SHAPE_H + diff --git a/bullet/src/BulletCollision/CollisionShapes/btConvexInternalShape.cpp b/bullet/src/BulletCollision/CollisionShapes/btConvexInternalShape.cpp new file mode 100644 index 0000000..a9c3be6 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btConvexInternalShape.cpp @@ -0,0 +1,77 @@ +/* +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. +*/ + +#include "btConvexInternalShape.h" + + +btConvexInternalShape::btConvexInternalShape() +: m_localScaling(btScalar(1.),btScalar(1.),btScalar(1.)), +m_collisionMargin(CONVEX_DISTANCE_MARGIN) +{ +} + + +void btConvexInternalShape::setLocalScaling(const btVector3& scaling) +{ + m_localScaling = scaling; +} + + + +void btConvexInternalShape::getAabbSlow(const btTransform& trans,btVector3&minAabb,btVector3&maxAabb) const +{ + + btScalar margin = getMargin(); + for (int i=0;i<3;i++) + { + btVector3 vec(btScalar(0.),btScalar(0.),btScalar(0.)); + vec[i] = btScalar(1.); + + btVector3 sv = localGetSupportingVertex(vec*trans.getBasis()); + + btVector3 tmp = trans(sv); + maxAabb[i] = tmp[i]+margin; + vec[i] = btScalar(-1.); + tmp = trans(localGetSupportingVertex(vec*trans.getBasis())); + minAabb[i] = tmp[i]-margin; + } +}; + + +btVector3 btConvexInternalShape::localGetSupportingVertex(const btVector3& vec)const +{ +#ifndef __SPU__ + + btVector3 supVertex = localGetSupportingVertexWithoutMargin(vec); + + if ( getMargin()!=btScalar(0.) ) + { + btVector3 vecnorm = vec; + if (vecnorm .length2() < (SIMD_EPSILON*SIMD_EPSILON)) + { + vecnorm.setValue(btScalar(-1.),btScalar(-1.),btScalar(-1.)); + } + vecnorm.normalize(); + supVertex+= getMargin() * vecnorm; + } + return supVertex; + +#else + return btVector3(0,0,0); +#endif //__SPU__ + + } + + diff --git a/bullet/src/BulletCollision/CollisionShapes/btConvexInternalShape.h b/bullet/src/BulletCollision/CollisionShapes/btConvexInternalShape.h new file mode 100644 index 0000000..fa7022d --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btConvexInternalShape.h @@ -0,0 +1,99 @@ + +#ifndef BT_CONVEX_INTERNAL_SHAPE_H +#define BT_CONVEX_INTERNAL_SHAPE_H + +#include "btConvexShape.h" + +///btConvexInternalShape carries some additional data, shared by most implementations +class btConvexInternalShape : public btConvexShape +{ + + protected: + + //local scaling. collisionMargin is not scaled ! + btVector3 m_localScaling; + + btVector3 m_implicitShapeDimensions; + + btScalar m_collisionMargin; + + btScalar m_padding[2]; + + + + +public: + + btConvexInternalShape(); + + virtual ~btConvexInternalShape() + { + + } + + + virtual btVector3 localGetSupportingVertex(const btVector3& vec)const; +#ifndef __SPU__ + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec) const= 0; + + //notice that the vectors should be unit length + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const= 0; +#endif //#ifndef __SPU__ + + const btVector3& getImplicitShapeDimensions() const + { + return m_implicitShapeDimensions; + } + + ///getAabb's default implementation is brute force, expected derived classes to implement a fast dedicated version + void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const + { + getAabbSlow(t,aabbMin,aabbMax); + } + + + + virtual void getAabbSlow(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const; + + + virtual void setLocalScaling(const btVector3& scaling); + virtual const btVector3& getLocalScaling() const + { + return m_localScaling; + } + + const btVector3& getLocalScalingNV() const + { + return m_localScaling; + } + + virtual void setMargin(btScalar margin) + { + m_collisionMargin = margin; + } + virtual btScalar getMargin() const + { + return m_collisionMargin; + } + + btScalar getMarginNV() const + { + return m_collisionMargin; + } + + virtual int getNumPreferredPenetrationDirections() const + { + return 0; + } + + virtual void getPreferredPenetrationDirection(int index, btVector3& penetrationVector) const + { + (void)penetrationVector; + (void)index; + btAssert(0); + } + +}; + + +#endif //BT_CONVEX_INTERNAL_SHAPE_H diff --git a/bullet/src/BulletCollision/CollisionShapes/btConvexShape.cpp b/bullet/src/BulletCollision/CollisionShapes/btConvexShape.cpp new file mode 100644 index 0000000..faf42df --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btConvexShape.cpp @@ -0,0 +1,18 @@ +/* +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. +*/ + +#include "btConvexShape.h" + + diff --git a/bullet/src/BulletCollision/CollisionShapes/btConvexShape.h b/bullet/src/BulletCollision/CollisionShapes/btConvexShape.h new file mode 100644 index 0000000..7ad8b05 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btConvexShape.h @@ -0,0 +1,74 @@ +/* +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. +*/ + +#ifndef CONVEX_SHAPE_INTERFACE1 +#define CONVEX_SHAPE_INTERFACE1 + +#include "btCollisionShape.h" + +#include "LinearMath/btVector3.h" +#include "LinearMath/btTransform.h" +#include "LinearMath/btMatrix3x3.h" +#include "btCollisionMargin.h" + +//todo: get rid of this btConvexCastResult thing! +struct btConvexCastResult; +#define MAX_PREFERRED_PENETRATION_DIRECTIONS 10 + +/// btConvexShape is an abstract shape interface. +/// It describes general convex shapes using the localGetSupportingVertex interface +/// used in combination with GJK or btConvexCast +ATTRIBUTE_ALIGNED16(class) btConvexShape : public btCollisionShape +{ + + +public: + + virtual ~btConvexShape() + { + + } + + + virtual btVector3 localGetSupportingVertex(const btVector3& vec)const =0; +#ifndef __SPU__ + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec) const= 0; + + //notice that the vectors should be unit length + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const= 0; +#endif //#ifndef __SPU__ + + + ///getAabb's default implementation is brute force, expected derived classes to implement a fast dedicated version + void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const =0; + + virtual void getAabbSlow(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const =0; + + virtual void setLocalScaling(const btVector3& scaling) =0; + virtual const btVector3& getLocalScaling() const =0; + + virtual void setMargin(btScalar margin)=0; + + virtual btScalar getMargin() const=0; + + virtual int getNumPreferredPenetrationDirections() const=0; + + virtual void getPreferredPenetrationDirection(int index, btVector3& penetrationVector) const=0; + +}; + + + +#endif //CONVEX_SHAPE_INTERFACE1 diff --git a/bullet/src/BulletCollision/CollisionShapes/btConvexTriangleMeshShape.cpp b/bullet/src/BulletCollision/CollisionShapes/btConvexTriangleMeshShape.cpp new file mode 100644 index 0000000..e5cec63 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btConvexTriangleMeshShape.cpp @@ -0,0 +1,206 @@ +/* +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. +*/ +#include "btConvexTriangleMeshShape.h" +#include "BulletCollision/CollisionShapes/btCollisionMargin.h" + +#include "LinearMath/btQuaternion.h" +#include "BulletCollision/CollisionShapes/btStridingMeshInterface.h" + + +btConvexTriangleMeshShape ::btConvexTriangleMeshShape (btStridingMeshInterface* meshInterface) +:m_stridingMesh(meshInterface) +{ + recalcLocalAabb(); +} + + + + +///It's not nice to have all this virtual function overhead, so perhaps we can also gather the points once +///but then we are duplicating +class LocalSupportVertexCallback: public btInternalTriangleIndexCallback +{ + + btVector3 m_supportVertexLocal; +public: + + btScalar m_maxDot; + btVector3 m_supportVecLocal; + + LocalSupportVertexCallback(const btVector3& supportVecLocal) + : m_supportVertexLocal(btScalar(0.),btScalar(0.),btScalar(0.)), + m_maxDot(btScalar(-1e30)), + m_supportVecLocal(supportVecLocal) + { + } + + virtual void internalProcessTriangleIndex(btVector3* triangle,int partId,int triangleIndex) + { + (void)triangleIndex; + (void)partId; + + for (int i=0;i<3;i++) + { + btScalar dot = m_supportVecLocal.dot(triangle[i]); + if (dot > m_maxDot) + { + m_maxDot = dot; + m_supportVertexLocal = triangle[i]; + } + } + } + + btVector3 GetSupportVertexLocal() + { + return m_supportVertexLocal; + } + +}; + + + + + +btVector3 btConvexTriangleMeshShape::localGetSupportingVertexWithoutMargin(const btVector3& vec0)const +{ + btVector3 supVec(btScalar(0.),btScalar(0.),btScalar(0.)); + + btVector3 vec = vec0; + btScalar lenSqr = vec.length2(); + if (lenSqr < btScalar(0.0001)) + { + vec.setValue(1,0,0); + } else + { + btScalar rlen = btScalar(1.) / btSqrt(lenSqr ); + vec *= rlen; + } + + LocalSupportVertexCallback supportCallback(vec); + btVector3 aabbMax(btScalar(1e30),btScalar(1e30),btScalar(1e30)); + m_stridingMesh->InternalProcessAllTriangles(&supportCallback,-aabbMax,aabbMax); + supVec = supportCallback.GetSupportVertexLocal(); + + return supVec; +} + +void btConvexTriangleMeshShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ + //use 'w' component of supportVerticesOut? + { + for (int i=0;iInternalProcessAllTriangles(&supportCallback,-aabbMax,aabbMax); + supportVerticesOut[j] = supportCallback.GetSupportVertexLocal(); + } + +} + + + +btVector3 btConvexTriangleMeshShape::localGetSupportingVertex(const btVector3& vec)const +{ + btVector3 supVertex = localGetSupportingVertexWithoutMargin(vec); + + if ( getMargin()!=btScalar(0.) ) + { + btVector3 vecnorm = vec; + if (vecnorm .length2() < (SIMD_EPSILON*SIMD_EPSILON)) + { + vecnorm.setValue(btScalar(-1.),btScalar(-1.),btScalar(-1.)); + } + vecnorm.normalize(); + supVertex+= getMargin() * vecnorm; + } + return supVertex; +} + + + + + + + + + +//currently just for debugging (drawing), perhaps future support for algebraic continuous collision detection +//Please note that you can debug-draw btConvexTriangleMeshShape with the Raytracer Demo +int btConvexTriangleMeshShape::getNumVertices() const +{ + //cache this? + return 0; + +} + +int btConvexTriangleMeshShape::getNumEdges() const +{ + return 0; +} + +void btConvexTriangleMeshShape::getEdge(int ,btPoint3& ,btPoint3& ) const +{ + btAssert(0); +} + +void btConvexTriangleMeshShape::getVertex(int ,btPoint3& ) const +{ + btAssert(0); +} + +int btConvexTriangleMeshShape::getNumPlanes() const +{ + return 0; +} + +void btConvexTriangleMeshShape::getPlane(btVector3& ,btPoint3& ,int ) const +{ + btAssert(0); +} + +//not yet +bool btConvexTriangleMeshShape::isInside(const btPoint3& ,btScalar ) const +{ + btAssert(0); + return false; +} + + + +void btConvexTriangleMeshShape::setLocalScaling(const btVector3& scaling) +{ + m_stridingMesh->setScaling(scaling); + + recalcLocalAabb(); + +} + + +const btVector3& btConvexTriangleMeshShape::getLocalScaling() const +{ + return m_stridingMesh->getScaling(); +} + diff --git a/bullet/src/BulletCollision/CollisionShapes/btConvexTriangleMeshShape.h b/bullet/src/BulletCollision/CollisionShapes/btConvexTriangleMeshShape.h new file mode 100644 index 0000000..8631dc2 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btConvexTriangleMeshShape.h @@ -0,0 +1,51 @@ +#ifndef CONVEX_TRIANGLEMESH_SHAPE_H +#define CONVEX_TRIANGLEMESH_SHAPE_H + + +#include "btPolyhedralConvexShape.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" // for the types + + +/// btConvexTriangleMeshShape is a convex hull of a triangle mesh. If you just have a point cloud, you can use btConvexHullShape instead. +/// It uses the btStridingMeshInterface instead of a point cloud. This can avoid the duplication of the triangle mesh data. +class btConvexTriangleMeshShape : public btPolyhedralConvexShape +{ + + class btStridingMeshInterface* m_stridingMesh; + +public: + btConvexTriangleMeshShape(btStridingMeshInterface* meshInterface); + + class btStridingMeshInterface* getStridingMesh() + { + return m_stridingMesh; + } + + virtual btVector3 localGetSupportingVertex(const btVector3& vec)const; + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const; + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const; + + virtual int getShapeType()const { return CONVEX_TRIANGLEMESH_SHAPE_PROXYTYPE; } + + //debugging + virtual const char* getName()const {return "ConvexTrimesh";} + + virtual int getNumVertices() const; + virtual int getNumEdges() const; + virtual void getEdge(int i,btPoint3& pa,btPoint3& pb) const; + virtual void getVertex(int i,btPoint3& vtx) const; + virtual int getNumPlanes() const; + virtual void getPlane(btVector3& planeNormal,btPoint3& planeSupport,int i ) const; + virtual bool isInside(const btPoint3& pt,btScalar tolerance) const; + + + virtual void setLocalScaling(const btVector3& scaling); + virtual const btVector3& getLocalScaling() const; + +}; + + + +#endif //CONVEX_TRIANGLEMESH_SHAPE_H + + diff --git a/bullet/src/BulletCollision/CollisionShapes/btCylinderShape.cpp b/bullet/src/BulletCollision/CollisionShapes/btCylinderShape.cpp new file mode 100644 index 0000000..fc8aa9a --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btCylinderShape.cpp @@ -0,0 +1,206 @@ +/* +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. +*/ +#include "btCylinderShape.h" +#include "LinearMath/btPoint3.h" + +btCylinderShape::btCylinderShape (const btVector3& halfExtents) +:btBoxShape(halfExtents), +m_upAxis(1) +{ + recalcLocalAabb(); +} + + +btCylinderShapeX::btCylinderShapeX (const btVector3& halfExtents) +:btCylinderShape(halfExtents) +{ + m_upAxis = 0; + recalcLocalAabb(); +} + + +btCylinderShapeZ::btCylinderShapeZ (const btVector3& halfExtents) +:btCylinderShape(halfExtents) +{ + m_upAxis = 2; + recalcLocalAabb(); +} + +void btCylinderShape::getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const +{ + //skip the box 'getAabb' + btPolyhedralConvexShape::getAabb(t,aabbMin,aabbMax); +} + + +inline btVector3 CylinderLocalSupportX(const btVector3& halfExtents,const btVector3& v) +{ +const int cylinderUpAxis = 0; +const int XX = 1; +const int YY = 0; +const int ZZ = 2; + + //mapping depends on how cylinder local orientation is + // extents of the cylinder is: X,Y is for radius, and Z for height + + + btScalar radius = halfExtents[XX]; + btScalar halfHeight = halfExtents[cylinderUpAxis]; + + + btVector3 tmp; + btScalar d ; + + btScalar s = btSqrt(v[XX] * v[XX] + v[ZZ] * v[ZZ]); + if (s != btScalar(0.0)) + { + d = radius / s; + tmp[XX] = v[XX] * d; + tmp[YY] = v[YY] < 0.0 ? -halfHeight : halfHeight; + tmp[ZZ] = v[ZZ] * d; + return tmp; + } + else + { + tmp[XX] = radius; + tmp[YY] = v[YY] < 0.0 ? -halfHeight : halfHeight; + tmp[ZZ] = btScalar(0.0); + return tmp; + } + + +} + + + + + + +inline btVector3 CylinderLocalSupportY(const btVector3& halfExtents,const btVector3& v) +{ + +const int cylinderUpAxis = 1; +const int XX = 0; +const int YY = 1; +const int ZZ = 2; + + + btScalar radius = halfExtents[XX]; + btScalar halfHeight = halfExtents[cylinderUpAxis]; + + + btVector3 tmp; + btScalar d ; + + btScalar s = btSqrt(v[XX] * v[XX] + v[ZZ] * v[ZZ]); + if (s != btScalar(0.0)) + { + d = radius / s; + tmp[XX] = v[XX] * d; + tmp[YY] = v[YY] < 0.0 ? -halfHeight : halfHeight; + tmp[ZZ] = v[ZZ] * d; + return tmp; + } + else + { + tmp[XX] = radius; + tmp[YY] = v[YY] < 0.0 ? -halfHeight : halfHeight; + tmp[ZZ] = btScalar(0.0); + return tmp; + } + +} + +inline btVector3 CylinderLocalSupportZ(const btVector3& halfExtents,const btVector3& v) +{ +const int cylinderUpAxis = 2; +const int XX = 0; +const int YY = 2; +const int ZZ = 1; + + //mapping depends on how cylinder local orientation is + // extents of the cylinder is: X,Y is for radius, and Z for height + + + btScalar radius = halfExtents[XX]; + btScalar halfHeight = halfExtents[cylinderUpAxis]; + + + btVector3 tmp; + btScalar d ; + + btScalar s = btSqrt(v[XX] * v[XX] + v[ZZ] * v[ZZ]); + if (s != btScalar(0.0)) + { + d = radius / s; + tmp[XX] = v[XX] * d; + tmp[YY] = v[YY] < 0.0 ? -halfHeight : halfHeight; + tmp[ZZ] = v[ZZ] * d; + return tmp; + } + else + { + tmp[XX] = radius; + tmp[YY] = v[YY] < 0.0 ? -halfHeight : halfHeight; + tmp[ZZ] = btScalar(0.0); + return tmp; + } + + +} + +btVector3 btCylinderShapeX::localGetSupportingVertexWithoutMargin(const btVector3& vec)const +{ + return CylinderLocalSupportX(getHalfExtents(),vec); +} + + +btVector3 btCylinderShapeZ::localGetSupportingVertexWithoutMargin(const btVector3& vec)const +{ + return CylinderLocalSupportZ(getHalfExtents(),vec); +} +btVector3 btCylinderShape::localGetSupportingVertexWithoutMargin(const btVector3& vec)const +{ + return CylinderLocalSupportY(getHalfExtents(),vec); +} + +void btCylinderShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ + for (int i=0;i=0); + btAssert(y>=0); + btAssert(xstartX) + startX = quantizedAabbMin[1]; + if (quantizedAabbMax[1]startJ) + startJ = quantizedAabbMin[2]; + if (quantizedAabbMax[2]startX) + startX = quantizedAabbMin[0]; + if (quantizedAabbMax[0]startJ) + startJ = quantizedAabbMin[2]; + if (quantizedAabbMax[2]startX) + startX = quantizedAabbMin[0]; + if (quantizedAabbMax[0]startJ) + startJ = quantizedAabbMin[1]; + if (quantizedAabbMax[1]processTriangle(vertices,x,j); + //second triangle + getVertex(x,j,vertices[0]); + getVertex(x+1,j+1,vertices[1]); + getVertex(x,j+1,vertices[2]); + callback->processTriangle(vertices,x,j); + } else + { + //first triangle + getVertex(x,j,vertices[0]); + getVertex(x,j+1,vertices[1]); + getVertex(x+1,j,vertices[2]); + callback->processTriangle(vertices,x,j); + //second triangle + getVertex(x+1,j,vertices[0]); + getVertex(x,j+1,vertices[1]); + getVertex(x+1,j+1,vertices[2]); + callback->processTriangle(vertices,x,j); + } + } + } + + + +} + +void btHeightfieldTerrainShape::calculateLocalInertia(btScalar ,btVector3& inertia) +{ + //moving concave objects not supported + + inertia.setValue(btScalar(0.),btScalar(0.),btScalar(0.)); +} + +void btHeightfieldTerrainShape::setLocalScaling(const btVector3& scaling) +{ + m_localScaling = scaling; +} +const btVector3& btHeightfieldTerrainShape::getLocalScaling() const +{ + return m_localScaling; +} diff --git a/bullet/src/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h b/bullet/src/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h new file mode 100644 index 0000000..111928c --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h @@ -0,0 +1,88 @@ +/* +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. +*/ + +#ifndef HEIGHTFIELD_TERRAIN_SHAPE_H +#define HEIGHTFIELD_TERRAIN_SHAPE_H + +#include "btConcaveShape.h" + +///btHeightfieldTerrainShape simulates a 2D heightfield terrain +class btHeightfieldTerrainShape : public btConcaveShape +{ +protected: + btVector3 m_localAabbMin; + btVector3 m_localAabbMax; + + ///terrain data + int m_width; + int m_length; + btScalar m_maxHeight; + union + { + unsigned char* m_heightfieldDataUnsignedChar; + btScalar* m_heightfieldDataFloat; + void* m_heightfieldDataUnknown; + }; + + bool m_useFloatData; + bool m_flipQuadEdges; + bool m_useDiamondSubdivision; + + int m_upAxis; + + btVector3 m_localScaling; + + virtual btScalar getHeightFieldValue(int x,int y) const; + void quantizeWithClamp(int* out, const btVector3& point) const; + void getVertex(int x,int y,btVector3& vertex) const; + + inline bool testQuantizedAabbAgainstQuantizedAabb(int* aabbMin1, int* aabbMax1,const int* aabbMin2,const int* aabbMax2) const + { + bool overlap = true; + overlap = (aabbMin1[0] > aabbMax2[0] || aabbMax1[0] < aabbMin2[0]) ? false : overlap; + overlap = (aabbMin1[2] > aabbMax2[2] || aabbMax1[2] < aabbMin2[2]) ? false : overlap; + overlap = (aabbMin1[1] > aabbMax2[1] || aabbMax1[1] < aabbMin2[1]) ? false : overlap; + return overlap; + } + +public: + btHeightfieldTerrainShape(int width,int height,void* heightfieldData, btScalar maxHeight,int upAxis,bool useFloatData,bool flipQuadEdges); + + virtual ~btHeightfieldTerrainShape(); + + + void setUseDiamondSubdivision(bool useDiamondSubdivision=true) { m_useDiamondSubdivision = useDiamondSubdivision;} + + virtual int getShapeType() const + { + return TERRAIN_SHAPE_PROXYTYPE; + } + + virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const; + + virtual void processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const; + + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia); + + virtual void setLocalScaling(const btVector3& scaling); + + virtual const btVector3& getLocalScaling() const; + + //debugging + virtual const char* getName()const {return "HEIGHTFIELD";} + +}; + +#endif //HEIGHTFIELD_TERRAIN_SHAPE_H diff --git a/bullet/src/BulletCollision/CollisionShapes/btMinkowskiSumShape.cpp b/bullet/src/BulletCollision/CollisionShapes/btMinkowskiSumShape.cpp new file mode 100644 index 0000000..bfaf83b --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btMinkowskiSumShape.cpp @@ -0,0 +1,57 @@ +/* +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. +*/ + +#include "btMinkowskiSumShape.h" + + +btMinkowskiSumShape::btMinkowskiSumShape(const btConvexShape* shapeA,const btConvexShape* shapeB) +:m_shapeA(shapeA), +m_shapeB(shapeB) +{ + m_transA.setIdentity(); + m_transB.setIdentity(); +} + +btVector3 btMinkowskiSumShape::localGetSupportingVertexWithoutMargin(const btVector3& vec)const +{ + btVector3 supVertexA = m_transA(m_shapeA->localGetSupportingVertexWithoutMargin(vec*m_transA.getBasis())); + btVector3 supVertexB = m_transB(m_shapeB->localGetSupportingVertexWithoutMargin(vec*m_transB.getBasis())); + return supVertexA + supVertexB; +} + +void btMinkowskiSumShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ + //todo: could make recursive use of batching. probably this shape is not used frequently. + for (int i=0;igetMargin() + m_shapeB->getMargin(); +} + + +void btMinkowskiSumShape::calculateLocalInertia(btScalar mass,btVector3& inertia) +{ + (void)mass; + btAssert(0); + inertia.setValue(0,0,0); +} diff --git a/bullet/src/BulletCollision/CollisionShapes/btMinkowskiSumShape.h b/bullet/src/BulletCollision/CollisionShapes/btMinkowskiSumShape.h new file mode 100644 index 0000000..d6e4056 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btMinkowskiSumShape.h @@ -0,0 +1,62 @@ +/* +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. +*/ + +#ifndef MINKOWSKI_SUM_SHAPE_H +#define MINKOWSKI_SUM_SHAPE_H + +#include "btConvexInternalShape.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" // for the types + +/// btMinkowskiSumShape represents implicit (getSupportingVertex) based minkowski sum of two convex implicit shapes. +class btMinkowskiSumShape : public btConvexInternalShape +{ + + btTransform m_transA; + btTransform m_transB; + const btConvexShape* m_shapeA; + const btConvexShape* m_shapeB; + +public: + + btMinkowskiSumShape(const btConvexShape* shapeA,const btConvexShape* shapeB); + + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const; + + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const; + + + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia); + + void setTransformA(const btTransform& transA) { m_transA = transA;} + void setTransformB(const btTransform& transB) { m_transB = transB;} + + const btTransform& getTransformA()const { return m_transA;} + const btTransform& GetTransformB()const { return m_transB;} + + + virtual int getShapeType() const { return MINKOWSKI_SUM_SHAPE_PROXYTYPE; } + + virtual btScalar getMargin() const; + + const btConvexShape* getShapeA() const { return m_shapeA;} + const btConvexShape* getShapeB() const { return m_shapeB;} + + virtual const char* getName()const + { + return "MinkowskiSum"; + } +}; + +#endif //MINKOWSKI_SUM_SHAPE_H diff --git a/bullet/src/BulletCollision/CollisionShapes/btMultiSphereShape.cpp b/bullet/src/BulletCollision/CollisionShapes/btMultiSphereShape.cpp new file mode 100644 index 0000000..454e2a4 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btMultiSphereShape.cpp @@ -0,0 +1,148 @@ +/* +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. +*/ + +#include "btMultiSphereShape.h" +#include "BulletCollision/CollisionShapes/btCollisionMargin.h" +#include "LinearMath/btQuaternion.h" + +btMultiSphereShape::btMultiSphereShape (const btVector3& inertiaHalfExtents,const btVector3* positions,const btScalar* radi,int numSpheres) +:m_inertiaHalfExtents(inertiaHalfExtents) +{ + btScalar startMargin = btScalar(1e30); + + m_numSpheres = numSpheres; + for (int i=0;i maxDot) + { + maxDot = newDot; + supVec = vtx; + } + } + + return supVec; + +} + + void btMultiSphereShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ + + for (int j=0;j maxDot) + { + maxDot = newDot; + supportVerticesOut[j] = vtx; + } + } + } +} + + + + + + + + +void btMultiSphereShape::calculateLocalInertia(btScalar mass,btVector3& inertia) +{ + //as an approximation, take the inertia of the box that bounds the spheres + + btTransform ident; + ident.setIdentity(); +// btVector3 aabbMin,aabbMax; + +// getAabb(ident,aabbMin,aabbMax); + + btVector3 halfExtents = m_inertiaHalfExtents;//(aabbMax - aabbMin)* btScalar(0.5); + + btScalar margin = CONVEX_DISTANCE_MARGIN; + + btScalar lx=btScalar(2.)*(halfExtents[0]+margin); + btScalar ly=btScalar(2.)*(halfExtents[1]+margin); + btScalar lz=btScalar(2.)*(halfExtents[2]+margin); + const btScalar x2 = lx*lx; + const btScalar y2 = ly*ly; + const btScalar z2 = lz*lz; + const btScalar scaledmass = mass * btScalar(.08333333); + + inertia[0] = scaledmass * (y2+z2); + inertia[1] = scaledmass * (x2+z2); + inertia[2] = scaledmass * (x2+y2); + +} + + + diff --git a/bullet/src/BulletCollision/CollisionShapes/btMultiSphereShape.h b/bullet/src/BulletCollision/CollisionShapes/btMultiSphereShape.h new file mode 100644 index 0000000..cd70fbe --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btMultiSphereShape.h @@ -0,0 +1,74 @@ +/* +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. +*/ + +#ifndef MULTI_SPHERE_MINKOWSKI_H +#define MULTI_SPHERE_MINKOWSKI_H + +#include "btConvexInternalShape.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" // for the types + +#define MAX_NUM_SPHERES 5 + +///btMultiSphereShape represents implicit convex hull of a collection of spheres (using getSupportingVertex) +class btMultiSphereShape : public btConvexInternalShape + +{ + + btVector3 m_localPositions[MAX_NUM_SPHERES]; + btScalar m_radi[MAX_NUM_SPHERES]; + btVector3 m_inertiaHalfExtents; + + int m_numSpheres; + + + + +public: + btMultiSphereShape (const btVector3& inertiaHalfExtents,const btVector3* positions,const btScalar* radi,int numSpheres); + + ///CollisionShape Interface + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia); + + /// btConvexShape Interface + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const; + + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const; + + int getSphereCount() const + { + return m_numSpheres; + } + + const btVector3& getSpherePosition(int index) const + { + return m_localPositions[index]; + } + + btScalar getSphereRadius(int index) const + { + return m_radi[index]; + } + + virtual int getShapeType() const { return MULTI_SPHERE_SHAPE_PROXYTYPE; } + + virtual const char* getName()const + { + return "MultiSphere"; + } + +}; + + +#endif //MULTI_SPHERE_MINKOWSKI_H diff --git a/bullet/src/BulletCollision/CollisionShapes/btOptimizedBvh.cpp b/bullet/src/BulletCollision/CollisionShapes/btOptimizedBvh.cpp new file mode 100644 index 0000000..a0c1b5f --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btOptimizedBvh.cpp @@ -0,0 +1,845 @@ +/* +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. +*/ + +#include "btOptimizedBvh.h" +#include "btStridingMeshInterface.h" +#include "LinearMath/btAabbUtil2.h" +#include "LinearMath/btIDebugDraw.h" + + + +btOptimizedBvh::btOptimizedBvh() : m_useQuantization(false), + m_traversalMode(TRAVERSAL_STACKLESS_CACHE_FRIENDLY) + //m_traversalMode(TRAVERSAL_STACKLESS) + //m_traversalMode(TRAVERSAL_RECURSIVE) +{ + +} + + +void btOptimizedBvh::build(btStridingMeshInterface* triangles, bool useQuantizedAabbCompression, const btVector3& bvhAabbMin, const btVector3& bvhAabbMax) +{ + m_useQuantization = useQuantizedAabbCompression; + + + // NodeArray triangleNodes; + + struct NodeTriangleCallback : public btInternalTriangleIndexCallback + { + + NodeArray& m_triangleNodes; + + NodeTriangleCallback& operator=(NodeTriangleCallback& other) + { + m_triangleNodes = other.m_triangleNodes; + return *this; + } + + NodeTriangleCallback(NodeArray& triangleNodes) + :m_triangleNodes(triangleNodes) + { + } + + virtual void internalProcessTriangleIndex(btVector3* triangle,int partId,int triangleIndex) + { + btOptimizedBvhNode node; + btVector3 aabbMin,aabbMax; + aabbMin.setValue(btScalar(1e30),btScalar(1e30),btScalar(1e30)); + aabbMax.setValue(btScalar(-1e30),btScalar(-1e30),btScalar(-1e30)); + aabbMin.setMin(triangle[0]); + aabbMax.setMax(triangle[0]); + aabbMin.setMin(triangle[1]); + aabbMax.setMax(triangle[1]); + aabbMin.setMin(triangle[2]); + aabbMax.setMax(triangle[2]); + + //with quantization? + node.m_aabbMinOrg = aabbMin; + node.m_aabbMaxOrg = aabbMax; + + node.m_escapeIndex = -1; + + //for child nodes + node.m_subPart = partId; + node.m_triangleIndex = triangleIndex; + m_triangleNodes.push_back(node); + } + }; + struct QuantizedNodeTriangleCallback : public btInternalTriangleIndexCallback + { + QuantizedNodeArray& m_triangleNodes; + const btOptimizedBvh* m_optimizedTree; // for quantization + + QuantizedNodeTriangleCallback& operator=(QuantizedNodeTriangleCallback& other) + { + m_triangleNodes = other.m_triangleNodes; + m_optimizedTree = other.m_optimizedTree; + return *this; + } + + QuantizedNodeTriangleCallback(QuantizedNodeArray& triangleNodes,const btOptimizedBvh* tree) + :m_triangleNodes(triangleNodes),m_optimizedTree(tree) + { + } + + virtual void internalProcessTriangleIndex(btVector3* triangle,int partId,int triangleIndex) + { + btAssert(partId==0); + //negative indices are reserved for escapeIndex + btAssert(triangleIndex>=0); + + btQuantizedBvhNode node; + btVector3 aabbMin,aabbMax; + aabbMin.setValue(btScalar(1e30),btScalar(1e30),btScalar(1e30)); + aabbMax.setValue(btScalar(-1e30),btScalar(-1e30),btScalar(-1e30)); + aabbMin.setMin(triangle[0]); + aabbMax.setMax(triangle[0]); + aabbMin.setMin(triangle[1]); + aabbMax.setMax(triangle[1]); + aabbMin.setMin(triangle[2]); + aabbMax.setMax(triangle[2]); + + m_optimizedTree->quantizeWithClamp(&node.m_quantizedAabbMin[0],aabbMin); + m_optimizedTree->quantizeWithClamp(&node.m_quantizedAabbMax[0],aabbMax); + + node.m_escapeIndexOrTriangleIndex = triangleIndex; + + m_triangleNodes.push_back(node); + } + }; + + + + int numLeafNodes = 0; + + + if (m_useQuantization) + { + + //initialize quantization values + setQuantizationValues(bvhAabbMin,bvhAabbMax); + + QuantizedNodeTriangleCallback callback(m_quantizedLeafNodes,this); + + + triangles->InternalProcessAllTriangles(&callback,m_bvhAabbMin,m_bvhAabbMax); + + //now we have an array of leafnodes in m_leafNodes + numLeafNodes = m_quantizedLeafNodes.size(); + + + m_quantizedContiguousNodes.resize(2*numLeafNodes); + + + } else + { + NodeTriangleCallback callback(m_leafNodes); + + btVector3 aabbMin(btScalar(-1e30),btScalar(-1e30),btScalar(-1e30)); + btVector3 aabbMax(btScalar(1e30),btScalar(1e30),btScalar(1e30)); + + triangles->InternalProcessAllTriangles(&callback,aabbMin,aabbMax); + + //now we have an array of leafnodes in m_leafNodes + numLeafNodes = m_leafNodes.size(); + + m_contiguousNodes.resize(2*numLeafNodes); + } + + m_curNodeIndex = 0; + + buildTree(0,numLeafNodes); + + ///if the entire tree is small then subtree size, we need to create a header info for the tree + if(m_useQuantization && !m_SubtreeHeaders.size()) + { + btBvhSubtreeInfo& subtree = m_SubtreeHeaders.expand(); + subtree.setAabbFromQuantizeNode(m_quantizedContiguousNodes[0]); + subtree.m_rootNodeIndex = 0; + subtree.m_subtreeSize = m_quantizedContiguousNodes[0].isLeafNode() ? 1 : m_quantizedContiguousNodes[0].getEscapeIndex(); + } +} + + + +void btOptimizedBvh::refitPartial(btStridingMeshInterface* meshInterface,const btVector3& aabbMin,const btVector3& aabbMax) +{ + //incrementally initialize quantization values + btAssert(m_useQuantization); + + btAssert(aabbMin.getX() > m_bvhAabbMin.getX()); + btAssert(aabbMin.getY() > m_bvhAabbMin.getY()); + btAssert(aabbMin.getZ() > m_bvhAabbMin.getZ()); + + btAssert(aabbMax.getX() < m_bvhAabbMax.getX()); + btAssert(aabbMax.getY() < m_bvhAabbMax.getY()); + btAssert(aabbMax.getZ() < m_bvhAabbMax.getZ()); + + ///we should update all quantization values, using updateBvhNodes(meshInterface); + ///but we only update chunks that overlap the given aabb + + unsigned short quantizedQueryAabbMin[3]; + unsigned short quantizedQueryAabbMax[3]; + + quantizeWithClamp(&quantizedQueryAabbMin[0],aabbMin); + quantizeWithClamp(&quantizedQueryAabbMax[0],aabbMax); + + int i; + for (i=0;im_SubtreeHeaders.size();i++) + { + btBvhSubtreeInfo& subtree = m_SubtreeHeaders[i]; + + bool overlap = testQuantizedAabbAgainstQuantizedAabb(quantizedQueryAabbMin,quantizedQueryAabbMax,subtree.m_quantizedAabbMin,subtree.m_quantizedAabbMax); + if (overlap) + { + updateBvhNodes(meshInterface,subtree.m_rootNodeIndex,subtree.m_rootNodeIndex+subtree.m_subtreeSize,i); + + subtree.setAabbFromQuantizeNode(m_quantizedContiguousNodes[subtree.m_rootNodeIndex]); + } + } + +} + +///just for debugging, to visualize the individual patches/subtrees +#ifdef DEBUG_PATCH_COLORS +btVector3 color[4]= +{ + btVector3(255,0,0), + btVector3(0,255,0), + btVector3(0,0,255), + btVector3(0,255,255) +}; +#endif //DEBUG_PATCH_COLORS + + +void btOptimizedBvh::updateBvhNodes(btStridingMeshInterface* meshInterface,int firstNode,int endNode,int index) +{ + (void)index; + + btAssert(m_useQuantization); + + int nodeSubPart=0; + + //get access info to trianglemesh data + const unsigned char *vertexbase; + int numverts; + PHY_ScalarType type; + int stride; + const unsigned char *indexbase; + int indexstride; + int numfaces; + PHY_ScalarType indicestype; + meshInterface->getLockedReadOnlyVertexIndexBase(&vertexbase,numverts, type,stride,&indexbase,indexstride,numfaces,indicestype,nodeSubPart); + + btVector3 triangleVerts[3]; + btVector3 aabbMin,aabbMax; + const btVector3& meshScaling = meshInterface->getScaling(); + + int i; + for (i=endNode-1;i>=firstNode;i--) + { + + + btQuantizedBvhNode& curNode = m_quantizedContiguousNodes[i]; + if (curNode.isLeafNode()) + { + //recalc aabb from triangle data + int nodeTriangleIndex = curNode.getTriangleIndex(); + //triangles->getLockedReadOnlyVertexIndexBase(vertexBase,numVerts, + + int* gfxbase = (int*)(indexbase+nodeTriangleIndex*indexstride); + + + for (int j=2;j>=0;j--) + { + + int graphicsindex = gfxbase[j]; + btScalar* graphicsbase = (btScalar*)(vertexbase+graphicsindex*stride); +#ifdef DEBUG_PATCH_COLORS + btVector3 mycolor = color[index&3]; + graphicsbase[8] = mycolor.getX(); + graphicsbase[9] = mycolor.getY(); + graphicsbase[10] = mycolor.getZ(); +#endif //DEBUG_PATCH_COLORS + + + triangleVerts[j] = btVector3( + graphicsbase[0]*meshScaling.getX(), + graphicsbase[1]*meshScaling.getY(), + graphicsbase[2]*meshScaling.getZ()); + } + + + + aabbMin.setValue(btScalar(1e30),btScalar(1e30),btScalar(1e30)); + aabbMax.setValue(btScalar(-1e30),btScalar(-1e30),btScalar(-1e30)); + aabbMin.setMin(triangleVerts[0]); + aabbMax.setMax(triangleVerts[0]); + aabbMin.setMin(triangleVerts[1]); + aabbMax.setMax(triangleVerts[1]); + aabbMin.setMin(triangleVerts[2]); + aabbMax.setMax(triangleVerts[2]); + + quantizeWithClamp(&curNode.m_quantizedAabbMin[0],aabbMin); + quantizeWithClamp(&curNode.m_quantizedAabbMax[0],aabbMax); + + } else + { + //combine aabb from both children + + btQuantizedBvhNode* leftChildNode = &m_quantizedContiguousNodes[i+1]; + + btQuantizedBvhNode* rightChildNode = leftChildNode->isLeafNode() ? &m_quantizedContiguousNodes[i+2] : + &m_quantizedContiguousNodes[i+1+leftChildNode->getEscapeIndex()]; + + + { + for (int i=0;i<3;i++) + { + curNode.m_quantizedAabbMin[i] = leftChildNode->m_quantizedAabbMin[i]; + if (curNode.m_quantizedAabbMin[i]>rightChildNode->m_quantizedAabbMin[i]) + curNode.m_quantizedAabbMin[i]=rightChildNode->m_quantizedAabbMin[i]; + + curNode.m_quantizedAabbMax[i] = leftChildNode->m_quantizedAabbMax[i]; + if (curNode.m_quantizedAabbMax[i] < rightChildNode->m_quantizedAabbMax[i]) + curNode.m_quantizedAabbMax[i] = rightChildNode->m_quantizedAabbMax[i]; + } + } + } + + } + + meshInterface->unLockReadOnlyVertexBase(nodeSubPart); + + +} + +void btOptimizedBvh::setQuantizationValues(const btVector3& bvhAabbMin,const btVector3& bvhAabbMax,btScalar quantizationMargin) +{ + //enlarge the AABB to avoid division by zero when initializing the quantization values + btVector3 clampValue(quantizationMargin,quantizationMargin,quantizationMargin); + m_bvhAabbMin = bvhAabbMin - clampValue; + m_bvhAabbMax = bvhAabbMax + clampValue; + btVector3 aabbSize = m_bvhAabbMax - m_bvhAabbMin; + m_bvhQuantization = btVector3(btScalar(65535.0),btScalar(65535.0),btScalar(65535.0)) / aabbSize; +} + + +void btOptimizedBvh::refit(btStridingMeshInterface* meshInterface) +{ + if (m_useQuantization) + { + //calculate new aabb + btVector3 aabbMin,aabbMax; + meshInterface->calculateAabbBruteForce(aabbMin,aabbMax); + + setQuantizationValues(aabbMin,aabbMax); + + updateBvhNodes(meshInterface,0,m_curNodeIndex,0); + + ///now update all subtree headers + + int i; + for (i=0;i gMaxStackDepth) + gMaxStackDepth = gStackDepth; +#endif //DEBUG_TREE_BUILDING + + + int splitAxis, splitIndex, i; + int numIndices =endIndex-startIndex; + int curIndex = m_curNodeIndex; + + assert(numIndices>0); + + if (numIndices==1) + { +#ifdef DEBUG_TREE_BUILDING + gStackDepth--; +#endif //DEBUG_TREE_BUILDING + + assignInternalNodeFromLeafNode(m_curNodeIndex,startIndex); + + m_curNodeIndex++; + return; + } + //calculate Best Splitting Axis and where to split it. Sort the incoming 'leafNodes' array within range 'startIndex/endIndex'. + + splitAxis = calcSplittingAxis(startIndex,endIndex); + + splitIndex = sortAndCalcSplittingIndex(startIndex,endIndex,splitAxis); + + int internalNodeIndex = m_curNodeIndex; + + setInternalNodeAabbMax(m_curNodeIndex,btVector3(btScalar(-1e30),btScalar(-1e30),btScalar(-1e30))); + setInternalNodeAabbMin(m_curNodeIndex,btVector3(btScalar(1e30),btScalar(1e30),btScalar(1e30))); + + for (i=startIndex;im_escapeIndex; + + int leftChildNodexIndex = m_curNodeIndex; + + //build left child tree + buildTree(startIndex,splitIndex); + + int rightChildNodexIndex = m_curNodeIndex; + //build right child tree + buildTree(splitIndex,endIndex); + +#ifdef DEBUG_TREE_BUILDING + gStackDepth--; +#endif //DEBUG_TREE_BUILDING + + int escapeIndex = m_curNodeIndex - curIndex; + + if (m_useQuantization) + { + //escapeIndex is the number of nodes of this subtree + const int sizeQuantizedNode =sizeof(btQuantizedBvhNode); + const int treeSizeInBytes = escapeIndex * sizeQuantizedNode; + if (treeSizeInBytes > MAX_SUBTREE_SIZE_IN_BYTES) + { + updateSubtreeHeaders(leftChildNodexIndex,rightChildNodexIndex); + } + } + + setInternalNodeEscapeIndex(internalNodeIndex,escapeIndex); + +} + +void btOptimizedBvh::updateSubtreeHeaders(int leftChildNodexIndex,int rightChildNodexIndex) +{ + btAssert(m_useQuantization); + + btQuantizedBvhNode& leftChildNode = m_quantizedContiguousNodes[leftChildNodexIndex]; + int leftSubTreeSize = leftChildNode.isLeafNode() ? 1 : leftChildNode.getEscapeIndex(); + int leftSubTreeSizeInBytes = leftSubTreeSize * sizeof(btQuantizedBvhNode); + + btQuantizedBvhNode& rightChildNode = m_quantizedContiguousNodes[rightChildNodexIndex]; + int rightSubTreeSize = rightChildNode.isLeafNode() ? 1 : rightChildNode.getEscapeIndex(); + int rightSubTreeSizeInBytes = rightSubTreeSize * sizeof(btQuantizedBvhNode); + + if(leftSubTreeSizeInBytes <= MAX_SUBTREE_SIZE_IN_BYTES) + { + btBvhSubtreeInfo& subtree = m_SubtreeHeaders.expand(); + subtree.setAabbFromQuantizeNode(leftChildNode); + subtree.m_rootNodeIndex = leftChildNodexIndex; + subtree.m_subtreeSize = leftSubTreeSize; + } + + if(rightSubTreeSizeInBytes <= MAX_SUBTREE_SIZE_IN_BYTES) + { + btBvhSubtreeInfo& subtree = m_SubtreeHeaders.expand(); + subtree.setAabbFromQuantizeNode(rightChildNode); + subtree.m_rootNodeIndex = rightChildNodexIndex; + subtree.m_subtreeSize = rightSubTreeSize; + } +} + + +int btOptimizedBvh::sortAndCalcSplittingIndex(int startIndex,int endIndex,int splitAxis) +{ + int i; + int splitIndex =startIndex; + int numIndices = endIndex - startIndex; + btScalar splitValue; + + btVector3 means(btScalar(0.),btScalar(0.),btScalar(0.)); + for (i=startIndex;i splitValue) + { + //swap + swapLeafNodes(i,splitIndex); + splitIndex++; + } + } + + //if the splitIndex causes unbalanced trees, fix this by using the center in between startIndex and endIndex + //otherwise the tree-building might fail due to stack-overflows in certain cases. + //unbalanced1 is unsafe: it can cause stack overflows + //bool unbalanced1 = ((splitIndex==startIndex) || (splitIndex == (endIndex-1))); + + //unbalanced2 should work too: always use center (perfect balanced trees) + //bool unbalanced2 = true; + + //this should be safe too: + int rangeBalancedIndices = numIndices/3; + bool unbalanced = ((splitIndex<=(startIndex+rangeBalancedIndices)) || (splitIndex >=(endIndex-1-rangeBalancedIndices))); + + if (unbalanced) + { + splitIndex = startIndex+ (numIndices>>1); + } + + bool unbal = (splitIndex==startIndex) || (splitIndex == (endIndex)); + btAssert(!unbal); + + return splitIndex; +} + + +int btOptimizedBvh::calcSplittingAxis(int startIndex,int endIndex) +{ + int i; + + btVector3 means(btScalar(0.),btScalar(0.),btScalar(0.)); + btVector3 variance(btScalar(0.),btScalar(0.),btScalar(0.)); + int numIndices = endIndex-startIndex; + + for (i=startIndex;im_aabbMinOrg,rootNode->m_aabbMaxOrg); + isLeafNode = rootNode->m_escapeIndex == -1; + + if (isLeafNode && aabbOverlap) + { + nodeCallback->processNode(rootNode->m_subPart,rootNode->m_triangleIndex); + } + + if (aabbOverlap || isLeafNode) + { + rootNode++; + curIndex++; + } else + { + escapeIndex = rootNode->m_escapeIndex; + rootNode += escapeIndex; + curIndex += escapeIndex; + } + } + if (maxIterations < walkIterations) + maxIterations = walkIterations; + +} + +/* +///this was the original recursive traversal, before we optimized towards stackless traversal +void btOptimizedBvh::walkTree(btOptimizedBvhNode* rootNode,btNodeOverlapCallback* nodeCallback,const btVector3& aabbMin,const btVector3& aabbMax) const +{ + bool isLeafNode, aabbOverlap = TestAabbAgainstAabb2(aabbMin,aabbMax,rootNode->m_aabbMin,rootNode->m_aabbMax); + if (aabbOverlap) + { + isLeafNode = (!rootNode->m_leftChild && !rootNode->m_rightChild); + if (isLeafNode) + { + nodeCallback->processNode(rootNode); + } else + { + walkTree(rootNode->m_leftChild,nodeCallback,aabbMin,aabbMax); + walkTree(rootNode->m_rightChild,nodeCallback,aabbMin,aabbMax); + } + } + +} +*/ + +void btOptimizedBvh::walkRecursiveQuantizedTreeAgainstQueryAabb(const btQuantizedBvhNode* currentNode,btNodeOverlapCallback* nodeCallback,unsigned short int* quantizedQueryAabbMin,unsigned short int* quantizedQueryAabbMax) const +{ + btAssert(m_useQuantization); + + bool aabbOverlap, isLeafNode; + + aabbOverlap = testQuantizedAabbAgainstQuantizedAabb(quantizedQueryAabbMin,quantizedQueryAabbMax,currentNode->m_quantizedAabbMin,currentNode->m_quantizedAabbMax); + isLeafNode = currentNode->isLeafNode(); + + if (aabbOverlap) + { + if (isLeafNode) + { + nodeCallback->processNode(0,currentNode->getTriangleIndex()); + } else + { + //process left and right children + const btQuantizedBvhNode* leftChildNode = currentNode+1; + walkRecursiveQuantizedTreeAgainstQueryAabb(leftChildNode,nodeCallback,quantizedQueryAabbMin,quantizedQueryAabbMax); + + const btQuantizedBvhNode* rightChildNode = leftChildNode->isLeafNode() ? leftChildNode+1:leftChildNode+leftChildNode->getEscapeIndex(); + walkRecursiveQuantizedTreeAgainstQueryAabb(rightChildNode,nodeCallback,quantizedQueryAabbMin,quantizedQueryAabbMax); + } + } +} + + + + + + + +void btOptimizedBvh::walkStacklessQuantizedTree(btNodeOverlapCallback* nodeCallback,unsigned short int* quantizedQueryAabbMin,unsigned short int* quantizedQueryAabbMax,int startNodeIndex,int endNodeIndex) const +{ + btAssert(m_useQuantization); + + int curIndex = startNodeIndex; + int walkIterations = 0; + int subTreeSize = endNodeIndex - startNodeIndex; + + const btQuantizedBvhNode* rootNode = &m_quantizedContiguousNodes[startNodeIndex]; + int escapeIndex; + + bool aabbOverlap, isLeafNode; + + while (curIndex < endNodeIndex) + { + +//#define VISUALLY_ANALYZE_BVH 1 +#ifdef VISUALLY_ANALYZE_BVH + //some code snippet to debugDraw aabb, to visually analyze bvh structure + static int drawPatch = 0; + //need some global access to a debugDrawer + extern btIDebugDraw* debugDrawerPtr; + if (curIndex==drawPatch) + { + btVector3 aabbMin,aabbMax; + aabbMin = unQuantize(rootNode->m_quantizedAabbMin); + aabbMax = unQuantize(rootNode->m_quantizedAabbMax); + btVector3 color(1,0,0); + debugDrawerPtr->drawAabb(aabbMin,aabbMax,color); + } +#endif//VISUALLY_ANALYZE_BVH + + //catch bugs in tree data + assert (walkIterations < subTreeSize); + + walkIterations++; + aabbOverlap = testQuantizedAabbAgainstQuantizedAabb(quantizedQueryAabbMin,quantizedQueryAabbMax,rootNode->m_quantizedAabbMin,rootNode->m_quantizedAabbMax); + isLeafNode = rootNode->isLeafNode(); + + if (isLeafNode && aabbOverlap) + { + nodeCallback->processNode(0,rootNode->getTriangleIndex()); + } + + if (aabbOverlap || isLeafNode) + { + rootNode++; + curIndex++; + } else + { + escapeIndex = rootNode->getEscapeIndex(); + rootNode += escapeIndex; + curIndex += escapeIndex; + } + } + if (maxIterations < walkIterations) + maxIterations = walkIterations; + +} + +//This traversal can be called from Playstation 3 SPU +void btOptimizedBvh::walkStacklessQuantizedTreeCacheFriendly(btNodeOverlapCallback* nodeCallback,unsigned short int* quantizedQueryAabbMin,unsigned short int* quantizedQueryAabbMax) const +{ + btAssert(m_useQuantization); + + int i; + + + for (i=0;im_SubtreeHeaders.size();i++) + { + const btBvhSubtreeInfo& subtree = m_SubtreeHeaders[i]; + + bool overlap = testQuantizedAabbAgainstQuantizedAabb(quantizedQueryAabbMin,quantizedQueryAabbMax,subtree.m_quantizedAabbMin,subtree.m_quantizedAabbMax); + if (overlap) + { + walkStacklessQuantizedTree(nodeCallback,quantizedQueryAabbMin,quantizedQueryAabbMax, + subtree.m_rootNodeIndex, + subtree.m_rootNodeIndex+subtree.m_subtreeSize); + } + } +} + + + + +void btOptimizedBvh::reportSphereOverlappingNodex(btNodeOverlapCallback* nodeCallback,const btVector3& aabbMin,const btVector3& aabbMax) const +{ + (void)nodeCallback; + (void)aabbMin; + (void)aabbMax; + //not yet, please use aabb + btAssert(0); +} + + +void btOptimizedBvh::quantizeWithClamp(unsigned short* out, const btVector3& point) const +{ + + btAssert(m_useQuantization); + + btVector3 clampedPoint(point); + clampedPoint.setMax(m_bvhAabbMin); + clampedPoint.setMin(m_bvhAabbMax); + + btVector3 v = (clampedPoint - m_bvhAabbMin) * m_bvhQuantization; + out[0] = (unsigned short)(v.getX()+0.5f); + out[1] = (unsigned short)(v.getY()+0.5f); + out[2] = (unsigned short)(v.getZ()+0.5f); +} + +btVector3 btOptimizedBvh::unQuantize(const unsigned short* vecIn) const +{ + btVector3 vecOut; + vecOut.setValue( + (btScalar)(vecIn[0]) / (m_bvhQuantization.getX()), + (btScalar)(vecIn[1]) / (m_bvhQuantization.getY()), + (btScalar)(vecIn[2]) / (m_bvhQuantization.getZ())); + vecOut += m_bvhAabbMin; + return vecOut; +} + + +void btOptimizedBvh::swapLeafNodes(int i,int splitIndex) +{ + if (m_useQuantization) + { + btQuantizedBvhNode tmp = m_quantizedLeafNodes[i]; + m_quantizedLeafNodes[i] = m_quantizedLeafNodes[splitIndex]; + m_quantizedLeafNodes[splitIndex] = tmp; + } else + { + btOptimizedBvhNode tmp = m_leafNodes[i]; + m_leafNodes[i] = m_leafNodes[splitIndex]; + m_leafNodes[splitIndex] = tmp; + } +} + +void btOptimizedBvh::assignInternalNodeFromLeafNode(int internalNode,int leafNodeIndex) +{ + if (m_useQuantization) + { + m_quantizedContiguousNodes[internalNode] = m_quantizedLeafNodes[leafNodeIndex]; + } else + { + m_contiguousNodes[internalNode] = m_leafNodes[leafNodeIndex]; + } +} diff --git a/bullet/src/BulletCollision/CollisionShapes/btOptimizedBvh.h b/bullet/src/BulletCollision/CollisionShapes/btOptimizedBvh.h new file mode 100644 index 0000000..f53336b --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btOptimizedBvh.h @@ -0,0 +1,330 @@ +/* +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. +*/ + +#ifndef OPTIMIZED_BVH_H +#define OPTIMIZED_BVH_H + + +#include "LinearMath/btVector3.h" + + +//http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/vclrf__m128.asp + + + +class btStridingMeshInterface; + +//Note: currently we have 16 bytes per quantized node +#define MAX_SUBTREE_SIZE_IN_BYTES 2048 + + +///btQuantizedBvhNode is a compressed aabb node, 16 bytes. +///Node can be used for leafnode or internal node. Leafnodes can point to 32-bit triangle index (non-negative range). +ATTRIBUTE_ALIGNED16 (struct) btQuantizedBvhNode +{ + + //12 bytes + unsigned short int m_quantizedAabbMin[3]; + unsigned short int m_quantizedAabbMax[3]; + //4 bytes + int m_escapeIndexOrTriangleIndex; + + bool isLeafNode() const + { + //skipindex is negative (internal node), triangleindex >=0 (leafnode) + return (m_escapeIndexOrTriangleIndex >= 0); + } + int getEscapeIndex() const + { + btAssert(!isLeafNode()); + return -m_escapeIndexOrTriangleIndex; + } + int getTriangleIndex() const + { + btAssert(isLeafNode()); + return m_escapeIndexOrTriangleIndex; + } +} +; + +/// btOptimizedBvhNode contains both internal and leaf node information. +/// Total node size is 44 bytes / node. You can use the compressed version of 16 bytes. +ATTRIBUTE_ALIGNED16 (struct) btOptimizedBvhNode +{ + //32 bytes + btVector3 m_aabbMinOrg; + btVector3 m_aabbMaxOrg; + + //4 + int m_escapeIndex; + + //8 + //for child nodes + int m_subPart; + int m_triangleIndex; + int m_padding[5];//bad, due to alignment + + +}; + + +///btBvhSubtreeInfo provides info to gather a subtree of limited size +ATTRIBUTE_ALIGNED16(class) btBvhSubtreeInfo +{ +public: + //12 bytes + unsigned short int m_quantizedAabbMin[3]; + unsigned short int m_quantizedAabbMax[3]; + //4 bytes, points to the root of the subtree + int m_rootNodeIndex; + //4 bytes + int m_subtreeSize; + int m_padding[3]; + + + void setAabbFromQuantizeNode(const btQuantizedBvhNode& quantizedNode) + { + m_quantizedAabbMin[0] = quantizedNode.m_quantizedAabbMin[0]; + m_quantizedAabbMin[1] = quantizedNode.m_quantizedAabbMin[1]; + m_quantizedAabbMin[2] = quantizedNode.m_quantizedAabbMin[2]; + m_quantizedAabbMax[0] = quantizedNode.m_quantizedAabbMax[0]; + m_quantizedAabbMax[1] = quantizedNode.m_quantizedAabbMax[1]; + m_quantizedAabbMax[2] = quantizedNode.m_quantizedAabbMax[2]; + } +} +; + + +class btNodeOverlapCallback +{ +public: + virtual ~btNodeOverlapCallback() {}; + + virtual void processNode(int subPart, int triangleIndex) = 0; +}; + +#include "LinearMath/btAlignedAllocator.h" +#include "LinearMath/btAlignedObjectArray.h" + + + +///for code readability: +typedef btAlignedObjectArray NodeArray; +typedef btAlignedObjectArray QuantizedNodeArray; +typedef btAlignedObjectArray BvhSubtreeInfoArray; + + +///OptimizedBvh store an AABB tree that can be quickly traversed on CPU (and SPU,GPU in future) +ATTRIBUTE_ALIGNED16(class) btOptimizedBvh +{ + NodeArray m_leafNodes; + NodeArray m_contiguousNodes; + + QuantizedNodeArray m_quantizedLeafNodes; + + QuantizedNodeArray m_quantizedContiguousNodes; + + int m_curNodeIndex; + + + //quantization data + bool m_useQuantization; + btVector3 m_bvhAabbMin; + btVector3 m_bvhAabbMax; + btVector3 m_bvhQuantization; + + enum btTraversalMode + { + TRAVERSAL_STACKLESS = 0, + TRAVERSAL_STACKLESS_CACHE_FRIENDLY, + TRAVERSAL_RECURSIVE + }; + + btTraversalMode m_traversalMode; + + + + + BvhSubtreeInfoArray m_SubtreeHeaders; + + + ///two versions, one for quantized and normal nodes. This allows code-reuse while maintaining readability (no template/macro!) + ///this might be refactored into a virtual, it is usually not calculated at run-time + void setInternalNodeAabbMin(int nodeIndex, const btVector3& aabbMin) + { + if (m_useQuantization) + { + quantizeWithClamp(&m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[0] ,aabbMin); + } else + { + m_contiguousNodes[nodeIndex].m_aabbMinOrg = aabbMin; + + } + } + void setInternalNodeAabbMax(int nodeIndex,const btVector3& aabbMax) + { + if (m_useQuantization) + { + quantizeWithClamp(&m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[0],aabbMax); + } else + { + m_contiguousNodes[nodeIndex].m_aabbMaxOrg = aabbMax; + } + } + + btVector3 getAabbMin(int nodeIndex) const + { + if (m_useQuantization) + { + return unQuantize(&m_quantizedLeafNodes[nodeIndex].m_quantizedAabbMin[0]); + } + //non-quantized + return m_leafNodes[nodeIndex].m_aabbMinOrg; + + } + btVector3 getAabbMax(int nodeIndex) const + { + if (m_useQuantization) + { + return unQuantize(&m_quantizedLeafNodes[nodeIndex].m_quantizedAabbMax[0]); + } + //non-quantized + return m_leafNodes[nodeIndex].m_aabbMaxOrg; + + } + + void setQuantizationValues(const btVector3& bvhAabbMin,const btVector3& bvhAabbMax,btScalar quantizationMargin=btScalar(1.0)); + + void setInternalNodeEscapeIndex(int nodeIndex, int escapeIndex) + { + if (m_useQuantization) + { + m_quantizedContiguousNodes[nodeIndex].m_escapeIndexOrTriangleIndex = -escapeIndex; + } + else + { + m_contiguousNodes[nodeIndex].m_escapeIndex = escapeIndex; + } + + } + + void mergeInternalNodeAabb(int nodeIndex,const btVector3& newAabbMin,const btVector3& newAabbMax) + { + if (m_useQuantization) + { + unsigned short int quantizedAabbMin[3]; + unsigned short int quantizedAabbMax[3]; + quantizeWithClamp(quantizedAabbMin,newAabbMin); + quantizeWithClamp(quantizedAabbMax,newAabbMax); + for (int i=0;i<3;i++) + { + if (m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[i] > quantizedAabbMin[i]) + m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[i] = quantizedAabbMin[i]; + + if (m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[i] < quantizedAabbMax[i]) + m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[i] = quantizedAabbMax[i]; + + } + } else + { + //non-quantized + m_contiguousNodes[nodeIndex].m_aabbMinOrg.setMin(newAabbMin); + m_contiguousNodes[nodeIndex].m_aabbMaxOrg.setMax(newAabbMax); + } + } + + void swapLeafNodes(int firstIndex,int secondIndex); + + void assignInternalNodeFromLeafNode(int internalNode,int leafNodeIndex); + +protected: + + + + void buildTree (int startIndex,int endIndex); + + int calcSplittingAxis(int startIndex,int endIndex); + + int sortAndCalcSplittingIndex(int startIndex,int endIndex,int splitAxis); + + void walkStacklessTree(btNodeOverlapCallback* nodeCallback,const btVector3& aabbMin,const btVector3& aabbMax) const; + + void walkStacklessQuantizedTree(btNodeOverlapCallback* nodeCallback,unsigned short int* quantizedQueryAabbMin,unsigned short int* quantizedQueryAabbMax,int startNodeIndex,int endNodeIndex) const; + + ///tree traversal designed for small-memory processors like PS3 SPU + void walkStacklessQuantizedTreeCacheFriendly(btNodeOverlapCallback* nodeCallback,unsigned short int* quantizedQueryAabbMin,unsigned short int* quantizedQueryAabbMax) const; + + ///use the 16-byte stackless 'skipindex' node tree to do a recursive traversal + void walkRecursiveQuantizedTreeAgainstQueryAabb(const btQuantizedBvhNode* currentNode,btNodeOverlapCallback* nodeCallback,unsigned short int* quantizedQueryAabbMin,unsigned short int* quantizedQueryAabbMax) const; + + ///use the 16-byte stackless 'skipindex' node tree to do a recursive traversal + void walkRecursiveQuantizedTreeAgainstQuantizedTree(const btQuantizedBvhNode* treeNodeA,const btQuantizedBvhNode* treeNodeB,btNodeOverlapCallback* nodeCallback) const; + + + inline bool testQuantizedAabbAgainstQuantizedAabb(unsigned short int* aabbMin1,unsigned short int* aabbMax1,const unsigned short int* aabbMin2,const unsigned short int* aabbMax2) const + { + bool overlap = true; + overlap = (aabbMin1[0] > aabbMax2[0] || aabbMax1[0] < aabbMin2[0]) ? false : overlap; + overlap = (aabbMin1[2] > aabbMax2[2] || aabbMax1[2] < aabbMin2[2]) ? false : overlap; + overlap = (aabbMin1[1] > aabbMax2[1] || aabbMax1[1] < aabbMin2[1]) ? false : overlap; + return overlap; + } + + void updateSubtreeHeaders(int leftChildNodexIndex,int rightChildNodexIndex); + +public: + btOptimizedBvh(); + + virtual ~btOptimizedBvh(); + + void build(btStridingMeshInterface* triangles,bool useQuantizedAabbCompression, const btVector3& bvhAabbMin, const btVector3& bvhAabbMax); + + void reportAabbOverlappingNodex(btNodeOverlapCallback* nodeCallback,const btVector3& aabbMin,const btVector3& aabbMax) const; + + void reportSphereOverlappingNodex(btNodeOverlapCallback* nodeCallback,const btVector3& aabbMin,const btVector3& aabbMax) const; + + void quantizeWithClamp(unsigned short* out, const btVector3& point) const; + + btVector3 unQuantize(const unsigned short* vecIn) const; + + ///setTraversalMode let's you choose between stackless, recursive or stackless cache friendly tree traversal. Note this is only implemented for quantized trees. + void setTraversalMode(btTraversalMode traversalMode) + { + m_traversalMode = traversalMode; + } + + void refit(btStridingMeshInterface* triangles); + + void refitPartial(btStridingMeshInterface* triangles,const btVector3& aabbMin, const btVector3& aabbMax); + + void updateBvhNodes(btStridingMeshInterface* meshInterface,int firstNode,int endNode,int index); + + + QuantizedNodeArray& getQuantizedNodeArray() + { + return m_quantizedContiguousNodes; + } + + BvhSubtreeInfoArray& getSubtreeInfoArray() + { + return m_SubtreeHeaders; + } + +} +; + + +#endif //OPTIMIZED_BVH_H + diff --git a/bullet/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp b/bullet/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp new file mode 100644 index 0000000..222342d --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp @@ -0,0 +1,148 @@ +/* +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. +*/ + +#include + +btPolyhedralConvexShape::btPolyhedralConvexShape() +:m_localAabbMin(1,1,1), +m_localAabbMax(-1,-1,-1), +m_isLocalAabbValid(false), +m_optionalHull(0) +{ + +} + + + +btVector3 btPolyhedralConvexShape::localGetSupportingVertexWithoutMargin(const btVector3& vec0)const +{ + int i; + btVector3 supVec(0,0,0); + + btScalar maxDot(btScalar(-1e30)); + + btVector3 vec = vec0; + btScalar lenSqr = vec.length2(); + if (lenSqr < btScalar(0.0001)) + { + vec.setValue(1,0,0); + } else + { + btScalar rlen = btScalar(1.) / btSqrt(lenSqr ); + vec *= rlen; + } + + btVector3 vtx; + btScalar newDot; + + for (i=0;i maxDot) + { + maxDot = newDot; + supVec = vtx; + } + } + + return supVec; + +} + +void btPolyhedralConvexShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ + int i; + + btVector3 vtx; + btScalar newDot; + + for (i=0;i supportVerticesOut[j][3]) + { + //WARNING: don't swap next lines, the w component would get overwritten! + supportVerticesOut[j] = vtx; + supportVerticesOut[j][3] = newDot; + } + } + } +} + + + +void btPolyhedralConvexShape::calculateLocalInertia(btScalar mass,btVector3& inertia) +{ + //not yet, return box inertia + + btScalar margin = getMargin(); + + btTransform ident; + ident.setIdentity(); + btVector3 aabbMin,aabbMax; + getAabb(ident,aabbMin,aabbMax); + btVector3 halfExtents = (aabbMax-aabbMin)*btScalar(0.5); + + btScalar lx=btScalar(2.)*(halfExtents.x()+margin); + btScalar ly=btScalar(2.)*(halfExtents.y()+margin); + btScalar lz=btScalar(2.)*(halfExtents.z()+margin); + const btScalar x2 = lx*lx; + const btScalar y2 = ly*ly; + const btScalar z2 = lz*lz; + const btScalar scaledmass = mass * btScalar(0.08333333); + + inertia = scaledmass * (btVector3(y2+z2,x2+z2,x2+y2)); + +} + + + +void btPolyhedralConvexShape::getAabb(const btTransform& trans,btVector3& aabbMin,btVector3& aabbMax) const +{ + getNonvirtualAabb(trans,aabbMin,aabbMax,getMargin()); +} + + + + +void btPolyhedralConvexShape::recalcLocalAabb() +{ + m_isLocalAabbValid = true; + + for (int i=0;i<3;i++) + { + btVector3 vec(btScalar(0.),btScalar(0.),btScalar(0.)); + vec[i] = btScalar(1.); + btVector3 tmp = localGetSupportingVertex(vec); + m_localAabbMax[i] = tmp[i]+m_collisionMargin; + vec[i] = btScalar(-1.); + tmp = localGetSupportingVertex(vec); + m_localAabbMin[i] = tmp[i]-m_collisionMargin; + } +} + + diff --git a/bullet/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.h b/bullet/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.h new file mode 100644 index 0000000..59e2300 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.h @@ -0,0 +1,93 @@ +/* +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. +*/ + +#ifndef BU_SHAPE +#define BU_SHAPE + +#include "LinearMath/btPoint3.h" +#include "LinearMath/btMatrix3x3.h" +#include "btConvexInternalShape.h" + + +///PolyhedralConvexShape is an interface class for feature based (vertex/edge/face) convex shapes. +class btPolyhedralConvexShape : public btConvexInternalShape +{ + +protected: + btVector3 m_localAabbMin; + btVector3 m_localAabbMax; + bool m_isLocalAabbValid; + +public: + + btPolyhedralConvexShape(); + + //brute force implementations + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const; + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const; + + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia); + + + inline void getNonvirtualAabb(const btTransform& trans,btVector3& aabbMin,btVector3& aabbMax, btScalar margin) const + { + + //lazy evaluation of local aabb + btAssert(m_isLocalAabbValid); + + btAssert(m_localAabbMin.getX() <= m_localAabbMax.getX()); + btAssert(m_localAabbMin.getY() <= m_localAabbMax.getY()); + btAssert(m_localAabbMin.getZ() <= m_localAabbMax.getZ()); + + + btVector3 localHalfExtents = btScalar(0.5)*(m_localAabbMax-m_localAabbMin); + btVector3 localCenter = btScalar(0.5)*(m_localAabbMax+m_localAabbMin); + + btMatrix3x3 abs_b = trans.getBasis().absolute(); + + btPoint3 center = trans(localCenter); + + btVector3 extent = btVector3(abs_b[0].dot(localHalfExtents), + abs_b[1].dot(localHalfExtents), + abs_b[2].dot(localHalfExtents)); + extent += btVector3(margin,margin,margin); + + aabbMin = center - extent; + aabbMax = center + extent; + + + } + + + virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const; + + void recalcLocalAabb(); + + virtual int getNumVertices() const = 0 ; + virtual int getNumEdges() const = 0; + virtual void getEdge(int i,btPoint3& pa,btPoint3& pb) const = 0; + virtual void getVertex(int i,btPoint3& vtx) const = 0; + virtual int getNumPlanes() const = 0; + virtual void getPlane(btVector3& planeNormal,btPoint3& planeSupport,int i ) const = 0; +// virtual int getIndex(int i) const = 0 ; + + virtual bool isInside(const btPoint3& pt,btScalar tolerance) const = 0; + + /// optional Hull is for optional Separating Axis Test Hull collision detection, see Hull.cpp + class Hull* m_optionalHull; + +}; + +#endif //BU_SHAPE diff --git a/bullet/src/BulletCollision/CollisionShapes/btSphereShape.cpp b/bullet/src/BulletCollision/CollisionShapes/btSphereShape.cpp new file mode 100644 index 0000000..1f3a498 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btSphereShape.cpp @@ -0,0 +1,77 @@ +/* +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. +*/ + +#include "btSphereShape.h" +#include "BulletCollision/CollisionShapes/btCollisionMargin.h" + +#include "LinearMath/btQuaternion.h" + + +btSphereShape ::btSphereShape (btScalar radius) +{ + m_implicitShapeDimensions.setX(radius); +} + +btVector3 btSphereShape::localGetSupportingVertexWithoutMargin(const btVector3& vec)const +{ + (void)vec; + return btVector3(btScalar(0.),btScalar(0.),btScalar(0.)); +} + +void btSphereShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ + (void)vectors; + + for (int i=0;iprocessTriangle(triangle,0,0); + + triangle[0] = projectedCenter - tangentDir0*radius - tangentDir1*radius; + triangle[1] = projectedCenter - tangentDir0*radius + tangentDir1*radius; + triangle[2] = projectedCenter + tangentDir0*radius + tangentDir1*radius; + + callback->processTriangle(triangle,0,1); + +} + +void btStaticPlaneShape::calculateLocalInertia(btScalar mass,btVector3& inertia) +{ + (void)mass; + + //moving concave objects not supported + + inertia.setValue(btScalar(0.),btScalar(0.),btScalar(0.)); +} + +void btStaticPlaneShape::setLocalScaling(const btVector3& scaling) +{ + m_localScaling = scaling; +} +const btVector3& btStaticPlaneShape::getLocalScaling() const +{ + return m_localScaling; +} diff --git a/bullet/src/BulletCollision/CollisionShapes/btStaticPlaneShape.h b/bullet/src/BulletCollision/CollisionShapes/btStaticPlaneShape.h new file mode 100644 index 0000000..e819818 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btStaticPlaneShape.h @@ -0,0 +1,61 @@ +/* +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. +*/ + +#ifndef STATIC_PLANE_SHAPE_H +#define STATIC_PLANE_SHAPE_H + +#include "btConcaveShape.h" + + +///StaticPlaneShape simulates an 'infinite' plane by dynamically reporting triangles approximated by intersection of the plane with the AABB. +///Assumed is that the other objects is not also infinite, so a reasonable sized AABB. +class btStaticPlaneShape : public btConcaveShape +{ +protected: + btVector3 m_localAabbMin; + btVector3 m_localAabbMax; + + btVector3 m_planeNormal; + btScalar m_planeConstant; + btVector3 m_localScaling; + +public: + btStaticPlaneShape(const btVector3& planeNormal,btScalar planeConstant); + + virtual ~btStaticPlaneShape(); + + + virtual int getShapeType() const + { + return STATIC_PLANE_PROXYTYPE; + } + + virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const; + + virtual void processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const; + + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia); + + virtual void setLocalScaling(const btVector3& scaling); + virtual const btVector3& getLocalScaling() const; + + + //debugging + virtual const char* getName()const {return "STATICPLANE";} + + +}; + +#endif //STATIC_PLANE_SHAPE_H diff --git a/bullet/src/BulletCollision/CollisionShapes/btStridingMeshInterface.cpp b/bullet/src/BulletCollision/CollisionShapes/btStridingMeshInterface.cpp new file mode 100644 index 0000000..779a4e2 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btStridingMeshInterface.cpp @@ -0,0 +1,125 @@ +/* +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. +*/ + +#include "btStridingMeshInterface.h" + +btStridingMeshInterface::~btStridingMeshInterface() +{ + +} + + +void btStridingMeshInterface::InternalProcessAllTriangles(btInternalTriangleIndexCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const +{ + (void)aabbMin; + (void)aabbMax; + int numtotalphysicsverts = 0; + int part,graphicssubparts = getNumSubParts(); + const unsigned char * vertexbase; + const unsigned char * indexbase; + int indexstride; + PHY_ScalarType type; + PHY_ScalarType gfxindextype; + int stride,numverts,numtriangles; + int gfxindex; + btVector3 triangle[3]; + btScalar* graphicsbase; + + btVector3 meshScaling = getScaling(); + + ///if the number of parts is big, the performance might drop due to the innerloop switch on indextype + for (part=0;partinternalProcessTriangleIndex(triangle,part,gfxindex); + } + break; + } + case PHY_SHORT: + { + for (gfxindex=0;gfxindexinternalProcessTriangleIndex(triangle,part,gfxindex); + } + break; + } + default: + btAssert((gfxindextype == PHY_INTEGER) || (gfxindextype == PHY_SHORT)); + } + + unLockReadOnlyVertexBase(part); + } +} + +void btStridingMeshInterface::calculateAabbBruteForce(btVector3& aabbMin,btVector3& aabbMax) +{ + + struct AabbCalculationCallback : public btInternalTriangleIndexCallback + { + btVector3 m_aabbMin; + btVector3 m_aabbMax; + + AabbCalculationCallback() + { + m_aabbMin.setValue(btScalar(1e30),btScalar(1e30),btScalar(1e30)); + m_aabbMax.setValue(btScalar(-1e30),btScalar(-1e30),btScalar(-1e30)); + } + + virtual void internalProcessTriangleIndex(btVector3* triangle,int partId,int triangleIndex) + { + (void)partId; + (void)triangleIndex; + + m_aabbMin.setMin(triangle[0]); + m_aabbMax.setMax(triangle[0]); + m_aabbMin.setMin(triangle[1]); + m_aabbMax.setMax(triangle[1]); + m_aabbMin.setMin(triangle[2]); + m_aabbMax.setMax(triangle[2]); + } + }; + + //first calculate the total aabb for all triangles + AabbCalculationCallback aabbCallback; + aabbMin.setValue(btScalar(-1e30),btScalar(-1e30),btScalar(-1e30)); + aabbMax.setValue(btScalar(1e30),btScalar(1e30),btScalar(1e30)); + InternalProcessAllTriangles(&aabbCallback,aabbMin,aabbMax); + + aabbMin = aabbCallback.m_aabbMin; + aabbMax = aabbCallback.m_aabbMax; +} + diff --git a/bullet/src/BulletCollision/CollisionShapes/btStridingMeshInterface.h b/bullet/src/BulletCollision/CollisionShapes/btStridingMeshInterface.h new file mode 100644 index 0000000..33eec71 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btStridingMeshInterface.h @@ -0,0 +1,89 @@ +/* +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. +*/ + +#ifndef STRIDING_MESHINTERFACE_H +#define STRIDING_MESHINTERFACE_H + +#include "LinearMath/btVector3.h" +#include "btTriangleCallback.h" + +/// PHY_ScalarType enumerates possible scalar types. +/// See the btStridingMeshInterface for its use +typedef enum PHY_ScalarType { + PHY_FLOAT, + PHY_DOUBLE, + PHY_INTEGER, + PHY_SHORT, + PHY_FIXEDPOINT88 +} PHY_ScalarType; + +/// btStridingMeshInterface is the interface class for high performance access to triangle meshes +/// It allows for sharing graphics and collision meshes. Also it provides locking/unlocking of graphics meshes that are in gpu memory. +class btStridingMeshInterface +{ + protected: + + btVector3 m_scaling; + + public: + btStridingMeshInterface() :m_scaling(btScalar(1.),btScalar(1.),btScalar(1.)) + { + + } + + virtual ~btStridingMeshInterface(); + + + + void InternalProcessAllTriangles(btInternalTriangleIndexCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const; + + ///brute force method to calculate aabb + void calculateAabbBruteForce(btVector3& aabbMin,btVector3& aabbMax); + + /// get read and write access to a subpart of a triangle mesh + /// this subpart has a continuous array of vertices and indices + /// in this way the mesh can be handled as chunks of memory with striding + /// very similar to OpenGL vertexarray support + /// make a call to unLockVertexBase when the read and write access is finished + virtual void getLockedVertexIndexBase(unsigned char **vertexbase, int& numverts,PHY_ScalarType& type, int& stride,unsigned char **indexbase,int & indexstride,int& numfaces,PHY_ScalarType& indicestype,int subpart=0)=0; + + virtual void getLockedReadOnlyVertexIndexBase(const unsigned char **vertexbase, int& numverts,PHY_ScalarType& type, int& stride,const unsigned char **indexbase,int & indexstride,int& numfaces,PHY_ScalarType& indicestype,int subpart=0) const=0; + + /// unLockVertexBase finishes the access to a subpart of the triangle mesh + /// make a call to unLockVertexBase when the read and write access (using getLockedVertexIndexBase) is finished + virtual void unLockVertexBase(int subpart)=0; + + virtual void unLockReadOnlyVertexBase(int subpart) const=0; + + + /// getNumSubParts returns the number of seperate subparts + /// each subpart has a continuous array of vertices and indices + virtual int getNumSubParts() const=0; + + virtual void preallocateVertices(int numverts)=0; + virtual void preallocateIndices(int numindices)=0; + + const btVector3& getScaling() const { + return m_scaling; + } + void setScaling(const btVector3& scaling) + { + m_scaling = scaling; + } + + +}; + +#endif //STRIDING_MESHINTERFACE_H diff --git a/bullet/src/BulletCollision/CollisionShapes/btTetrahedronShape.cpp b/bullet/src/BulletCollision/CollisionShapes/btTetrahedronShape.cpp new file mode 100644 index 0000000..c1dcb3b --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btTetrahedronShape.cpp @@ -0,0 +1,195 @@ + +/* +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. +*/ +#include "btTetrahedronShape.h" +#include "LinearMath/btMatrix3x3.h" + +btBU_Simplex1to4::btBU_Simplex1to4() +:m_numVertices(0) +{ +} + +btBU_Simplex1to4::btBU_Simplex1to4(const btPoint3& pt0) +:m_numVertices(0) +{ + addVertex(pt0); +} + +btBU_Simplex1to4::btBU_Simplex1to4(const btPoint3& pt0,const btPoint3& pt1) +:m_numVertices(0) +{ + addVertex(pt0); + addVertex(pt1); +} + +btBU_Simplex1to4::btBU_Simplex1to4(const btPoint3& pt0,const btPoint3& pt1,const btPoint3& pt2) +:m_numVertices(0) +{ + addVertex(pt0); + addVertex(pt1); + addVertex(pt2); +} + +btBU_Simplex1to4::btBU_Simplex1to4(const btPoint3& pt0,const btPoint3& pt1,const btPoint3& pt2,const btPoint3& pt3) +:m_numVertices(0) +{ + addVertex(pt0); + addVertex(pt1); + addVertex(pt2); + addVertex(pt3); +} + + + + + +void btBU_Simplex1to4::addVertex(const btPoint3& pt) +{ + m_vertices[m_numVertices++] = pt; + + recalcLocalAabb(); +} + + +int btBU_Simplex1to4::getNumVertices() const +{ + return m_numVertices; +} + +int btBU_Simplex1to4::getNumEdges() const +{ + //euler formula, F-E+V = 2, so E = F+V-2 + + switch (m_numVertices) + { + case 0: + return 0; + case 1: return 0; + case 2: return 1; + case 3: return 3; + case 4: return 6; + + + } + + return 0; +} + +void btBU_Simplex1to4::getEdge(int i,btPoint3& pa,btPoint3& pb) const +{ + + switch (m_numVertices) + { + + case 2: + pa = m_vertices[0]; + pb = m_vertices[1]; + break; + case 3: + switch (i) + { + case 0: + pa = m_vertices[0]; + pb = m_vertices[1]; + break; + case 1: + pa = m_vertices[1]; + pb = m_vertices[2]; + break; + case 2: + pa = m_vertices[2]; + pb = m_vertices[0]; + break; + + } + break; + case 4: + switch (i) + { + case 0: + pa = m_vertices[0]; + pb = m_vertices[1]; + break; + case 1: + pa = m_vertices[1]; + pb = m_vertices[2]; + break; + case 2: + pa = m_vertices[2]; + pb = m_vertices[0]; + break; + case 3: + pa = m_vertices[0]; + pb = m_vertices[3]; + break; + case 4: + pa = m_vertices[1]; + pb = m_vertices[3]; + break; + case 5: + pa = m_vertices[2]; + pb = m_vertices[3]; + break; + } + + } + + + + +} + +void btBU_Simplex1to4::getVertex(int i,btPoint3& vtx) const +{ + vtx = m_vertices[i]; +} + +int btBU_Simplex1to4::getNumPlanes() const +{ + switch (m_numVertices) + { + case 0: + return 0; + case 1: + return 0; + case 2: + return 0; + case 3: + return 2; + case 4: + return 4; + default: + { + } + } + return 0; +} + + +void btBU_Simplex1to4::getPlane(btVector3&, btPoint3& ,int ) const +{ + +} + +int btBU_Simplex1to4::getIndex(int ) const +{ + return 0; +} + +bool btBU_Simplex1to4::isInside(const btPoint3& ,btScalar ) const +{ + return false; +} + diff --git a/bullet/src/BulletCollision/CollisionShapes/btTetrahedronShape.h b/bullet/src/BulletCollision/CollisionShapes/btTetrahedronShape.h new file mode 100644 index 0000000..2eae5ee --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btTetrahedronShape.h @@ -0,0 +1,75 @@ +/* +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. +*/ + +#ifndef BU_SIMPLEX_1TO4_SHAPE +#define BU_SIMPLEX_1TO4_SHAPE + + +#include "btPolyhedralConvexShape.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" + + +///BU_Simplex1to4 implements feature based and implicit simplex of up to 4 vertices (tetrahedron, triangle, line, vertex). +class btBU_Simplex1to4 : public btPolyhedralConvexShape +{ +protected: + + int m_numVertices; + btPoint3 m_vertices[4]; + +public: + btBU_Simplex1to4(); + + btBU_Simplex1to4(const btPoint3& pt0); + btBU_Simplex1to4(const btPoint3& pt0,const btPoint3& pt1); + btBU_Simplex1to4(const btPoint3& pt0,const btPoint3& pt1,const btPoint3& pt2); + btBU_Simplex1to4(const btPoint3& pt0,const btPoint3& pt1,const btPoint3& pt2,const btPoint3& pt3); + + + void reset() + { + m_numVertices = 0; + } + + + virtual int getShapeType() const{ return TETRAHEDRAL_SHAPE_PROXYTYPE; } + + void addVertex(const btPoint3& pt); + + //PolyhedralConvexShape interface + + virtual int getNumVertices() const; + + virtual int getNumEdges() const; + + virtual void getEdge(int i,btPoint3& pa,btPoint3& pb) const; + + virtual void getVertex(int i,btPoint3& vtx) const; + + virtual int getNumPlanes() const; + + virtual void getPlane(btVector3& planeNormal,btPoint3& planeSupport,int i) const; + + virtual int getIndex(int i) const; + + virtual bool isInside(const btPoint3& pt,btScalar tolerance) const; + + + ///getName is for debugging + virtual const char* getName()const { return "btBU_Simplex1to4";} + +}; + +#endif //BU_SIMPLEX_1TO4_SHAPE diff --git a/bullet/src/BulletCollision/CollisionShapes/btTriangleBuffer.cpp b/bullet/src/BulletCollision/CollisionShapes/btTriangleBuffer.cpp new file mode 100644 index 0000000..21374fd --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btTriangleBuffer.cpp @@ -0,0 +1,42 @@ +/* +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. +*/ + +#include "btTriangleBuffer.h" + + +///example usage of this class: +// btTriangleBuffer triBuf; +// concaveShape->processAllTriangles(&triBuf,aabbMin, aabbMax); +// for (int i=0;i m_triangleBuffer; + +public: + + + virtual void processTriangle(btVector3* triangle, int partId, int triangleIndex); + + int getNumTriangles() const + { + return int(m_triangleBuffer.size()); + } + + const btTriangle& getTriangle(int index) const + { + return m_triangleBuffer[index]; + } + + void clearBuffer() + { + m_triangleBuffer.clear(); + } + +}; + + +#endif //BT_TRIANGLE_BUFFER_H + diff --git a/bullet/src/BulletCollision/CollisionShapes/btTriangleCallback.cpp b/bullet/src/BulletCollision/CollisionShapes/btTriangleCallback.cpp new file mode 100644 index 0000000..07624fa --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btTriangleCallback.cpp @@ -0,0 +1,28 @@ +/* +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. +*/ + +#include "btTriangleCallback.h" + +btTriangleCallback::~btTriangleCallback() +{ + +} + + +btInternalTriangleIndexCallback::~btInternalTriangleIndexCallback() +{ + +} + diff --git a/bullet/src/BulletCollision/CollisionShapes/btTriangleCallback.h b/bullet/src/BulletCollision/CollisionShapes/btTriangleCallback.h new file mode 100644 index 0000000..8652add --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btTriangleCallback.h @@ -0,0 +1,40 @@ +/* +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. +*/ + +#ifndef TRIANGLE_CALLBACK_H +#define TRIANGLE_CALLBACK_H + +#include "LinearMath/btVector3.h" + + +class btTriangleCallback +{ +public: + + virtual ~btTriangleCallback(); + virtual void processTriangle(btVector3* triangle, int partId, int triangleIndex) = 0; +}; + +class btInternalTriangleIndexCallback +{ +public: + + virtual ~btInternalTriangleIndexCallback(); + virtual void internalProcessTriangleIndex(btVector3* triangle,int partId,int triangleIndex) = 0; +}; + + + +#endif //TRIANGLE_CALLBACK_H diff --git a/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.cpp b/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.cpp new file mode 100644 index 0000000..435abd6 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.cpp @@ -0,0 +1,65 @@ +/* +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. +*/ + +#include "btTriangleIndexVertexArray.h" + +btTriangleIndexVertexArray::btTriangleIndexVertexArray(int numTriangles,int* triangleIndexBase,int triangleIndexStride,int numVertices,btScalar* vertexBase,int vertexStride) +{ + btIndexedMesh mesh; + + mesh.m_numTriangles = numTriangles; + mesh.m_triangleIndexBase = (const unsigned char *)triangleIndexBase; + mesh.m_triangleIndexStride = triangleIndexStride; + mesh.m_numVertices = numVertices; + mesh.m_vertexBase = (const unsigned char *)vertexBase; + mesh.m_vertexStride = vertexStride; + + addIndexedMesh(mesh); + +} + +void btTriangleIndexVertexArray::getLockedVertexIndexBase(unsigned char **vertexbase, int& numverts,PHY_ScalarType& type, int& vertexStride,unsigned char **indexbase,int & indexstride,int& numfaces,PHY_ScalarType& indicestype,int subpart) +{ + btAssert(subpart< getNumSubParts() ); + + btIndexedMesh& mesh = m_indexedMeshes[subpart]; + + numverts = mesh.m_numVertices; + (*vertexbase) = (unsigned char *) mesh.m_vertexBase; + type = PHY_FLOAT; + vertexStride = mesh.m_vertexStride; + + numfaces = mesh.m_numTriangles; + + (*indexbase) = (unsigned char *)mesh.m_triangleIndexBase; + indexstride = mesh.m_triangleIndexStride; + indicestype = PHY_INTEGER; +} + +void btTriangleIndexVertexArray::getLockedReadOnlyVertexIndexBase(const unsigned char **vertexbase, int& numverts,PHY_ScalarType& type, int& vertexStride,const unsigned char **indexbase,int & indexstride,int& numfaces,PHY_ScalarType& indicestype,int subpart) const +{ + const btIndexedMesh& mesh = m_indexedMeshes[subpart]; + + numverts = mesh.m_numVertices; + (*vertexbase) = (const unsigned char *)mesh.m_vertexBase; + type = PHY_FLOAT; + vertexStride = mesh.m_vertexStride; + + numfaces = mesh.m_numTriangles; + (*indexbase) = (const unsigned char *)mesh.m_triangleIndexBase; + indexstride = mesh.m_triangleIndexStride; + indicestype = PHY_INTEGER; +} + diff --git a/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h b/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h new file mode 100644 index 0000000..7b70900 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h @@ -0,0 +1,97 @@ +/* +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. +*/ + +#ifndef BT_TRIANGLE_INDEX_VERTEX_ARRAY_H +#define BT_TRIANGLE_INDEX_VERTEX_ARRAY_H + +#include "btStridingMeshInterface.h" +#include "LinearMath/btAlignedObjectArray.h" + +///IndexedMesh indexes into existing vertex and index arrays, in a similar way OpenGL glDrawElements +///instead of the number of indices, we pass the number of triangles +///todo: explain with pictures +ATTRIBUTE_ALIGNED16( struct) btIndexedMesh +{ + int m_numTriangles; + const unsigned char * m_triangleIndexBase; + int m_triangleIndexStride; + int m_numVertices; + const unsigned char * m_vertexBase; + int m_vertexStride; + int pad[2]; +} +; + + +typedef btAlignedObjectArray IndexedMeshArray; + +///TriangleIndexVertexArray allows to use multiple meshes, by indexing into existing triangle/index arrays. +///Additional meshes can be added using addIndexedMesh +///No duplcate is made of the vertex/index data, it only indexes into external vertex/index arrays. +///So keep those arrays around during the lifetime of this btTriangleIndexVertexArray. +ATTRIBUTE_ALIGNED16( class) btTriangleIndexVertexArray : public btStridingMeshInterface +{ + IndexedMeshArray m_indexedMeshes; + int m_pad[3]; + + +public: + + btTriangleIndexVertexArray() + { + } + + //just to be backwards compatible + btTriangleIndexVertexArray(int numTriangleIndices,int* triangleIndexBase,int triangleIndexStride,int numVertices,btScalar* vertexBase,int vertexStride); + + void addIndexedMesh(const btIndexedMesh& mesh) + { + m_indexedMeshes.push_back(mesh); + } + + + virtual void getLockedVertexIndexBase(unsigned char **vertexbase, int& numverts,PHY_ScalarType& type, int& vertexStride,unsigned char **indexbase,int & indexstride,int& numfaces,PHY_ScalarType& indicestype,int subpart=0); + + virtual void getLockedReadOnlyVertexIndexBase(const unsigned char **vertexbase, int& numverts,PHY_ScalarType& type, int& vertexStride,const unsigned char **indexbase,int & indexstride,int& numfaces,PHY_ScalarType& indicestype,int subpart=0) const; + + /// unLockVertexBase finishes the access to a subpart of the triangle mesh + /// make a call to unLockVertexBase when the read and write access (using getLockedVertexIndexBase) is finished + virtual void unLockVertexBase(int subpart) {(void)subpart;} + + virtual void unLockReadOnlyVertexBase(int subpart) const {(void)subpart;} + + /// getNumSubParts returns the number of seperate subparts + /// each subpart has a continuous array of vertices and indices + virtual int getNumSubParts() const { + return (int)m_indexedMeshes.size(); + } + + IndexedMeshArray& getIndexedMeshArray() + { + return m_indexedMeshes; + } + + const IndexedMeshArray& getIndexedMeshArray() const + { + return m_indexedMeshes; + } + + virtual void preallocateVertices(int numverts){(void) numverts;} + virtual void preallocateIndices(int numindices){(void) numindices;} + +} +; + +#endif //BT_TRIANGLE_INDEX_VERTEX_ARRAY_H diff --git a/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.cpp b/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.cpp new file mode 100644 index 0000000..6746778 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.cpp @@ -0,0 +1,60 @@ +/* +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. +*/ + +#include "btTriangleMesh.h" +#include + + +btTriangleMesh::btTriangleMesh () +{ + +} + +void btTriangleMesh::getLockedVertexIndexBase(unsigned char **vertexbase, int& numverts,PHY_ScalarType& type, int& stride,unsigned char **indexbase,int & indexstride,int& numfaces,PHY_ScalarType& indicestype,int subpart) +{ + (void)subpart; + numverts = m_vertices.size(); + *vertexbase = (unsigned char*)&m_vertices[0]; + type = PHY_FLOAT; + stride = sizeof(btVector3); + + numfaces = m_indices.size()/3; + *indexbase = (unsigned char*) &m_indices[0]; + indicestype = PHY_INTEGER; + indexstride = 3*sizeof(int); + +} + +void btTriangleMesh::getLockedReadOnlyVertexIndexBase(const unsigned char **vertexbase, int& numverts,PHY_ScalarType& type, int& stride,const unsigned char **indexbase,int & indexstride,int& numfaces,PHY_ScalarType& indicestype,int subpart) const +{ + (void)subpart; + numverts = m_vertices.size(); + *vertexbase = (unsigned char*)&m_vertices[0]; + type = PHY_FLOAT; + stride = sizeof(btVector3); + + numfaces = m_indices.size()/3; + *indexbase = (unsigned char*) &m_indices[0]; + indicestype = PHY_INTEGER; + indexstride = 3*sizeof(int); + +} + + + +int btTriangleMesh::getNumSubParts() const +{ + return 1; +} diff --git a/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.h b/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.h new file mode 100644 index 0000000..4037711 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.h @@ -0,0 +1,75 @@ +/* +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. +*/ + + +#ifndef TRIANGLE_MESH_H +#define TRIANGLE_MESH_H + +#include "btStridingMeshInterface.h" +#include "LinearMath/btVector3.h" +#include "LinearMath/btAlignedObjectArray.h" + +///TriangleMesh provides storage for a concave triangle mesh. It can be used as data for the btTriangleMeshShape. +class btTriangleMesh : public btStridingMeshInterface +{ + btAlignedObjectArray m_vertices; + btAlignedObjectArray m_indices; + + public: + btTriangleMesh (); + + void addTriangle(const btVector3& vertex0,const btVector3& vertex1,const btVector3& vertex2) + { + int curIndex = m_indices.size(); + m_vertices.push_back(vertex0); + m_vertices.push_back(vertex1); + m_vertices.push_back(vertex2); + + m_indices.push_back(curIndex++); + m_indices.push_back(curIndex++); + m_indices.push_back(curIndex++); + } + + int getNumTriangles() const + { + return m_indices.size() / 3; + } + + + +//StridingMeshInterface interface implementation + + virtual void getLockedVertexIndexBase(unsigned char **vertexbase, int& numverts,PHY_ScalarType& type, int& stride,unsigned char **indexbase,int & indexstride,int& numfaces,PHY_ScalarType& indicestype,int subpart=0); + + virtual void getLockedReadOnlyVertexIndexBase(const unsigned char **vertexbase, int& numverts,PHY_ScalarType& type, int& stride,const unsigned char **indexbase,int & indexstride,int& numfaces,PHY_ScalarType& indicestype,int subpart=0) const; + + /// unLockVertexBase finishes the access to a subpart of the triangle mesh + /// make a call to unLockVertexBase when the read and write access (using getLockedVertexIndexBase) is finished + virtual void unLockVertexBase(int subpart) {(void) subpart;} + + virtual void unLockReadOnlyVertexBase(int subpart) const { (void) subpart;} + + /// getNumSubParts returns the number of seperate subparts + /// each subpart has a continuous array of vertices and indices + virtual int getNumSubParts() const; + + virtual void preallocateVertices(int numverts){(void) numverts;} + virtual void preallocateIndices(int numindices){(void) numindices;} + + +}; + +#endif //TRIANGLE_MESH_H + diff --git a/bullet/src/BulletCollision/CollisionShapes/btTriangleMeshShape.cpp b/bullet/src/BulletCollision/CollisionShapes/btTriangleMeshShape.cpp new file mode 100644 index 0000000..9bdccbe --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btTriangleMeshShape.cpp @@ -0,0 +1,203 @@ +/* +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. +*/ + +#include "btTriangleMeshShape.h" +#include "LinearMath/btVector3.h" +#include "LinearMath/btQuaternion.h" +#include "btStridingMeshInterface.h" +#include "LinearMath/btAabbUtil2.h" +#include "BulletCollision/CollisionShapes/btCollisionMargin.h" + +#include "stdio.h" + +btTriangleMeshShape::btTriangleMeshShape(btStridingMeshInterface* meshInterface) +: m_meshInterface(meshInterface) +{ + recalcLocalAabb(); +} + + +btTriangleMeshShape::~btTriangleMeshShape() +{ + +} + + + + +void btTriangleMeshShape::getAabb(const btTransform& trans,btVector3& aabbMin,btVector3& aabbMax) const +{ + + btVector3 localHalfExtents = btScalar(0.5)*(m_localAabbMax-m_localAabbMin); + btVector3 localCenter = btScalar(0.5)*(m_localAabbMax+m_localAabbMin); + + btMatrix3x3 abs_b = trans.getBasis().absolute(); + + btPoint3 center = trans(localCenter); + + btVector3 extent = btVector3(abs_b[0].dot(localHalfExtents), + abs_b[1].dot(localHalfExtents), + abs_b[2].dot(localHalfExtents)); + extent += btVector3(getMargin(),getMargin(),getMargin()); + + aabbMin = center - extent; + aabbMax = center + extent; + + +} + +void btTriangleMeshShape::recalcLocalAabb() +{ + for (int i=0;i<3;i++) + { + btVector3 vec(btScalar(0.),btScalar(0.),btScalar(0.)); + vec[i] = btScalar(1.); + btVector3 tmp = localGetSupportingVertex(vec); + m_localAabbMax[i] = tmp[i]+m_collisionMargin; + vec[i] = btScalar(-1.); + tmp = localGetSupportingVertex(vec); + m_localAabbMin[i] = tmp[i]-m_collisionMargin; + } +} + + + +class SupportVertexCallback : public btTriangleCallback +{ + + btVector3 m_supportVertexLocal; +public: + + btTransform m_worldTrans; + btScalar m_maxDot; + btVector3 m_supportVecLocal; + + SupportVertexCallback(const btVector3& supportVecWorld,const btTransform& trans) + : m_supportVertexLocal(btScalar(0.),btScalar(0.),btScalar(0.)), m_worldTrans(trans) ,m_maxDot(btScalar(-1e30)) + + { + m_supportVecLocal = supportVecWorld * m_worldTrans.getBasis(); + } + + virtual void processTriangle( btVector3* triangle,int partId, int triangleIndex) + { + (void)partId; + (void)triangleIndex; + for (int i=0;i<3;i++) + { + btScalar dot = m_supportVecLocal.dot(triangle[i]); + if (dot > m_maxDot) + { + m_maxDot = dot; + m_supportVertexLocal = triangle[i]; + } + } + } + + btVector3 GetSupportVertexWorldSpace() + { + return m_worldTrans(m_supportVertexLocal); + } + + btVector3 GetSupportVertexLocal() + { + return m_supportVertexLocal; + } + +}; + + +void btTriangleMeshShape::setLocalScaling(const btVector3& scaling) +{ + m_meshInterface->setScaling(scaling); + recalcLocalAabb(); +} + +const btVector3& btTriangleMeshShape::getLocalScaling() const +{ + return m_meshInterface->getScaling(); +} + + + + + + +//#define DEBUG_TRIANGLE_MESH + + +void btTriangleMeshShape::processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const +{ + struct FilteredCallback : public btInternalTriangleIndexCallback + { + btTriangleCallback* m_callback; + btVector3 m_aabbMin; + btVector3 m_aabbMax; + + FilteredCallback(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) + :m_callback(callback), + m_aabbMin(aabbMin), + m_aabbMax(aabbMax) + { + } + + virtual void internalProcessTriangleIndex(btVector3* triangle,int partId,int triangleIndex) + { + if (TestTriangleAgainstAabb2(&triangle[0],m_aabbMin,m_aabbMax)) + { + //check aabb in triangle-space, before doing this + m_callback->processTriangle(triangle,partId,triangleIndex); + } + + } + + }; + + FilteredCallback filterCallback(callback,aabbMin,aabbMax); + + m_meshInterface->InternalProcessAllTriangles(&filterCallback,aabbMin,aabbMax); +} + + + + + + +void btTriangleMeshShape::calculateLocalInertia(btScalar mass,btVector3& inertia) +{ + (void)mass; + //moving concave objects not supported + btAssert(0); + inertia.setValue(btScalar(0.),btScalar(0.),btScalar(0.)); +} + + +btVector3 btTriangleMeshShape::localGetSupportingVertex(const btVector3& vec) const +{ + btVector3 supportVertex; + + btTransform ident; + ident.setIdentity(); + + SupportVertexCallback supportCallback(vec,ident); + + btVector3 aabbMax(btScalar(1e30),btScalar(1e30),btScalar(1e30)); + + processAllTriangles(&supportCallback,-aabbMax,aabbMax); + + supportVertex = supportCallback.GetSupportVertexLocal(); + + return supportVertex; +} diff --git a/bullet/src/BulletCollision/CollisionShapes/btTriangleMeshShape.h b/bullet/src/BulletCollision/CollisionShapes/btTriangleMeshShape.h new file mode 100644 index 0000000..eb00a56 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btTriangleMeshShape.h @@ -0,0 +1,78 @@ +/* +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. +*/ + +#ifndef TRIANGLE_MESH_SHAPE_H +#define TRIANGLE_MESH_SHAPE_H + +#include "btConcaveShape.h" +#include "btStridingMeshInterface.h" + + +///Concave triangle mesh. Uses an interface to access the triangles to allow for sharing graphics/physics triangles. +class btTriangleMeshShape : public btConcaveShape +{ +protected: + btVector3 m_localAabbMin; + btVector3 m_localAabbMax; + btStridingMeshInterface* m_meshInterface; + + +public: + btTriangleMeshShape(btStridingMeshInterface* meshInterface); + + virtual ~btTriangleMeshShape(); + + virtual btVector3 localGetSupportingVertex(const btVector3& vec) const; + + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const + { + assert(0); + return localGetSupportingVertex(vec); + } + + void recalcLocalAabb(); + + virtual int getShapeType() const + { + return TRIANGLE_MESH_SHAPE_PROXYTYPE; + } + + virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const; + + virtual void processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const; + + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia); + + virtual void setLocalScaling(const btVector3& scaling); + virtual const btVector3& getLocalScaling() const; + + btStridingMeshInterface* getMeshInterface() + { + return m_meshInterface; + } + + const btStridingMeshInterface* getMeshInterface() const + { + return m_meshInterface; + } + + + //debugging + virtual const char* getName()const {return "TRIANGLEMESH";} + + +}; + +#endif //TRIANGLE_MESH_SHAPE_H diff --git a/bullet/src/BulletCollision/CollisionShapes/btTriangleShape.h b/bullet/src/BulletCollision/CollisionShapes/btTriangleShape.h new file mode 100644 index 0000000..6ec3ab3 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btTriangleShape.h @@ -0,0 +1,179 @@ +/* +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. +*/ + +#ifndef OBB_TRIANGLE_MINKOWSKI_H +#define OBB_TRIANGLE_MINKOWSKI_H + +#include "btConvexShape.h" +#include "btBoxShape.h" + +class btTriangleShape : public btPolyhedralConvexShape +{ + + +public: + + btVector3 m_vertices1[3]; + + + virtual int getNumVertices() const + { + return 3; + } + + const btVector3& getVertexPtr(int index) const + { + return m_vertices1[index]; + } + virtual void getVertex(int index,btVector3& vert) const + { + vert = m_vertices1[index]; + } + virtual int getShapeType() const + { + return TRIANGLE_SHAPE_PROXYTYPE; + } + + virtual int getNumEdges() const + { + return 3; + } + + virtual void getEdge(int i,btPoint3& pa,btPoint3& pb) const + { + getVertex(i,pa); + getVertex((i+1)%3,pb); + } + + + virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax)const + { +// btAssert(0); + getAabbSlow(t,aabbMin,aabbMax); + } + + btVector3 localGetSupportingVertexWithoutMargin(const btVector3& dir)const + { + btVector3 dots(dir.dot(m_vertices1[0]), dir.dot(m_vertices1[1]), dir.dot(m_vertices1[2])); + return m_vertices1[dots.maxAxis()]; + + } + + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const + { + for (int i=0;i= -tolerance && dist <= tolerance) + { + //inside check on edge-planes + int i; + for (i=0;i<3;i++) + { + btPoint3 pa,pb; + getEdge(i,pa,pb); + btVector3 edge = pb-pa; + btVector3 edgeNormal = edge.cross(normal); + edgeNormal.normalize(); + btScalar dist = pt.dot( edgeNormal); + btScalar edgeConst = pa.dot(edgeNormal); + dist -= edgeConst; + if (dist < -tolerance) + return false; + } + + return true; + } + + return false; + } + //debugging + virtual const char* getName()const + { + return "Triangle"; + } + + virtual int getNumPreferredPenetrationDirections() const + { + return 2; + } + + virtual void getPreferredPenetrationDirection(int index, btVector3& penetrationVector) const + { + calcNormal(penetrationVector); + if (index) + penetrationVector *= btScalar(-1.); + } + + +}; + +#endif //OBB_TRIANGLE_MINKOWSKI_H + diff --git a/bullet/src/BulletCollision/CollisionShapes/btUniformScalingShape.cpp b/bullet/src/BulletCollision/CollisionShapes/btUniformScalingShape.cpp new file mode 100644 index 0000000..c7cbbb8 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btUniformScalingShape.cpp @@ -0,0 +1,114 @@ +/* +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 "btUniformScalingShape.h" + +btUniformScalingShape::btUniformScalingShape( btConvexShape* convexChildShape,btScalar uniformScalingFactor): +m_childConvexShape(convexChildShape), +m_uniformScalingFactor(uniformScalingFactor) +{ +} + +btUniformScalingShape::~btUniformScalingShape() +{ +} + + +btVector3 btUniformScalingShape::localGetSupportingVertexWithoutMargin(const btVector3& vec)const +{ + btVector3 tmpVertex; + tmpVertex = m_childConvexShape->localGetSupportingVertexWithoutMargin(vec); + return tmpVertex*m_uniformScalingFactor; +} + +void btUniformScalingShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ + m_childConvexShape->batchedUnitVectorGetSupportingVertexWithoutMargin(vectors,supportVerticesOut,numVectors); + int i; + for (i=0;ilocalGetSupportingVertex(vec); + return tmpVertex*m_uniformScalingFactor; +} + + +void btUniformScalingShape::calculateLocalInertia(btScalar mass,btVector3& inertia) +{ + + ///this linear upscaling is not realistic, but we don't deal with large mass ratios... + btVector3 tmpInertia; + m_childConvexShape->calculateLocalInertia(mass,tmpInertia); + inertia = tmpInertia * m_uniformScalingFactor; +} + + + ///getAabb's default implementation is brute force, expected derived classes to implement a fast dedicated version +void btUniformScalingShape::getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const +{ + m_childConvexShape->getAabb(t,aabbMin,aabbMax); + btVector3 aabbCenter = (aabbMax+aabbMin)*btScalar(0.5); + btVector3 scaledAabbHalfExtends = (aabbMax-aabbMin)*btScalar(0.5)*m_uniformScalingFactor; + + aabbMin = aabbCenter - scaledAabbHalfExtends; + aabbMax = aabbCenter + scaledAabbHalfExtends; + +} + +void btUniformScalingShape::getAabbSlow(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const +{ + m_childConvexShape->getAabbSlow(t,aabbMin,aabbMax); + btVector3 aabbCenter = (aabbMax+aabbMin)*btScalar(0.5); + btVector3 scaledAabbHalfExtends = (aabbMax-aabbMin)*btScalar(0.5)*m_uniformScalingFactor; + + aabbMin = aabbCenter - scaledAabbHalfExtends; + aabbMax = aabbCenter + scaledAabbHalfExtends; +} + +void btUniformScalingShape::setLocalScaling(const btVector3& scaling) +{ + m_childConvexShape->setLocalScaling(scaling); +} + +const btVector3& btUniformScalingShape::getLocalScaling() const +{ + return m_childConvexShape->getLocalScaling(); +} + +void btUniformScalingShape::setMargin(btScalar margin) +{ + m_childConvexShape->setMargin(margin); +} +btScalar btUniformScalingShape::getMargin() const +{ + return m_childConvexShape->getMargin() * m_uniformScalingFactor; +} + +int btUniformScalingShape::getNumPreferredPenetrationDirections() const +{ + return m_childConvexShape->getNumPreferredPenetrationDirections(); +} + +void btUniformScalingShape::getPreferredPenetrationDirection(int index, btVector3& penetrationVector) const +{ + return m_childConvexShape->getPreferredPenetrationDirection(index,penetrationVector); +} diff --git a/bullet/src/BulletCollision/CollisionShapes/btUniformScalingShape.h b/bullet/src/BulletCollision/CollisionShapes/btUniformScalingShape.h new file mode 100644 index 0000000..6c4b799 --- /dev/null +++ b/bullet/src/BulletCollision/CollisionShapes/btUniformScalingShape.h @@ -0,0 +1,86 @@ +/* +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. +*/ + +#ifndef BT_UNIFORM_SCALING_SHAPE_H +#define BT_UNIFORM_SCALING_SHAPE_H + +#include "btConvexShape.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" // for the types + +class btUniformScalingShape : public btConvexShape +{ + btConvexShape* m_childConvexShape; + + btScalar m_uniformScalingFactor; + + public: + + btUniformScalingShape( btConvexShape* convexChildShape, btScalar uniformScalingFactor); + + virtual ~btUniformScalingShape(); + + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const; + + virtual btVector3 localGetSupportingVertex(const btVector3& vec)const; + + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const; + + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia); + + btScalar getUniformScalingFactor() const + { + return m_uniformScalingFactor; + } + + btConvexShape* getChildShape() + { + return m_childConvexShape; + } + + const btConvexShape* getChildShape() const + { + return m_childConvexShape; + } + + virtual const char* getName()const + { + return "UniformScalingShape"; + } + + virtual int getShapeType() const { return UNIFORM_SCALING_SHAPE_PROXYTYPE; } + + + /////////////////////////// + + + ///getAabb's default implementation is brute force, expected derived classes to implement a fast dedicated version + void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const; + + virtual void getAabbSlow(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const; + + virtual void setLocalScaling(const btVector3& scaling) ; + virtual const btVector3& getLocalScaling() const ; + + virtual void setMargin(btScalar margin); + virtual btScalar getMargin() const; + + virtual int getNumPreferredPenetrationDirections() const; + + virtual void getPreferredPenetrationDirection(int index, btVector3& penetrationVector) const; + + +}; + +#endif //BT_UNIFORM_SCALING_SHAPE_H diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.cpp b/bullet/src/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.cpp new file mode 100644 index 0000000..ecfa444 --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.cpp @@ -0,0 +1,210 @@ +/* +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. +*/ + + +#include "btContinuousConvexCollision.h" +#include "BulletCollision/CollisionShapes/btConvexShape.h" +#include "BulletCollision/CollisionShapes/btMinkowskiSumShape.h" +#include "BulletCollision/NarrowPhaseCollision/btSimplexSolverInterface.h" +#include "LinearMath/btTransformUtil.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" + +#include "btGjkPairDetector.h" +#include "btPointCollector.h" + + + +btContinuousConvexCollision::btContinuousConvexCollision ( const btConvexShape* convexA,const btConvexShape* convexB,btSimplexSolverInterface* simplexSolver, btConvexPenetrationDepthSolver* penetrationDepthSolver) +:m_simplexSolver(simplexSolver), +m_penetrationDepthSolver(penetrationDepthSolver), +m_convexA(convexA),m_convexB(convexB) +{ +} + +/// This maximum should not be necessary. It allows for untested/degenerate cases in production code. +/// You don't want your game ever to lock-up. +#define MAX_ITERATIONS 1000 + +bool btContinuousConvexCollision::calcTimeOfImpact( + const btTransform& fromA, + const btTransform& toA, + const btTransform& fromB, + const btTransform& toB, + CastResult& result) +{ + + m_simplexSolver->reset(); + + /// compute linear and angular velocity for this interval, to interpolate + btVector3 linVelA,angVelA,linVelB,angVelB; + btTransformUtil::calculateVelocity(fromA,toA,btScalar(1.),linVelA,angVelA); + btTransformUtil::calculateVelocity(fromB,toB,btScalar(1.),linVelB,angVelB); + + btScalar boundingRadiusA = m_convexA->getAngularMotionDisc(); + btScalar boundingRadiusB = m_convexB->getAngularMotionDisc(); + + btScalar maxAngularProjectedVelocity = angVelA.length() * boundingRadiusA + angVelB.length() * boundingRadiusB; + + btScalar radius = btScalar(0.001); + + btScalar lambda = btScalar(0.); + btVector3 v(1,0,0); + + int maxIter = MAX_ITERATIONS; + + btVector3 n; + n.setValue(btScalar(0.),btScalar(0.),btScalar(0.)); + bool hasResult = false; + btVector3 c; + + btScalar lastLambda = lambda; + //btScalar epsilon = btScalar(0.001); + + int numIter = 0; + //first solution, using GJK + + + btTransform identityTrans; + identityTrans.setIdentity(); + + btSphereShape raySphere(btScalar(0.0)); + raySphere.setMargin(btScalar(0.)); + + +// result.drawCoordSystem(sphereTr); + + btPointCollector pointCollector1; + + { + + btGjkPairDetector gjk(m_convexA,m_convexB,m_simplexSolver,m_penetrationDepthSolver); + btGjkPairDetector::ClosestPointInput input; + + //we don't use margins during CCD + // gjk.setIgnoreMargin(true); + + input.m_transformA = fromA; + input.m_transformB = fromB; + gjk.getClosestPoints(input,pointCollector1,0); + + hasResult = pointCollector1.m_hasResult; + c = pointCollector1.m_pointInWorld; + } + + if (hasResult) + { + btScalar dist; + dist = pointCollector1.m_distance; + n = pointCollector1.m_normalOnBInWorld; + + + + //not close enough + while (dist > radius) + { + numIter++; + if (numIter > maxIter) + { + return false; //todo: report a failure + } + btScalar dLambda = btScalar(0.); + + btScalar projectedLinearVelocity = (linVelB-linVelA).dot(n); + + //calculate safe moving fraction from distance / (linear+rotational velocity) + + //btScalar clippedDist = GEN_min(angularConservativeRadius,dist); + //btScalar clippedDist = dist; + + + dLambda = dist / (projectedLinearVelocity+ maxAngularProjectedVelocity); + + lambda = lambda + dLambda; + + if (lambda > btScalar(1.)) + return false; + + if (lambda < btScalar(0.)) + return false; + + + //todo: next check with relative epsilon + if (lambda <= lastLambda) + { + return false; + //n.setValue(0,0,0); + break; + } + lastLambda = lambda; + + + + //interpolate to next lambda + btTransform interpolatedTransA,interpolatedTransB,relativeTrans; + + btTransformUtil::integrateTransform(fromA,linVelA,angVelA,lambda,interpolatedTransA); + btTransformUtil::integrateTransform(fromB,linVelB,angVelB,lambda,interpolatedTransB); + relativeTrans = interpolatedTransB.inverseTimes(interpolatedTransA); + + result.DebugDraw( lambda ); + + btPointCollector pointCollector; + btGjkPairDetector gjk(m_convexA,m_convexB,m_simplexSolver,m_penetrationDepthSolver); + btGjkPairDetector::ClosestPointInput input; + input.m_transformA = interpolatedTransA; + input.m_transformB = interpolatedTransB; + gjk.getClosestPoints(input,pointCollector,0); + if (pointCollector.m_hasResult) + { + if (pointCollector.m_distance < btScalar(0.)) + { + //degenerate ?! + result.m_fraction = lastLambda; + n = pointCollector.m_normalOnBInWorld; + result.m_normal=n;//.setValue(1,1,1);// = n; + return true; + } + c = pointCollector.m_pointInWorld; + n = pointCollector.m_normalOnBInWorld; + dist = pointCollector.m_distance; + } else + { + //?? + return false; + } + + } + + result.m_fraction = lambda; + result.m_normal = n; + return true; + } + + return false; + +/* +//todo: + //if movement away from normal, discard result + btVector3 move = transBLocalTo.getOrigin() - transBLocalFrom.getOrigin(); + if (result.m_fraction < btScalar(1.)) + { + if (move.dot(result.m_normal) <= btScalar(0.)) + { + } + } +*/ + +} + diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.h b/bullet/src/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.h new file mode 100644 index 0000000..4121df3 --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.h @@ -0,0 +1,52 @@ +/* +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. +*/ + + +#ifndef CONTINUOUS_COLLISION_CONVEX_CAST_H +#define CONTINUOUS_COLLISION_CONVEX_CAST_H + +#include "btConvexCast.h" +#include "btSimplexSolverInterface.h" +class btConvexPenetrationDepthSolver; +class btConvexShape; + +/// btContinuousConvexCollision implements angular and linear time of impact for convex objects. +/// Based on Brian Mirtich's Conservative Advancement idea (PhD thesis). +/// Algorithm operates in worldspace, in order to keep inbetween motion globally consistent. +/// It uses GJK at the moment. Future improvement would use minkowski sum / supporting vertex, merging innerloops +class btContinuousConvexCollision : public btConvexCast +{ + btSimplexSolverInterface* m_simplexSolver; + btConvexPenetrationDepthSolver* m_penetrationDepthSolver; + const btConvexShape* m_convexA; + const btConvexShape* m_convexB; + + +public: + + btContinuousConvexCollision (const btConvexShape* shapeA,const btConvexShape* shapeB ,btSimplexSolverInterface* simplexSolver,btConvexPenetrationDepthSolver* penetrationDepthSolver); + + virtual bool calcTimeOfImpact( + const btTransform& fromA, + const btTransform& toA, + const btTransform& fromB, + const btTransform& toB, + CastResult& result); + + +}; + +#endif //CONTINUOUS_COLLISION_CONVEX_CAST_H + diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexCast.cpp b/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexCast.cpp new file mode 100644 index 0000000..dfa1f0a --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexCast.cpp @@ -0,0 +1,20 @@ +/* +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. +*/ + +#include "btConvexCast.h" + +btConvexCast::~btConvexCast() +{ +} diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexCast.h b/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexCast.h new file mode 100644 index 0000000..a7a3f70 --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexCast.h @@ -0,0 +1,71 @@ +/* +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. +*/ + + +#ifndef CONVEX_CAST_H +#define CONVEX_CAST_H + +#include "LinearMath/btTransform.h" +#include "LinearMath/btVector3.h" +#include "LinearMath/btScalar.h" +class btMinkowskiSumShape; +#include "LinearMath/btIDebugDraw.h" + +/// btConvexCast is an interface for Casting +class btConvexCast +{ +public: + + + virtual ~btConvexCast(); + + ///RayResult stores the closest result + /// alternatively, add a callback method to decide about closest/all results + struct CastResult + { + //virtual bool addRayResult(const btVector3& normal,btScalar fraction) = 0; + + virtual void DebugDraw(btScalar fraction) {(void)fraction;} + virtual void drawCoordSystem(const btTransform& trans) {(void)trans;} + + CastResult() + :m_fraction(btScalar(1e30)), + m_debugDrawer(0) + { + } + + + virtual ~CastResult() {}; + + btVector3 m_normal; + btScalar m_fraction; + btTransform m_hitTransformA; + btTransform m_hitTransformB; + + btIDebugDraw* m_debugDrawer; + + }; + + + /// cast a convex against another convex object + virtual bool calcTimeOfImpact( + const btTransform& fromA, + const btTransform& toA, + const btTransform& fromB, + const btTransform& toB, + CastResult& result) = 0; +}; + +#endif //CONVEX_CAST_H diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h b/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h new file mode 100644 index 0000000..5bc2ad7 --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h @@ -0,0 +1,43 @@ +/* +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. +*/ + + +#ifndef CONVEX_PENETRATION_DEPTH_H +#define CONVEX_PENETRATION_DEPTH_H + +class btStackAlloc; +class btVector3; +#include "btSimplexSolverInterface.h" +class btConvexShape; +#include "LinearMath/btPoint3.h" +class btTransform; + +///ConvexPenetrationDepthSolver provides an interface for penetration depth calculation. +class btConvexPenetrationDepthSolver +{ +public: + + virtual ~btConvexPenetrationDepthSolver() {}; + virtual bool calcPenDepth( btSimplexSolverInterface& simplexSolver, + const btConvexShape* convexA,const btConvexShape* convexB, + const btTransform& transA,const btTransform& transB, + btVector3& v, btPoint3& pa, btPoint3& pb, + class btIDebugDraw* debugDraw,btStackAlloc* stackAlloc + ) = 0; + + +}; +#endif //CONVEX_PENETRATION_DEPTH_H + diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h b/bullet/src/BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h new file mode 100644 index 0000000..b172147 --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h @@ -0,0 +1,88 @@ +/* +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. +*/ + + +#ifndef DISCRETE_COLLISION_DETECTOR1_INTERFACE_H +#define DISCRETE_COLLISION_DETECTOR1_INTERFACE_H +#include "LinearMath/btTransform.h" +#include "LinearMath/btVector3.h" +class btStackAlloc; + +/// This interface is made to be used by an iterative approach to do TimeOfImpact calculations +/// This interface allows to query for closest points and penetration depth between two (convex) objects +/// the closest point is on the second object (B), and the normal points from the surface on B towards A. +/// distance is between closest points on B and closest point on A. So you can calculate closest point on A +/// by taking closestPointInA = closestPointInB + m_distance * m_normalOnSurfaceB +struct btDiscreteCollisionDetectorInterface +{ + + struct Result + { + + virtual ~Result(){} + + ///setShapeIdentifiers provides experimental support for per-triangle material / custom material combiner + virtual void setShapeIdentifiers(int partId0,int index0, int partId1,int index1)=0; + virtual void addContactPoint(const btVector3& normalOnBInWorld,const btVector3& pointInWorld,btScalar depth)=0; + }; + + struct ClosestPointInput + { + ClosestPointInput() + :m_maximumDistanceSquared(btScalar(1e30)), + m_stackAlloc(0) + { + } + + btTransform m_transformA; + btTransform m_transformB; + btScalar m_maximumDistanceSquared; + btStackAlloc* m_stackAlloc; + }; + + virtual ~btDiscreteCollisionDetectorInterface() {}; + + // + // give either closest points (distance > 0) or penetration (distance) + // the normal always points from B towards A + // + virtual void getClosestPoints(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw) = 0; + +}; + +struct btStorageResult : public btDiscreteCollisionDetectorInterface::Result +{ + btVector3 m_normalOnSurfaceB; + btVector3 m_closestPointInB; + btScalar m_distance; //negative means penetration ! + + btStorageResult() : m_distance(btScalar(1e30)) + { + + } + virtual ~btStorageResult() {}; + + virtual void addContactPoint(const btVector3& normalOnBInWorld,const btVector3& pointInWorld,btScalar depth) + { + if (depth < m_distance) + { + m_normalOnSurfaceB = normalOnBInWorld; + m_closestPointInB = pointInWorld; + m_distance = depth; + } + } +}; + +#endif //DISCRETE_COLLISION_DETECTOR_INTERFACE1_H diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkConvexCast.cpp b/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkConvexCast.cpp new file mode 100644 index 0000000..df5f83c --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkConvexCast.cpp @@ -0,0 +1,174 @@ +/* +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. +*/ + + + +#include "btGjkConvexCast.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" +#include "BulletCollision/CollisionShapes/btMinkowskiSumShape.h" +#include "btGjkPairDetector.h" +#include "btPointCollector.h" + + +btGjkConvexCast::btGjkConvexCast(const btConvexShape* convexA,const btConvexShape* convexB,btSimplexSolverInterface* simplexSolver) +:m_simplexSolver(simplexSolver), +m_convexA(convexA), +m_convexB(convexB) +{ +} + +bool btGjkConvexCast::calcTimeOfImpact( + const btTransform& fromA, + const btTransform& toA, + const btTransform& fromB, + const btTransform& toB, + CastResult& result) +{ + + + btMinkowskiSumShape combi(m_convexA,m_convexB); + btMinkowskiSumShape* convex = &combi; + + btTransform rayFromLocalA; + btTransform rayToLocalA; + + rayFromLocalA = fromA.inverse()* fromB; + rayToLocalA = toA.inverse()* toB; + + + btTransform trA,trB; + trA = btTransform(fromA); + trB = btTransform(fromB); + trA.setOrigin(btPoint3(0,0,0)); + trB.setOrigin(btPoint3(0,0,0)); + + convex->setTransformA(trA); + convex->setTransformB(trB); + + + + + btScalar radius = btScalar(0.01); + + btScalar lambda = btScalar(0.); + btVector3 s = rayFromLocalA.getOrigin(); + btVector3 r = rayToLocalA.getOrigin()-rayFromLocalA.getOrigin(); + btVector3 x = s; + btVector3 n; + n.setValue(0,0,0); + bool hasResult = false; + btVector3 c; + + btScalar lastLambda = lambda; + + //first solution, using GJK + + //no penetration support for now, perhaps pass a pointer when we really want it + btConvexPenetrationDepthSolver* penSolverPtr = 0; + + btTransform identityTrans; + identityTrans.setIdentity(); + + btSphereShape raySphere(btScalar(0.0)); + raySphere.setMargin(btScalar(0.)); + + btTransform sphereTr; + sphereTr.setIdentity(); + sphereTr.setOrigin( rayFromLocalA.getOrigin()); + + result.drawCoordSystem(sphereTr); + { + btPointCollector pointCollector1; + btGjkPairDetector gjk(&raySphere,convex,m_simplexSolver,penSolverPtr); + + btGjkPairDetector::ClosestPointInput input; + input.m_transformA = sphereTr; + input.m_transformB = identityTrans; + gjk.getClosestPoints(input,pointCollector1,0); + + hasResult = pointCollector1.m_hasResult; + c = pointCollector1.m_pointInWorld; + n = pointCollector1.m_normalOnBInWorld; + } + + + + if (hasResult) + { + btScalar dist; + dist = (c-x).length(); + if (dist < radius) + { + //penetration + lastLambda = btScalar(1.); + } + + //not close enough + while (dist > radius) + { + + n = x - c; + btScalar nDotr = n.dot(r); + + if (nDotr >= -(SIMD_EPSILON*SIMD_EPSILON)) + return false; + + lambda = lambda - n.dot(n) / nDotr; + if (lambda <= lastLambda) + break; + + lastLambda = lambda; + + x = s + lambda * r; + + sphereTr.setOrigin( x ); + result.drawCoordSystem(sphereTr); + btPointCollector pointCollector; + btGjkPairDetector gjk(&raySphere,convex,m_simplexSolver,penSolverPtr); + btGjkPairDetector::ClosestPointInput input; + input.m_transformA = sphereTr; + input.m_transformB = identityTrans; + gjk.getClosestPoints(input,pointCollector,0); + if (pointCollector.m_hasResult) + { + if (pointCollector.m_distance < btScalar(0.)) + { + //degeneracy, report a hit + result.m_fraction = lastLambda; + result.m_normal = n; + return true; + } + c = pointCollector.m_pointInWorld; + dist = (c-x).length(); + } else + { + //?? + return false; + } + + } + + if (lastLambda < btScalar(1.)) + { + + result.m_fraction = lastLambda; + result.m_normal = n; + return true; + } + } + + return false; +} + diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkConvexCast.h b/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkConvexCast.h new file mode 100644 index 0000000..2fd2ed3 --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkConvexCast.h @@ -0,0 +1,50 @@ +/* +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. +*/ + + + +#ifndef GJK_CONVEX_CAST_H +#define GJK_CONVEX_CAST_H + +#include "BulletCollision/CollisionShapes/btCollisionMargin.h" + +#include "LinearMath/btVector3.h" +#include "btConvexCast.h" +class btConvexShape; +class btMinkowskiSumShape; +#include "btSimplexSolverInterface.h" + +///GjkConvexCast performs a raycast on a convex object using support mapping. +class btGjkConvexCast : public btConvexCast +{ + btSimplexSolverInterface* m_simplexSolver; + const btConvexShape* m_convexA; + const btConvexShape* m_convexB; + +public: + + btGjkConvexCast(const btConvexShape* convexA,const btConvexShape* convexB,btSimplexSolverInterface* simplexSolver); + + /// cast a convex against another convex object + virtual bool calcTimeOfImpact( + const btTransform& fromA, + const btTransform& toA, + const btTransform& fromB, + const btTransform& toB, + CastResult& result); + +}; + +#endif //GJK_CONVEX_CAST_H diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpa.cpp b/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpa.cpp new file mode 100644 index 0000000..14f2c0e --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpa.cpp @@ -0,0 +1,628 @@ +/* +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. +*/ + +/* +GJK-EPA collision solver by Nathanael Presson +Nov.2006 +*/ + +#include "btGjkEpa.h" +#include //for memset +#include + +#if defined(DEBUG) || defined (_DEBUG) +#include //for debug printf +#ifdef __SPU__ +#include +#define printf spu_printf +#endif //__SPU__ +#endif + +namespace gjkepa_impl +{ + +// +// Port. typedefs +// + +typedef btScalar F; +typedef bool Z; +typedef int I; +typedef unsigned int U; +typedef unsigned char U1; +typedef unsigned short U2; + +typedef btVector3 Vector3; +typedef btMatrix3x3 Rotation; + +// +// Config +// + +#if 0 +#define BTLOCALSUPPORT localGetSupportingVertexWithoutMargin +#else +#define BTLOCALSUPPORT localGetSupportingVertex +#endif + +// +// Const +// + + +#define cstInf SIMD_INFINITY +#define cstPi SIMD_PI +#define cst2Pi SIMD_2_PI +#define GJK_maxiterations (128) +#define GJK_hashsize (1<<6) +#define GJK_hashmask (GJK_hashsize-1) +#define GJK_insimplex_eps F(0.0001) +#define GJK_sqinsimplex_eps (GJK_insimplex_eps*GJK_insimplex_eps) +#define EPA_maxiterations 256 +#define EPA_inface_eps F(0.01) +#define EPA_accuracy F(0.001) + +// +// Utils +// + +static inline F Abs(F v) { return(v<0?-v:v); } +static inline F Sign(F v) { return(F(v<0?-1:1)); } +template static inline void Swap(T& a,T& b) { T +t(a);a=b;b=t; } +template static inline T Min(const T& a,const T& b) { +return(a static inline T Max(const T& a,const T& b) { +return(a>b?a:b); } +static inline void ClearMemory(void* p,U sz) { memset(p,0,(size_t)sz); +} +#if 0 +template static inline void Raise(const T& object) { +throw(object); } +#else +template static inline void Raise(const T&) {} +#endif + + + +// +// GJK +// +struct GJK + { + struct Mkv + { + Vector3 w; /* Minkowski vertice */ + Vector3 r; /* Ray */ + }; + struct He + { + Vector3 v; + He* n; + }; + btStackAlloc* sa; + btBlock* sablock; + He* table[GJK_hashsize]; + Rotation wrotations[2]; + Vector3 positions[2]; + const btConvexShape* shapes[2]; + Mkv simplex[5]; + Vector3 ray; + U order; + U iterations; + F margin; + Z failed; + // + GJK(btStackAlloc* psa, + const Rotation& wrot0,const Vector3& pos0,const btConvexShape* shape0, + const Rotation& wrot1,const Vector3& pos1,const btConvexShape* shape1, + F pmargin=0) + { + wrotations[0]=wrot0;positions[0]=pos0;shapes[0]=shape0; + wrotations[1]=wrot1;positions[1]=pos1;shapes[1]=shape1; + sa =psa; + sablock =sa->beginBlock(); + margin =pmargin; + failed =false; + } + // + ~GJK() + { + sa->endBlock(sablock); + } + // vdh : very dumm hash + static inline U Hash(const Vector3& v) + { + //this doesn't compile under GCC 3.3.5, so add the ()... + //const U h(U(v[0]*15461)^U(v[1]*83003)^U(v[2]*15473)); + //return(((*((const U*)&h))*169639)&GJK_hashmask); + const U h((U)(v[0]*15461)^(U)(v[1]*83003)^(U)(v[2]*15473)); + return(((*((const U*)&h))*169639)&GJK_hashmask); + } + // + inline Vector3 LocalSupport(const Vector3& d,U i) const + { + return(wrotations[i]*shapes[i]->BTLOCALSUPPORT(d*wrotations[i])+positions[i]); + } + // + inline void Support(const Vector3& d,Mkv& v) const + { + v.r = d; + v.w = LocalSupport(d,0)-LocalSupport(-d,1)+d*margin; + } + #define SPX(_i_) simplex[_i_] + #define SPXW(_i_) simplex[_i_].w + // + inline Z FetchSupport() + { + const U h(Hash(ray)); + He* e = (He*)(table[h]); + while(e) { if(e->v==ray) { --order;return(false); } else e=e->n; } + e=(He*)sa->allocate(sizeof(He));e->v=ray;e->n=table[h];table[h]=e; + Support(ray,simplex[++order]); + return(ray.dot(SPXW(order))>0); + } + // + inline Z SolveSimplex2(const Vector3& ao,const Vector3& ab) + { + if(ab.dot(ao)>=0) + { + const Vector3 cabo(cross(ab,ao)); + if(cabo.length2()>GJK_sqinsimplex_eps) + { ray=cross(cabo,ab); } + else + { return(true); } + } + else + { order=0;SPX(0)=SPX(1);ray=ao; } + return(false); + } + // + inline Z SolveSimplex3(const Vector3& ao,const Vector3& ab,const Vector3& +ac) + { + return(SolveSimplex3a(ao,ab,ac,cross(ab,ac))); + } + // + inline Z SolveSimplex3a(const Vector3& ao,const Vector3& ab,const Vector3& +ac,const Vector3& cabc) + { + if((cross(cabc,ab)).dot(ao)<-GJK_insimplex_eps) + { order=1;SPX(0)=SPX(1);SPX(1)=SPX(2);return(SolveSimplex2(ao,ab)); } + else if((cross(cabc,ac)).dot(ao)>+GJK_insimplex_eps) + { order=1;SPX(1)=SPX(2);return(SolveSimplex2(ao,ac)); } + else + { + const F d(cabc.dot(ao)); + if(Abs(d)>GJK_insimplex_eps) + { + if(d>0) + { ray=cabc; } + else + { ray=-cabc;Swap(SPX(0),SPX(1)); } + return(false); + } else return(true); + } + } + // + inline Z SolveSimplex4(const Vector3& ao,const Vector3& ab,const Vector3& +ac,const Vector3& ad) + { + Vector3 crs; + if((crs=cross(ab,ac)).dot(ao)>GJK_insimplex_eps) + { +order=2;SPX(0)=SPX(1);SPX(1)=SPX(2);SPX(2)=SPX(3);return(SolveSimplex3a(ao,ab,ac,crs)); +} + else if((crs=cross(ac,ad)).dot(ao)>GJK_insimplex_eps) + { order=2;SPX(2)=SPX(3);return(SolveSimplex3a(ao,ac,ad,crs)); } + else if((crs=cross(ad,ab)).dot(ao)>GJK_insimplex_eps) + { +order=2;SPX(1)=SPX(0);SPX(0)=SPX(2);SPX(2)=SPX(3);return(SolveSimplex3a(ao,ad,ab,crs)); +} + else return(true); + } + // + inline Z SearchOrigin(const Vector3& initray=Vector3(1,0,0)) + { + iterations = 0; + order = (U)-1; + failed = false; + ray = initray.normalized(); + ClearMemory(table,sizeof(void*)*GJK_hashsize); + FetchSupport(); + ray = -SPXW(0); + for(;iterations0?rl:1; + if(FetchSupport()) + { + Z found(false); + switch(order) + { + case 1: found=SolveSimplex2(-SPXW(1),SPXW(0)-SPXW(1));break; + case 2: found=SolveSimplex3(-SPXW(2),SPXW(1)-SPXW(2),SPXW(0)-SPXW(2));break; + case 3: found=SolveSimplex4(-SPXW(3),SPXW(2)-SPXW(3),SPXW(1)-SPXW(3),SPXW(0)-SPXW(3));break; + } + if(found) return(true); + } else return(false); + } + failed=true; + return(false); + } + // + inline Z EncloseOrigin() + { + switch(order) + { + /* Point */ + case 0: break; + /* Line */ + case 1: + { + const Vector3 ab(SPXW(1)-SPXW(0)); + const Vector3 b[]={ cross(ab,Vector3(1,0,0)), + cross(ab,Vector3(0,1,0)), + cross(ab,Vector3(0,0,1))}; + const F m[]={b[0].length2(),b[1].length2(),b[2].length2()}; + const Rotation r(btQuaternion(ab.normalized(),cst2Pi/3)); + Vector3 w(b[m[0]>m[1]?m[0]>m[2]?0:2:m[1]>m[2]?1:2]); + Support(w.normalized(),simplex[4]);w=r*w; + Support(w.normalized(),simplex[2]);w=r*w; + Support(w.normalized(),simplex[3]);w=r*w; + order=4; + return(true); + } + break; + /* Triangle */ + case 2: + { + const +Vector3 n(cross((SPXW(1)-SPXW(0)),(SPXW(2)-SPXW(0))).normalized()); + Support( n,simplex[3]); + Support(-n,simplex[4]); + order=4; + return(true); + } + break; + /* Tetrahedron */ + case 3: return(true); + /* Hexahedron */ + case 4: return(true); + } + return(false); + } + #undef SPX + #undef SPXW + }; + +// +// EPA +// +struct EPA + { + // + struct Face + { + const GJK::Mkv* v[3]; + Face* f[3]; + U e[3]; + Vector3 n; + F d; + U mark; + Face* prev; + Face* next; + Face() {} + }; + // + GJK* gjk; + btStackAlloc* sa; + Face* root; + U nfaces; + U iterations; + Vector3 features[2][3]; + Vector3 nearest[2]; + Vector3 normal; + F depth; + Z failed; + // + EPA(GJK* pgjk) + { + gjk = pgjk; + sa = pgjk->sa; + } + // + ~EPA() + { + } + // + inline Vector3 GetCoordinates(const Face* face) const + { + const Vector3 o(face->n*-face->d); + const F a[]={ cross(face->v[0]->w-o,face->v[1]->w-o).length(), + cross(face->v[1]->w-o,face->v[2]->w-o).length(), + cross(face->v[2]->w-o,face->v[0]->w-o).length()}; + const F sm(a[0]+a[1]+a[2]); + return(Vector3(a[1],a[2],a[0])/(sm>0?sm:1)); + } + // + inline Face* FindBest() const + { + Face* bf = 0; + if(root) + { + Face* cf = root; + F bd(cstInf); + do { + if(cf->dd;bf=cf; } + } while(0!=(cf=cf->next)); + } + return(bf); + } + // + inline Z Set(Face* f,const GJK::Mkv* a,const GJK::Mkv* b,const GJK::Mkv* +c) const + { + const Vector3 nrm(cross(b->w-a->w,c->w-a->w)); + const F len(nrm.length()); + const Z valid( (cross(a->w,b->w).dot(nrm)>=-EPA_inface_eps)&& + (cross(b->w,c->w).dot(nrm)>=-EPA_inface_eps)&& + (cross(c->w,a->w).dot(nrm)>=-EPA_inface_eps)); + f->v[0] = a; + f->v[1] = b; + f->v[2] = c; + f->mark = 0; + f->n = nrm/(len>0?len:cstInf); + f->d = Max(0,-f->n.dot(a->w)); + return(valid); + } + // + inline Face* NewFace(const GJK::Mkv* a,const GJK::Mkv* b,const GJK::Mkv* c) + { + Face* pf = (Face*)sa->allocate(sizeof(Face)); + if(Set(pf,a,b,c)) + { + if(root) root->prev=pf; + pf->prev=0; + pf->next=root; + root =pf; + ++nfaces; + } + else + { + pf->prev=pf->next=0; + } + return(pf); + } + // + inline void Detach(Face* face) + { + if(face->prev||face->next) + { + --nfaces; + if(face==root) + { root=face->next;root->prev=0; } + else + { + if(face->next==0) + { face->prev->next=0; } + else + { face->prev->next=face->next;face->next->prev=face->prev; } + } + face->prev=face->next=0; + } + } + // + inline void Link(Face* f0,U e0,Face* f1,U e1) const + { + f0->f[e0]=f1;f1->e[e1]=e0; + f1->f[e1]=f0;f0->e[e0]=e1; + } + // + GJK::Mkv* Support(const Vector3& w) const + { + GJK::Mkv* v =(GJK::Mkv*)sa->allocate(sizeof(GJK::Mkv)); + gjk->Support(w,*v); + return(v); + } + // + U BuildHorizon(U markid,const GJK::Mkv* w,Face& f,U e,Face*& cf,Face*& +ff) + { + static const U mod3[]={0,1,2,0,1}; + U ne(0); + if(f.mark!=markid) + { + const U e1(mod3[e+1]); + if((f.n.dot(w->w)+f.d)>0) + { + Face* nf = NewFace(f.v[e1],f.v[e],w); + Link(nf,0,&f,e); + if(cf) Link(cf,1,nf,2); else ff=nf; + cf=nf;ne=1; + } + else + { + const U e2(mod3[e+2]); + Detach(&f); + f.mark = markid; + ne += BuildHorizon(markid,w,*f.f[e1],f.e[e1],cf,ff); + ne += BuildHorizon(markid,w,*f.f[e2],f.e[e2],cf,ff); + } + } + return(ne); + } + // + inline F EvaluatePD(F accuracy=EPA_accuracy) + { + btBlock* sablock = sa->beginBlock(); + Face* bestface = 0; + U markid(1); + depth = -cstInf; + normal = Vector3(0,0,0); + root = 0; + nfaces = 0; + iterations = 0; + failed = false; + /* Prepare hull */ + if(gjk->EncloseOrigin()) + { + const U* pfidx = 0; + U nfidx= 0; + const U* peidx = 0; + U neidx = 0; + GJK::Mkv* basemkv[5]; + Face* basefaces[6]; + switch(gjk->order) + { + /* Tetrahedron */ + case 3: { + static const U fidx[4][3]={{2,1,0},{3,0,1},{3,1,2},{3,2,0}}; + static const +U eidx[6][4]={{0,0,2,1},{0,1,1,1},{0,2,3,1},{1,0,3,2},{2,0,1,2},{3,0,2,2}}; + pfidx=(const U*)fidx;nfidx=4;peidx=(const U*)eidx;neidx=6; + } break; + /* Hexahedron */ + case 4: { + static const +U fidx[6][3]={{2,0,4},{4,1,2},{1,4,0},{0,3,1},{0,2,3},{1,3,2}}; + static const +U eidx[9][4]={{0,0,4,0},{0,1,2,1},{0,2,1,2},{1,1,5,2},{1,0,2,0},{2,2,3,2},{3,1,5,0},{3,0,4,2},{5,1,4,1}}; + pfidx=(const U*)fidx;nfidx=6;peidx=(const U*)eidx;neidx=9; + } break; + } + U i; + + for( i=0;i<=gjk->order;++i) { +basemkv[i]=(GJK::Mkv*)sa->allocate(sizeof(GJK::Mkv));*basemkv[i]=gjk->simplex[i]; +} + for( i=0;iendBlock(sablock); + return(depth); + } + /* Expand hull */ + for(;iterationsn); + const F d(bf->n.dot(w->w)+bf->d); + bestface = bf; + if(d<-accuracy) + { + Face* cf =0; + Face* ff =0; + U nf = 0; + Detach(bf); + bf->mark=++markid; + for(U i=0;i<3;++i) { +nf+=BuildHorizon(markid,w,*bf->f[i],bf->e[i],cf,ff); } + if(nf<=2) { break; } + Link(cf,1,ff,2); + } else break; + } else break; + } + /* Extract contact */ + if(bestface) + { + const Vector3 b(GetCoordinates(bestface)); + normal = bestface->n; + depth = Max(0,bestface->d); + for(U i=0;i<2;++i) + { + const F s(F(i?-1:1)); + for(U j=0;j<3;++j) + { + features[i][j]=gjk->LocalSupport(s*bestface->v[j]->r,i); + } + } + nearest[0] = features[0][0]*b.x()+features[0][1]*b.y()+features[0][2]*b.z(); + nearest[1] = features[1][0]*b.x()+features[1][1]*b.y()+features[1][2]*b.z(); + } else failed=true; + sa->endBlock(sablock); + return(depth); + } + }; +} + +// +// Api +// + +using namespace gjkepa_impl; + + + +// +bool btGjkEpaSolver::Collide(const btConvexShape *shape0,const btTransform &wtrs0, + const btConvexShape *shape1,const btTransform &wtrs1, + btScalar radialmargin, + btStackAlloc* stackAlloc, + sResults& results) +{ + + +/* Initialize */ +results.witnesses[0] = +results.witnesses[1] = +results.normal = Vector3(0,0,0); +results.depth = 0; +results.status = sResults::Separated; +results.epa_iterations = 0; +results.gjk_iterations = 0; +/* Use GJK to locate origin */ +GJK gjk(stackAlloc, + wtrs0.getBasis(),wtrs0.getOrigin(),shape0, + wtrs1.getBasis(),wtrs1.getOrigin(),shape1, + radialmargin+EPA_accuracy); +const Z collide(gjk.SearchOrigin()); +results.gjk_iterations = gjk.iterations+1; +if(collide) + { + /* Then EPA for penetration depth */ + EPA epa(&gjk); + const F pd(epa.EvaluatePD()); + results.epa_iterations = epa.iterations+1; + if(pd>0) + { + results.status = sResults::Penetrating; + results.normal = epa.normal; + results.depth = pd; + results.witnesses[0] = epa.nearest[0]; + results.witnesses[1] = epa.nearest[1]; + return(true); + } else { if(epa.failed) results.status=sResults::EPA_Failed; } + } else { if(gjk.failed) results.status=sResults::GJK_Failed; } +return(false); +} + + + + + diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpa.h b/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpa.h new file mode 100644 index 0000000..b2a590d --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpa.h @@ -0,0 +1,53 @@ +/* +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. +*/ + +/* +GJK-EPA collision solver by Nathanael Presson +Nov.2006 +*/ + + +#ifndef _05E48D53_04E0_49ad_BB0A_D74FE62E7366_ +#define _05E48D53_04E0_49ad_BB0A_D74FE62E7366_ +#include "BulletCollision/CollisionShapes/btConvexShape.h" + +class btStackAlloc; + +///btGjkEpaSolver contributed under zlib by Nathanael Presson +struct btGjkEpaSolver +{ +struct sResults + { + enum eStatus + { + Separated, /* Shapes doesnt penetrate */ + Penetrating, /* Shapes are penetrating */ + GJK_Failed, /* GJK phase fail, no big issue, shapes are probably just 'touching' */ + EPA_Failed, /* EPA phase fail, bigger problem, need to save parameters, and debug */ + } status; + btVector3 witnesses[2]; + btVector3 normal; + btScalar depth; + int epa_iterations; + int gjk_iterations; + }; +static bool Collide(const btConvexShape* shape0,const btTransform& wtrs0, + const btConvexShape* shape1,const btTransform& wtrs1, + btScalar radialmargin, + btStackAlloc* stackAlloc, + sResults& results); +}; + +#endif diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp b/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp new file mode 100644 index 0000000..f4cf11d --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp @@ -0,0 +1,50 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +EPA Copyright (c) Ricardo Padrela 2006 + +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 "BulletCollision/CollisionShapes/btConvexShape.h" +#include "btGjkEpaPenetrationDepthSolver.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkEpa.h" + +bool btGjkEpaPenetrationDepthSolver::calcPenDepth( btSimplexSolverInterface& simplexSolver, + const btConvexShape* pConvexA, const btConvexShape* pConvexB, + const btTransform& transformA, const btTransform& transformB, + btVector3& v, btPoint3& wWitnessOnA, btPoint3& wWitnessOnB, + class btIDebugDraw* debugDraw, btStackAlloc* stackAlloc ) +{ + + (void)debugDraw; + (void)v; + (void)simplexSolver; + + const btScalar radialmargin(btScalar(0.)); + + btGjkEpaSolver::sResults results; + if(btGjkEpaSolver::Collide( pConvexA,transformA, + pConvexB,transformB, + radialmargin,stackAlloc,results)) + { + // debugDraw->drawLine(results.witnesses[1],results.witnesses[1]+results.normal,btVector3(255,0,0)); + //resultOut->addContactPoint(results.normal,results.witnesses[1],-results.depth); + wWitnessOnA = results.witnesses[0]; + wWitnessOnB = results.witnesses[1]; + return true; + } + + return false; +} + + diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h b/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h new file mode 100644 index 0000000..c3bde33 --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h @@ -0,0 +1,39 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +EPA Copyright (c) Ricardo Padrela 2006 + +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. +*/ +#ifndef BT_GJP_EPA_PENETRATION_DEPTH_H +#define BT_GJP_EPA_PENETRATION_DEPTH_H + +#include "btConvexPenetrationDepthSolver.h" + +///EpaPenetrationDepthSolver uses the Expanding Polytope Algorithm to +///calculate the penetration depth between two convex shapes. +class btGjkEpaPenetrationDepthSolver : public btConvexPenetrationDepthSolver +{ + public : + + bool calcPenDepth( btSimplexSolverInterface& simplexSolver, + const btConvexShape* pConvexA, const btConvexShape* pConvexB, + const btTransform& transformA, const btTransform& transformB, + btVector3& v, btPoint3& wWitnessOnA, btPoint3& wWitnessOnB, + class btIDebugDraw* debugDraw,btStackAlloc* stackAlloc ); + + private : + +}; + +#endif // BT_GJP_EPA_PENETRATION_DEPTH_H + diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp b/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp new file mode 100644 index 0000000..22ecbff --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp @@ -0,0 +1,299 @@ +/* +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. +*/ + +#include "btGjkPairDetector.h" +#include "BulletCollision/CollisionShapes/btConvexShape.h" +#include "BulletCollision/NarrowPhaseCollision/btSimplexSolverInterface.h" +#include "BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h" + +#if defined(DEBUG) || defined (_DEBUG) +#include //for debug printf +#ifdef __SPU__ +#include +#define printf spu_printf +#endif //__SPU__ +#endif + +//must be above the machine epsilon +#define REL_ERROR2 btScalar(1.0e-6) + +//temp globals, to improve GJK/EPA/penetration calculations +int gNumDeepPenetrationChecks = 0; +int gNumGjkChecks = 0; + + + +btGjkPairDetector::btGjkPairDetector(const btConvexShape* objectA,const btConvexShape* objectB,btSimplexSolverInterface* simplexSolver,btConvexPenetrationDepthSolver* penetrationDepthSolver) +:m_cachedSeparatingAxis(btScalar(0.),btScalar(0.),btScalar(1.)), +m_penetrationDepthSolver(penetrationDepthSolver), +m_simplexSolver(simplexSolver), +m_minkowskiA(objectA), +m_minkowskiB(objectB), +m_ignoreMargin(false), +m_lastUsedMethod(-1), +m_catchDegeneracies(1) +{ +} + +void btGjkPairDetector::getClosestPoints(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw) +{ + btScalar distance=btScalar(0.); + btVector3 normalInB(btScalar(0.),btScalar(0.),btScalar(0.)); + btVector3 pointOnA,pointOnB; + btTransform localTransA = input.m_transformA; + btTransform localTransB = input.m_transformB; + btVector3 positionOffset = (localTransA.getOrigin() + localTransB.getOrigin()) * btScalar(0.5); + localTransA.getOrigin() -= positionOffset; + localTransB.getOrigin() -= positionOffset; + + btScalar marginA = m_minkowskiA->getMargin(); + btScalar marginB = m_minkowskiB->getMargin(); + + gNumGjkChecks++; + + //for CCD we don't use margins + if (m_ignoreMargin) + { + marginA = btScalar(0.); + marginB = btScalar(0.); + } + + m_curIter = 0; + int gGjkMaxIter = 1000;//this is to catch invalid input, perhaps check for #NaN? + m_cachedSeparatingAxis.setValue(0,1,0); + + bool isValid = false; + bool checkSimplex = false; + bool checkPenetration = true; + m_degenerateSimplex = 0; + + m_lastUsedMethod = -1; + + { + btScalar squaredDistance = SIMD_INFINITY; + btScalar delta = btScalar(0.); + + btScalar margin = marginA + marginB; + + + + m_simplexSolver->reset(); + + for ( ; ; ) + //while (true) + { + + btVector3 seperatingAxisInA = (-m_cachedSeparatingAxis)* input.m_transformA.getBasis(); + btVector3 seperatingAxisInB = m_cachedSeparatingAxis* input.m_transformB.getBasis(); + + btVector3 pInA = m_minkowskiA->localGetSupportingVertexWithoutMargin(seperatingAxisInA); + btVector3 qInB = m_minkowskiB->localGetSupportingVertexWithoutMargin(seperatingAxisInB); + btPoint3 pWorld = localTransA(pInA); + btPoint3 qWorld = localTransB(qInB); + + btVector3 w = pWorld - qWorld; + delta = m_cachedSeparatingAxis.dot(w); + + // potential exit, they don't overlap + if ((delta > btScalar(0.0)) && (delta * delta > squaredDistance * input.m_maximumDistanceSquared)) + { + checkPenetration = false; + break; + } + + //exit 0: the new point is already in the simplex, or we didn't come any closer + if (m_simplexSolver->inSimplex(w)) + { + m_degenerateSimplex = 1; + checkSimplex = true; + break; + } + // are we getting any closer ? + btScalar f0 = squaredDistance - delta; + btScalar f1 = squaredDistance * REL_ERROR2; + + if (f0 <= f1) + { + if (f0 <= btScalar(0.)) + { + m_degenerateSimplex = 2; + } + checkSimplex = true; + break; + } + //add current vertex to simplex + m_simplexSolver->addVertex(w, pWorld, qWorld); + + //calculate the closest point to the origin (update vector v) + if (!m_simplexSolver->closest(m_cachedSeparatingAxis)) + { + m_degenerateSimplex = 3; + checkSimplex = true; + break; + } + + btScalar previousSquaredDistance = squaredDistance; + squaredDistance = m_cachedSeparatingAxis.length2(); + + //redundant m_simplexSolver->compute_points(pointOnA, pointOnB); + + //are we getting any closer ? + if (previousSquaredDistance - squaredDistance <= SIMD_EPSILON * previousSquaredDistance) + { + m_simplexSolver->backup_closest(m_cachedSeparatingAxis); + checkSimplex = true; + break; + } + + //degeneracy, this is typically due to invalid/uninitialized worldtransforms for a btCollisionObject + if (m_curIter++ > gGjkMaxIter) + { + #if defined(DEBUG) || defined (_DEBUG) + + printf("btGjkPairDetector maxIter exceeded:%i\n",m_curIter); + printf("sepAxis=(%f,%f,%f), squaredDistance = %f, shapeTypeA=%i,shapeTypeB=%i\n", + m_cachedSeparatingAxis.getX(), + m_cachedSeparatingAxis.getY(), + m_cachedSeparatingAxis.getZ(), + squaredDistance, + m_minkowskiA->getShapeType(), + m_minkowskiB->getShapeType()); + + #endif + break; + + } + + + bool check = (!m_simplexSolver->fullSimplex()); + //bool check = (!m_simplexSolver->fullSimplex() && squaredDistance > SIMD_EPSILON * m_simplexSolver->maxVertex()); + + if (!check) + { + //do we need this backup_closest here ? + m_simplexSolver->backup_closest(m_cachedSeparatingAxis); + break; + } + } + + if (checkSimplex) + { + m_simplexSolver->compute_points(pointOnA, pointOnB); + normalInB = pointOnA-pointOnB; + btScalar lenSqr = m_cachedSeparatingAxis.length2(); + //valid normal + if (lenSqr < 0.0001) + { + m_degenerateSimplex = 5; + } + if (lenSqr > SIMD_EPSILON*SIMD_EPSILON) + { + btScalar rlen = btScalar(1.) / btSqrt(lenSqr ); + normalInB *= rlen; //normalize + btScalar s = btSqrt(squaredDistance); + + btAssert(s > btScalar(0.0)); + pointOnA -= m_cachedSeparatingAxis * (marginA / s); + pointOnB += m_cachedSeparatingAxis * (marginB / s); + distance = ((btScalar(1.)/rlen) - margin); + isValid = true; + + m_lastUsedMethod = 1; + } else + { + m_lastUsedMethod = 2; + } + } + + bool catchDegeneratePenetrationCase = + (m_catchDegeneracies && m_penetrationDepthSolver && m_degenerateSimplex && ((distance+margin) < 0.01)); + + //if (checkPenetration && !isValid) + if (checkPenetration && (!isValid || catchDegeneratePenetrationCase )) + { + //penetration case + + //if there is no way to handle penetrations, bail out + if (m_penetrationDepthSolver) + { + // Penetration depth case. + btVector3 tmpPointOnA,tmpPointOnB; + + gNumDeepPenetrationChecks++; + + bool isValid2 = m_penetrationDepthSolver->calcPenDepth( + *m_simplexSolver, + m_minkowskiA,m_minkowskiB, + localTransA,localTransB, + m_cachedSeparatingAxis, tmpPointOnA, tmpPointOnB, + debugDraw,input.m_stackAlloc + ); + + if (isValid2) + { + btVector3 tmpNormalInB = tmpPointOnB-tmpPointOnA; + btScalar lenSqr = tmpNormalInB.length2(); + if (lenSqr > (SIMD_EPSILON*SIMD_EPSILON)) + { + tmpNormalInB /= btSqrt(lenSqr); + btScalar distance2 = -(tmpPointOnA-tmpPointOnB).length(); + //only replace valid penetrations when the result is deeper (check) + if (!isValid || (distance2 < distance)) + { + distance = distance2; + pointOnA = tmpPointOnA; + pointOnB = tmpPointOnB; + normalInB = tmpNormalInB; + isValid = true; + m_lastUsedMethod = 3; + } else + { + + } + } else + { + //isValid = false; + m_lastUsedMethod = 4; + } + } else + { + m_lastUsedMethod = 5; + } + + } + } + } + + if (isValid) + { +#ifdef __SPU__ + //spu_printf("distance\n"); +#endif //__CELLOS_LV2__ + + + output.addContactPoint( + normalInB, + pointOnB+positionOffset, + distance); + //printf("gjk add:%f",distance); + } + + +} + + + + + diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h b/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h new file mode 100644 index 0000000..8120cef --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h @@ -0,0 +1,85 @@ +/* +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. +*/ + + + + +#ifndef GJK_PAIR_DETECTOR_H +#define GJK_PAIR_DETECTOR_H + +#include "btDiscreteCollisionDetectorInterface.h" +#include "LinearMath/btPoint3.h" +#include "BulletCollision/CollisionShapes/btCollisionMargin.h" + +class btConvexShape; +#include "btSimplexSolverInterface.h" +class btConvexPenetrationDepthSolver; + +/// btGjkPairDetector uses GJK to implement the btDiscreteCollisionDetectorInterface +class btGjkPairDetector : public btDiscreteCollisionDetectorInterface +{ + + + btVector3 m_cachedSeparatingAxis; + btConvexPenetrationDepthSolver* m_penetrationDepthSolver; + btSimplexSolverInterface* m_simplexSolver; + const btConvexShape* m_minkowskiA; + const btConvexShape* m_minkowskiB; + bool m_ignoreMargin; + + +public: + + //some debugging to fix degeneracy problems + int m_lastUsedMethod; + int m_curIter; + int m_degenerateSimplex; + int m_catchDegeneracies; + + + btGjkPairDetector(const btConvexShape* objectA,const btConvexShape* objectB,btSimplexSolverInterface* simplexSolver,btConvexPenetrationDepthSolver* penetrationDepthSolver); + virtual ~btGjkPairDetector() {}; + + virtual void getClosestPoints(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw); + + void setMinkowskiA(btConvexShape* minkA) + { + m_minkowskiA = minkA; + } + + void setMinkowskiB(btConvexShape* minkB) + { + m_minkowskiB = minkB; + } + void setCachedSeperatingAxis(const btVector3& seperatingAxis) + { + m_cachedSeparatingAxis = seperatingAxis; + } + + void setPenetrationDepthSolver(btConvexPenetrationDepthSolver* penetrationDepthSolver) + { + m_penetrationDepthSolver = penetrationDepthSolver; + } + + ///don't use setIgnoreMargin, it's for Bullet's internal use + void setIgnoreMargin(bool ignoreMargin) + { + m_ignoreMargin = ignoreMargin; + } + + +}; + +#endif //GJK_PAIR_DETECTOR_H diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btManifoldPoint.h b/bullet/src/BulletCollision/NarrowPhaseCollision/btManifoldPoint.h new file mode 100644 index 0000000..06ef506 --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btManifoldPoint.h @@ -0,0 +1,99 @@ +/* +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. +*/ + +#ifndef MANIFOLD_CONTACT_POINT_H +#define MANIFOLD_CONTACT_POINT_H + +#include "LinearMath/btVector3.h" +#include "LinearMath/btTransformUtil.h" + + + + + +/// ManifoldContactPoint collects and maintains persistent contactpoints. +/// used to improve stability and performance of rigidbody dynamics response. +class btManifoldPoint + { + public: + btManifoldPoint() + :m_userPersistentData(0), + m_lifeTime(0) + { + } + + btManifoldPoint( const btVector3 &pointA, const btVector3 &pointB, + const btVector3 &normal, + btScalar distance ) : + m_localPointA( pointA ), + m_localPointB( pointB ), + m_normalWorldOnB( normal ), + m_distance1( distance ), + m_combinedFriction(btScalar(0.)), + m_combinedRestitution(btScalar(0.)), + m_userPersistentData(0), + m_lifeTime(0) + { + + + } + + + + btVector3 m_localPointA; + btVector3 m_localPointB; + btVector3 m_positionWorldOnB; + ///m_positionWorldOnA is redundant information, see getPositionWorldOnA(), but for clarity + btVector3 m_positionWorldOnA; + btVector3 m_normalWorldOnB; + + btScalar m_distance1; + btScalar m_combinedFriction; + btScalar m_combinedRestitution; + + + mutable void* m_userPersistentData; + + int m_lifeTime;//lifetime of the contactpoint in frames + + btScalar getDistance() const + { + return m_distance1; + } + int getLifeTime() const + { + return m_lifeTime; + } + + const btVector3& getPositionWorldOnA() const { + return m_positionWorldOnA; +// return m_positionWorldOnB + m_normalWorldOnB * m_distance1; + } + + const btVector3& getPositionWorldOnB() const + { + return m_positionWorldOnB; + } + + void setDistance(btScalar dist) + { + m_distance1 = dist; + } + + + + }; + +#endif //MANIFOLD_CONTACT_POINT_H diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.cpp b/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.cpp new file mode 100644 index 0000000..54f8e3f --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.cpp @@ -0,0 +1,334 @@ +/* +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. +*/ + +#include "btMinkowskiPenetrationDepthSolver.h" +#include "BulletCollision/CollisionShapes/btMinkowskiSumShape.h" +#include "BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h" +#include "BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h" + + + + +#define NUM_UNITSPHERE_POINTS 42 +static btVector3 sPenetrationDirections[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2] = +{ +btVector3(btScalar(0.000000) , btScalar(-0.000000),btScalar(-1.000000)), +btVector3(btScalar(0.723608) , btScalar(-0.525725),btScalar(-0.447219)), +btVector3(btScalar(-0.276388) , btScalar(-0.850649),btScalar(-0.447219)), +btVector3(btScalar(-0.894426) , btScalar(-0.000000),btScalar(-0.447216)), +btVector3(btScalar(-0.276388) , btScalar(0.850649),btScalar(-0.447220)), +btVector3(btScalar(0.723608) , btScalar(0.525725),btScalar(-0.447219)), +btVector3(btScalar(0.276388) , btScalar(-0.850649),btScalar(0.447220)), +btVector3(btScalar(-0.723608) , btScalar(-0.525725),btScalar(0.447219)), +btVector3(btScalar(-0.723608) , btScalar(0.525725),btScalar(0.447219)), +btVector3(btScalar(0.276388) , btScalar(0.850649),btScalar(0.447219)), +btVector3(btScalar(0.894426) , btScalar(0.000000),btScalar(0.447216)), +btVector3(btScalar(-0.000000) , btScalar(0.000000),btScalar(1.000000)), +btVector3(btScalar(0.425323) , btScalar(-0.309011),btScalar(-0.850654)), +btVector3(btScalar(-0.162456) , btScalar(-0.499995),btScalar(-0.850654)), +btVector3(btScalar(0.262869) , btScalar(-0.809012),btScalar(-0.525738)), +btVector3(btScalar(0.425323) , btScalar(0.309011),btScalar(-0.850654)), +btVector3(btScalar(0.850648) , btScalar(-0.000000),btScalar(-0.525736)), +btVector3(btScalar(-0.525730) , btScalar(-0.000000),btScalar(-0.850652)), +btVector3(btScalar(-0.688190) , btScalar(-0.499997),btScalar(-0.525736)), +btVector3(btScalar(-0.162456) , btScalar(0.499995),btScalar(-0.850654)), +btVector3(btScalar(-0.688190) , btScalar(0.499997),btScalar(-0.525736)), +btVector3(btScalar(0.262869) , btScalar(0.809012),btScalar(-0.525738)), +btVector3(btScalar(0.951058) , btScalar(0.309013),btScalar(0.000000)), +btVector3(btScalar(0.951058) , btScalar(-0.309013),btScalar(0.000000)), +btVector3(btScalar(0.587786) , btScalar(-0.809017),btScalar(0.000000)), +btVector3(btScalar(0.000000) , btScalar(-1.000000),btScalar(0.000000)), +btVector3(btScalar(-0.587786) , btScalar(-0.809017),btScalar(0.000000)), +btVector3(btScalar(-0.951058) , btScalar(-0.309013),btScalar(-0.000000)), +btVector3(btScalar(-0.951058) , btScalar(0.309013),btScalar(-0.000000)), +btVector3(btScalar(-0.587786) , btScalar(0.809017),btScalar(-0.000000)), +btVector3(btScalar(-0.000000) , btScalar(1.000000),btScalar(-0.000000)), +btVector3(btScalar(0.587786) , btScalar(0.809017),btScalar(-0.000000)), +btVector3(btScalar(0.688190) , btScalar(-0.499997),btScalar(0.525736)), +btVector3(btScalar(-0.262869) , btScalar(-0.809012),btScalar(0.525738)), +btVector3(btScalar(-0.850648) , btScalar(0.000000),btScalar(0.525736)), +btVector3(btScalar(-0.262869) , btScalar(0.809012),btScalar(0.525738)), +btVector3(btScalar(0.688190) , btScalar(0.499997),btScalar(0.525736)), +btVector3(btScalar(0.525730) , btScalar(0.000000),btScalar(0.850652)), +btVector3(btScalar(0.162456) , btScalar(-0.499995),btScalar(0.850654)), +btVector3(btScalar(-0.425323) , btScalar(-0.309011),btScalar(0.850654)), +btVector3(btScalar(-0.425323) , btScalar(0.309011),btScalar(0.850654)), +btVector3(btScalar(0.162456) , btScalar(0.499995),btScalar(0.850654)) +}; + + +bool btMinkowskiPenetrationDepthSolver::calcPenDepth(btSimplexSolverInterface& simplexSolver, + const btConvexShape* convexA,const btConvexShape* convexB, + const btTransform& transA,const btTransform& transB, + btVector3& v, btPoint3& pa, btPoint3& pb, + class btIDebugDraw* debugDraw,btStackAlloc* stackAlloc + ) +{ + + (void)stackAlloc; + (void)v; + + + struct btIntermediateResult : public btDiscreteCollisionDetectorInterface::Result + { + + btIntermediateResult():m_hasResult(false) + { + } + + btVector3 m_normalOnBInWorld; + btVector3 m_pointInWorld; + btScalar m_depth; + bool m_hasResult; + + virtual void setShapeIdentifiers(int partId0,int index0, int partId1,int index1) + { + (void)partId0; + (void)index0; + (void)partId1; + (void)index1; + } + void addContactPoint(const btVector3& normalOnBInWorld,const btVector3& pointInWorld,btScalar depth) + { + m_normalOnBInWorld = normalOnBInWorld; + m_pointInWorld = pointInWorld; + m_depth = depth; + m_hasResult = true; + } + }; + + //just take fixed number of orientation, and sample the penetration depth in that direction + btScalar minProj = btScalar(1e30); + btVector3 minNorm; + btVector3 minVertex; + btVector3 minA,minB; + btVector3 seperatingAxisInA,seperatingAxisInB; + btVector3 pInA,qInB,pWorld,qWorld,w; + +#define USE_BATCHED_SUPPORT 1 +#ifdef USE_BATCHED_SUPPORT + + btVector3 supportVerticesABatch[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2]; + btVector3 supportVerticesBBatch[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2]; + btVector3 seperatingAxisInABatch[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2]; + btVector3 seperatingAxisInBBatch[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2]; + int i; + + int numSampleDirections = NUM_UNITSPHERE_POINTS; + + for (i=0;igetNumPreferredPenetrationDirections(); + if (numPDA) + { + for (int i=0;igetPreferredPenetrationDirection(i,norm); + norm = transA.getBasis() * norm; + sPenetrationDirections[numSampleDirections] = norm; + seperatingAxisInABatch[numSampleDirections] = (-norm) * transA.getBasis(); + seperatingAxisInBBatch[numSampleDirections] = norm * transB.getBasis(); + numSampleDirections++; + } + } + } + + { + int numPDB = convexB->getNumPreferredPenetrationDirections(); + if (numPDB) + { + for (int i=0;igetPreferredPenetrationDirection(i,norm); + norm = transB.getBasis() * norm; + sPenetrationDirections[numSampleDirections] = norm; + seperatingAxisInABatch[numSampleDirections] = (-norm) * transA.getBasis(); + seperatingAxisInBBatch[numSampleDirections] = norm * transB.getBasis(); + numSampleDirections++; + } + } + } + + + + convexA->batchedUnitVectorGetSupportingVertexWithoutMargin(seperatingAxisInABatch,supportVerticesABatch,numSampleDirections); + convexB->batchedUnitVectorGetSupportingVertexWithoutMargin(seperatingAxisInBBatch,supportVerticesBBatch,numSampleDirections); + + for (i=0;igetNumPreferredPenetrationDirections(); + if (numPDA) + { + for (int i=0;igetPreferredPenetrationDirection(i,norm); + norm = transA.getBasis() * norm; + sPenetrationDirections[numSampleDirections] = norm; + numSampleDirections++; + } + } + } + + { + int numPDB = convexB->getNumPreferredPenetrationDirections(); + if (numPDB) + { + for (int i=0;igetPreferredPenetrationDirection(i,norm); + norm = transB.getBasis() * norm; + sPenetrationDirections[numSampleDirections] = norm; + numSampleDirections++; + } + } + } + + for (int i=0;ilocalGetSupportingVertexWithoutMargin(seperatingAxisInA); + qInB = convexB->localGetSupportingVertexWithoutMargin(seperatingAxisInB); + pWorld = transA(pInA); + qWorld = transB(qInB); + w = qWorld - pWorld; + btScalar delta = norm.dot(w); + //find smallest delta + if (delta < minProj) + { + minProj = delta; + minNorm = norm; + minA = pWorld; + minB = qWorld; + } + } +#endif //USE_BATCHED_SUPPORT + + //add the margins + + minA += minNorm*convexA->getMargin(); + minB -= minNorm*convexB->getMargin(); + //no penetration + if (minProj < btScalar(0.)) + return false; + + minProj += (convexA->getMargin() + convexB->getMargin()); + + + + + +//#define DEBUG_DRAW 1 +#ifdef DEBUG_DRAW + if (debugDraw) + { + btVector3 color(0,1,0); + debugDraw->drawLine(minA,minB,color); + color = btVector3 (1,1,1); + btVector3 vec = minB-minA; + btScalar prj2 = minNorm.dot(vec); + debugDraw->drawLine(minA,minA+(minNorm*minProj),color); + + } +#endif //DEBUG_DRAW + + + + btGjkPairDetector gjkdet(convexA,convexB,&simplexSolver,0); + + btScalar offsetDist = minProj; + btVector3 offset = minNorm * offsetDist; + + + + btGjkPairDetector::ClosestPointInput input; + + btVector3 newOrg = transA.getOrigin() + offset; + + btTransform displacedTrans = transA; + displacedTrans.setOrigin(newOrg); + + input.m_transformA = displacedTrans; + input.m_transformB = transB; + input.m_maximumDistanceSquared = btScalar(1e30);//minProj; + + btIntermediateResult res; + gjkdet.getClosestPoints(input,res,debugDraw); + + btScalar correctedMinNorm = minProj - res.m_depth; + + + //the penetration depth is over-estimated, relax it + btScalar penetration_relaxation= btScalar(1.); + minNorm*=penetration_relaxation; + + if (res.m_hasResult) + { + + pa = res.m_pointInWorld - minNorm * correctedMinNorm; + pb = res.m_pointInWorld; + +#ifdef DEBUG_DRAW + if (debugDraw) + { + btVector3 color(1,0,0); + debugDraw->drawLine(pa,pb,color); + } +#endif//DEBUG_DRAW + + + } + return res.m_hasResult; +} + + + diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h b/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h new file mode 100644 index 0000000..37c796d --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h @@ -0,0 +1,37 @@ +/* +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. +*/ + +#ifndef MINKOWSKI_PENETRATION_DEPTH_SOLVER_H +#define MINKOWSKI_PENETRATION_DEPTH_SOLVER_H + +#include "btConvexPenetrationDepthSolver.h" + +///MinkowskiPenetrationDepthSolver implements bruteforce penetration depth estimation. +///Implementation is based on sampling the depth using support mapping, and using GJK step to get the witness points. +class btMinkowskiPenetrationDepthSolver : public btConvexPenetrationDepthSolver +{ +public: + + virtual bool calcPenDepth( btSimplexSolverInterface& simplexSolver, + const btConvexShape* convexA,const btConvexShape* convexB, + const btTransform& transA,const btTransform& transB, + btVector3& v, btPoint3& pa, btPoint3& pb, + class btIDebugDraw* debugDraw,btStackAlloc* stackAlloc + ); + +}; + +#endif //MINKOWSKI_PENETRATION_DEPTH_SOLVER_H + diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btPersistentManifold.cpp b/bullet/src/BulletCollision/NarrowPhaseCollision/btPersistentManifold.cpp new file mode 100644 index 0000000..3616c6f --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btPersistentManifold.cpp @@ -0,0 +1,246 @@ +/* +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. +*/ + + +#include "btPersistentManifold.h" +#include "LinearMath/btTransform.h" +#include + +btScalar gContactBreakingThreshold = btScalar(0.02); +ContactDestroyedCallback gContactDestroyedCallback = 0; + + + +btPersistentManifold::btPersistentManifold() +:m_body0(0), +m_body1(0), +m_cachedPoints (0), +m_index1(0) +{ +} + + +void btPersistentManifold::clearManifold() +{ + int i; + for (i=0;i +void btPersistentManifold::DebugPersistency() +{ + int i; + printf("DebugPersistency : numPoints %d\n",m_cachedPoints); + for (i=0;i1) + printf("error in clearUserCache\n"); + } + } + assert(occurance<=0); +#endif //DEBUG_PERSISTENCY + + if (pt.m_userPersistentData && gContactDestroyedCallback) + { + (*gContactDestroyedCallback)(pt.m_userPersistentData); + pt.m_userPersistentData = 0; + } + +#ifdef DEBUG_PERSISTENCY + DebugPersistency(); +#endif + } + + +} + + +int btPersistentManifold::sortCachedPoints(const btManifoldPoint& pt) +{ + + //calculate 4 possible cases areas, and take biggest area + //also need to keep 'deepest' + + int maxPenetrationIndex = -1; +#define KEEP_DEEPEST_POINT 1 +#ifdef KEEP_DEEPEST_POINT + btScalar maxPenetration = pt.getDistance(); + for (int i=0;i<4;i++) + { + if (m_pointCache[i].getDistance() < maxPenetration) + { + maxPenetrationIndex = i; + maxPenetration = m_pointCache[i].getDistance(); + } + } +#endif //KEEP_DEEPEST_POINT + + btScalar res0(btScalar(0.)),res1(btScalar(0.)),res2(btScalar(0.)),res3(btScalar(0.)); + if (maxPenetrationIndex != 0) + { + btVector3 a0 = pt.m_localPointA-m_pointCache[1].m_localPointA; + btVector3 b0 = m_pointCache[3].m_localPointA-m_pointCache[2].m_localPointA; + btVector3 cross = a0.cross(b0); + res0 = cross.length2(); + } + if (maxPenetrationIndex != 1) + { + btVector3 a1 = pt.m_localPointA-m_pointCache[0].m_localPointA; + btVector3 b1 = m_pointCache[3].m_localPointA-m_pointCache[2].m_localPointA; + btVector3 cross = a1.cross(b1); + res1 = cross.length2(); + } + + if (maxPenetrationIndex != 2) + { + btVector3 a2 = pt.m_localPointA-m_pointCache[0].m_localPointA; + btVector3 b2 = m_pointCache[3].m_localPointA-m_pointCache[1].m_localPointA; + btVector3 cross = a2.cross(b2); + res2 = cross.length2(); + } + + if (maxPenetrationIndex != 3) + { + btVector3 a3 = pt.m_localPointA-m_pointCache[0].m_localPointA; + btVector3 b3 = m_pointCache[2].m_localPointA-m_pointCache[1].m_localPointA; + btVector3 cross = a3.cross(b3); + res3 = cross.length2(); + } + + btVector4 maxvec(res0,res1,res2,res3); + int biggestarea = maxvec.closestAxis4(); + return biggestarea; +} + + +int btPersistentManifold::getCacheEntry(const btManifoldPoint& newPoint) const +{ + btScalar shortestDist = getContactBreakingThreshold() * getContactBreakingThreshold(); + int size = getNumContacts(); + int nearestPoint = -1; + for( int i = 0; i < size; i++ ) + { + const btManifoldPoint &mp = m_pointCache[i]; + + btVector3 diffA = mp.m_localPointA- newPoint.m_localPointA; + const btScalar distToManiPoint = diffA.dot(diffA); + if( distToManiPoint < shortestDist ) + { + shortestDist = distToManiPoint; + nearestPoint = i; + } + } + return nearestPoint; +} + +void btPersistentManifold::AddManifoldPoint(const btManifoldPoint& newPoint) +{ + assert(validContactDistance(newPoint)); + + int insertIndex = getNumContacts(); + if (insertIndex == MANIFOLD_CACHE_SIZE) + { +#if MANIFOLD_CACHE_SIZE >= 4 + //sort cache so best points come first, based on area + insertIndex = sortCachedPoints(newPoint); +#else + insertIndex = 0; +#endif + + + } else + { + m_cachedPoints++; + + + } + replaceContactPoint(newPoint,insertIndex); +} + +btScalar btPersistentManifold::getContactBreakingThreshold() const +{ + return gContactBreakingThreshold; +} + +void btPersistentManifold::refreshContactPoints(const btTransform& trA,const btTransform& trB) +{ + int i; + + /// first refresh worldspace positions and distance + for (i=getNumContacts()-1;i>=0;i--) + { + btManifoldPoint &manifoldPoint = m_pointCache[i]; + manifoldPoint.m_positionWorldOnA = trA( manifoldPoint.m_localPointA ); + manifoldPoint.m_positionWorldOnB = trB( manifoldPoint.m_localPointB ); + manifoldPoint.m_distance1 = (manifoldPoint.m_positionWorldOnA - manifoldPoint.m_positionWorldOnB).dot(manifoldPoint.m_normalWorldOnB); + manifoldPoint.m_lifeTime++; + } + + /// then + btScalar distance2d; + btVector3 projectedDifference,projectedPoint; + for (i=getNumContacts()-1;i>=0;i--) + { + + btManifoldPoint &manifoldPoint = m_pointCache[i]; + //contact becomes invalid when signed distance exceeds margin (projected on contactnormal direction) + if (!validContactDistance(manifoldPoint)) + { + removeContactPoint(i); + } else + { + //contact also becomes invalid when relative movement orthogonal to normal exceeds margin + projectedPoint = manifoldPoint.m_positionWorldOnA - manifoldPoint.m_normalWorldOnB * manifoldPoint.m_distance1; + projectedDifference = manifoldPoint.m_positionWorldOnB - projectedPoint; + distance2d = projectedDifference.dot(projectedDifference); + if (distance2d > getContactBreakingThreshold()*getContactBreakingThreshold() ) + { + removeContactPoint(i); + } + } + } +#ifdef DEBUG_PERSISTENCY + DebugPersistency(); +#endif // +} + + + + + diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btPersistentManifold.h b/bullet/src/BulletCollision/NarrowPhaseCollision/btPersistentManifold.h new file mode 100644 index 0000000..b79eeca --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btPersistentManifold.h @@ -0,0 +1,161 @@ +/* +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. +*/ + +#ifndef PERSISTENT_MANIFOLD_H +#define PERSISTENT_MANIFOLD_H + + +#include "LinearMath/btVector3.h" +#include "LinearMath/btTransform.h" +#include "btManifoldPoint.h" + +struct btCollisionResult; + +///contact breaking and merging threshold +extern btScalar gContactBreakingThreshold; + +typedef bool (*ContactDestroyedCallback)(void* userPersistentData); +extern ContactDestroyedCallback gContactDestroyedCallback; + + + + +#define MANIFOLD_CACHE_SIZE 4 + +///btPersistentManifold maintains contact points, and reduces them to 4. +///It does contact filtering/contact reduction. +ATTRIBUTE_ALIGNED16( class) btPersistentManifold +{ + + btManifoldPoint m_pointCache[MANIFOLD_CACHE_SIZE]; + + /// this two body pointers can point to the physics rigidbody class. + /// void* will allow any rigidbody class + void* m_body0; + void* m_body1; + int m_cachedPoints; + + + /// sort cached points so most isolated points come first + int sortCachedPoints(const btManifoldPoint& pt); + + int findContactPoint(const btManifoldPoint* unUsed, int numUnused,const btManifoldPoint& pt); + +public: + + int m_index1; + + btPersistentManifold(); + + btPersistentManifold(void* body0,void* body1) + : m_body0(body0),m_body1(body1),m_cachedPoints(0) + { + } + + inline void* getBody0() { return m_body0;} + inline void* getBody1() { return m_body1;} + + inline const void* getBody0() const { return m_body0;} + inline const void* getBody1() const { return m_body1;} + + void setBodies(void* body0,void* body1) + { + m_body0 = body0; + m_body1 = body1; + } + + void clearUserCache(btManifoldPoint& pt); + +#ifdef DEBUG_PERSISTENCY + void DebugPersistency(); +#endif // + + inline int getNumContacts() const { return m_cachedPoints;} + + inline const btManifoldPoint& getContactPoint(int index) const + { + btAssert(index < m_cachedPoints); + return m_pointCache[index]; + } + + inline btManifoldPoint& getContactPoint(int index) + { + btAssert(index < m_cachedPoints); + return m_pointCache[index]; + } + + /// todo: get this margin from the current physics / collision environment + btScalar getContactBreakingThreshold() const; + + int getCacheEntry(const btManifoldPoint& newPoint) const; + + void AddManifoldPoint( const btManifoldPoint& newPoint); + + void removeContactPoint (int index) + { + clearUserCache(m_pointCache[index]); + + int lastUsedIndex = getNumContacts() - 1; +// m_pointCache[index] = m_pointCache[lastUsedIndex]; + if(index != lastUsedIndex) + { + m_pointCache[index] = m_pointCache[lastUsedIndex]; + //get rid of duplicated userPersistentData pointer + m_pointCache[lastUsedIndex].m_userPersistentData = 0; + } + + btAssert(m_pointCache[lastUsedIndex].m_userPersistentData==0); + m_cachedPoints--; + } + void replaceContactPoint(const btManifoldPoint& newPoint,int insertIndex) + { + btAssert(validContactDistance(newPoint)); + +#define MAINTAIN_PERSISTENCY 1 +#ifdef MAINTAIN_PERSISTENCY + int lifeTime = m_pointCache[insertIndex].getLifeTime(); + btAssert(lifeTime>=0); + void* cache = m_pointCache[insertIndex].m_userPersistentData; + + m_pointCache[insertIndex] = newPoint; + + m_pointCache[insertIndex].m_userPersistentData = cache; + m_pointCache[insertIndex].m_lifeTime = lifeTime; +#else + clearUserCache(m_pointCache[insertIndex]); + m_pointCache[insertIndex] = newPoint; + +#endif + } + + bool validContactDistance(const btManifoldPoint& pt) const + { + return pt.m_distance1 <= getContactBreakingThreshold(); + } + /// calculated new worldspace coordinates and depth, and reject points that exceed the collision margin + void refreshContactPoints( const btTransform& trA,const btTransform& trB); + + void clearManifold(); + + + +} +; + + + + + +#endif //PERSISTENT_MANIFOLD_H diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btPointCollector.h b/bullet/src/BulletCollision/NarrowPhaseCollision/btPointCollector.h new file mode 100644 index 0000000..f0c4bb7 --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btPointCollector.h @@ -0,0 +1,61 @@ +/* +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. +*/ + +#ifndef POINT_COLLECTOR_H +#define POINT_COLLECTOR_H + +#include "btDiscreteCollisionDetectorInterface.h" + + + +struct btPointCollector : public btDiscreteCollisionDetectorInterface::Result +{ + + + btVector3 m_normalOnBInWorld; + btVector3 m_pointInWorld; + btScalar m_distance;//negative means penetration + + bool m_hasResult; + + btPointCollector () + : m_distance(btScalar(1e30)),m_hasResult(false) + { + } + + virtual void setShapeIdentifiers(int partId0,int index0, int partId1,int index1) + { + (void)partId0; + (void)index0; + (void)partId1; + (void)index1; + //?? + } + + virtual void addContactPoint(const btVector3& normalOnBInWorld,const btVector3& pointInWorld,btScalar depth) + { + if (depth< m_distance) + { + m_hasResult = true; + m_normalOnBInWorld = normalOnBInWorld; + m_pointInWorld = pointInWorld; + //negative means penetration + m_distance = depth; + } + } +}; + +#endif //POINT_COLLECTOR_H + diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.cpp b/bullet/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.cpp new file mode 100644 index 0000000..364903a --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.cpp @@ -0,0 +1,101 @@ +/* +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. +*/ + + +#include "btRaycastCallback.h" + +btTriangleRaycastCallback::btTriangleRaycastCallback(const btVector3& from,const btVector3& to) + : + m_from(from), + m_to(to), + m_hitFraction(btScalar(1.)) +{ + +} + + + +void btTriangleRaycastCallback::processTriangle(btVector3* triangle,int partId, int triangleIndex) +{ + + + const btVector3 &vert0=triangle[0]; + const btVector3 &vert1=triangle[1]; + const btVector3 &vert2=triangle[2]; + + btVector3 v10; v10 = vert1 - vert0 ; + btVector3 v20; v20 = vert2 - vert0 ; + + btVector3 triangleNormal; triangleNormal = v10.cross( v20 ); + + const btScalar dist = vert0.dot(triangleNormal); + btScalar dist_a = triangleNormal.dot(m_from) ; + dist_a-= dist; + btScalar dist_b = triangleNormal.dot(m_to); + dist_b -= dist; + + if ( dist_a * dist_b >= btScalar(0.0) ) + { + return ; // same sign + } + + const btScalar proj_length=dist_a-dist_b; + const btScalar distance = (dist_a)/(proj_length); + // Now we have the intersection point on the plane, we'll see if it's inside the triangle + // Add an epsilon as a tolerance for the raycast, + // in case the ray hits exacly on the edge of the triangle. + // It must be scaled for the triangle size. + + if(distance < m_hitFraction) + { + + + btScalar edge_tolerance =triangleNormal.length2(); + edge_tolerance *= btScalar(-0.0001); + btVector3 point; point.setInterpolate3( m_from, m_to, distance); + { + btVector3 v0p; v0p = vert0 - point; + btVector3 v1p; v1p = vert1 - point; + btVector3 cp0; cp0 = v0p.cross( v1p ); + + if ( (btScalar)(cp0.dot(triangleNormal)) >=edge_tolerance) + { + + + btVector3 v2p; v2p = vert2 - point; + btVector3 cp1; + cp1 = v1p.cross( v2p); + if ( (btScalar)(cp1.dot(triangleNormal)) >=edge_tolerance) + { + btVector3 cp2; + cp2 = v2p.cross(v0p); + + if ( (btScalar)(cp2.dot(triangleNormal)) >=edge_tolerance) + { + + if ( dist_a > 0 ) + { + m_hitFraction = reportHit(triangleNormal,distance,partId,triangleIndex); + } + else + { + m_hitFraction = reportHit(-triangleNormal,distance,partId,triangleIndex); + } + } + } + } + } + } +} diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h b/bullet/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h new file mode 100644 index 0000000..2dd219b --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h @@ -0,0 +1,42 @@ +/* +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. +*/ + +#ifndef RAYCAST_TRI_CALLBACK_H +#define RAYCAST_TRI_CALLBACK_H + +#include "BulletCollision/CollisionShapes/btTriangleCallback.h" +struct btBroadphaseProxy; + + +class btTriangleRaycastCallback: public btTriangleCallback +{ +public: + + //input + btVector3 m_from; + btVector3 m_to; + + btScalar m_hitFraction; + + btTriangleRaycastCallback(const btVector3& from,const btVector3& to); + + virtual void processTriangle(btVector3* triangle, int partId, int triangleIndex); + + virtual btScalar reportHit(const btVector3& hitNormalLocal, btScalar hitFraction, int partId, int triangleIndex ) = 0; + +}; + +#endif //RAYCAST_TRI_CALLBACK_H + diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btSimplexSolverInterface.h b/bullet/src/BulletCollision/NarrowPhaseCollision/btSimplexSolverInterface.h new file mode 100644 index 0000000..b3ce330 --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btSimplexSolverInterface.h @@ -0,0 +1,64 @@ +/* +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. +*/ + + + +#ifndef SIMPLEX_SOLVER_INTERFACE_H +#define SIMPLEX_SOLVER_INTERFACE_H + +#include "LinearMath/btVector3.h" +#include "LinearMath/btPoint3.h" + +#define NO_VIRTUAL_INTERFACE 1 +#ifdef NO_VIRTUAL_INTERFACE +#include "btVoronoiSimplexSolver.h" +#define btSimplexSolverInterface btVoronoiSimplexSolver +#else + +/// btSimplexSolverInterface can incrementally calculate distance between origin and up to 4 vertices +/// Used by GJK or Linear Casting. Can be implemented by the Johnson-algorithm or alternative approaches based on +/// voronoi regions or barycentric coordinates +class btSimplexSolverInterface +{ + public: + virtual ~btSimplexSolverInterface() {}; + + virtual void reset() = 0; + + virtual void addVertex(const btVector3& w, const btPoint3& p, const btPoint3& q) = 0; + + virtual bool closest(btVector3& v) = 0; + + virtual btScalar maxVertex() = 0; + + virtual bool fullSimplex() const = 0; + + virtual int getSimplex(btPoint3 *pBuf, btPoint3 *qBuf, btVector3 *yBuf) const = 0; + + virtual bool inSimplex(const btVector3& w) = 0; + + virtual void backup_closest(btVector3& v) = 0; + + virtual bool emptySimplex() const = 0; + + virtual void compute_points(btPoint3& p1, btPoint3& p2) = 0; + + virtual int numVertices() const =0; + + +}; +#endif +#endif //SIMPLEX_SOLVER_INTERFACE_H + diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.cpp b/bullet/src/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.cpp new file mode 100644 index 0000000..a2b3e94 --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.cpp @@ -0,0 +1,139 @@ +/* +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. +*/ + + +#include "btSubSimplexConvexCast.h" +#include "BulletCollision/CollisionShapes/btConvexShape.h" +#include "BulletCollision/CollisionShapes/btMinkowskiSumShape.h" +#include "BulletCollision/NarrowPhaseCollision/btSimplexSolverInterface.h" + + +btSubsimplexConvexCast::btSubsimplexConvexCast (const btConvexShape* convexA,const btConvexShape* convexB,btSimplexSolverInterface* simplexSolver) +:m_simplexSolver(simplexSolver), +m_convexA(convexA),m_convexB(convexB) +{ +} + +///Typically the conservative advancement reaches solution in a few iterations, clip it to 32 for degenerate cases. +///See discussion about this here http://continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=565 +#ifdef BT_USE_DOUBLE_PRECISION +#define MAX_ITERATIONS 64 +#else +#define MAX_ITERATIONS 32 +#endif +bool btSubsimplexConvexCast::calcTimeOfImpact( + const btTransform& fromA, + const btTransform& toA, + const btTransform& fromB, + const btTransform& toB, + CastResult& result) +{ + + btMinkowskiSumShape combi(m_convexA,m_convexB); + btMinkowskiSumShape* convex = &combi; + + btTransform rayFromLocalA; + btTransform rayToLocalA; + + rayFromLocalA = fromA.inverse()* fromB; + rayToLocalA = toA.inverse()* toB; + + + m_simplexSolver->reset(); + + convex->setTransformB(btTransform(rayFromLocalA.getBasis())); + + //btScalar radius = btScalar(0.01); + + btScalar lambda = btScalar(0.); + //todo: need to verify this: + //because of minkowski difference, we need the inverse direction + + btVector3 s = -rayFromLocalA.getOrigin(); + btVector3 r = -(rayToLocalA.getOrigin()-rayFromLocalA.getOrigin()); + btVector3 x = s; + btVector3 v; + btVector3 arbitraryPoint = convex->localGetSupportingVertex(r); + + v = x - arbitraryPoint; + + int maxIter = MAX_ITERATIONS; + + btVector3 n; + n.setValue(btScalar(0.),btScalar(0.),btScalar(0.)); + bool hasResult = false; + btVector3 c; + + btScalar lastLambda = lambda; + + + btScalar dist2 = v.length2(); +#ifdef BT_USE_DOUBLE_PRECISION + btScalar epsilon = btScalar(0.0001); +#else + btScalar epsilon = btScalar(0.0001); +#endif //BT_USE_DOUBLE_PRECISION + btVector3 w,p; + btScalar VdotR; + + while ( (dist2 > epsilon) && maxIter--) + { + p = convex->localGetSupportingVertex( v); + w = x - p; + + btScalar VdotW = v.dot(w); + + if ( VdotW > btScalar(0.)) + { + VdotR = v.dot(r); + + if (VdotR >= -(SIMD_EPSILON*SIMD_EPSILON)) + return false; + else + { + lambda = lambda - VdotW / VdotR; + x = s + lambda * r; + m_simplexSolver->reset(); + //check next line + w = x-p; + lastLambda = lambda; + n = v; + hasResult = true; + } + } + m_simplexSolver->addVertex( w, x , p); + if (m_simplexSolver->closest(v)) + { + dist2 = v.length2(); + hasResult = true; + //printf("V=%f , %f, %f\n",v[0],v[1],v[2]); + //printf("DIST2=%f\n",dist2); + //printf("numverts = %i\n",m_simplexSolver->numVertices()); + } else + { + dist2 = btScalar(0.); + } + } + + //int numiter = MAX_ITERATIONS - maxIter; +// printf("number of iterations: %d", numiter); + result.m_fraction = lambda; + result.m_normal = n; + + return true; +} + + + diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h b/bullet/src/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h new file mode 100644 index 0000000..eaa4725 --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h @@ -0,0 +1,50 @@ +/* +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. +*/ + + +#ifndef SUBSIMPLEX_CONVEX_CAST_H +#define SUBSIMPLEX_CONVEX_CAST_H + +#include "btConvexCast.h" +#include "btSimplexSolverInterface.h" +class btConvexShape; + +/// btSubsimplexConvexCast implements Gino van den Bergens' paper +///"Ray Casting against bteral Convex Objects with Application to Continuous Collision Detection" +/// GJK based Ray Cast, optimized version +/// Objects should not start in overlap, otherwise results are not defined. +class btSubsimplexConvexCast : public btConvexCast +{ + btSimplexSolverInterface* m_simplexSolver; + const btConvexShape* m_convexA; + const btConvexShape* m_convexB; + +public: + + btSubsimplexConvexCast (const btConvexShape* shapeA,const btConvexShape* shapeB,btSimplexSolverInterface* simplexSolver); + + //virtual ~btSubsimplexConvexCast(); + ///SimsimplexConvexCast calculateTimeOfImpact calculates the time of impact+normal for the linear cast (sweep) between two moving objects. + ///Precondition is that objects should not penetration/overlap at the start from the interval. Overlap can be tested using btGjkPairDetector. + virtual bool calcTimeOfImpact( + const btTransform& fromA, + const btTransform& toA, + const btTransform& fromB, + const btTransform& toB, + CastResult& result); + +}; + +#endif //SUBSIMPLEX_CONVEX_CAST_H diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.cpp b/bullet/src/BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.cpp new file mode 100644 index 0000000..14cc071 --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.cpp @@ -0,0 +1,607 @@ + +/* +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. + + Elsevier CDROM license agreements grants nonexclusive license to use the software + for any purpose, commercial or non-commercial as long as the following credit is included + identifying the original source of the software: + + Parts of the source are "from the book Real-Time Collision Detection by + Christer Ericson, published by Morgan Kaufmann Publishers, + (c) 2005 Elsevier Inc." + +*/ + + +#include "btVoronoiSimplexSolver.h" +#include +#include + +#define VERTA 0 +#define VERTB 1 +#define VERTC 2 +#define VERTD 3 + +#define CATCH_DEGENERATE_TETRAHEDRON 1 +void btVoronoiSimplexSolver::removeVertex(int index) +{ + + assert(m_numVertices>0); + m_numVertices--; + m_simplexVectorW[index] = m_simplexVectorW[m_numVertices]; + m_simplexPointsP[index] = m_simplexPointsP[m_numVertices]; + m_simplexPointsQ[index] = m_simplexPointsQ[m_numVertices]; +} + +void btVoronoiSimplexSolver::reduceVertices (const btUsageBitfield& usedVerts) +{ + if ((numVertices() >= 4) && (!usedVerts.usedVertexD)) + removeVertex(3); + + if ((numVertices() >= 3) && (!usedVerts.usedVertexC)) + removeVertex(2); + + if ((numVertices() >= 2) && (!usedVerts.usedVertexB)) + removeVertex(1); + + if ((numVertices() >= 1) && (!usedVerts.usedVertexA)) + removeVertex(0); + +} + + + + + +//clear the simplex, remove all the vertices +void btVoronoiSimplexSolver::reset() +{ + m_cachedValidClosest = false; + m_numVertices = 0; + m_needsUpdate = true; + m_lastW = btVector3(btScalar(1e30),btScalar(1e30),btScalar(1e30)); + m_cachedBC.reset(); +} + + + + //add a vertex +void btVoronoiSimplexSolver::addVertex(const btVector3& w, const btPoint3& p, const btPoint3& q) +{ + m_lastW = w; + m_needsUpdate = true; + + m_simplexVectorW[m_numVertices] = w; + m_simplexPointsP[m_numVertices] = p; + m_simplexPointsQ[m_numVertices] = q; + + m_numVertices++; +} + +bool btVoronoiSimplexSolver::updateClosestVectorAndPoints() +{ + + if (m_needsUpdate) + { + m_cachedBC.reset(); + + m_needsUpdate = false; + + switch (numVertices()) + { + case 0: + m_cachedValidClosest = false; + break; + case 1: + { + m_cachedP1 = m_simplexPointsP[0]; + m_cachedP2 = m_simplexPointsQ[0]; + m_cachedV = m_cachedP1-m_cachedP2; //== m_simplexVectorW[0] + m_cachedBC.reset(); + m_cachedBC.setBarycentricCoordinates(btScalar(1.),btScalar(0.),btScalar(0.),btScalar(0.)); + m_cachedValidClosest = m_cachedBC.isValid(); + break; + }; + case 2: + { + //closest point origin from line segment + const btVector3& from = m_simplexVectorW[0]; + const btVector3& to = m_simplexVectorW[1]; + btVector3 nearest; + + btVector3 p (btScalar(0.),btScalar(0.),btScalar(0.)); + btVector3 diff = p - from; + btVector3 v = to - from; + btScalar t = v.dot(diff); + + if (t > 0) { + btScalar dotVV = v.dot(v); + if (t < dotVV) { + t /= dotVV; + diff -= t*v; + m_cachedBC.m_usedVertices.usedVertexA = true; + m_cachedBC.m_usedVertices.usedVertexB = true; + } else { + t = 1; + diff -= v; + //reduce to 1 point + m_cachedBC.m_usedVertices.usedVertexB = true; + } + } else + { + t = 0; + //reduce to 1 point + m_cachedBC.m_usedVertices.usedVertexA = true; + } + m_cachedBC.setBarycentricCoordinates(1-t,t); + nearest = from + t*v; + + m_cachedP1 = m_simplexPointsP[0] + t * (m_simplexPointsP[1] - m_simplexPointsP[0]); + m_cachedP2 = m_simplexPointsQ[0] + t * (m_simplexPointsQ[1] - m_simplexPointsQ[0]); + m_cachedV = m_cachedP1 - m_cachedP2; + + reduceVertices(m_cachedBC.m_usedVertices); + + m_cachedValidClosest = m_cachedBC.isValid(); + break; + } + case 3: + { + //closest point origin from triangle + btVector3 p (btScalar(0.),btScalar(0.),btScalar(0.)); + + const btVector3& a = m_simplexVectorW[0]; + const btVector3& b = m_simplexVectorW[1]; + const btVector3& c = m_simplexVectorW[2]; + + closestPtPointTriangle(p,a,b,c,m_cachedBC); + m_cachedP1 = m_simplexPointsP[0] * m_cachedBC.m_barycentricCoords[0] + + m_simplexPointsP[1] * m_cachedBC.m_barycentricCoords[1] + + m_simplexPointsP[2] * m_cachedBC.m_barycentricCoords[2]; + + m_cachedP2 = m_simplexPointsQ[0] * m_cachedBC.m_barycentricCoords[0] + + m_simplexPointsQ[1] * m_cachedBC.m_barycentricCoords[1] + + m_simplexPointsQ[2] * m_cachedBC.m_barycentricCoords[2]; + + m_cachedV = m_cachedP1-m_cachedP2; + + reduceVertices (m_cachedBC.m_usedVertices); + m_cachedValidClosest = m_cachedBC.isValid(); + + break; + } + case 4: + { + + + btVector3 p (btScalar(0.),btScalar(0.),btScalar(0.)); + + const btVector3& a = m_simplexVectorW[0]; + const btVector3& b = m_simplexVectorW[1]; + const btVector3& c = m_simplexVectorW[2]; + const btVector3& d = m_simplexVectorW[3]; + + bool hasSeperation = closestPtPointTetrahedron(p,a,b,c,d,m_cachedBC); + + if (hasSeperation) + { + + m_cachedP1 = m_simplexPointsP[0] * m_cachedBC.m_barycentricCoords[0] + + m_simplexPointsP[1] * m_cachedBC.m_barycentricCoords[1] + + m_simplexPointsP[2] * m_cachedBC.m_barycentricCoords[2] + + m_simplexPointsP[3] * m_cachedBC.m_barycentricCoords[3]; + + m_cachedP2 = m_simplexPointsQ[0] * m_cachedBC.m_barycentricCoords[0] + + m_simplexPointsQ[1] * m_cachedBC.m_barycentricCoords[1] + + m_simplexPointsQ[2] * m_cachedBC.m_barycentricCoords[2] + + m_simplexPointsQ[3] * m_cachedBC.m_barycentricCoords[3]; + + m_cachedV = m_cachedP1-m_cachedP2; + reduceVertices (m_cachedBC.m_usedVertices); + } else + { +// printf("sub distance got penetration\n"); + + if (m_cachedBC.m_degenerate) + { + m_cachedValidClosest = false; + } else + { + m_cachedValidClosest = true; + //degenerate case == false, penetration = true + zero + m_cachedV.setValue(btScalar(0.),btScalar(0.),btScalar(0.)); + } + break; + } + + m_cachedValidClosest = m_cachedBC.isValid(); + + //closest point origin from tetrahedron + break; + } + default: + { + m_cachedValidClosest = false; + } + }; + } + + return m_cachedValidClosest; + +} + +//return/calculate the closest vertex +bool btVoronoiSimplexSolver::closest(btVector3& v) +{ + bool succes = updateClosestVectorAndPoints(); + v = m_cachedV; + return succes; +} + + + +btScalar btVoronoiSimplexSolver::maxVertex() +{ + int i, numverts = numVertices(); + btScalar maxV = btScalar(0.); + for (i=0;i= btScalar(0.0) && d4 <= d3) + { + result.m_closestPointOnSimplex = b; + result.m_usedVertices.usedVertexB = true; + result.setBarycentricCoordinates(0,1,0); + + return true; // b; // barycentric coordinates (0,1,0) + } + // Check if P in edge region of AB, if so return projection of P onto AB + btScalar vc = d1*d4 - d3*d2; + if (vc <= btScalar(0.0) && d1 >= btScalar(0.0) && d3 <= btScalar(0.0)) { + btScalar v = d1 / (d1 - d3); + result.m_closestPointOnSimplex = a + v * ab; + result.m_usedVertices.usedVertexA = true; + result.m_usedVertices.usedVertexB = true; + result.setBarycentricCoordinates(1-v,v,0); + return true; + //return a + v * ab; // barycentric coordinates (1-v,v,0) + } + + // Check if P in vertex region outside C + btVector3 cp = p - c; + btScalar d5 = ab.dot(cp); + btScalar d6 = ac.dot(cp); + if (d6 >= btScalar(0.0) && d5 <= d6) + { + result.m_closestPointOnSimplex = c; + result.m_usedVertices.usedVertexC = true; + result.setBarycentricCoordinates(0,0,1); + return true;//c; // barycentric coordinates (0,0,1) + } + + // Check if P in edge region of AC, if so return projection of P onto AC + btScalar vb = d5*d2 - d1*d6; + if (vb <= btScalar(0.0) && d2 >= btScalar(0.0) && d6 <= btScalar(0.0)) { + btScalar w = d2 / (d2 - d6); + result.m_closestPointOnSimplex = a + w * ac; + result.m_usedVertices.usedVertexA = true; + result.m_usedVertices.usedVertexC = true; + result.setBarycentricCoordinates(1-w,0,w); + return true; + //return a + w * ac; // barycentric coordinates (1-w,0,w) + } + + // Check if P in edge region of BC, if so return projection of P onto BC + btScalar va = d3*d6 - d5*d4; + if (va <= btScalar(0.0) && (d4 - d3) >= btScalar(0.0) && (d5 - d6) >= btScalar(0.0)) { + btScalar w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); + + result.m_closestPointOnSimplex = b + w * (c - b); + result.m_usedVertices.usedVertexB = true; + result.m_usedVertices.usedVertexC = true; + result.setBarycentricCoordinates(0,1-w,w); + return true; + // return b + w * (c - b); // barycentric coordinates (0,1-w,w) + } + + // P inside face region. Compute Q through its barycentric coordinates (u,v,w) + btScalar denom = btScalar(1.0) / (va + vb + vc); + btScalar v = vb * denom; + btScalar w = vc * denom; + + result.m_closestPointOnSimplex = a + ab * v + ac * w; + result.m_usedVertices.usedVertexA = true; + result.m_usedVertices.usedVertexB = true; + result.m_usedVertices.usedVertexC = true; + result.setBarycentricCoordinates(1-v-w,v,w); + + return true; +// return a + ab * v + ac * w; // = u*a + v*b + w*c, u = va * denom = btScalar(1.0) - v - w + +} + + + + + +/// Test if point p and d lie on opposite sides of plane through abc +int btVoronoiSimplexSolver::pointOutsideOfPlane(const btPoint3& p, const btPoint3& a, const btPoint3& b, const btPoint3& c, const btPoint3& d) +{ + btVector3 normal = (b-a).cross(c-a); + + btScalar signp = (p - a).dot(normal); // [AP AB AC] + btScalar signd = (d - a).dot( normal); // [AD AB AC] + +#ifdef CATCH_DEGENERATE_TETRAHEDRON +#ifdef BT_USE_DOUBLE_PRECISION +if (signd * signd < (btScalar(1e-8) * btScalar(1e-8))) + { + return -1; + } +#else + if (signd * signd < (btScalar(1e-4) * btScalar(1e-4))) + { +// printf("affine dependent/degenerate\n");// + return -1; + } +#endif + +#endif + // Points on opposite sides if expression signs are opposite + return signp * signd < btScalar(0.); +} + + +bool btVoronoiSimplexSolver::closestPtPointTetrahedron(const btPoint3& p, const btPoint3& a, const btPoint3& b, const btPoint3& c, const btPoint3& d, btSubSimplexClosestResult& finalResult) +{ + btSubSimplexClosestResult tempResult; + + // Start out assuming point inside all halfspaces, so closest to itself + finalResult.m_closestPointOnSimplex = p; + finalResult.m_usedVertices.reset(); + finalResult.m_usedVertices.usedVertexA = true; + finalResult.m_usedVertices.usedVertexB = true; + finalResult.m_usedVertices.usedVertexC = true; + finalResult.m_usedVertices.usedVertexD = true; + + int pointOutsideABC = pointOutsideOfPlane(p, a, b, c, d); + int pointOutsideACD = pointOutsideOfPlane(p, a, c, d, b); + int pointOutsideADB = pointOutsideOfPlane(p, a, d, b, c); + int pointOutsideBDC = pointOutsideOfPlane(p, b, d, c, a); + + if (pointOutsideABC < 0 || pointOutsideACD < 0 || pointOutsideADB < 0 || pointOutsideBDC < 0) + { + finalResult.m_degenerate = true; + return false; + } + + if (!pointOutsideABC && !pointOutsideACD && !pointOutsideADB && !pointOutsideBDC) + { + return false; + } + + + btScalar bestSqDist = FLT_MAX; + // If point outside face abc then compute closest point on abc + if (pointOutsideABC) + { + closestPtPointTriangle(p, a, b, c,tempResult); + btPoint3 q = tempResult.m_closestPointOnSimplex; + + btScalar sqDist = (q - p).dot( q - p); + // Update best closest point if (squared) distance is less than current best + if (sqDist < bestSqDist) { + bestSqDist = sqDist; + finalResult.m_closestPointOnSimplex = q; + //convert result bitmask! + finalResult.m_usedVertices.reset(); + finalResult.m_usedVertices.usedVertexA = tempResult.m_usedVertices.usedVertexA; + finalResult.m_usedVertices.usedVertexB = tempResult.m_usedVertices.usedVertexB; + finalResult.m_usedVertices.usedVertexC = tempResult.m_usedVertices.usedVertexC; + finalResult.setBarycentricCoordinates( + tempResult.m_barycentricCoords[VERTA], + tempResult.m_barycentricCoords[VERTB], + tempResult.m_barycentricCoords[VERTC], + 0 + ); + + } + } + + + // Repeat test for face acd + if (pointOutsideACD) + { + closestPtPointTriangle(p, a, c, d,tempResult); + btPoint3 q = tempResult.m_closestPointOnSimplex; + //convert result bitmask! + + btScalar sqDist = (q - p).dot( q - p); + if (sqDist < bestSqDist) + { + bestSqDist = sqDist; + finalResult.m_closestPointOnSimplex = q; + finalResult.m_usedVertices.reset(); + finalResult.m_usedVertices.usedVertexA = tempResult.m_usedVertices.usedVertexA; + + finalResult.m_usedVertices.usedVertexC = tempResult.m_usedVertices.usedVertexB; + finalResult.m_usedVertices.usedVertexD = tempResult.m_usedVertices.usedVertexC; + finalResult.setBarycentricCoordinates( + tempResult.m_barycentricCoords[VERTA], + 0, + tempResult.m_barycentricCoords[VERTB], + tempResult.m_barycentricCoords[VERTC] + ); + + } + } + // Repeat test for face adb + + + if (pointOutsideADB) + { + closestPtPointTriangle(p, a, d, b,tempResult); + btPoint3 q = tempResult.m_closestPointOnSimplex; + //convert result bitmask! + + btScalar sqDist = (q - p).dot( q - p); + if (sqDist < bestSqDist) + { + bestSqDist = sqDist; + finalResult.m_closestPointOnSimplex = q; + finalResult.m_usedVertices.reset(); + finalResult.m_usedVertices.usedVertexA = tempResult.m_usedVertices.usedVertexA; + finalResult.m_usedVertices.usedVertexB = tempResult.m_usedVertices.usedVertexC; + + finalResult.m_usedVertices.usedVertexD = tempResult.m_usedVertices.usedVertexB; + finalResult.setBarycentricCoordinates( + tempResult.m_barycentricCoords[VERTA], + tempResult.m_barycentricCoords[VERTC], + 0, + tempResult.m_barycentricCoords[VERTB] + ); + + } + } + // Repeat test for face bdc + + + if (pointOutsideBDC) + { + closestPtPointTriangle(p, b, d, c,tempResult); + btPoint3 q = tempResult.m_closestPointOnSimplex; + //convert result bitmask! + btScalar sqDist = (q - p).dot( q - p); + if (sqDist < bestSqDist) + { + bestSqDist = sqDist; + finalResult.m_closestPointOnSimplex = q; + finalResult.m_usedVertices.reset(); + // + finalResult.m_usedVertices.usedVertexB = tempResult.m_usedVertices.usedVertexA; + finalResult.m_usedVertices.usedVertexC = tempResult.m_usedVertices.usedVertexC; + finalResult.m_usedVertices.usedVertexD = tempResult.m_usedVertices.usedVertexB; + + finalResult.setBarycentricCoordinates( + 0, + tempResult.m_barycentricCoords[VERTA], + tempResult.m_barycentricCoords[VERTC], + tempResult.m_barycentricCoords[VERTB] + ); + + } + } + + //help! we ended up full ! + + if (finalResult.m_usedVertices.usedVertexA && + finalResult.m_usedVertices.usedVertexB && + finalResult.m_usedVertices.usedVertexC && + finalResult.m_usedVertices.usedVertexD) + { + return true; + } + + return true; +} + diff --git a/bullet/src/BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h b/bullet/src/BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h new file mode 100644 index 0000000..3fd1b08 --- /dev/null +++ b/bullet/src/BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h @@ -0,0 +1,157 @@ +/* +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. +*/ + + + +#ifndef btVoronoiSimplexSolver_H +#define btVoronoiSimplexSolver_H + +#include "btSimplexSolverInterface.h" + + + +#define VORONOI_SIMPLEX_MAX_VERTS 5 + +struct btUsageBitfield{ + btUsageBitfield() + { + reset(); + } + + void reset() + { + usedVertexA = false; + usedVertexB = false; + usedVertexC = false; + usedVertexD = false; + } + unsigned short usedVertexA : 1; + unsigned short usedVertexB : 1; + unsigned short usedVertexC : 1; + unsigned short usedVertexD : 1; + unsigned short unused1 : 1; + unsigned short unused2 : 1; + unsigned short unused3 : 1; + unsigned short unused4 : 1; +}; + + +struct btSubSimplexClosestResult +{ + btPoint3 m_closestPointOnSimplex; + //MASK for m_usedVertices + //stores the simplex vertex-usage, using the MASK, + // if m_usedVertices & MASK then the related vertex is used + btUsageBitfield m_usedVertices; + btScalar m_barycentricCoords[4]; + bool m_degenerate; + + void reset() + { + m_degenerate = false; + setBarycentricCoordinates(); + m_usedVertices.reset(); + } + bool isValid() + { + bool valid = (m_barycentricCoords[0] >= btScalar(0.)) && + (m_barycentricCoords[1] >= btScalar(0.)) && + (m_barycentricCoords[2] >= btScalar(0.)) && + (m_barycentricCoords[3] >= btScalar(0.)); + + + return valid; + } + void setBarycentricCoordinates(btScalar a=btScalar(0.),btScalar b=btScalar(0.),btScalar c=btScalar(0.),btScalar d=btScalar(0.)) + { + m_barycentricCoords[0] = a; + m_barycentricCoords[1] = b; + m_barycentricCoords[2] = c; + m_barycentricCoords[3] = d; + } + +}; + +/// btVoronoiSimplexSolver is an implementation of the closest point distance algorithm from a 1-4 points simplex to the origin. +/// Can be used with GJK, as an alternative to Johnson distance algorithm. +#ifdef NO_VIRTUAL_INTERFACE +class btVoronoiSimplexSolver +#else +class btVoronoiSimplexSolver : public btSimplexSolverInterface +#endif +{ +public: + + int m_numVertices; + + btVector3 m_simplexVectorW[VORONOI_SIMPLEX_MAX_VERTS]; + btPoint3 m_simplexPointsP[VORONOI_SIMPLEX_MAX_VERTS]; + btPoint3 m_simplexPointsQ[VORONOI_SIMPLEX_MAX_VERTS]; + + + + btPoint3 m_cachedP1; + btPoint3 m_cachedP2; + btVector3 m_cachedV; + btVector3 m_lastW; + bool m_cachedValidClosest; + + btSubSimplexClosestResult m_cachedBC; + + bool m_needsUpdate; + + void removeVertex(int index); + void reduceVertices (const btUsageBitfield& usedVerts); + bool updateClosestVectorAndPoints(); + + bool closestPtPointTetrahedron(const btPoint3& p, const btPoint3& a, const btPoint3& b, const btPoint3& c, const btPoint3& d, btSubSimplexClosestResult& finalResult); + int pointOutsideOfPlane(const btPoint3& p, const btPoint3& a, const btPoint3& b, const btPoint3& c, const btPoint3& d); + bool closestPtPointTriangle(const btPoint3& p, const btPoint3& a, const btPoint3& b, const btPoint3& c,btSubSimplexClosestResult& result); + +public: + + void reset(); + + void addVertex(const btVector3& w, const btPoint3& p, const btPoint3& q); + + + bool closest(btVector3& v); + + btScalar maxVertex(); + + bool fullSimplex() const + { + return (m_numVertices == 4); + } + + int getSimplex(btPoint3 *pBuf, btPoint3 *qBuf, btVector3 *yBuf) const; + + bool inSimplex(const btVector3& w); + + void backup_closest(btVector3& v) ; + + bool emptySimplex() const ; + + void compute_points(btPoint3& p1, btPoint3& p2) ; + + int numVertices() const + { + return m_numVertices; + } + + +}; + +#endif //VoronoiSimplexSolver diff --git a/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp b/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp new file mode 100644 index 0000000..79cd8e5 --- /dev/null +++ b/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp @@ -0,0 +1,287 @@ +/* +Bullet Continuous Collision Detection and Physics Library +btConeTwistConstraint is 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: Marcus Hennix +*/ + + +#include "btConeTwistConstraint.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "LinearMath/btTransformUtil.h" +#include "LinearMath/btSimdMinMax.h" +#include + +btConeTwistConstraint::btConeTwistConstraint() +:btTypedConstraint(CONETWIST_CONSTRAINT_TYPE) +{ +} + + +btConeTwistConstraint::btConeTwistConstraint(btRigidBody& rbA,btRigidBody& rbB, + const btTransform& rbAFrame,const btTransform& rbBFrame) + :btTypedConstraint(CONETWIST_CONSTRAINT_TYPE, rbA,rbB),m_rbAFrame(rbAFrame),m_rbBFrame(rbBFrame), + m_angularOnly(false) +{ + // flip axis for correct angles + m_rbBFrame.getBasis()[1][0] *= btScalar(-1.); + m_rbBFrame.getBasis()[1][1] *= btScalar(-1.); + m_rbBFrame.getBasis()[1][2] *= btScalar(-1.); + + m_swingSpan1 = btScalar(1e30); + m_swingSpan2 = btScalar(1e30); + m_twistSpan = btScalar(1e30); + m_biasFactor = 0.3f; + m_relaxationFactor = 1.0f; + + m_solveTwistLimit = false; + m_solveSwingLimit = false; + +} + +btConeTwistConstraint::btConeTwistConstraint(btRigidBody& rbA,const btTransform& rbAFrame) + :btTypedConstraint(CONETWIST_CONSTRAINT_TYPE,rbA),m_rbAFrame(rbAFrame), + m_angularOnly(false) +{ + m_rbBFrame = m_rbAFrame; + + // flip axis for correct angles + m_rbBFrame.getBasis()[1][0] *= btScalar(-1.); + m_rbBFrame.getBasis()[1][1] *= btScalar(-1.); + m_rbBFrame.getBasis()[1][2] *= btScalar(-1.); + + m_rbBFrame.getBasis()[2][0] *= btScalar(-1.); + m_rbBFrame.getBasis()[2][1] *= btScalar(-1.); + m_rbBFrame.getBasis()[2][2] *= btScalar(-1.); + + m_swingSpan1 = btScalar(1e30); + m_swingSpan2 = btScalar(1e30); + m_twistSpan = btScalar(1e30); + m_biasFactor = 0.3f; + m_relaxationFactor = 1.0f; + + m_solveTwistLimit = false; + m_solveSwingLimit = false; + +} + +void btConeTwistConstraint::buildJacobian() +{ + m_appliedImpulse = btScalar(0.); + + //set bias, sign, clear accumulator + m_swingCorrection = btScalar(0.); + m_twistLimitSign = btScalar(0.); + m_solveTwistLimit = false; + m_solveSwingLimit = false; + m_accTwistLimitImpulse = btScalar(0.); + m_accSwingLimitImpulse = btScalar(0.); + + if (!m_angularOnly) + { + btVector3 pivotAInW = m_rbA.getCenterOfMassTransform()*m_rbAFrame.getOrigin(); + btVector3 pivotBInW = m_rbB.getCenterOfMassTransform()*m_rbBFrame.getOrigin(); + btVector3 relPos = pivotBInW - pivotAInW; + + btVector3 normal[3]; + if (relPos.length2() > SIMD_EPSILON) + { + normal[0] = relPos.normalized(); + } + else + { + normal[0].setValue(btScalar(1.0),0,0); + } + + btPlaneSpace1(normal[0], normal[1], normal[2]); + + for (int i=0;i<3;i++) + { + new (&m_jac[i]) btJacobianEntry( + m_rbA.getCenterOfMassTransform().getBasis().transpose(), + m_rbB.getCenterOfMassTransform().getBasis().transpose(), + pivotAInW - m_rbA.getCenterOfMassPosition(), + pivotBInW - m_rbB.getCenterOfMassPosition(), + normal[i], + m_rbA.getInvInertiaDiagLocal(), + m_rbA.getInvMass(), + m_rbB.getInvInertiaDiagLocal(), + m_rbB.getInvMass()); + } + } + + btVector3 b1Axis1,b1Axis2,b1Axis3; + btVector3 b2Axis1,b2Axis2; + + b1Axis1 = getRigidBodyA().getCenterOfMassTransform().getBasis() * this->m_rbAFrame.getBasis().getColumn(0); + b2Axis1 = getRigidBodyB().getCenterOfMassTransform().getBasis() * this->m_rbBFrame.getBasis().getColumn(0); + + btScalar swing1=btScalar(0.),swing2 = btScalar(0.); + + // Get Frame into world space + if (m_swingSpan1 >= btScalar(0.05f)) + { + b1Axis2 = getRigidBodyA().getCenterOfMassTransform().getBasis() * this->m_rbAFrame.getBasis().getColumn(1); + swing1 = btAtan2Fast( b2Axis1.dot(b1Axis2),b2Axis1.dot(b1Axis1) ); + } + + if (m_swingSpan2 >= btScalar(0.05f)) + { + b1Axis3 = getRigidBodyA().getCenterOfMassTransform().getBasis() * this->m_rbAFrame.getBasis().getColumn(2); + swing2 = btAtan2Fast( b2Axis1.dot(b1Axis3),b2Axis1.dot(b1Axis1) ); + } + + btScalar RMaxAngle1Sq = 1.0f / (m_swingSpan1*m_swingSpan1); + btScalar RMaxAngle2Sq = 1.0f / (m_swingSpan2*m_swingSpan2); + btScalar EllipseAngle = btFabs(swing1)* RMaxAngle1Sq + btFabs(swing2) * RMaxAngle2Sq; + + if (EllipseAngle > 1.0f) + { + m_swingCorrection = EllipseAngle-1.0f; + m_solveSwingLimit = true; + + // Calculate necessary axis & factors + m_swingAxis = b2Axis1.cross(b1Axis2* b2Axis1.dot(b1Axis2) + b1Axis3* b2Axis1.dot(b1Axis3)); + m_swingAxis.normalize(); + + btScalar swingAxisSign = (b2Axis1.dot(b1Axis1) >= 0.0f) ? 1.0f : -1.0f; + m_swingAxis *= swingAxisSign; + + m_kSwing = btScalar(1.) / (getRigidBodyA().computeAngularImpulseDenominator(m_swingAxis) + + getRigidBodyB().computeAngularImpulseDenominator(m_swingAxis)); + + } + + // Twist limits + if (m_twistSpan >= btScalar(0.)) + { + btVector3 b2Axis2 = getRigidBodyB().getCenterOfMassTransform().getBasis() * this->m_rbBFrame.getBasis().getColumn(1); + btQuaternion rotationArc = shortestArcQuat(b2Axis1,b1Axis1); + btVector3 TwistRef = quatRotate(rotationArc,b2Axis2); + btScalar twist = btAtan2Fast( TwistRef.dot(b1Axis3), TwistRef.dot(b1Axis2) ); + + btScalar lockedFreeFactor = (m_twistSpan > btScalar(0.05f)) ? m_limitSoftness : btScalar(0.); + if (twist <= -m_twistSpan*lockedFreeFactor) + { + m_twistCorrection = -(twist + m_twistSpan); + m_solveTwistLimit = true; + + m_twistAxis = (b2Axis1 + b1Axis1) * 0.5f; + m_twistAxis.normalize(); + m_twistAxis *= -1.0f; + + m_kTwist = btScalar(1.) / (getRigidBodyA().computeAngularImpulseDenominator(m_twistAxis) + + getRigidBodyB().computeAngularImpulseDenominator(m_twistAxis)); + + } else + if (twist > m_twistSpan*lockedFreeFactor) + { + m_twistCorrection = (twist - m_twistSpan); + m_solveTwistLimit = true; + + m_twistAxis = (b2Axis1 + b1Axis1) * 0.5f; + m_twistAxis.normalize(); + + m_kTwist = btScalar(1.) / (getRigidBodyA().computeAngularImpulseDenominator(m_twistAxis) + + getRigidBodyB().computeAngularImpulseDenominator(m_twistAxis)); + + } + } +} + +void btConeTwistConstraint::solveConstraint(btScalar timeStep) +{ + + btVector3 pivotAInW = m_rbA.getCenterOfMassTransform()*m_rbAFrame.getOrigin(); + btVector3 pivotBInW = m_rbB.getCenterOfMassTransform()*m_rbBFrame.getOrigin(); + + btScalar tau = btScalar(0.3); + btScalar damping = btScalar(1.); + + //linear part + if (!m_angularOnly) + { + btVector3 rel_pos1 = pivotAInW - m_rbA.getCenterOfMassPosition(); + btVector3 rel_pos2 = pivotBInW - m_rbB.getCenterOfMassPosition(); + + btVector3 vel1 = m_rbA.getVelocityInLocalPoint(rel_pos1); + btVector3 vel2 = m_rbB.getVelocityInLocalPoint(rel_pos2); + btVector3 vel = vel1 - vel2; + + for (int i=0;i<3;i++) + { + const btVector3& normal = m_jac[i].m_linearJointAxis; + btScalar jacDiagABInv = btScalar(1.) / m_jac[i].getDiagonal(); + + btScalar rel_vel; + rel_vel = normal.dot(vel); + //positional error (zeroth order error) + btScalar depth = -(pivotAInW - pivotBInW).dot(normal); //this is the error projected on the normal + btScalar impulse = depth*tau/timeStep * jacDiagABInv - rel_vel * jacDiagABInv; + m_appliedImpulse += impulse; + btVector3 impulse_vector = normal * impulse; + m_rbA.applyImpulse(impulse_vector, pivotAInW - m_rbA.getCenterOfMassPosition()); + m_rbB.applyImpulse(-impulse_vector, pivotBInW - m_rbB.getCenterOfMassPosition()); + } + } + + { + ///solve angular part + const btVector3& angVelA = getRigidBodyA().getAngularVelocity(); + const btVector3& angVelB = getRigidBodyB().getAngularVelocity(); + + // solve swing limit + if (m_solveSwingLimit) + { + btScalar amplitude = ((angVelB - angVelA).dot( m_swingAxis )*m_relaxationFactor*m_relaxationFactor + m_swingCorrection*(btScalar(1.)/timeStep)*m_biasFactor); + btScalar impulseMag = amplitude * m_kSwing; + + // Clamp the accumulated impulse + btScalar temp = m_accSwingLimitImpulse; + m_accSwingLimitImpulse = btMax(m_accSwingLimitImpulse + impulseMag, 0.0f ); + impulseMag = m_accSwingLimitImpulse - temp; + + btVector3 impulse = m_swingAxis * impulseMag; + + m_rbA.applyTorqueImpulse(impulse); + m_rbB.applyTorqueImpulse(-impulse); + + } + + // solve twist limit + if (m_solveTwistLimit) + { + btScalar amplitude = ((angVelB - angVelA).dot( m_twistAxis )*m_relaxationFactor*m_relaxationFactor + m_twistCorrection*(btScalar(1.)/timeStep)*m_biasFactor ); + btScalar impulseMag = amplitude * m_kTwist; + + // Clamp the accumulated impulse + btScalar temp = m_accTwistLimitImpulse; + m_accTwistLimitImpulse = btMax(m_accTwistLimitImpulse + impulseMag, 0.0f ); + impulseMag = m_accTwistLimitImpulse - temp; + + btVector3 impulse = m_twistAxis * impulseMag; + + m_rbA.applyTorqueImpulse(impulse); + m_rbB.applyTorqueImpulse(-impulse); + + } + + } + +} + +void btConeTwistConstraint::updateRHS(btScalar timeStep) +{ + (void)timeStep; + +} diff --git a/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.h b/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.h new file mode 100644 index 0000000..8e33e19 --- /dev/null +++ b/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.h @@ -0,0 +1,126 @@ +/* +Bullet Continuous Collision Detection and Physics Library +btConeTwistConstraint is 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: Marcus Hennix +*/ + + + +#ifndef CONETWISTCONSTRAINT_H +#define CONETWISTCONSTRAINT_H + +#include "../../LinearMath/btVector3.h" +#include "btJacobianEntry.h" +#include "btTypedConstraint.h" + +class btRigidBody; + + +///btConeTwistConstraint can be used to simulate ragdoll joints (upper arm, leg etc) +class btConeTwistConstraint : public btTypedConstraint +{ +#ifdef IN_PARALLELL_SOLVER +public: +#endif + btJacobianEntry m_jac[3]; //3 orthogonal linear constraints + + btTransform m_rbAFrame; + btTransform m_rbBFrame; + + btScalar m_limitSoftness; + btScalar m_biasFactor; + btScalar m_relaxationFactor; + + btScalar m_swingSpan1; + btScalar m_swingSpan2; + btScalar m_twistSpan; + + btVector3 m_swingAxis; + btVector3 m_twistAxis; + + btScalar m_kSwing; + btScalar m_kTwist; + + btScalar m_twistLimitSign; + btScalar m_swingCorrection; + btScalar m_twistCorrection; + + btScalar m_accSwingLimitImpulse; + btScalar m_accTwistLimitImpulse; + + bool m_angularOnly; + bool m_solveTwistLimit; + bool m_solveSwingLimit; + + +public: + + btConeTwistConstraint(btRigidBody& rbA,btRigidBody& rbB,const btTransform& rbAFrame, const btTransform& rbBFrame); + + btConeTwistConstraint(btRigidBody& rbA,const btTransform& rbAFrame); + + btConeTwistConstraint(); + + virtual void buildJacobian(); + + virtual void solveConstraint(btScalar timeStep); + + void updateRHS(btScalar timeStep); + + const btRigidBody& getRigidBodyA() const + { + return m_rbA; + } + const btRigidBody& getRigidBodyB() const + { + return m_rbB; + } + + void setAngularOnly(bool angularOnly) + { + m_angularOnly = angularOnly; + } + + void setLimit(btScalar _swingSpan1,btScalar _swingSpan2,btScalar _twistSpan, btScalar _softness = 0.8f, btScalar _biasFactor = 0.3f, btScalar _relaxationFactor = 1.0f) + { + m_swingSpan1 = _swingSpan1; + m_swingSpan2 = _swingSpan2; + m_twistSpan = _twistSpan; + + m_limitSoftness = _softness; + m_biasFactor = _biasFactor; + m_relaxationFactor = _relaxationFactor; + } + + const btTransform& getAFrame() { return m_rbAFrame; }; + const btTransform& getBFrame() { return m_rbBFrame; }; + + inline int getSolveTwistLimit() + { + return m_solveTwistLimit; + } + + inline int getSolveSwingLimit() + { + return m_solveTwistLimit; + } + + inline btScalar getTwistLimitSign() + { + return m_twistLimitSign; + } + +}; + +#endif //CONETWISTCONSTRAINT_H diff --git a/bullet/src/BulletDynamics/ConstraintSolver/btConstraintSolver.h b/bullet/src/BulletDynamics/ConstraintSolver/btConstraintSolver.h new file mode 100644 index 0000000..9b161eb --- /dev/null +++ b/bullet/src/BulletDynamics/ConstraintSolver/btConstraintSolver.h @@ -0,0 +1,52 @@ +/* +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. +*/ + +#ifndef CONSTRAINT_SOLVER_H +#define CONSTRAINT_SOLVER_H + +#include "LinearMath/btScalar.h" + +class btPersistentManifold; +class btRigidBody; +class btCollisionObject; +class btTypedConstraint; +struct btContactSolverInfo; +struct btBroadphaseProxy; +class btIDebugDraw; +class btStackAlloc; + +/// btConstraintSolver provides solver interface +class btConstraintSolver +{ + +public: + + virtual ~btConstraintSolver() {} + + virtual void prepareSolve (int numBodies, int numManifolds) {;} + + ///solve a group of constraints + virtual btScalar solveGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints, const btContactSolverInfo& info,class btIDebugDraw* debugDrawer, btStackAlloc* stackAlloc) = 0; + + virtual void allSolved (const btContactSolverInfo& info,class btIDebugDraw* debugDrawer, btStackAlloc* stackAlloc) {;} + + ///clear internal cached data and reset random seed + virtual void reset() = 0; +}; + + + + +#endif //CONSTRAINT_SOLVER_H diff --git a/bullet/src/BulletDynamics/ConstraintSolver/btContactConstraint.cpp b/bullet/src/BulletDynamics/ConstraintSolver/btContactConstraint.cpp new file mode 100644 index 0000000..d67b827 --- /dev/null +++ b/bullet/src/BulletDynamics/ConstraintSolver/btContactConstraint.cpp @@ -0,0 +1,417 @@ +/* +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. +*/ + + +#include "btContactConstraint.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "LinearMath/btVector3.h" +#include "btJacobianEntry.h" +#include "btContactSolverInfo.h" +#include "LinearMath/btMinMax.h" +#include "BulletCollision/NarrowPhaseCollision/btManifoldPoint.h" + +#define ASSERT2 assert + +#define USE_INTERNAL_APPLY_IMPULSE 1 + + +//bilateral constraint between two dynamic objects +void resolveSingleBilateral(btRigidBody& body1, const btVector3& pos1, + btRigidBody& body2, const btVector3& pos2, + btScalar distance, const btVector3& normal,btScalar& impulse ,btScalar timeStep) +{ + (void)timeStep; + (void)distance; + + + btScalar normalLenSqr = normal.length2(); + ASSERT2(btFabs(normalLenSqr) < btScalar(1.1)); + if (normalLenSqr > btScalar(1.1)) + { + impulse = btScalar(0.); + return; + } + btVector3 rel_pos1 = pos1 - body1.getCenterOfMassPosition(); + btVector3 rel_pos2 = pos2 - body2.getCenterOfMassPosition(); + //this jacobian entry could be re-used for all iterations + + btVector3 vel1 = body1.getVelocityInLocalPoint(rel_pos1); + btVector3 vel2 = body2.getVelocityInLocalPoint(rel_pos2); + btVector3 vel = vel1 - vel2; + + + btJacobianEntry jac(body1.getCenterOfMassTransform().getBasis().transpose(), + body2.getCenterOfMassTransform().getBasis().transpose(), + rel_pos1,rel_pos2,normal,body1.getInvInertiaDiagLocal(),body1.getInvMass(), + body2.getInvInertiaDiagLocal(),body2.getInvMass()); + + btScalar jacDiagAB = jac.getDiagonal(); + btScalar jacDiagABInv = btScalar(1.) / jacDiagAB; + + btScalar rel_vel = jac.getRelativeVelocity( + body1.getLinearVelocity(), + body1.getCenterOfMassTransform().getBasis().transpose() * body1.getAngularVelocity(), + body2.getLinearVelocity(), + body2.getCenterOfMassTransform().getBasis().transpose() * body2.getAngularVelocity()); + btScalar a; + a=jacDiagABInv; + + + rel_vel = normal.dot(vel); + + //todo: move this into proper structure + btScalar contactDamping = btScalar(0.2); + +#ifdef ONLY_USE_LINEAR_MASS + btScalar massTerm = btScalar(1.) / (body1.getInvMass() + body2.getInvMass()); + impulse = - contactDamping * rel_vel * massTerm; +#else + btScalar velocityImpulse = -contactDamping * rel_vel * jacDiagABInv; + impulse = velocityImpulse; +#endif +} + + + +//response between two dynamic objects with friction +btScalar resolveSingleCollision( + btRigidBody& body1, + btRigidBody& body2, + btManifoldPoint& contactPoint, + const btContactSolverInfo& solverInfo) +{ + + const btVector3& pos1_ = contactPoint.getPositionWorldOnA(); + const btVector3& pos2_ = contactPoint.getPositionWorldOnB(); + const btVector3& normal = contactPoint.m_normalWorldOnB; + + //constant over all iterations + btVector3 rel_pos1 = pos1_ - body1.getCenterOfMassPosition(); + btVector3 rel_pos2 = pos2_ - body2.getCenterOfMassPosition(); + + btVector3 vel1 = body1.getVelocityInLocalPoint(rel_pos1); + btVector3 vel2 = body2.getVelocityInLocalPoint(rel_pos2); + btVector3 vel = vel1 - vel2; + btScalar rel_vel; + rel_vel = normal.dot(vel); + + btScalar Kfps = btScalar(1.) / solverInfo.m_timeStep ; + + // btScalar damping = solverInfo.m_damping ; + btScalar Kerp = solverInfo.m_erp; + btScalar Kcor = Kerp *Kfps; + + btConstraintPersistentData* cpd = (btConstraintPersistentData*) contactPoint.m_userPersistentData; + assert(cpd); + btScalar distance = cpd->m_penetration; + btScalar positionalError = Kcor *-distance; + btScalar velocityError = cpd->m_restitution - rel_vel;// * damping; + + btScalar penetrationImpulse = positionalError * cpd->m_jacDiagABInv; + + btScalar velocityImpulse = velocityError * cpd->m_jacDiagABInv; + + btScalar normalImpulse = penetrationImpulse+velocityImpulse; + + // See Erin Catto's GDC 2006 paper: Clamp the accumulated impulse + btScalar oldNormalImpulse = cpd->m_appliedImpulse; + btScalar sum = oldNormalImpulse + normalImpulse; + cpd->m_appliedImpulse = btScalar(0.) > sum ? btScalar(0.): sum; + + normalImpulse = cpd->m_appliedImpulse - oldNormalImpulse; + +#ifdef USE_INTERNAL_APPLY_IMPULSE + if (body1.getInvMass()) + { + body1.internalApplyImpulse(contactPoint.m_normalWorldOnB*body1.getInvMass(),cpd->m_angularComponentA,normalImpulse); + } + if (body2.getInvMass()) + { + body2.internalApplyImpulse(contactPoint.m_normalWorldOnB*body2.getInvMass(),cpd->m_angularComponentB,-normalImpulse); + } +#else //USE_INTERNAL_APPLY_IMPULSE + body1.applyImpulse(normal*(normalImpulse), rel_pos1); + body2.applyImpulse(-normal*(normalImpulse), rel_pos2); +#endif //USE_INTERNAL_APPLY_IMPULSE + + return normalImpulse; +} + + +btScalar resolveSingleFriction( + btRigidBody& body1, + btRigidBody& body2, + btManifoldPoint& contactPoint, + const btContactSolverInfo& solverInfo) +{ + + (void)solverInfo; + + const btVector3& pos1 = contactPoint.getPositionWorldOnA(); + const btVector3& pos2 = contactPoint.getPositionWorldOnB(); + + btVector3 rel_pos1 = pos1 - body1.getCenterOfMassPosition(); + btVector3 rel_pos2 = pos2 - body2.getCenterOfMassPosition(); + + btConstraintPersistentData* cpd = (btConstraintPersistentData*) contactPoint.m_userPersistentData; + assert(cpd); + + btScalar combinedFriction = cpd->m_friction; + + btScalar limit = cpd->m_appliedImpulse * combinedFriction; + + if (cpd->m_appliedImpulse>btScalar(0.)) + //friction + { + //apply friction in the 2 tangential directions + + // 1st tangent + btVector3 vel1 = body1.getVelocityInLocalPoint(rel_pos1); + btVector3 vel2 = body2.getVelocityInLocalPoint(rel_pos2); + btVector3 vel = vel1 - vel2; + + btScalar j1,j2; + + { + + btScalar vrel = cpd->m_frictionWorldTangential0.dot(vel); + + // calculate j that moves us to zero relative velocity + j1 = -vrel * cpd->m_jacDiagABInvTangent0; + btScalar oldTangentImpulse = cpd->m_accumulatedTangentImpulse0; + cpd->m_accumulatedTangentImpulse0 = oldTangentImpulse + j1; + GEN_set_min(cpd->m_accumulatedTangentImpulse0, limit); + GEN_set_max(cpd->m_accumulatedTangentImpulse0, -limit); + j1 = cpd->m_accumulatedTangentImpulse0 - oldTangentImpulse; + + } + { + // 2nd tangent + + btScalar vrel = cpd->m_frictionWorldTangential1.dot(vel); + + // calculate j that moves us to zero relative velocity + j2 = -vrel * cpd->m_jacDiagABInvTangent1; + btScalar oldTangentImpulse = cpd->m_accumulatedTangentImpulse1; + cpd->m_accumulatedTangentImpulse1 = oldTangentImpulse + j2; + GEN_set_min(cpd->m_accumulatedTangentImpulse1, limit); + GEN_set_max(cpd->m_accumulatedTangentImpulse1, -limit); + j2 = cpd->m_accumulatedTangentImpulse1 - oldTangentImpulse; + } + +#ifdef USE_INTERNAL_APPLY_IMPULSE + if (body1.getInvMass()) + { + body1.internalApplyImpulse(cpd->m_frictionWorldTangential0*body1.getInvMass(),cpd->m_frictionAngularComponent0A,j1); + body1.internalApplyImpulse(cpd->m_frictionWorldTangential1*body1.getInvMass(),cpd->m_frictionAngularComponent1A,j2); + } + if (body2.getInvMass()) + { + body2.internalApplyImpulse(cpd->m_frictionWorldTangential0*body2.getInvMass(),cpd->m_frictionAngularComponent0B,-j1); + body2.internalApplyImpulse(cpd->m_frictionWorldTangential1*body2.getInvMass(),cpd->m_frictionAngularComponent1B,-j2); + } +#else //USE_INTERNAL_APPLY_IMPULSE + body1.applyImpulse((j1 * cpd->m_frictionWorldTangential0)+(j2 * cpd->m_frictionWorldTangential1), rel_pos1); + body2.applyImpulse((j1 * -cpd->m_frictionWorldTangential0)+(j2 * -cpd->m_frictionWorldTangential1), rel_pos2); +#endif //USE_INTERNAL_APPLY_IMPULSE + + + } + return cpd->m_appliedImpulse; +} + + +btScalar resolveSingleFrictionOriginal( + btRigidBody& body1, + btRigidBody& body2, + btManifoldPoint& contactPoint, + const btContactSolverInfo& solverInfo) +{ + + (void)solverInfo; + + const btVector3& pos1 = contactPoint.getPositionWorldOnA(); + const btVector3& pos2 = contactPoint.getPositionWorldOnB(); + + btVector3 rel_pos1 = pos1 - body1.getCenterOfMassPosition(); + btVector3 rel_pos2 = pos2 - body2.getCenterOfMassPosition(); + + btConstraintPersistentData* cpd = (btConstraintPersistentData*) contactPoint.m_userPersistentData; + assert(cpd); + + btScalar combinedFriction = cpd->m_friction; + + btScalar limit = cpd->m_appliedImpulse * combinedFriction; + //if (contactPoint.m_appliedImpulse>btScalar(0.)) + //friction + { + //apply friction in the 2 tangential directions + + { + // 1st tangent + btVector3 vel1 = body1.getVelocityInLocalPoint(rel_pos1); + btVector3 vel2 = body2.getVelocityInLocalPoint(rel_pos2); + btVector3 vel = vel1 - vel2; + + btScalar vrel = cpd->m_frictionWorldTangential0.dot(vel); + + // calculate j that moves us to zero relative velocity + btScalar j = -vrel * cpd->m_jacDiagABInvTangent0; + btScalar total = cpd->m_accumulatedTangentImpulse0 + j; + GEN_set_min(total, limit); + GEN_set_max(total, -limit); + j = total - cpd->m_accumulatedTangentImpulse0; + cpd->m_accumulatedTangentImpulse0 = total; + body1.applyImpulse(j * cpd->m_frictionWorldTangential0, rel_pos1); + body2.applyImpulse(j * -cpd->m_frictionWorldTangential0, rel_pos2); + } + + + { + // 2nd tangent + btVector3 vel1 = body1.getVelocityInLocalPoint(rel_pos1); + btVector3 vel2 = body2.getVelocityInLocalPoint(rel_pos2); + btVector3 vel = vel1 - vel2; + + btScalar vrel = cpd->m_frictionWorldTangential1.dot(vel); + + // calculate j that moves us to zero relative velocity + btScalar j = -vrel * cpd->m_jacDiagABInvTangent1; + btScalar total = cpd->m_accumulatedTangentImpulse1 + j; + GEN_set_min(total, limit); + GEN_set_max(total, -limit); + j = total - cpd->m_accumulatedTangentImpulse1; + cpd->m_accumulatedTangentImpulse1 = total; + body1.applyImpulse(j * cpd->m_frictionWorldTangential1, rel_pos1); + body2.applyImpulse(j * -cpd->m_frictionWorldTangential1, rel_pos2); + } + } + return cpd->m_appliedImpulse; +} + + +//velocity + friction +//response between two dynamic objects with friction +btScalar resolveSingleCollisionCombined( + btRigidBody& body1, + btRigidBody& body2, + btManifoldPoint& contactPoint, + const btContactSolverInfo& solverInfo) +{ + + const btVector3& pos1 = contactPoint.getPositionWorldOnA(); + const btVector3& pos2 = contactPoint.getPositionWorldOnB(); + const btVector3& normal = contactPoint.m_normalWorldOnB; + + btVector3 rel_pos1 = pos1 - body1.getCenterOfMassPosition(); + btVector3 rel_pos2 = pos2 - body2.getCenterOfMassPosition(); + + btVector3 vel1 = body1.getVelocityInLocalPoint(rel_pos1); + btVector3 vel2 = body2.getVelocityInLocalPoint(rel_pos2); + btVector3 vel = vel1 - vel2; + btScalar rel_vel; + rel_vel = normal.dot(vel); + + btScalar Kfps = btScalar(1.) / solverInfo.m_timeStep ; + + //btScalar damping = solverInfo.m_damping ; + btScalar Kerp = solverInfo.m_erp; + btScalar Kcor = Kerp *Kfps; + + btConstraintPersistentData* cpd = (btConstraintPersistentData*) contactPoint.m_userPersistentData; + assert(cpd); + btScalar distance = cpd->m_penetration; + btScalar positionalError = Kcor *-distance; + btScalar velocityError = cpd->m_restitution - rel_vel;// * damping; + + btScalar penetrationImpulse = positionalError * cpd->m_jacDiagABInv; + + btScalar velocityImpulse = velocityError * cpd->m_jacDiagABInv; + + btScalar normalImpulse = penetrationImpulse+velocityImpulse; + + // See Erin Catto's GDC 2006 paper: Clamp the accumulated impulse + btScalar oldNormalImpulse = cpd->m_appliedImpulse; + btScalar sum = oldNormalImpulse + normalImpulse; + cpd->m_appliedImpulse = btScalar(0.) > sum ? btScalar(0.): sum; + + normalImpulse = cpd->m_appliedImpulse - oldNormalImpulse; + + +#ifdef USE_INTERNAL_APPLY_IMPULSE + if (body1.getInvMass()) + { + body1.internalApplyImpulse(contactPoint.m_normalWorldOnB*body1.getInvMass(),cpd->m_angularComponentA,normalImpulse); + } + if (body2.getInvMass()) + { + body2.internalApplyImpulse(contactPoint.m_normalWorldOnB*body2.getInvMass(),cpd->m_angularComponentB,-normalImpulse); + } +#else //USE_INTERNAL_APPLY_IMPULSE + body1.applyImpulse(normal*(normalImpulse), rel_pos1); + body2.applyImpulse(-normal*(normalImpulse), rel_pos2); +#endif //USE_INTERNAL_APPLY_IMPULSE + + { + //friction + btVector3 vel1 = body1.getVelocityInLocalPoint(rel_pos1); + btVector3 vel2 = body2.getVelocityInLocalPoint(rel_pos2); + btVector3 vel = vel1 - vel2; + + rel_vel = normal.dot(vel); + + + btVector3 lat_vel = vel - normal * rel_vel; + btScalar lat_rel_vel = lat_vel.length(); + + btScalar combinedFriction = cpd->m_friction; + + if (cpd->m_appliedImpulse > 0) + if (lat_rel_vel > SIMD_EPSILON) + { + lat_vel /= lat_rel_vel; + btVector3 temp1 = body1.getInvInertiaTensorWorld() * rel_pos1.cross(lat_vel); + btVector3 temp2 = body2.getInvInertiaTensorWorld() * rel_pos2.cross(lat_vel); + btScalar friction_impulse = lat_rel_vel / + (body1.getInvMass() + body2.getInvMass() + lat_vel.dot(temp1.cross(rel_pos1) + temp2.cross(rel_pos2))); + btScalar normal_impulse = cpd->m_appliedImpulse * combinedFriction; + + GEN_set_min(friction_impulse, normal_impulse); + GEN_set_max(friction_impulse, -normal_impulse); + body1.applyImpulse(lat_vel * -friction_impulse, rel_pos1); + body2.applyImpulse(lat_vel * friction_impulse, rel_pos2); + } + } + + + + return normalImpulse; +} + +btScalar resolveSingleFrictionEmpty( + btRigidBody& body1, + btRigidBody& body2, + btManifoldPoint& contactPoint, + const btContactSolverInfo& solverInfo) +{ + (void)contactPoint; + (void)body1; + (void)body2; + (void)solverInfo; + + + return btScalar(0.); +}; + diff --git a/bullet/src/BulletDynamics/ConstraintSolver/btContactConstraint.h b/bullet/src/BulletDynamics/ConstraintSolver/btContactConstraint.h new file mode 100644 index 0000000..6771682 --- /dev/null +++ b/bullet/src/BulletDynamics/ConstraintSolver/btContactConstraint.h @@ -0,0 +1,122 @@ +/* +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. +*/ + +#ifndef CONTACT_CONSTRAINT_H +#define CONTACT_CONSTRAINT_H + +//todo: make into a proper class working with the iterative constraint solver + +class btRigidBody; +#include "LinearMath/btVector3.h" +#include "LinearMath/btScalar.h" +struct btContactSolverInfo; +class btManifoldPoint; + +enum { + DEFAULT_CONTACT_SOLVER_TYPE=0, + CONTACT_SOLVER_TYPE1, + CONTACT_SOLVER_TYPE2, + USER_CONTACT_SOLVER_TYPE1, + MAX_CONTACT_SOLVER_TYPES +}; + + +typedef btScalar (*ContactSolverFunc)(btRigidBody& body1, + btRigidBody& body2, + class btManifoldPoint& contactPoint, + const btContactSolverInfo& info); + +///stores some extra information to each contact point. It is not in the contact point, because that want to keep the collision detection independent from the constraint solver. +struct btConstraintPersistentData +{ + inline btConstraintPersistentData() + :m_appliedImpulse(btScalar(0.)), + m_prevAppliedImpulse(btScalar(0.)), + m_accumulatedTangentImpulse0(btScalar(0.)), + m_accumulatedTangentImpulse1(btScalar(0.)), + m_jacDiagABInv(btScalar(0.)), + m_persistentLifeTime(0), + m_restitution(btScalar(0.)), + m_friction(btScalar(0.)), + m_penetration(btScalar(0.)), + m_contactSolverFunc(0), + m_frictionSolverFunc(0) + { + } + + + /// total applied impulse during most recent frame + btScalar m_appliedImpulse; + btScalar m_prevAppliedImpulse; + btScalar m_accumulatedTangentImpulse0; + btScalar m_accumulatedTangentImpulse1; + + btScalar m_jacDiagABInv; + btScalar m_jacDiagABInvTangent0; + btScalar m_jacDiagABInvTangent1; + int m_persistentLifeTime; + btScalar m_restitution; + btScalar m_friction; + btScalar m_penetration; + btVector3 m_frictionWorldTangential0; + btVector3 m_frictionWorldTangential1; + + btVector3 m_frictionAngularComponent0A; + btVector3 m_frictionAngularComponent0B; + btVector3 m_frictionAngularComponent1A; + btVector3 m_frictionAngularComponent1B; + + //some data doesn't need to be persistent over frames: todo: clean/reuse this + btVector3 m_angularComponentA; + btVector3 m_angularComponentB; + + ContactSolverFunc m_contactSolverFunc; + ContactSolverFunc m_frictionSolverFunc; + +}; + +///bilateral constraint between two dynamic objects +///positive distance = separation, negative distance = penetration +void resolveSingleBilateral(btRigidBody& body1, const btVector3& pos1, + btRigidBody& body2, const btVector3& pos2, + btScalar distance, const btVector3& normal,btScalar& impulse ,btScalar timeStep); + + +///contact constraint resolution: +///calculate and apply impulse to satisfy non-penetration and non-negative relative velocity constraint +///positive distance = separation, negative distance = penetration +btScalar resolveSingleCollision( + btRigidBody& body1, + btRigidBody& body2, + btManifoldPoint& contactPoint, + const btContactSolverInfo& info); + +btScalar resolveSingleFriction( + btRigidBody& body1, + btRigidBody& body2, + btManifoldPoint& contactPoint, + const btContactSolverInfo& solverInfo + ); + + + +btScalar resolveSingleCollisionCombined( + btRigidBody& body1, + btRigidBody& body2, + btManifoldPoint& contactPoint, + const btContactSolverInfo& solverInfo + ); + +#endif //CONTACT_CONSTRAINT_H diff --git a/bullet/src/BulletDynamics/ConstraintSolver/btContactSolverInfo.h b/bullet/src/BulletDynamics/ConstraintSolver/btContactSolverInfo.h new file mode 100644 index 0000000..0efa605 --- /dev/null +++ b/bullet/src/BulletDynamics/ConstraintSolver/btContactSolverInfo.h @@ -0,0 +1,51 @@ +/* +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. +*/ + +#ifndef CONTACT_SOLVER_INFO +#define CONTACT_SOLVER_INFO + +struct btContactSolverInfoData +{ + btScalar m_tau; + btScalar m_damping; + btScalar m_friction; + btScalar m_timeStep; + btScalar m_restitution; + int m_numIterations; + btScalar m_maxErrorReduction; + btScalar m_sor; + btScalar m_erp; + +}; + +struct btContactSolverInfo : public btContactSolverInfoData +{ + + inline btContactSolverInfo() + { + m_tau = btScalar(0.6); + m_damping = btScalar(1.0); + m_friction = btScalar(0.3); + m_restitution = btScalar(0.); + m_maxErrorReduction = btScalar(20.); + m_numIterations = 10; + m_erp = btScalar(0.4); + m_sor = btScalar(1.3); + } + + +}; + +#endif //CONTACT_SOLVER_INFO diff --git a/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp b/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp new file mode 100644 index 0000000..ae1c4c1 --- /dev/null +++ b/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp @@ -0,0 +1,390 @@ +/* +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. +*/ + + +#include "btGeneric6DofConstraint.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "LinearMath/btTransformUtil.h" +#include + +static const btScalar kSign[] = { btScalar(1.0), btScalar(-1.0), btScalar(1.0) }; +static const int kAxisA[] = { 1, 0, 0 }; +static const int kAxisB[] = { 2, 2, 1 }; +#define GENERIC_D6_DISABLE_WARMSTARTING 1 + +btGeneric6DofConstraint::btGeneric6DofConstraint() +:btTypedConstraint(D6_CONSTRAINT_TYPE) +{ +} + +btGeneric6DofConstraint::btGeneric6DofConstraint(btRigidBody& rbA, btRigidBody& rbB, const btTransform& frameInA, const btTransform& frameInB) +: btTypedConstraint(D6_CONSTRAINT_TYPE, rbA, rbB) +, m_frameInA(frameInA) +, m_frameInB(frameInB) +{ + //free means upper < lower, + //locked means upper == lower + //limited means upper > lower + //so start all locked + for (int i=0; i<6;++i) + { + m_lowerLimit[i] = btScalar(0.0); + m_upperLimit[i] = btScalar(0.0); + m_accumulatedImpulse[i] = btScalar(0.0); + } + +} + + +void btGeneric6DofConstraint::buildJacobian() +{ + btVector3 localNormalInA(0,0,0); + + const btVector3& pivotInA = m_frameInA.getOrigin(); + const btVector3& pivotInB = m_frameInB.getOrigin(); + + btVector3 pivotAInW = m_rbA.getCenterOfMassTransform() * m_frameInA.getOrigin(); + btVector3 pivotBInW = m_rbB.getCenterOfMassTransform() * m_frameInB.getOrigin(); + + btVector3 rel_pos1 = pivotAInW - m_rbA.getCenterOfMassPosition(); + btVector3 rel_pos2 = pivotBInW - m_rbB.getCenterOfMassPosition(); + + int i; + //linear part + for (i=0;i<3;i++) + { + if (isLimited(i)) + { + localNormalInA[i] = 1; + btVector3 normalWorld = m_rbA.getCenterOfMassTransform().getBasis() * localNormalInA; + + + // Create linear atom + new (&m_jacLinear[i]) btJacobianEntry( + m_rbA.getCenterOfMassTransform().getBasis().transpose(), + m_rbB.getCenterOfMassTransform().getBasis().transpose(), + m_rbA.getCenterOfMassTransform()*pivotInA - m_rbA.getCenterOfMassPosition(), + m_rbB.getCenterOfMassTransform()*pivotInB - m_rbB.getCenterOfMassPosition(), + normalWorld, + m_rbA.getInvInertiaDiagLocal(), + m_rbA.getInvMass(), + m_rbB.getInvInertiaDiagLocal(), + m_rbB.getInvMass()); + + //optionally disable warmstarting +#ifdef GENERIC_D6_DISABLE_WARMSTARTING + m_accumulatedImpulse[i] = btScalar(0.); +#endif //GENERIC_D6_DISABLE_WARMSTARTING + + // Apply accumulated impulse + btVector3 impulse_vector = m_accumulatedImpulse[i] * normalWorld; + + m_rbA.applyImpulse( impulse_vector, rel_pos1); + m_rbB.applyImpulse(-impulse_vector, rel_pos2); + + localNormalInA[i] = 0; + } + } + + // angular part + for (i=0;i<3;i++) + { + if (isLimited(i+3)) + { + btVector3 axisA = m_rbA.getCenterOfMassTransform().getBasis() * m_frameInA.getBasis().getColumn( kAxisA[i] ); + btVector3 axisB = m_rbB.getCenterOfMassTransform().getBasis() * m_frameInB.getBasis().getColumn( kAxisB[i] ); + + // Dirk: This is IMO mathematically the correct way, but we should consider axisA and axisB being near parallel maybe + btVector3 axis = kSign[i] * axisA.cross(axisB); + + // Create angular atom + new (&m_jacAng[i]) btJacobianEntry(axis, + m_rbA.getCenterOfMassTransform().getBasis().transpose(), + m_rbB.getCenterOfMassTransform().getBasis().transpose(), + m_rbA.getInvInertiaDiagLocal(), + m_rbB.getInvInertiaDiagLocal()); + +#ifdef GENERIC_D6_DISABLE_WARMSTARTING + m_accumulatedImpulse[i + 3] = btScalar(0.); +#endif //GENERIC_D6_DISABLE_WARMSTARTING + + // Apply accumulated impulse + btVector3 impulse_vector = m_accumulatedImpulse[i + 3] * axis; + + m_rbA.applyTorqueImpulse( impulse_vector); + m_rbB.applyTorqueImpulse(-impulse_vector); + } + } +} + +btScalar getMatrixElem(const btMatrix3x3& mat,int index) +{ + int row = index%3; + int col = index / 3; + return mat[row][col]; +} + +///MatrixToEulerXYZ from http://www.geometrictools.com/LibFoundation/Mathematics/Wm4Matrix3.inl.html +bool MatrixToEulerXYZ(const btMatrix3x3& mat,btVector3& xyz) +{ + // rot = cy*cz -cy*sz sy + // cz*sx*sy+cx*sz cx*cz-sx*sy*sz -cy*sx + // -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy + +/// 0..8 + + if (getMatrixElem(mat,2) < btScalar(1.0)) + { + if (getMatrixElem(mat,2) > btScalar(-1.0)) + { + xyz[0] = btAtan2(-getMatrixElem(mat,5),getMatrixElem(mat,8)); + xyz[1] = btAsin(getMatrixElem(mat,2)); + xyz[2] = btAtan2(-getMatrixElem(mat,1),getMatrixElem(mat,0)); + return true; + } + else + { + // WARNING. Not unique. XA - ZA = -atan2(r10,r11) + xyz[0] = -btAtan2(getMatrixElem(mat,3),getMatrixElem(mat,4)); + xyz[1] = -SIMD_HALF_PI; + xyz[2] = btScalar(0.0); + return false; + } + } + else + { + // WARNING. Not unique. XAngle + ZAngle = atan2(r10,r11) + xyz[0] = btAtan2(getMatrixElem(mat,3),getMatrixElem(mat,4)); + xyz[1] = SIMD_HALF_PI; + xyz[2] = 0.0; + + } + + return false; +} + + +void btGeneric6DofConstraint::solveConstraint(btScalar timeStep) +{ + btScalar tau = btScalar(0.1); + btScalar damping = btScalar(1.0); + + btVector3 pivotAInW = m_rbA.getCenterOfMassTransform() * m_frameInA.getOrigin(); + btVector3 pivotBInW = m_rbB.getCenterOfMassTransform() * m_frameInB.getOrigin(); + + btVector3 rel_pos1 = pivotAInW - m_rbA.getCenterOfMassPosition(); + btVector3 rel_pos2 = pivotBInW - m_rbB.getCenterOfMassPosition(); + + btVector3 localNormalInA(0,0,0); + int i; + + // linear + for (i=0;i<3;i++) + { + if (isLimited(i)) + { + btVector3 angvelA = m_rbA.getCenterOfMassTransform().getBasis().transpose() * m_rbA.getAngularVelocity(); + btVector3 angvelB = m_rbB.getCenterOfMassTransform().getBasis().transpose() * m_rbB.getAngularVelocity(); + + localNormalInA.setValue(0,0,0); + localNormalInA[i] = 1; + btVector3 normalWorld = m_rbA.getCenterOfMassTransform().getBasis() * localNormalInA; + + btScalar jacDiagABInv = btScalar(1.) / m_jacLinear[i].getDiagonal(); + + //velocity error (first order error) + btScalar rel_vel = m_jacLinear[i].getRelativeVelocity(m_rbA.getLinearVelocity(),angvelA, + m_rbB.getLinearVelocity(),angvelB); + + //positional error (zeroth order error) + btScalar depth = -(pivotAInW - pivotBInW).dot(normalWorld); + btScalar lo = btScalar(-1e30); + btScalar hi = btScalar(1e30); + + //handle the limits + if (m_lowerLimit[i] < m_upperLimit[i]) + { + { + if (depth > m_upperLimit[i]) + { + depth -= m_upperLimit[i]; + lo = btScalar(0.); + + } else + { + if (depth < m_lowerLimit[i]) + { + depth -= m_lowerLimit[i]; + hi = btScalar(0.); + } else + { + continue; + } + } + } + } + + btScalar normalImpulse= (tau*depth/timeStep - damping*rel_vel) * jacDiagABInv; + btScalar oldNormalImpulse = m_accumulatedImpulse[i]; + btScalar sum = oldNormalImpulse + normalImpulse; + m_accumulatedImpulse[i] = sum > hi ? btScalar(0.) : sum < lo ? btScalar(0.) : sum; + normalImpulse = m_accumulatedImpulse[i] - oldNormalImpulse; + + btVector3 impulse_vector = normalWorld * normalImpulse; + m_rbA.applyImpulse( impulse_vector, rel_pos1); + m_rbB.applyImpulse(-impulse_vector, rel_pos2); + + localNormalInA[i] = 0; + } + } + + btVector3 axis; + btScalar angle; + btTransform frameAWorld = m_rbA.getCenterOfMassTransform() * m_frameInA; + btTransform frameBWorld = m_rbB.getCenterOfMassTransform() * m_frameInB; + + btTransformUtil::calculateDiffAxisAngle(frameAWorld,frameBWorld,axis,angle); + btQuaternion diff(axis,angle); + btMatrix3x3 diffMat (diff); + btVector3 xyz; + ///this is not perfect, we can first check which axis are limited, and choose a more appropriate order + MatrixToEulerXYZ(diffMat,xyz); + + // angular + for (i=0;i<3;i++) + { + if (isLimited(i+3)) + { + btVector3 angvelA = m_rbA.getCenterOfMassTransform().getBasis().transpose() * m_rbA.getAngularVelocity(); + btVector3 angvelB = m_rbB.getCenterOfMassTransform().getBasis().transpose() * m_rbB.getAngularVelocity(); + + btScalar jacDiagABInv = btScalar(1.) / m_jacAng[i].getDiagonal(); + + //velocity error (first order error) + btScalar rel_vel = m_jacAng[i].getRelativeVelocity(m_rbA.getLinearVelocity(),angvelA, + m_rbB.getLinearVelocity(),angvelB); + + //positional error (zeroth order error) + btVector3 axisA = m_rbA.getCenterOfMassTransform().getBasis() * m_frameInA.getBasis().getColumn( kAxisA[i] ); + btVector3 axisB = m_rbB.getCenterOfMassTransform().getBasis() * m_frameInB.getBasis().getColumn( kAxisB[i] ); + + btScalar rel_pos = kSign[i] * axisA.dot(axisB); + + btScalar lo = btScalar(-1e30); + btScalar hi = btScalar(1e30); + + //handle the twist limit + if (m_lowerLimit[i+3] < m_upperLimit[i+3]) + { + //clamp the values + btScalar loLimit = m_lowerLimit[i+3] > -3.1415 ? m_lowerLimit[i+3] : btScalar(-1e30); + btScalar hiLimit = m_upperLimit[i+3] < 3.1415 ? m_upperLimit[i+3] : btScalar(1e30); + + btScalar projAngle = btScalar(-1.)*xyz[i]; + + if (projAngle < loLimit) + { + hi = btScalar(0.); + rel_pos = (loLimit - projAngle); + } else + { + if (projAngle > hiLimit) + { + lo = btScalar(0.); + rel_pos = (hiLimit - projAngle); + } else + { + continue; + } + } + } + + //impulse + + btScalar normalImpulse= -(tau*rel_pos/timeStep + damping*rel_vel) * jacDiagABInv; + btScalar oldNormalImpulse = m_accumulatedImpulse[i+3]; + btScalar sum = oldNormalImpulse + normalImpulse; + m_accumulatedImpulse[i+3] = sum > hi ? btScalar(0.) : sum < lo ? btScalar(0.) : sum; + normalImpulse = m_accumulatedImpulse[i+3] - oldNormalImpulse; + + // Dirk: Not needed - we could actually project onto Jacobian entry here (same as above) + btVector3 axis = kSign[i] * axisA.cross(axisB); + btVector3 impulse_vector = axis * normalImpulse; + + m_rbA.applyTorqueImpulse( impulse_vector); + m_rbB.applyTorqueImpulse(-impulse_vector); + } + } +} + +void btGeneric6DofConstraint::updateRHS(btScalar timeStep) +{ + (void)timeStep; + +} + +btScalar btGeneric6DofConstraint::computeAngle(int axis) const + { + btScalar angle = btScalar(0.f); + + switch (axis) + { + case 0: + { + btVector3 v1 = m_rbA.getCenterOfMassTransform().getBasis() * m_frameInA.getBasis().getColumn(1); + btVector3 v2 = m_rbB.getCenterOfMassTransform().getBasis() * m_frameInB.getBasis().getColumn(1); + btVector3 w2 = m_rbB.getCenterOfMassTransform().getBasis() * m_frameInB.getBasis().getColumn(2); + + btScalar s = v1.dot(w2); + btScalar c = v1.dot(v2); + + angle = btAtan2( s, c ); + } + break; + + case 1: + { + btVector3 w1 = m_rbA.getCenterOfMassTransform().getBasis() * m_frameInA.getBasis().getColumn(2); + btVector3 w2 = m_rbB.getCenterOfMassTransform().getBasis() * m_frameInB.getBasis().getColumn(2); + btVector3 u2 = m_rbB.getCenterOfMassTransform().getBasis() * m_frameInB.getBasis().getColumn(0); + + btScalar s = w1.dot(u2); + btScalar c = w1.dot(w2); + + angle = btAtan2( s, c ); + } + break; + + case 2: + { + btVector3 u1 = m_rbA.getCenterOfMassTransform().getBasis() * m_frameInA.getBasis().getColumn(0); + btVector3 u2 = m_rbB.getCenterOfMassTransform().getBasis() * m_frameInB.getBasis().getColumn(0); + btVector3 v2 = m_rbB.getCenterOfMassTransform().getBasis() * m_frameInB.getBasis().getColumn(1); + + btScalar s = u1.dot(v2); + btScalar c = u1.dot(u2); + + angle = btAtan2( s, c ); + } + break; + default: + btAssert ( 0 ) ; + + break ; + } + + return angle; + } + diff --git a/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h b/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h new file mode 100644 index 0000000..8b58ef3 --- /dev/null +++ b/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h @@ -0,0 +1,120 @@ +/* +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. +*/ + +#ifndef GENERIC_6DOF_CONSTRAINT_H +#define GENERIC_6DOF_CONSTRAINT_H + +#include "LinearMath/btVector3.h" +#include "btJacobianEntry.h" +#include "btTypedConstraint.h" + +class btRigidBody; + + + +/// btGeneric6DofConstraint between two rigidbodies each with a pivotpoint that descibes the axis location in local space +/// btGeneric6DofConstraint can leave any of the 6 degree of freedom 'free' or 'locked' +/// Work in progress (is still a Hinge actually) +class btGeneric6DofConstraint : public btTypedConstraint +{ + btJacobianEntry m_jacLinear[3]; // 3 orthogonal linear constraints + btJacobianEntry m_jacAng[3]; // 3 orthogonal angular constraints + + btTransform m_frameInA; // the constraint space w.r.t body A + btTransform m_frameInB; // the constraint space w.r.t body B + + btScalar m_lowerLimit[6]; // the constraint lower limits + btScalar m_upperLimit[6]; // the constraint upper limits + + btScalar m_accumulatedImpulse[6]; + + btGeneric6DofConstraint& operator=(btGeneric6DofConstraint& other) + { + btAssert(0); + (void) other; + return *this; + } + +public: + btGeneric6DofConstraint(btRigidBody& rbA, btRigidBody& rbB, const btTransform& frameInA, const btTransform& frameInB ); + + btGeneric6DofConstraint(); + + + virtual void buildJacobian(); + + virtual void solveConstraint(btScalar timeStep); + + void updateRHS(btScalar timeStep); + + btScalar computeAngle(int axis) const; + + void setLinearLowerLimit(const btVector3& linearLower) + { + m_lowerLimit[0] = linearLower.getX(); + m_lowerLimit[1] = linearLower.getY(); + m_lowerLimit[2] = linearLower.getZ(); + } + + void setLinearUpperLimit(const btVector3& linearUpper) + { + m_upperLimit[0] = linearUpper.getX(); + m_upperLimit[1] = linearUpper.getY(); + m_upperLimit[2] = linearUpper.getZ(); + } + + void setAngularLowerLimit(const btVector3& angularLower) + { + m_lowerLimit[3] = angularLower.getX(); + m_lowerLimit[4] = angularLower.getY(); + m_lowerLimit[5] = angularLower.getZ(); + } + + void setAngularUpperLimit(const btVector3& angularUpper) + { + m_upperLimit[3] = angularUpper.getX(); + m_upperLimit[4] = angularUpper.getY(); + m_upperLimit[5] = angularUpper.getZ(); + } + + //first 3 are linear, next 3 are angular + void SetLimit(int axis, btScalar lo, btScalar hi) + { + m_lowerLimit[axis] = lo; + m_upperLimit[axis] = hi; + } + + //free means upper < lower, + //locked means upper == lower + //limited means upper > lower + //limitIndex: first 3 are linear, next 3 are angular + bool isLimited(int limitIndex) + { + return (m_upperLimit[limitIndex] >= m_lowerLimit[limitIndex]); + } + + const btRigidBody& getRigidBodyA() const + { + return m_rbA; + } + const btRigidBody& getRigidBodyB() const + { + return m_rbB; + } + + +}; + +#endif //GENERIC_6DOF_CONSTRAINT_H diff --git a/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.cpp b/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.cpp new file mode 100644 index 0000000..1eac19d --- /dev/null +++ b/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.cpp @@ -0,0 +1,398 @@ +/* +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. +*/ + + +#include "btHingeConstraint.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "LinearMath/btTransformUtil.h" +#include "LinearMath/btSimdMinMax.h" +#include + + +btHingeConstraint::btHingeConstraint() +: btTypedConstraint (HINGE_CONSTRAINT_TYPE), +m_enableAngularMotor(false) +{ +} + +btHingeConstraint::btHingeConstraint(btRigidBody& rbA,btRigidBody& rbB, const btVector3& pivotInA,const btVector3& pivotInB, + btVector3& axisInA,btVector3& axisInB) + :btTypedConstraint(HINGE_CONSTRAINT_TYPE, rbA,rbB), + m_angularOnly(false), + m_enableAngularMotor(false) +{ + m_rbAFrame.getOrigin() = pivotInA; + + // since no frame is given, assume this to be zero angle and just pick rb transform axis + btVector3 rbAxisA1 = rbA.getCenterOfMassTransform().getBasis().getColumn(0); + btScalar projection = rbAxisA1.dot(axisInA); + if (projection > SIMD_EPSILON) + rbAxisA1 = rbAxisA1*projection - axisInA; + else + rbAxisA1 = rbA.getCenterOfMassTransform().getBasis().getColumn(1); + + btVector3 rbAxisA2 = rbAxisA1.cross(axisInA); + + m_rbAFrame.getBasis().setValue( rbAxisA1.getX(),rbAxisA2.getX(),axisInA.getX(), + rbAxisA1.getY(),rbAxisA2.getY(),axisInA.getY(), + rbAxisA1.getZ(),rbAxisA2.getZ(),axisInA.getZ() ); + + btQuaternion rotationArc = shortestArcQuat(axisInA,axisInB); + btVector3 rbAxisB1 = quatRotate(rotationArc,rbAxisA1); + btVector3 rbAxisB2 = rbAxisB1.cross(axisInB); + + + m_rbBFrame.getOrigin() = pivotInB; + m_rbBFrame.getBasis().setValue( rbAxisB1.getX(),rbAxisB2.getX(),-axisInB.getX(), + rbAxisB1.getY(),rbAxisB2.getY(),-axisInB.getY(), + rbAxisB1.getZ(),rbAxisB2.getZ(),-axisInB.getZ() ); + + //start with free + m_lowerLimit = btScalar(1e30); + m_upperLimit = btScalar(-1e30); + m_biasFactor = 0.3f; + m_relaxationFactor = 1.0f; + m_limitSoftness = 0.9f; + m_solveLimit = false; + +} + + +btHingeConstraint::btHingeConstraint(btRigidBody& rbA,const btVector3& pivotInA,btVector3& axisInA) +:btTypedConstraint(HINGE_CONSTRAINT_TYPE, rbA), m_angularOnly(false), m_enableAngularMotor(false) +{ + + // since no frame is given, assume this to be zero angle and just pick rb transform axis + // fixed axis in worldspace + btVector3 rbAxisA1 = rbA.getCenterOfMassTransform().getBasis().getColumn(0); + btScalar projection = rbAxisA1.dot(axisInA); + if (projection > SIMD_EPSILON) + rbAxisA1 = rbAxisA1*projection - axisInA; + else + rbAxisA1 = rbA.getCenterOfMassTransform().getBasis().getColumn(1); + + btVector3 rbAxisA2 = axisInA.cross(rbAxisA1); + + m_rbAFrame.getOrigin() = pivotInA; + m_rbAFrame.getBasis().setValue( rbAxisA1.getX(),rbAxisA2.getX(),axisInA.getX(), + rbAxisA1.getY(),rbAxisA2.getY(),axisInA.getY(), + rbAxisA1.getZ(),rbAxisA2.getZ(),axisInA.getZ() ); + + + btVector3 axisInB = rbA.getCenterOfMassTransform().getBasis() * -axisInA; + + btQuaternion rotationArc = shortestArcQuat(axisInA,axisInB); + btVector3 rbAxisB1 = quatRotate(rotationArc,rbAxisA1); + btVector3 rbAxisB2 = axisInB.cross(rbAxisB1); + + + m_rbBFrame.getOrigin() = rbA.getCenterOfMassTransform()(pivotInA); + m_rbBFrame.getBasis().setValue( rbAxisB1.getX(),rbAxisB2.getX(),axisInB.getX(), + rbAxisB1.getY(),rbAxisB2.getY(),axisInB.getY(), + rbAxisB1.getZ(),rbAxisB2.getZ(),axisInB.getZ() ); + + //start with free + m_lowerLimit = btScalar(1e30); + m_upperLimit = btScalar(-1e30); + m_biasFactor = 0.3f; + m_relaxationFactor = 1.0f; + m_limitSoftness = 0.9f; + m_solveLimit = false; +} + +btHingeConstraint::btHingeConstraint(btRigidBody& rbA,btRigidBody& rbB, + const btTransform& rbAFrame, const btTransform& rbBFrame) +:btTypedConstraint(HINGE_CONSTRAINT_TYPE, rbA,rbB),m_rbAFrame(rbAFrame),m_rbBFrame(rbBFrame), +m_angularOnly(false), +m_enableAngularMotor(false) +{ + // flip axis + m_rbBFrame.getBasis()[0][2] *= btScalar(-1.); + m_rbBFrame.getBasis()[1][2] *= btScalar(-1.); + m_rbBFrame.getBasis()[2][2] *= btScalar(-1.); + + //start with free + m_lowerLimit = btScalar(1e30); + m_upperLimit = btScalar(-1e30); + m_biasFactor = 0.3f; + m_relaxationFactor = 1.0f; + m_limitSoftness = 0.9f; + m_solveLimit = false; +} + + + +btHingeConstraint::btHingeConstraint(btRigidBody& rbA, const btTransform& rbAFrame) +:btTypedConstraint(HINGE_CONSTRAINT_TYPE, rbA),m_rbAFrame(rbAFrame),m_rbBFrame(rbAFrame), +m_angularOnly(false), +m_enableAngularMotor(false) +{ + // flip axis + m_rbBFrame.getBasis()[0][2] *= btScalar(-1.); + m_rbBFrame.getBasis()[1][2] *= btScalar(-1.); + m_rbBFrame.getBasis()[2][2] *= btScalar(-1.); + + + //start with free + m_lowerLimit = btScalar(1e30); + m_upperLimit = btScalar(-1e30); + m_biasFactor = 0.3f; + m_relaxationFactor = 1.0f; + m_limitSoftness = 0.9f; + m_solveLimit = false; +} + +void btHingeConstraint::buildJacobian() +{ + m_appliedImpulse = btScalar(0.); + + if (!m_angularOnly) + { + btVector3 pivotAInW = m_rbA.getCenterOfMassTransform()*m_rbAFrame.getOrigin(); + btVector3 pivotBInW = m_rbB.getCenterOfMassTransform()*m_rbBFrame.getOrigin(); + btVector3 relPos = pivotBInW - pivotAInW; + + btVector3 normal[3]; + if (relPos.length2() > SIMD_EPSILON) + { + normal[0] = relPos.normalized(); + } + else + { + normal[0].setValue(btScalar(1.0),0,0); + } + + btPlaneSpace1(normal[0], normal[1], normal[2]); + + for (int i=0;i<3;i++) + { + new (&m_jac[i]) btJacobianEntry( + m_rbA.getCenterOfMassTransform().getBasis().transpose(), + m_rbB.getCenterOfMassTransform().getBasis().transpose(), + pivotAInW - m_rbA.getCenterOfMassPosition(), + pivotBInW - m_rbB.getCenterOfMassPosition(), + normal[i], + m_rbA.getInvInertiaDiagLocal(), + m_rbA.getInvMass(), + m_rbB.getInvInertiaDiagLocal(), + m_rbB.getInvMass()); + } + } + + //calculate two perpendicular jointAxis, orthogonal to hingeAxis + //these two jointAxis require equal angular velocities for both bodies + + //this is unused for now, it's a todo + btVector3 jointAxis0local; + btVector3 jointAxis1local; + + btPlaneSpace1(m_rbAFrame.getBasis().getColumn(2),jointAxis0local,jointAxis1local); + + getRigidBodyA().getCenterOfMassTransform().getBasis() * m_rbAFrame.getBasis().getColumn(2); + btVector3 jointAxis0 = getRigidBodyA().getCenterOfMassTransform().getBasis() * jointAxis0local; + btVector3 jointAxis1 = getRigidBodyA().getCenterOfMassTransform().getBasis() * jointAxis1local; + btVector3 hingeAxisWorld = getRigidBodyA().getCenterOfMassTransform().getBasis() * m_rbAFrame.getBasis().getColumn(2); + + new (&m_jacAng[0]) btJacobianEntry(jointAxis0, + m_rbA.getCenterOfMassTransform().getBasis().transpose(), + m_rbB.getCenterOfMassTransform().getBasis().transpose(), + m_rbA.getInvInertiaDiagLocal(), + m_rbB.getInvInertiaDiagLocal()); + + new (&m_jacAng[1]) btJacobianEntry(jointAxis1, + m_rbA.getCenterOfMassTransform().getBasis().transpose(), + m_rbB.getCenterOfMassTransform().getBasis().transpose(), + m_rbA.getInvInertiaDiagLocal(), + m_rbB.getInvInertiaDiagLocal()); + + new (&m_jacAng[2]) btJacobianEntry(hingeAxisWorld, + m_rbA.getCenterOfMassTransform().getBasis().transpose(), + m_rbB.getCenterOfMassTransform().getBasis().transpose(), + m_rbA.getInvInertiaDiagLocal(), + m_rbB.getInvInertiaDiagLocal()); + + + // Compute limit information + btScalar hingeAngle = getHingeAngle(); + + //set bias, sign, clear accumulator + m_correction = btScalar(0.); + m_limitSign = btScalar(0.); + m_solveLimit = false; + m_accLimitImpulse = btScalar(0.); + + if (m_lowerLimit < m_upperLimit) + { + if (hingeAngle <= m_lowerLimit*m_limitSoftness) + { + m_correction = (m_lowerLimit - hingeAngle); + m_limitSign = 1.0f; + m_solveLimit = true; + } + else if (hingeAngle >= m_upperLimit*m_limitSoftness) + { + m_correction = m_upperLimit - hingeAngle; + m_limitSign = -1.0f; + m_solveLimit = true; + } + } + + //Compute K = J*W*J' for hinge axis + btVector3 axisA = getRigidBodyA().getCenterOfMassTransform().getBasis() * m_rbAFrame.getBasis().getColumn(2); + m_kHinge = 1.0f / (getRigidBodyA().computeAngularImpulseDenominator(axisA) + + getRigidBodyB().computeAngularImpulseDenominator(axisA)); + +} + +void btHingeConstraint::solveConstraint(btScalar timeStep) +{ + + btVector3 pivotAInW = m_rbA.getCenterOfMassTransform()*m_rbAFrame.getOrigin(); + btVector3 pivotBInW = m_rbB.getCenterOfMassTransform()*m_rbBFrame.getOrigin(); + + btScalar tau = btScalar(0.3); + btScalar damping = btScalar(1.); + +//linear part + if (!m_angularOnly) + { + btVector3 rel_pos1 = pivotAInW - m_rbA.getCenterOfMassPosition(); + btVector3 rel_pos2 = pivotBInW - m_rbB.getCenterOfMassPosition(); + + btVector3 vel1 = m_rbA.getVelocityInLocalPoint(rel_pos1); + btVector3 vel2 = m_rbB.getVelocityInLocalPoint(rel_pos2); + btVector3 vel = vel1 - vel2; + + for (int i=0;i<3;i++) + { + const btVector3& normal = m_jac[i].m_linearJointAxis; + btScalar jacDiagABInv = btScalar(1.) / m_jac[i].getDiagonal(); + + btScalar rel_vel; + rel_vel = normal.dot(vel); + //positional error (zeroth order error) + btScalar depth = -(pivotAInW - pivotBInW).dot(normal); //this is the error projected on the normal + btScalar impulse = depth*tau/timeStep * jacDiagABInv - rel_vel * jacDiagABInv; + m_appliedImpulse += impulse; + btVector3 impulse_vector = normal * impulse; + m_rbA.applyImpulse(impulse_vector, pivotAInW - m_rbA.getCenterOfMassPosition()); + m_rbB.applyImpulse(-impulse_vector, pivotBInW - m_rbB.getCenterOfMassPosition()); + } + } + + + { + ///solve angular part + + // get axes in world space + btVector3 axisA = getRigidBodyA().getCenterOfMassTransform().getBasis() * m_rbAFrame.getBasis().getColumn(2); + btVector3 axisB = getRigidBodyB().getCenterOfMassTransform().getBasis() * m_rbBFrame.getBasis().getColumn(2); + + const btVector3& angVelA = getRigidBodyA().getAngularVelocity(); + const btVector3& angVelB = getRigidBodyB().getAngularVelocity(); + + btVector3 angVelAroundHingeAxisA = axisA * axisA.dot(angVelA); + btVector3 angVelAroundHingeAxisB = axisB * axisB.dot(angVelB); + + btVector3 angAorthog = angVelA - angVelAroundHingeAxisA; + btVector3 angBorthog = angVelB - angVelAroundHingeAxisB; + btVector3 velrelOrthog = angAorthog-angBorthog; + { + //solve orthogonal angular velocity correction + btScalar relaxation = btScalar(1.); + btScalar len = velrelOrthog.length(); + if (len > btScalar(0.00001)) + { + btVector3 normal = velrelOrthog.normalized(); + btScalar denom = getRigidBodyA().computeAngularImpulseDenominator(normal) + + getRigidBodyB().computeAngularImpulseDenominator(normal); + // scale for mass and relaxation + //todo: expose this 0.9 factor to developer + velrelOrthog *= (btScalar(1.)/denom) * m_relaxationFactor; + } + + //solve angular positional correction + btVector3 angularError = -axisA.cross(axisB) *(btScalar(1.)/timeStep); + btScalar len2 = angularError.length(); + if (len2>btScalar(0.00001)) + { + btVector3 normal2 = angularError.normalized(); + btScalar denom2 = getRigidBodyA().computeAngularImpulseDenominator(normal2) + + getRigidBodyB().computeAngularImpulseDenominator(normal2); + angularError *= (btScalar(1.)/denom2) * relaxation; + } + + m_rbA.applyTorqueImpulse(-velrelOrthog+angularError); + m_rbB.applyTorqueImpulse(velrelOrthog-angularError); + + // solve limit + if (m_solveLimit) + { + btScalar amplitude = ( (angVelB - angVelA).dot( axisA )*m_relaxationFactor + m_correction* (btScalar(1.)/timeStep)*m_biasFactor ) * m_limitSign; + + btScalar impulseMag = amplitude * m_kHinge; + + // Clamp the accumulated impulse + btScalar temp = m_accLimitImpulse; + m_accLimitImpulse = btMax(m_accLimitImpulse + impulseMag, btScalar(0) ); + impulseMag = m_accLimitImpulse - temp; + + + btVector3 impulse = axisA * impulseMag * m_limitSign; + m_rbA.applyTorqueImpulse(impulse); + m_rbB.applyTorqueImpulse(-impulse); + } + } + + //apply motor + if (m_enableAngularMotor) + { + //todo: add limits too + btVector3 angularLimit(0,0,0); + + btVector3 velrel = angVelAroundHingeAxisA - angVelAroundHingeAxisB; + btScalar projRelVel = velrel.dot(axisA); + + btScalar desiredMotorVel = m_motorTargetVelocity; + btScalar motor_relvel = desiredMotorVel - projRelVel; + + btScalar unclippedMotorImpulse = m_kHinge * motor_relvel;; + //todo: should clip against accumulated impulse + btScalar clippedMotorImpulse = unclippedMotorImpulse > m_maxMotorImpulse ? m_maxMotorImpulse : unclippedMotorImpulse; + clippedMotorImpulse = clippedMotorImpulse < -m_maxMotorImpulse ? -m_maxMotorImpulse : clippedMotorImpulse; + btVector3 motorImp = clippedMotorImpulse * axisA; + + m_rbA.applyTorqueImpulse(motorImp+angularLimit); + m_rbB.applyTorqueImpulse(-motorImp-angularLimit); + + } + } + +} + +void btHingeConstraint::updateRHS(btScalar timeStep) +{ + (void)timeStep; + +} + +btScalar btHingeConstraint::getHingeAngle() +{ + const btVector3 refAxis0 = getRigidBodyA().getCenterOfMassTransform().getBasis() * m_rbAFrame.getBasis().getColumn(0); + const btVector3 refAxis1 = getRigidBodyA().getCenterOfMassTransform().getBasis() * m_rbAFrame.getBasis().getColumn(1); + const btVector3 swingAxis = getRigidBodyB().getCenterOfMassTransform().getBasis() * m_rbBFrame.getBasis().getColumn(1); + + return btAtan2Fast( swingAxis.dot(refAxis0), swingAxis.dot(refAxis1) ); +} diff --git a/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.h b/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.h new file mode 100644 index 0000000..798d1d2 --- /dev/null +++ b/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.h @@ -0,0 +1,130 @@ +/* +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. +*/ + +/* Hinge Constraint by Dirk Gregorius. Limits added by Marcus Hennix at Starbreeze Studios */ + +#ifndef HINGECONSTRAINT_H +#define HINGECONSTRAINT_H + +#include "LinearMath/btVector3.h" +#include "btJacobianEntry.h" +#include "btTypedConstraint.h" + +class btRigidBody; + +/// hinge constraint between two rigidbodies each with a pivotpoint that descibes the axis location in local space +/// axis defines the orientation of the hinge axis +class btHingeConstraint : public btTypedConstraint +{ +#ifdef IN_PARALLELL_SOLVER +public: +#endif + btJacobianEntry m_jac[3]; //3 orthogonal linear constraints + btJacobianEntry m_jacAng[3]; //2 orthogonal angular constraints+ 1 for limit/motor + + btTransform m_rbAFrame; // constraint axii. Assumes z is hinge axis. + btTransform m_rbBFrame; + + btScalar m_motorTargetVelocity; + btScalar m_maxMotorImpulse; + + btScalar m_limitSoftness; + btScalar m_biasFactor; + btScalar m_relaxationFactor; + + btScalar m_lowerLimit; + btScalar m_upperLimit; + + btScalar m_kHinge; + + btScalar m_limitSign; + btScalar m_correction; + + btScalar m_accLimitImpulse; + + bool m_angularOnly; + bool m_enableAngularMotor; + bool m_solveLimit; + + +public: + + btHingeConstraint(btRigidBody& rbA,btRigidBody& rbB, const btVector3& pivotInA,const btVector3& pivotInB, btVector3& axisInA,btVector3& axisInB); + + btHingeConstraint(btRigidBody& rbA,const btVector3& pivotInA,btVector3& axisInA); + + btHingeConstraint(btRigidBody& rbA,btRigidBody& rbB, const btTransform& rbAFrame, const btTransform& rbBFrame); + + btHingeConstraint(btRigidBody& rbA,const btTransform& rbAFrame); + + btHingeConstraint(); + + virtual void buildJacobian(); + + virtual void solveConstraint(btScalar timeStep); + + void updateRHS(btScalar timeStep); + + const btRigidBody& getRigidBodyA() const + { + return m_rbA; + } + const btRigidBody& getRigidBodyB() const + { + return m_rbB; + } + + void setAngularOnly(bool angularOnly) + { + m_angularOnly = angularOnly; + } + + void enableAngularMotor(bool enableMotor,btScalar targetVelocity,btScalar maxMotorImpulse) + { + m_enableAngularMotor = enableMotor; + m_motorTargetVelocity = targetVelocity; + m_maxMotorImpulse = maxMotorImpulse; + } + + void setLimit(btScalar low,btScalar high,btScalar _softness = 0.9f, btScalar _biasFactor = 0.3f, btScalar _relaxationFactor = 1.0f) + { + m_lowerLimit = low; + m_upperLimit = high; + + m_limitSoftness = _softness; + m_biasFactor = _biasFactor; + m_relaxationFactor = _relaxationFactor; + + } + + btScalar getHingeAngle(); + + + const btTransform& getAFrame() { return m_rbAFrame; }; + const btTransform& getBFrame() { return m_rbBFrame; }; + + inline int getSolveLimit() + { + return m_solveLimit; + } + + inline btScalar getLimitSign() + { + return m_limitSign; + } + +}; + +#endif //HINGECONSTRAINT_H diff --git a/bullet/src/BulletDynamics/ConstraintSolver/btJacobianEntry.h b/bullet/src/BulletDynamics/ConstraintSolver/btJacobianEntry.h new file mode 100644 index 0000000..745a6cb --- /dev/null +++ b/bullet/src/BulletDynamics/ConstraintSolver/btJacobianEntry.h @@ -0,0 +1,156 @@ +/* +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. +*/ + +#ifndef JACOBIAN_ENTRY_H +#define JACOBIAN_ENTRY_H + +#include "LinearMath/btVector3.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" + + +//notes: +// Another memory optimization would be to store m_1MinvJt in the remaining 3 w components +// which makes the btJacobianEntry memory layout 16 bytes +// if you only are interested in angular part, just feed massInvA and massInvB zero + +/// Jacobian entry is an abstraction that allows to describe constraints +/// it can be used in combination with a constraint solver +/// Can be used to relate the effect of an impulse to the constraint error +class btJacobianEntry +{ +public: + btJacobianEntry() {}; + //constraint between two different rigidbodies + btJacobianEntry( + const btMatrix3x3& world2A, + const btMatrix3x3& world2B, + const btVector3& rel_pos1,const btVector3& rel_pos2, + const btVector3& jointAxis, + const btVector3& inertiaInvA, + const btScalar massInvA, + const btVector3& inertiaInvB, + const btScalar massInvB) + :m_linearJointAxis(jointAxis) + { + m_aJ = world2A*(rel_pos1.cross(m_linearJointAxis)); + m_bJ = world2B*(rel_pos2.cross(-m_linearJointAxis)); + m_0MinvJt = inertiaInvA * m_aJ; + m_1MinvJt = inertiaInvB * m_bJ; + m_Adiag = massInvA + m_0MinvJt.dot(m_aJ) + massInvB + m_1MinvJt.dot(m_bJ); + + btAssert(m_Adiag > btScalar(0.0)); + } + + //angular constraint between two different rigidbodies + btJacobianEntry(const btVector3& jointAxis, + const btMatrix3x3& world2A, + const btMatrix3x3& world2B, + const btVector3& inertiaInvA, + const btVector3& inertiaInvB) + :m_linearJointAxis(btVector3(btScalar(0.),btScalar(0.),btScalar(0.))) + { + m_aJ= world2A*jointAxis; + m_bJ = world2B*-jointAxis; + m_0MinvJt = inertiaInvA * m_aJ; + m_1MinvJt = inertiaInvB * m_bJ; + m_Adiag = m_0MinvJt.dot(m_aJ) + m_1MinvJt.dot(m_bJ); + + btAssert(m_Adiag > btScalar(0.0)); + } + + //angular constraint between two different rigidbodies + btJacobianEntry(const btVector3& axisInA, + const btVector3& axisInB, + const btVector3& inertiaInvA, + const btVector3& inertiaInvB) + : m_linearJointAxis(btVector3(btScalar(0.),btScalar(0.),btScalar(0.))) + , m_aJ(axisInA) + , m_bJ(-axisInB) + { + m_0MinvJt = inertiaInvA * m_aJ; + m_1MinvJt = inertiaInvB * m_bJ; + m_Adiag = m_0MinvJt.dot(m_aJ) + m_1MinvJt.dot(m_bJ); + + btAssert(m_Adiag > btScalar(0.0)); + } + + //constraint on one rigidbody + btJacobianEntry( + const btMatrix3x3& world2A, + const btVector3& rel_pos1,const btVector3& rel_pos2, + const btVector3& jointAxis, + const btVector3& inertiaInvA, + const btScalar massInvA) + :m_linearJointAxis(jointAxis) + { + m_aJ= world2A*(rel_pos1.cross(jointAxis)); + m_bJ = world2A*(rel_pos2.cross(-jointAxis)); + m_0MinvJt = inertiaInvA * m_aJ; + m_1MinvJt = btVector3(btScalar(0.),btScalar(0.),btScalar(0.)); + m_Adiag = massInvA + m_0MinvJt.dot(m_aJ); + + btAssert(m_Adiag > btScalar(0.0)); + } + + btScalar getDiagonal() const { return m_Adiag; } + + // for two constraints on the same rigidbody (for example vehicle friction) + btScalar getNonDiagonal(const btJacobianEntry& jacB, const btScalar massInvA) const + { + const btJacobianEntry& jacA = *this; + btScalar lin = massInvA * jacA.m_linearJointAxis.dot(jacB.m_linearJointAxis); + btScalar ang = jacA.m_0MinvJt.dot(jacB.m_aJ); + return lin + ang; + } + + + + // for two constraints on sharing two same rigidbodies (for example two contact points between two rigidbodies) + btScalar getNonDiagonal(const btJacobianEntry& jacB,const btScalar massInvA,const btScalar massInvB) const + { + const btJacobianEntry& jacA = *this; + btVector3 lin = jacA.m_linearJointAxis * jacB.m_linearJointAxis; + btVector3 ang0 = jacA.m_0MinvJt * jacB.m_aJ; + btVector3 ang1 = jacA.m_1MinvJt * jacB.m_bJ; + btVector3 lin0 = massInvA * lin ; + btVector3 lin1 = massInvB * lin; + btVector3 sum = ang0+ang1+lin0+lin1; + return sum[0]+sum[1]+sum[2]; + } + + btScalar getRelativeVelocity(const btVector3& linvelA,const btVector3& angvelA,const btVector3& linvelB,const btVector3& angvelB) + { + btVector3 linrel = linvelA - linvelB; + btVector3 angvela = angvelA * m_aJ; + btVector3 angvelb = angvelB * m_bJ; + linrel *= m_linearJointAxis; + angvela += angvelb; + angvela += linrel; + btScalar rel_vel2 = angvela[0]+angvela[1]+angvela[2]; + return rel_vel2 + SIMD_EPSILON; + } +//private: + + btVector3 m_linearJointAxis; + btVector3 m_aJ; + btVector3 m_bJ; + btVector3 m_0MinvJt; + btVector3 m_1MinvJt; + //Optimization: can be stored in the w/last component of one of the vectors + btScalar m_Adiag; + +}; + +#endif //JACOBIAN_ENTRY_H diff --git a/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.cpp b/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.cpp new file mode 100644 index 0000000..c950e1c --- /dev/null +++ b/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.cpp @@ -0,0 +1,117 @@ +/* +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. +*/ + + +#include "btPoint2PointConstraint.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include + + + +btPoint2PointConstraint::btPoint2PointConstraint() +:btTypedConstraint(POINT2POINT_CONSTRAINT_TYPE) +{ +} + +btPoint2PointConstraint::btPoint2PointConstraint(btRigidBody& rbA,btRigidBody& rbB, const btVector3& pivotInA,const btVector3& pivotInB) +:btTypedConstraint(POINT2POINT_CONSTRAINT_TYPE,rbA,rbB),m_pivotInA(pivotInA),m_pivotInB(pivotInB) +{ + +} + + +btPoint2PointConstraint::btPoint2PointConstraint(btRigidBody& rbA,const btVector3& pivotInA) +:btTypedConstraint(POINT2POINT_CONSTRAINT_TYPE,rbA),m_pivotInA(pivotInA),m_pivotInB(rbA.getCenterOfMassTransform()(pivotInA)) +{ + +} + +void btPoint2PointConstraint::buildJacobian() +{ + m_appliedImpulse = btScalar(0.); + + btVector3 normal(0,0,0); + + for (int i=0;i<3;i++) + { + normal[i] = 1; + new (&m_jac[i]) btJacobianEntry( + m_rbA.getCenterOfMassTransform().getBasis().transpose(), + m_rbB.getCenterOfMassTransform().getBasis().transpose(), + m_rbA.getCenterOfMassTransform()*m_pivotInA - m_rbA.getCenterOfMassPosition(), + m_rbB.getCenterOfMassTransform()*m_pivotInB - m_rbB.getCenterOfMassPosition(), + normal, + m_rbA.getInvInertiaDiagLocal(), + m_rbA.getInvMass(), + m_rbB.getInvInertiaDiagLocal(), + m_rbB.getInvMass()); + normal[i] = 0; + } + +} + +void btPoint2PointConstraint::solveConstraint(btScalar timeStep) +{ + btVector3 pivotAInW = m_rbA.getCenterOfMassTransform()*m_pivotInA; + btVector3 pivotBInW = m_rbB.getCenterOfMassTransform()*m_pivotInB; + + + btVector3 normal(0,0,0); + + +// btVector3 angvelA = m_rbA.getCenterOfMassTransform().getBasis().transpose() * m_rbA.getAngularVelocity(); +// btVector3 angvelB = m_rbB.getCenterOfMassTransform().getBasis().transpose() * m_rbB.getAngularVelocity(); + + for (int i=0;i<3;i++) + { + normal[i] = 1; + btScalar jacDiagABInv = btScalar(1.) / m_jac[i].getDiagonal(); + + btVector3 rel_pos1 = pivotAInW - m_rbA.getCenterOfMassPosition(); + btVector3 rel_pos2 = pivotBInW - m_rbB.getCenterOfMassPosition(); + //this jacobian entry could be re-used for all iterations + + btVector3 vel1 = m_rbA.getVelocityInLocalPoint(rel_pos1); + btVector3 vel2 = m_rbB.getVelocityInLocalPoint(rel_pos2); + btVector3 vel = vel1 - vel2; + + btScalar rel_vel; + rel_vel = normal.dot(vel); + + /* + //velocity error (first order error) + btScalar rel_vel = m_jac[i].getRelativeVelocity(m_rbA.getLinearVelocity(),angvelA, + m_rbB.getLinearVelocity(),angvelB); + */ + + //positional error (zeroth order error) + btScalar depth = -(pivotAInW - pivotBInW).dot(normal); //this is the error projected on the normal + + btScalar impulse = depth*m_setting.m_tau/timeStep * jacDiagABInv - m_setting.m_damping * rel_vel * jacDiagABInv; + m_appliedImpulse+=impulse; + btVector3 impulse_vector = normal * impulse; + m_rbA.applyImpulse(impulse_vector, pivotAInW - m_rbA.getCenterOfMassPosition()); + m_rbB.applyImpulse(-impulse_vector, pivotBInW - m_rbB.getCenterOfMassPosition()); + + normal[i] = 0; + } +} + +void btPoint2PointConstraint::updateRHS(btScalar timeStep) +{ + (void)timeStep; + +} + diff --git a/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h b/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h new file mode 100644 index 0000000..ebf18c1 --- /dev/null +++ b/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h @@ -0,0 +1,80 @@ +/* +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. +*/ + +#ifndef POINT2POINTCONSTRAINT_H +#define POINT2POINTCONSTRAINT_H + +#include "LinearMath/btVector3.h" +#include "btJacobianEntry.h" +#include "btTypedConstraint.h" + +class btRigidBody; + +struct btConstraintSetting +{ + btConstraintSetting() : + m_tau(btScalar(0.3)), + m_damping(btScalar(1.)) + { + } + btScalar m_tau; + btScalar m_damping; +}; + +/// point to point constraint between two rigidbodies each with a pivotpoint that descibes the 'ballsocket' location in local space +class btPoint2PointConstraint : public btTypedConstraint +{ +#ifdef IN_PARALLELL_SOLVER +public: +#endif + btJacobianEntry m_jac[3]; //3 orthogonal linear constraints + + btVector3 m_pivotInA; + btVector3 m_pivotInB; + + + +public: + + btConstraintSetting m_setting; + + btPoint2PointConstraint(btRigidBody& rbA,btRigidBody& rbB, const btVector3& pivotInA,const btVector3& pivotInB); + + btPoint2PointConstraint(btRigidBody& rbA,const btVector3& pivotInA); + + btPoint2PointConstraint(); + + virtual void buildJacobian(); + + + virtual void solveConstraint(btScalar timeStep); + + void updateRHS(btScalar timeStep); + + void setPivotA(const btVector3& pivotA) + { + m_pivotInA = pivotA; + } + + void setPivotB(const btVector3& pivotB) + { + m_pivotInB = pivotB; + } + + + +}; + +#endif //POINT2POINTCONSTRAINT_H diff --git a/bullet/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp b/bullet/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp new file mode 100644 index 0000000..f0effd1 --- /dev/null +++ b/bullet/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp @@ -0,0 +1,1165 @@ +/* +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. +*/ + + +#include "btSequentialImpulseConstraintSolver.h" +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "btContactConstraint.h" +#include "btSolve2LinearConstraint.h" +#include "btContactSolverInfo.h" +#include "LinearMath/btIDebugDraw.h" +#include "btJacobianEntry.h" +#include "LinearMath/btMinMax.h" +#include "BulletDynamics/ConstraintSolver/btTypedConstraint.h" +#include +#include "LinearMath/btStackAlloc.h" +#include "LinearMath/btQuickprof.h" +#include "btSolverBody.h" +#include "btSolverConstraint.h" + +#include "LinearMath/btAlignedObjectArray.h" + +#ifdef USE_PROFILE +#include "LinearMath/btQuickprof.h" +#endif //USE_PROFILE + +int totalCpd = 0; + +int gTotalContactPoints = 0; + +struct btOrderIndex +{ + int m_manifoldIndex; + int m_pointIndex; +}; + + + +#define SEQUENTIAL_IMPULSE_MAX_SOLVER_POINTS 16384 +static btOrderIndex gOrder[SEQUENTIAL_IMPULSE_MAX_SOLVER_POINTS]; + + +unsigned long btSequentialImpulseConstraintSolver::btRand2() +{ + m_btSeed2 = (1664525L*m_btSeed2 + 1013904223L) & 0xffffffff; + return m_btSeed2; +} + + + +//See ODE: adam's all-int straightforward(?) dRandInt (0..n-1) +int btSequentialImpulseConstraintSolver::btRandInt2 (int n) +{ + // seems good; xor-fold and modulus + const unsigned long un = n; + unsigned long r = btRand2(); + + // note: probably more aggressive than it needs to be -- might be + // able to get away without one or two of the innermost branches. + if (un <= 0x00010000UL) { + r ^= (r >> 16); + if (un <= 0x00000100UL) { + r ^= (r >> 8); + if (un <= 0x00000010UL) { + r ^= (r >> 4); + if (un <= 0x00000004UL) { + r ^= (r >> 2); + if (un <= 0x00000002UL) { + r ^= (r >> 1); + } + } + } + } + } + + return (int) (r % un); +} + + + + + +bool MyContactDestroyedCallback(void* userPersistentData) +{ + assert (userPersistentData); + btConstraintPersistentData* cpd = (btConstraintPersistentData*)userPersistentData; + delete cpd; + totalCpd--; + //printf("totalCpd = %i. DELETED Ptr %x\n",totalCpd,userPersistentData); + return true; +} + + + +btSequentialImpulseConstraintSolver::btSequentialImpulseConstraintSolver() +:m_solverMode(SOLVER_RANDMIZE_ORDER | SOLVER_CACHE_FRIENDLY), //not using SOLVER_USE_WARMSTARTING, +m_btSeed2(0) +{ + gContactDestroyedCallback = &MyContactDestroyedCallback; + + //initialize default friction/contact funcs + int i,j; + for (i=0;im_angularVelocity = rigidbody->getAngularVelocity(); + solverBody->m_centerOfMassPosition = rigidbody->getCenterOfMassPosition(); + solverBody->m_friction = rigidbody->getFriction(); +// solverBody->m_invInertiaWorld = rigidbody->getInvInertiaTensorWorld(); + solverBody->m_invMass = rigidbody->getInvMass(); + solverBody->m_linearVelocity = rigidbody->getLinearVelocity(); + solverBody->m_originalBody = rigidbody; + solverBody->m_angularFactor = rigidbody->getAngularFactor(); +} + +btScalar penetrationResolveFactor = btScalar(0.9); +btScalar restitutionCurve(btScalar rel_vel, btScalar restitution) +{ + btScalar rest = restitution * -rel_vel; + return rest; +} + + + + + + +//velocity + friction +//response between two dynamic objects with friction +SIMD_FORCE_INLINE btScalar resolveSingleCollisionCombinedCacheFriendly( + btSolverBody& body1, + btSolverBody& body2, + btSolverConstraint& contactConstraint, + const btContactSolverInfo& solverInfo) +{ + (void)solverInfo; + + btScalar normalImpulse(0.f); + { + if (contactConstraint.m_penetration < 0.f) + return 0.f; + + // Optimized version of projected relative velocity, use precomputed cross products with normal + // body1.getVelocityInLocalPoint(contactConstraint.m_rel_posA,vel1); + // body2.getVelocityInLocalPoint(contactConstraint.m_rel_posB,vel2); + // btVector3 vel = vel1 - vel2; + // btScalar rel_vel = contactConstraint.m_contactNormal.dot(vel); + + btScalar rel_vel; + btScalar vel1Dotn = contactConstraint.m_contactNormal.dot(body1.m_linearVelocity) + + contactConstraint.m_relpos1CrossNormal.dot(body1.m_angularVelocity); + btScalar vel2Dotn = contactConstraint.m_contactNormal.dot(body2.m_linearVelocity) + + contactConstraint.m_relpos2CrossNormal.dot(body2.m_angularVelocity); + + rel_vel = vel1Dotn-vel2Dotn; + + + btScalar positionalError = contactConstraint.m_penetration; + btScalar velocityError = contactConstraint.m_restitution - rel_vel;// * damping; + + btScalar penetrationImpulse = positionalError * contactConstraint.m_jacDiagABInv; + btScalar velocityImpulse = velocityError * contactConstraint.m_jacDiagABInv; + btScalar normalImpulse = penetrationImpulse+velocityImpulse; + + // See Erin Catto's GDC 2006 paper: Clamp the accumulated impulse + btScalar oldNormalImpulse = contactConstraint.m_appliedImpulse; + btScalar sum = oldNormalImpulse + normalImpulse; + contactConstraint.m_appliedImpulse = btScalar(0.) > sum ? btScalar(0.): sum; + + btScalar oldVelocityImpulse = contactConstraint.m_appliedVelocityImpulse; + btScalar velocitySum = oldVelocityImpulse + velocityImpulse; + contactConstraint.m_appliedVelocityImpulse = btScalar(0.) > velocitySum ? btScalar(0.): velocitySum; + + normalImpulse = contactConstraint.m_appliedImpulse - oldNormalImpulse; + + if (body1.m_invMass) + { + body1.internalApplyImpulse(contactConstraint.m_contactNormal*body1.m_invMass, + contactConstraint.m_angularComponentA,normalImpulse); + } + if (body2.m_invMass) + { + body2.internalApplyImpulse(contactConstraint.m_contactNormal*body2.m_invMass, + contactConstraint.m_angularComponentB,-normalImpulse); + } + + } + + + + return normalImpulse; +} + + +#ifndef NO_FRICTION_TANGENTIALS + +SIMD_FORCE_INLINE btScalar resolveSingleFrictionCacheFriendly( + btSolverBody& body1, + btSolverBody& body2, + btSolverConstraint& contactConstraint, + const btContactSolverInfo& solverInfo, + btScalar appliedNormalImpulse) +{ + (void)solverInfo; + + + const btScalar combinedFriction = contactConstraint.m_friction; + + const btScalar limit = appliedNormalImpulse * combinedFriction; + + if (appliedNormalImpulse>btScalar(0.)) + //friction + { + + btScalar j1; + { + + btScalar rel_vel; + const btScalar vel1Dotn = contactConstraint.m_contactNormal.dot(body1.m_linearVelocity) + + contactConstraint.m_relpos1CrossNormal.dot(body1.m_angularVelocity); + const btScalar vel2Dotn = contactConstraint.m_contactNormal.dot(body2.m_linearVelocity) + + contactConstraint.m_relpos2CrossNormal.dot(body2.m_angularVelocity); + rel_vel = vel1Dotn-vel2Dotn; + + // calculate j that moves us to zero relative velocity + j1 = -rel_vel * contactConstraint.m_jacDiagABInv; + btScalar oldTangentImpulse = contactConstraint.m_appliedImpulse; + contactConstraint.m_appliedImpulse = oldTangentImpulse + j1; + GEN_set_min(contactConstraint.m_appliedImpulse, limit); + GEN_set_max(contactConstraint.m_appliedImpulse, -limit); + j1 = contactConstraint.m_appliedImpulse - oldTangentImpulse; + + } + + if (body1.m_invMass) + { + body1.internalApplyImpulse(contactConstraint.m_contactNormal*body1.m_invMass,contactConstraint.m_angularComponentA,j1); + } + if (body2.m_invMass) + { + body2.internalApplyImpulse(contactConstraint.m_contactNormal*body2.m_invMass,contactConstraint.m_angularComponentB,-j1); + } + + } + return 0.f; +} + + +#else + +//velocity + friction +//response between two dynamic objects with friction +btScalar resolveSingleFrictionCacheFriendly( + btSolverBody& body1, + btSolverBody& body2, + btSolverConstraint& contactConstraint, + const btContactSolverInfo& solverInfo) +{ + + btVector3 vel1; + btVector3 vel2; + btScalar normalImpulse(0.f); + + { + const btVector3& normal = contactConstraint.m_contactNormal; + if (contactConstraint.m_penetration < 0.f) + return 0.f; + + + body1.getVelocityInLocalPoint(contactConstraint.m_rel_posA,vel1); + body2.getVelocityInLocalPoint(contactConstraint.m_rel_posB,vel2); + btVector3 vel = vel1 - vel2; + btScalar rel_vel; + rel_vel = normal.dot(vel); + + btVector3 lat_vel = vel - normal * rel_vel; + btScalar lat_rel_vel = lat_vel.length2(); + + btScalar combinedFriction = contactConstraint.m_friction; + const btVector3& rel_pos1 = contactConstraint.m_rel_posA; + const btVector3& rel_pos2 = contactConstraint.m_rel_posB; + + + //if (contactConstraint.m_appliedVelocityImpulse > 0.f) + if (lat_rel_vel > SIMD_EPSILON*SIMD_EPSILON) + { + lat_rel_vel = btSqrt(lat_rel_vel); + + lat_vel /= lat_rel_vel; + btVector3 temp1 = body1.m_invInertiaWorld * rel_pos1.cross(lat_vel); + btVector3 temp2 = body2.m_invInertiaWorld * rel_pos2.cross(lat_vel); + btScalar friction_impulse = lat_rel_vel / + (body1.m_invMass + body2.m_invMass + lat_vel.dot(temp1.cross(rel_pos1) + temp2.cross(rel_pos2))); + btScalar normal_impulse = contactConstraint.m_appliedVelocityImpulse * combinedFriction; + + GEN_set_min(friction_impulse, normal_impulse); + GEN_set_max(friction_impulse, -normal_impulse); + body1.applyImpulse(lat_vel * -friction_impulse, rel_pos1); + body2.applyImpulse(lat_vel * friction_impulse, rel_pos2); + } + } + + return normalImpulse; +} + +#endif //NO_FRICTION_TANGENTIALS + +btAlignedObjectArray tmpSolverBodyPool; +btAlignedObjectArray tmpSolverConstraintPool; +btAlignedObjectArray tmpSolverFrictionConstraintPool; + + +btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendly(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer,btStackAlloc* stackAlloc) +{ + (void)stackAlloc; + (void)debugDrawer; + + if (!(numConstraints + numManifolds)) + { +// printf("empty\n"); + return 0.f; + } + + BEGIN_PROFILE("refreshManifolds"); + + int i; + for (i=0;igetBody0(); + btRigidBody* rb1 = (btRigidBody*)manifold->getBody1(); + + manifold->refreshContactPoints(rb0->getCenterOfMassTransform(),rb1->getCenterOfMassTransform()); + + } + + END_PROFILE("refreshManifolds"); + + + BEGIN_PROFILE("gatherSolverData"); + + //int sizeofSB = sizeof(btSolverBody); + //int sizeofSC = sizeof(btSolverConstraint); + + + //if (1) + { + //if m_stackAlloc, try to pack bodies/constraints to speed up solving +// btBlock* sablock; +// sablock = stackAlloc->beginBlock(); + + // int memsize = 16; +// unsigned char* stackMemory = stackAlloc->allocate(memsize); + + + //todo: use stack allocator for this temp memory + int minReservation = numManifolds*2; + + tmpSolverBodyPool.reserve(minReservation); + + { + for (int i=0;igetIslandTag() >= 0)) + { + btAssert(rb->getCompanionId() < 0); + int solverBodyId = tmpSolverBodyPool.size(); + btSolverBody& solverBody = tmpSolverBodyPool.expand(); + initSolverBody(&solverBody,rb); + rb->setCompanionId(solverBodyId); + } + } + } + + + tmpSolverConstraintPool.reserve(minReservation); + tmpSolverFrictionConstraintPool.reserve(minReservation); + { + int i; + + for (i=0;igetBody0(); + btRigidBody* rb1 = (btRigidBody*)manifold->getBody1(); + + + int solverBodyIdA=-1; + int solverBodyIdB=-1; + + if (manifold->getNumContacts()) + { + + + + if (rb0->getIslandTag() >= 0) + { + solverBodyIdA = rb0->getCompanionId(); + } else + { + //create a static body + solverBodyIdA = tmpSolverBodyPool.size(); + btSolverBody& solverBody = tmpSolverBodyPool.expand(); + initSolverBody(&solverBody,rb0); + } + + if (rb1->getIslandTag() >= 0) + { + solverBodyIdB = rb1->getCompanionId(); + } else + { + //create a static body + solverBodyIdB = tmpSolverBodyPool.size(); + btSolverBody& solverBody = tmpSolverBodyPool.expand(); + initSolverBody(&solverBody,rb1); + } + } + + for (int j=0;jgetNumContacts();j++) + { + + btManifoldPoint& cp = manifold->getContactPoint(j); + + int frictionIndex = tmpSolverConstraintPool.size(); + + if (cp.getDistance() <= btScalar(0.)) + { + + const btVector3& pos1 = cp.getPositionWorldOnA(); + const btVector3& pos2 = cp.getPositionWorldOnB(); + + btVector3 rel_pos1 = pos1 - rb0->getCenterOfMassPosition(); + btVector3 rel_pos2 = pos2 - rb1->getCenterOfMassPosition(); + + + btScalar relaxation = 1.f; + + { + btSolverConstraint& solverConstraint = tmpSolverConstraintPool.expand(); + + solverConstraint.m_solverBodyIdA = solverBodyIdA; + solverConstraint.m_solverBodyIdB = solverBodyIdB; + solverConstraint.m_constraintType = btSolverConstraint::BT_SOLVER_CONTACT_1D; + + + + { + //can be optimized, the cross products are already calculated + btScalar denom0 = rb0->computeImpulseDenominator(pos1,cp.m_normalWorldOnB); + btScalar denom1 = rb1->computeImpulseDenominator(pos2,cp.m_normalWorldOnB); + btScalar denom = relaxation/(denom0+denom1); + solverConstraint.m_jacDiagABInv = denom; + } + + solverConstraint.m_contactNormal = cp.m_normalWorldOnB; + solverConstraint.m_relpos1CrossNormal = rel_pos1.cross(cp.m_normalWorldOnB); + solverConstraint.m_relpos2CrossNormal = rel_pos2.cross(cp.m_normalWorldOnB); + + + btVector3 vel1 = rb0->getVelocityInLocalPoint(rel_pos1); + btVector3 vel2 = rb1->getVelocityInLocalPoint(rel_pos2); + + btVector3 vel = vel1 - vel2; + btScalar rel_vel; + rel_vel = cp.m_normalWorldOnB.dot(vel); + + + solverConstraint.m_penetration = cp.getDistance();///btScalar(infoGlobal.m_numIterations); + solverConstraint.m_friction = cp.m_combinedFriction; + btScalar rest = restitutionCurve(rel_vel, cp.m_combinedRestitution); + if (rest <= btScalar(0.)) + { + rest = 0.f; + }; + + btScalar penVel = -solverConstraint.m_penetration/infoGlobal.m_timeStep; + if (rest > penVel) + { + rest = btScalar(0.); + } + solverConstraint.m_restitution = rest; + + solverConstraint.m_penetration *= -(infoGlobal.m_erp/infoGlobal.m_timeStep); + + solverConstraint.m_appliedImpulse = 0.f; + solverConstraint.m_appliedVelocityImpulse = 0.f; + + + btVector3 torqueAxis0 = rel_pos1.cross(cp.m_normalWorldOnB); + solverConstraint.m_angularComponentA = rb0->getInvInertiaTensorWorld()*torqueAxis0; + btVector3 torqueAxis1 = rel_pos2.cross(cp.m_normalWorldOnB); + solverConstraint.m_angularComponentB = rb1->getInvInertiaTensorWorld()*torqueAxis1; + } + + //create 2 '1d axis' constraints for 2 tangential friction directions + + //re-calculate friction direction every frame, todo: check if this is really needed + btVector3 frictionTangential0a, frictionTangential1b; + + btPlaneSpace1(cp.m_normalWorldOnB,frictionTangential0a,frictionTangential1b); + + { + btSolverConstraint& solverConstraint = tmpSolverFrictionConstraintPool.expand(); + solverConstraint.m_contactNormal = frictionTangential0a; + + solverConstraint.m_solverBodyIdA = solverBodyIdA; + solverConstraint.m_solverBodyIdB = solverBodyIdB; + solverConstraint.m_constraintType = btSolverConstraint::BT_SOLVER_FRICTION_1D; + solverConstraint.m_frictionIndex = frictionIndex; + + solverConstraint.m_friction = cp.m_combinedFriction; + + solverConstraint.m_appliedImpulse = btScalar(0.); + solverConstraint.m_appliedVelocityImpulse = 0.f; + + btScalar denom0 = rb0->computeImpulseDenominator(pos1,solverConstraint.m_contactNormal); + btScalar denom1 = rb1->computeImpulseDenominator(pos2,solverConstraint.m_contactNormal); + btScalar denom = relaxation/(denom0+denom1); + solverConstraint.m_jacDiagABInv = denom; + + { + btVector3 ftorqueAxis0 = rel_pos1.cross(solverConstraint.m_contactNormal); + solverConstraint.m_relpos1CrossNormal = ftorqueAxis0; + solverConstraint.m_angularComponentA = rb0->getInvInertiaTensorWorld()*ftorqueAxis0; + } + { + btVector3 ftorqueAxis0 = rel_pos2.cross(solverConstraint.m_contactNormal); + solverConstraint.m_relpos2CrossNormal = ftorqueAxis0; + solverConstraint.m_angularComponentB = rb1->getInvInertiaTensorWorld()*ftorqueAxis0; + } + + } + + + { + + btSolverConstraint& solverConstraint = tmpSolverFrictionConstraintPool.expand(); + solverConstraint.m_contactNormal = frictionTangential1b; + + solverConstraint.m_solverBodyIdA = solverBodyIdA; + solverConstraint.m_solverBodyIdB = solverBodyIdB; + solverConstraint.m_constraintType = btSolverConstraint::BT_SOLVER_FRICTION_1D; + solverConstraint.m_frictionIndex = frictionIndex; + + solverConstraint.m_friction = cp.m_combinedFriction; + + solverConstraint.m_appliedImpulse = btScalar(0.); + solverConstraint.m_appliedVelocityImpulse = 0.f; + + btScalar denom0 = rb0->computeImpulseDenominator(pos1,solverConstraint.m_contactNormal); + btScalar denom1 = rb1->computeImpulseDenominator(pos2,solverConstraint.m_contactNormal); + btScalar denom = relaxation/(denom0+denom1); + solverConstraint.m_jacDiagABInv = denom; + { + btVector3 ftorqueAxis1 = rel_pos1.cross(solverConstraint.m_contactNormal); + solverConstraint.m_relpos1CrossNormal = ftorqueAxis1; + solverConstraint.m_angularComponentA = rb0->getInvInertiaTensorWorld()*ftorqueAxis1; + } + { + btVector3 ftorqueAxis1 = rel_pos2.cross(solverConstraint.m_contactNormal); + solverConstraint.m_relpos2CrossNormal = ftorqueAxis1; + solverConstraint.m_angularComponentB = rb1->getInvInertiaTensorWorld()*ftorqueAxis1; + } + } + + } + } + } + } + } + END_PROFILE("gatherSolverData"); + + BEGIN_PROFILE("prepareConstraints"); + + btContactSolverInfo info = infoGlobal; + + { + int j; + for (j=0;jbuildJacobian(); + } + } + + btAlignedObjectArray gOrderTmpConstraintPool; + btAlignedObjectArray gOrderFrictionConstraintPool; + + int numConstraintPool = tmpSolverConstraintPool.size(); + int numFrictionPool = tmpSolverFrictionConstraintPool.size(); + + ///todo: use stack allocator for such temporarily memory, same for solver bodies/constraints + gOrderTmpConstraintPool.resize(numConstraintPool); + gOrderFrictionConstraintPool.resize(numFrictionPool); + { + int i; + for (i=0;igetRigidBodyA().getIslandTag() >= 0) && (constraint->getRigidBodyA().getCompanionId() >= 0)) + { + tmpSolverBodyPool[constraint->getRigidBodyA().getCompanionId()].writebackVelocity(); + } + if ((constraint->getRigidBodyB().getIslandTag() >= 0) && (constraint->getRigidBodyB().getCompanionId() >= 0)) + { + tmpSolverBodyPool[constraint->getRigidBodyB().getCompanionId()].writebackVelocity(); + } + + constraint->solveConstraint(info.m_timeStep); + + if ((constraint->getRigidBodyA().getIslandTag() >= 0) && (constraint->getRigidBodyA().getCompanionId() >= 0)) + { + tmpSolverBodyPool[constraint->getRigidBodyA().getCompanionId()].readVelocity(); + } + if ((constraint->getRigidBodyB().getIslandTag() >= 0) && (constraint->getRigidBodyB().getCompanionId() >= 0)) + { + tmpSolverBodyPool[constraint->getRigidBodyB().getCompanionId()].readVelocity(); + } + + } + + { + int numPoolConstraints = tmpSolverConstraintPool.size(); + for (j=0;jgetNumContacts();p++) + { + gOrder[totalPoints].m_manifoldIndex = j; + gOrder[totalPoints].m_pointIndex = p; + totalPoints++; + } + } + } + + { + int j; + for (j=0;jbuildJacobian(); + } + } + + END_PROFILE("prepareConstraints"); + + + BEGIN_PROFILE("solveConstraints"); + + //should traverse the contacts random order... + int iteration; + + { + for ( iteration = 0;iterationsolveConstraint(info.m_timeStep); + } + + for (j=0;jgetBody0(), + (btRigidBody*)manifold->getBody1() + ,manifold->getContactPoint(gOrder[j].m_pointIndex),info,iteration,debugDrawer); + } + + for (j=0;jgetBody0(), + (btRigidBody*)manifold->getBody1(),manifold->getContactPoint(gOrder[j].m_pointIndex),info,iteration,debugDrawer); + } + } + } + + END_PROFILE("solveConstraints"); + + +#ifdef USE_PROFILE + btProfiler::endBlock("solve"); +#endif //USE_PROFILE + + + + + return btScalar(0.); +} + + + + + + + +void btSequentialImpulseConstraintSolver::prepareConstraints(btPersistentManifold* manifoldPtr, const btContactSolverInfo& info,btIDebugDraw* debugDrawer) +{ + + (void)debugDrawer; + + btRigidBody* body0 = (btRigidBody*)manifoldPtr->getBody0(); + btRigidBody* body1 = (btRigidBody*)manifoldPtr->getBody1(); + + + //only necessary to refresh the manifold once (first iteration). The integration is done outside the loop + { + manifoldPtr->refreshContactPoints(body0->getCenterOfMassTransform(),body1->getCenterOfMassTransform()); + + int numpoints = manifoldPtr->getNumContacts(); + + gTotalContactPoints += numpoints; + + btVector3 color(0,1,0); + for (int i=0;igetContactPoint(i); + if (cp.getDistance() <= btScalar(0.)) + { + const btVector3& pos1 = cp.getPositionWorldOnA(); + const btVector3& pos2 = cp.getPositionWorldOnB(); + + btVector3 rel_pos1 = pos1 - body0->getCenterOfMassPosition(); + btVector3 rel_pos2 = pos2 - body1->getCenterOfMassPosition(); + + + //this jacobian entry is re-used for all iterations + btJacobianEntry jac(body0->getCenterOfMassTransform().getBasis().transpose(), + body1->getCenterOfMassTransform().getBasis().transpose(), + rel_pos1,rel_pos2,cp.m_normalWorldOnB,body0->getInvInertiaDiagLocal(),body0->getInvMass(), + body1->getInvInertiaDiagLocal(),body1->getInvMass()); + + + btScalar jacDiagAB = jac.getDiagonal(); + + btConstraintPersistentData* cpd = (btConstraintPersistentData*) cp.m_userPersistentData; + if (cpd) + { + //might be invalid + cpd->m_persistentLifeTime++; + if (cpd->m_persistentLifeTime != cp.getLifeTime()) + { + //printf("Invalid: cpd->m_persistentLifeTime = %i cp.getLifeTime() = %i\n",cpd->m_persistentLifeTime,cp.getLifeTime()); + new (cpd) btConstraintPersistentData; + cpd->m_persistentLifeTime = cp.getLifeTime(); + + } else + { + //printf("Persistent: cpd->m_persistentLifeTime = %i cp.getLifeTime() = %i\n",cpd->m_persistentLifeTime,cp.getLifeTime()); + + } + } else + { + + cpd = new btConstraintPersistentData; + assert(cpd); + + totalCpd ++; + //printf("totalCpd = %i Created Ptr %x\n",totalCpd,cpd); + cp.m_userPersistentData = cpd; + cpd->m_persistentLifeTime = cp.getLifeTime(); + //printf("CREATED: %x . cpd->m_persistentLifeTime = %i cp.getLifeTime() = %i\n",cpd,cpd->m_persistentLifeTime,cp.getLifeTime()); + + } + assert(cpd); + + cpd->m_jacDiagABInv = btScalar(1.) / jacDiagAB; + + //Dependent on Rigidbody A and B types, fetch the contact/friction response func + //perhaps do a similar thing for friction/restutution combiner funcs... + + cpd->m_frictionSolverFunc = m_frictionDispatch[body0->m_frictionSolverType][body1->m_frictionSolverType]; + cpd->m_contactSolverFunc = m_contactDispatch[body0->m_contactSolverType][body1->m_contactSolverType]; + + btVector3 vel1 = body0->getVelocityInLocalPoint(rel_pos1); + btVector3 vel2 = body1->getVelocityInLocalPoint(rel_pos2); + btVector3 vel = vel1 - vel2; + btScalar rel_vel; + rel_vel = cp.m_normalWorldOnB.dot(vel); + + btScalar combinedRestitution = cp.m_combinedRestitution; + + cpd->m_penetration = cp.getDistance();///btScalar(info.m_numIterations); + cpd->m_friction = cp.m_combinedFriction; + cpd->m_restitution = restitutionCurve(rel_vel, combinedRestitution); + if (cpd->m_restitution <= btScalar(0.)) + { + cpd->m_restitution = btScalar(0.0); + + }; + + //restitution and penetration work in same direction so + //rel_vel + + btScalar penVel = -cpd->m_penetration/info.m_timeStep; + + if (cpd->m_restitution > penVel) + { + cpd->m_penetration = btScalar(0.); + } + + + + btScalar relaxation = info.m_damping; + if (m_solverMode & SOLVER_USE_WARMSTARTING) + { + cpd->m_appliedImpulse *= relaxation; + } else + { + cpd->m_appliedImpulse =btScalar(0.); + } + + //for friction + cpd->m_prevAppliedImpulse = cpd->m_appliedImpulse; + + //re-calculate friction direction every frame, todo: check if this is really needed + btPlaneSpace1(cp.m_normalWorldOnB,cpd->m_frictionWorldTangential0,cpd->m_frictionWorldTangential1); + + +#define NO_FRICTION_WARMSTART 1 + + #ifdef NO_FRICTION_WARMSTART + cpd->m_accumulatedTangentImpulse0 = btScalar(0.); + cpd->m_accumulatedTangentImpulse1 = btScalar(0.); + #endif //NO_FRICTION_WARMSTART + btScalar denom0 = body0->computeImpulseDenominator(pos1,cpd->m_frictionWorldTangential0); + btScalar denom1 = body1->computeImpulseDenominator(pos2,cpd->m_frictionWorldTangential0); + btScalar denom = relaxation/(denom0+denom1); + cpd->m_jacDiagABInvTangent0 = denom; + + + denom0 = body0->computeImpulseDenominator(pos1,cpd->m_frictionWorldTangential1); + denom1 = body1->computeImpulseDenominator(pos2,cpd->m_frictionWorldTangential1); + denom = relaxation/(denom0+denom1); + cpd->m_jacDiagABInvTangent1 = denom; + + btVector3 totalImpulse = + #ifndef NO_FRICTION_WARMSTART + cpd->m_frictionWorldTangential0*cpd->m_accumulatedTangentImpulse0+ + cpd->m_frictionWorldTangential1*cpd->m_accumulatedTangentImpulse1+ + #endif //NO_FRICTION_WARMSTART + cp.m_normalWorldOnB*cpd->m_appliedImpulse; + + + + /// + { + btVector3 torqueAxis0 = rel_pos1.cross(cp.m_normalWorldOnB); + cpd->m_angularComponentA = body0->getInvInertiaTensorWorld()*torqueAxis0; + btVector3 torqueAxis1 = rel_pos2.cross(cp.m_normalWorldOnB); + cpd->m_angularComponentB = body1->getInvInertiaTensorWorld()*torqueAxis1; + } + { + btVector3 ftorqueAxis0 = rel_pos1.cross(cpd->m_frictionWorldTangential0); + cpd->m_frictionAngularComponent0A = body0->getInvInertiaTensorWorld()*ftorqueAxis0; + } + { + btVector3 ftorqueAxis1 = rel_pos1.cross(cpd->m_frictionWorldTangential1); + cpd->m_frictionAngularComponent1A = body0->getInvInertiaTensorWorld()*ftorqueAxis1; + } + { + btVector3 ftorqueAxis0 = rel_pos2.cross(cpd->m_frictionWorldTangential0); + cpd->m_frictionAngularComponent0B = body1->getInvInertiaTensorWorld()*ftorqueAxis0; + } + { + btVector3 ftorqueAxis1 = rel_pos2.cross(cpd->m_frictionWorldTangential1); + cpd->m_frictionAngularComponent1B = body1->getInvInertiaTensorWorld()*ftorqueAxis1; + } + + /// + + + + //apply previous frames impulse on both bodies + body0->applyImpulse(totalImpulse, rel_pos1); + body1->applyImpulse(-totalImpulse, rel_pos2); + } + + } + } +} + + +btScalar btSequentialImpulseConstraintSolver::solveCombinedContactFriction(btRigidBody* body0,btRigidBody* body1, btManifoldPoint& cp, const btContactSolverInfo& info,int iter,btIDebugDraw* debugDrawer) +{ + btScalar maxImpulse = btScalar(0.); + + { + + btVector3 color(0,1,0); + { + if (cp.getDistance() <= btScalar(0.)) + { + + if (iter == 0) + { + if (debugDrawer) + debugDrawer->drawContactPoint(cp.m_positionWorldOnB,cp.m_normalWorldOnB,cp.getDistance(),cp.getLifeTime(),color); + } + + { + + //btConstraintPersistentData* cpd = (btConstraintPersistentData*) cp.m_userPersistentData; + btScalar impulse = resolveSingleCollisionCombined( + *body0,*body1, + cp, + info); + + if (maxImpulse < impulse) + maxImpulse = impulse; + + } + } + } + } + return maxImpulse; +} + + + +btScalar btSequentialImpulseConstraintSolver::solve(btRigidBody* body0,btRigidBody* body1, btManifoldPoint& cp, const btContactSolverInfo& info,int iter,btIDebugDraw* debugDrawer) +{ + + btScalar maxImpulse = btScalar(0.); + + { + + btVector3 color(0,1,0); + { + if (cp.getDistance() <= btScalar(0.)) + { + + if (iter == 0) + { + if (debugDrawer) + debugDrawer->drawContactPoint(cp.m_positionWorldOnB,cp.m_normalWorldOnB,cp.getDistance(),cp.getLifeTime(),color); + } + + { + + btConstraintPersistentData* cpd = (btConstraintPersistentData*) cp.m_userPersistentData; + btScalar impulse = cpd->m_contactSolverFunc( + *body0,*body1, + cp, + info); + + if (maxImpulse < impulse) + maxImpulse = impulse; + + } + } + } + } + return maxImpulse; +} + +btScalar btSequentialImpulseConstraintSolver::solveFriction(btRigidBody* body0,btRigidBody* body1, btManifoldPoint& cp, const btContactSolverInfo& info,int iter,btIDebugDraw* debugDrawer) +{ + + (void)debugDrawer; + (void)iter; + + + { + + btVector3 color(0,1,0); + { + + if (cp.getDistance() <= btScalar(0.)) + { + + btConstraintPersistentData* cpd = (btConstraintPersistentData*) cp.m_userPersistentData; + cpd->m_frictionSolverFunc( + *body0,*body1, + cp, + info); + + + } + } + + + } + return btScalar(0.); +} + + +void btSequentialImpulseConstraintSolver::reset() +{ + m_btSeed2 = 0; +} + diff --git a/bullet/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h b/bullet/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h new file mode 100644 index 0000000..9304c53 --- /dev/null +++ b/bullet/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h @@ -0,0 +1,114 @@ +/* +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. +*/ + +#ifndef SEQUENTIAL_IMPULSE_CONSTRAINT_SOLVER_H +#define SEQUENTIAL_IMPULSE_CONSTRAINT_SOLVER_H + +#include "btConstraintSolver.h" +class btIDebugDraw; +#include "btContactConstraint.h" + + + +/// btSequentialImpulseConstraintSolver uses a Propagation Method and Sequentially applies impulses +/// The approach is the 3D version of Erin Catto's GDC 2006 tutorial. See http://www.gphysics.com +/// Although Sequential Impulse is more intuitive, it is mathematically equivalent to Projected Successive Overrelaxation (iterative LCP) +/// Applies impulses for combined restitution and penetration recovery and to simulate friction +class btSequentialImpulseConstraintSolver : public btConstraintSolver +{ + +protected: + btScalar solve(btRigidBody* body0,btRigidBody* body1, btManifoldPoint& cp, const btContactSolverInfo& info,int iter,btIDebugDraw* debugDrawer); + btScalar solveFriction(btRigidBody* body0,btRigidBody* body1, btManifoldPoint& cp, const btContactSolverInfo& info,int iter,btIDebugDraw* debugDrawer); + void prepareConstraints(btPersistentManifold* manifoldPtr, const btContactSolverInfo& info,btIDebugDraw* debugDrawer); + + ContactSolverFunc m_contactDispatch[MAX_CONTACT_SOLVER_TYPES][MAX_CONTACT_SOLVER_TYPES]; + ContactSolverFunc m_frictionDispatch[MAX_CONTACT_SOLVER_TYPES][MAX_CONTACT_SOLVER_TYPES]; + + //choose between several modes, different friction model etc. + int m_solverMode; + ///m_btSeed2 is used for re-arranging the constraint rows. improves convergence/quality of friction + unsigned long m_btSeed2; + +public: + + enum eSolverMode + { + SOLVER_RANDMIZE_ORDER = 1, + SOLVER_FRICTION_SEPARATE = 2, + SOLVER_USE_WARMSTARTING = 4, + SOLVER_CACHE_FRIENDLY = 8 + }; + + btSequentialImpulseConstraintSolver(); + + ///Advanced: Override the default contact solving function for contacts, for certain types of rigidbody + ///See btRigidBody::m_contactSolverType and btRigidBody::m_frictionSolverType + void setContactSolverFunc(ContactSolverFunc func,int type0,int type1) + { + m_contactDispatch[type0][type1] = func; + } + + ///Advanced: Override the default friction solving function for contacts, for certain types of rigidbody + ///See btRigidBody::m_contactSolverType and btRigidBody::m_frictionSolverType + void SetFrictionSolverFunc(ContactSolverFunc func,int type0,int type1) + { + m_frictionDispatch[type0][type1] = func; + } + + virtual ~btSequentialImpulseConstraintSolver() {} + + virtual btScalar solveGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& info, btIDebugDraw* debugDrawer, btStackAlloc* stackAlloc); + + virtual btScalar solveGroupCacheFriendly(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer,btStackAlloc* stackAlloc); + + ///clear internal cached data and reset random seed + virtual void reset(); + + btScalar solveCombinedContactFriction(btRigidBody* body0,btRigidBody* body1, btManifoldPoint& cp, const btContactSolverInfo& info,int iter,btIDebugDraw* debugDrawer); + + + void setSolverMode(int mode) + { + m_solverMode = mode; + } + + int getSolverMode() const + { + return m_solverMode; + } + + unsigned long btRand2(); + + int btRandInt2 (int n); + + void setRandSeed(unsigned long seed) + { + m_btSeed2 = seed; + } + unsigned long getRandSeed() const + { + return m_btSeed2; + } + +}; + +#ifndef BT_PREFER_SIMD +typedef btSequentialImpulseConstraintSolver btSequentialImpulseConstraintSolverPrefered; +#endif + + +#endif //SEQUENTIAL_IMPULSE_CONSTRAINT_SOLVER_H + diff --git a/bullet/src/BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.cpp b/bullet/src/BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.cpp new file mode 100644 index 0000000..0279064 --- /dev/null +++ b/bullet/src/BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.cpp @@ -0,0 +1,255 @@ +/* +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. +*/ + + + +#include "btSolve2LinearConstraint.h" + +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "LinearMath/btVector3.h" +#include "btJacobianEntry.h" + + +void btSolve2LinearConstraint::resolveUnilateralPairConstraint( + btRigidBody* body1, + btRigidBody* body2, + + const btMatrix3x3& world2A, + const btMatrix3x3& world2B, + + const btVector3& invInertiaADiag, + const btScalar invMassA, + const btVector3& linvelA,const btVector3& angvelA, + const btVector3& rel_posA1, + const btVector3& invInertiaBDiag, + const btScalar invMassB, + const btVector3& linvelB,const btVector3& angvelB, + const btVector3& rel_posA2, + + btScalar depthA, const btVector3& normalA, + const btVector3& rel_posB1,const btVector3& rel_posB2, + btScalar depthB, const btVector3& normalB, + btScalar& imp0,btScalar& imp1) +{ + (void)linvelA; + (void)linvelB; + (void)angvelB; + (void)angvelA; + + + + imp0 = btScalar(0.); + imp1 = btScalar(0.); + + btScalar len = btFabs(normalA.length()) - btScalar(1.); + if (btFabs(len) >= SIMD_EPSILON) + return; + + btAssert(len < SIMD_EPSILON); + + + //this jacobian entry could be re-used for all iterations + btJacobianEntry jacA(world2A,world2B,rel_posA1,rel_posA2,normalA,invInertiaADiag,invMassA, + invInertiaBDiag,invMassB); + btJacobianEntry jacB(world2A,world2B,rel_posB1,rel_posB2,normalB,invInertiaADiag,invMassA, + invInertiaBDiag,invMassB); + + //const btScalar vel0 = jacA.getRelativeVelocity(linvelA,angvelA,linvelB,angvelB); + //const btScalar vel1 = jacB.getRelativeVelocity(linvelA,angvelA,linvelB,angvelB); + + const btScalar vel0 = normalA.dot(body1->getVelocityInLocalPoint(rel_posA1)-body2->getVelocityInLocalPoint(rel_posA1)); + const btScalar vel1 = normalB.dot(body1->getVelocityInLocalPoint(rel_posB1)-body2->getVelocityInLocalPoint(rel_posB1)); + +// btScalar penetrationImpulse = (depth*contactTau*timeCorrection) * massTerm;//jacDiagABInv + btScalar massTerm = btScalar(1.) / (invMassA + invMassB); + + + // calculate rhs (or error) terms + const btScalar dv0 = depthA * m_tau * massTerm - vel0 * m_damping; + const btScalar dv1 = depthB * m_tau * massTerm - vel1 * m_damping; + + + // dC/dv * dv = -C + + // jacobian * impulse = -error + // + + //impulse = jacobianInverse * -error + + // inverting 2x2 symmetric system (offdiagonal are equal!) + // + + + btScalar nonDiag = jacA.getNonDiagonal(jacB,invMassA,invMassB); + btScalar invDet = btScalar(1.0) / (jacA.getDiagonal() * jacB.getDiagonal() - nonDiag * nonDiag ); + + //imp0 = dv0 * jacA.getDiagonal() * invDet + dv1 * -nonDiag * invDet; + //imp1 = dv1 * jacB.getDiagonal() * invDet + dv0 * - nonDiag * invDet; + + imp0 = dv0 * jacA.getDiagonal() * invDet + dv1 * -nonDiag * invDet; + imp1 = dv1 * jacB.getDiagonal() * invDet + dv0 * - nonDiag * invDet; + + //[a b] [d -c] + //[c d] inverse = (1 / determinant) * [-b a] where determinant is (ad - bc) + + //[jA nD] * [imp0] = [dv0] + //[nD jB] [imp1] [dv1] + +} + + + +void btSolve2LinearConstraint::resolveBilateralPairConstraint( + btRigidBody* body1, + btRigidBody* body2, + const btMatrix3x3& world2A, + const btMatrix3x3& world2B, + + const btVector3& invInertiaADiag, + const btScalar invMassA, + const btVector3& linvelA,const btVector3& angvelA, + const btVector3& rel_posA1, + const btVector3& invInertiaBDiag, + const btScalar invMassB, + const btVector3& linvelB,const btVector3& angvelB, + const btVector3& rel_posA2, + + btScalar depthA, const btVector3& normalA, + const btVector3& rel_posB1,const btVector3& rel_posB2, + btScalar depthB, const btVector3& normalB, + btScalar& imp0,btScalar& imp1) +{ + + (void)linvelA; + (void)linvelB; + (void)angvelA; + (void)angvelB; + + + + imp0 = btScalar(0.); + imp1 = btScalar(0.); + + btScalar len = btFabs(normalA.length()) - btScalar(1.); + if (btFabs(len) >= SIMD_EPSILON) + return; + + btAssert(len < SIMD_EPSILON); + + + //this jacobian entry could be re-used for all iterations + btJacobianEntry jacA(world2A,world2B,rel_posA1,rel_posA2,normalA,invInertiaADiag,invMassA, + invInertiaBDiag,invMassB); + btJacobianEntry jacB(world2A,world2B,rel_posB1,rel_posB2,normalB,invInertiaADiag,invMassA, + invInertiaBDiag,invMassB); + + //const btScalar vel0 = jacA.getRelativeVelocity(linvelA,angvelA,linvelB,angvelB); + //const btScalar vel1 = jacB.getRelativeVelocity(linvelA,angvelA,linvelB,angvelB); + + const btScalar vel0 = normalA.dot(body1->getVelocityInLocalPoint(rel_posA1)-body2->getVelocityInLocalPoint(rel_posA1)); + const btScalar vel1 = normalB.dot(body1->getVelocityInLocalPoint(rel_posB1)-body2->getVelocityInLocalPoint(rel_posB1)); + + // calculate rhs (or error) terms + const btScalar dv0 = depthA * m_tau - vel0 * m_damping; + const btScalar dv1 = depthB * m_tau - vel1 * m_damping; + + // dC/dv * dv = -C + + // jacobian * impulse = -error + // + + //impulse = jacobianInverse * -error + + // inverting 2x2 symmetric system (offdiagonal are equal!) + // + + + btScalar nonDiag = jacA.getNonDiagonal(jacB,invMassA,invMassB); + btScalar invDet = btScalar(1.0) / (jacA.getDiagonal() * jacB.getDiagonal() - nonDiag * nonDiag ); + + //imp0 = dv0 * jacA.getDiagonal() * invDet + dv1 * -nonDiag * invDet; + //imp1 = dv1 * jacB.getDiagonal() * invDet + dv0 * - nonDiag * invDet; + + imp0 = dv0 * jacA.getDiagonal() * invDet + dv1 * -nonDiag * invDet; + imp1 = dv1 * jacB.getDiagonal() * invDet + dv0 * - nonDiag * invDet; + + //[a b] [d -c] + //[c d] inverse = (1 / determinant) * [-b a] where determinant is (ad - bc) + + //[jA nD] * [imp0] = [dv0] + //[nD jB] [imp1] [dv1] + + if ( imp0 > btScalar(0.0)) + { + if ( imp1 > btScalar(0.0) ) + { + //both positive + } + else + { + imp1 = btScalar(0.); + + // now imp0>0 imp1<0 + imp0 = dv0 / jacA.getDiagonal(); + if ( imp0 > btScalar(0.0) ) + { + } else + { + imp0 = btScalar(0.); + } + } + } + else + { + imp0 = btScalar(0.); + + imp1 = dv1 / jacB.getDiagonal(); + if ( imp1 <= btScalar(0.0) ) + { + imp1 = btScalar(0.); + // now imp0>0 imp1<0 + imp0 = dv0 / jacA.getDiagonal(); + if ( imp0 > btScalar(0.0) ) + { + } else + { + imp0 = btScalar(0.); + } + } else + { + } + } +} + + +/* +void btSolve2LinearConstraint::resolveAngularConstraint( const btMatrix3x3& invInertiaAWS, + const btScalar invMassA, + const btVector3& linvelA,const btVector3& angvelA, + const btVector3& rel_posA1, + const btMatrix3x3& invInertiaBWS, + const btScalar invMassB, + const btVector3& linvelB,const btVector3& angvelB, + const btVector3& rel_posA2, + + btScalar depthA, const btVector3& normalA, + const btVector3& rel_posB1,const btVector3& rel_posB2, + btScalar depthB, const btVector3& normalB, + btScalar& imp0,btScalar& imp1) +{ + +} +*/ + diff --git a/bullet/src/BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.h b/bullet/src/BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.h new file mode 100644 index 0000000..7e33c62 --- /dev/null +++ b/bullet/src/BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.h @@ -0,0 +1,107 @@ +/* +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. +*/ + +#ifndef SOLVE_2LINEAR_CONSTRAINT_H +#define SOLVE_2LINEAR_CONSTRAINT_H + +#include "LinearMath/btMatrix3x3.h" +#include "LinearMath/btVector3.h" + + +class btRigidBody; + + + +/// constraint class used for lateral tyre friction. +class btSolve2LinearConstraint +{ + btScalar m_tau; + btScalar m_damping; + +public: + + btSolve2LinearConstraint(btScalar tau,btScalar damping) + { + m_tau = tau; + m_damping = damping; + } + // + // solve unilateral constraint (equality, direct method) + // + void resolveUnilateralPairConstraint( + btRigidBody* body0, + btRigidBody* body1, + + const btMatrix3x3& world2A, + const btMatrix3x3& world2B, + + const btVector3& invInertiaADiag, + const btScalar invMassA, + const btVector3& linvelA,const btVector3& angvelA, + const btVector3& rel_posA1, + const btVector3& invInertiaBDiag, + const btScalar invMassB, + const btVector3& linvelB,const btVector3& angvelB, + const btVector3& rel_posA2, + + btScalar depthA, const btVector3& normalA, + const btVector3& rel_posB1,const btVector3& rel_posB2, + btScalar depthB, const btVector3& normalB, + btScalar& imp0,btScalar& imp1); + + + // + // solving 2x2 lcp problem (inequality, direct solution ) + // + void resolveBilateralPairConstraint( + btRigidBody* body0, + btRigidBody* body1, + const btMatrix3x3& world2A, + const btMatrix3x3& world2B, + + const btVector3& invInertiaADiag, + const btScalar invMassA, + const btVector3& linvelA,const btVector3& angvelA, + const btVector3& rel_posA1, + const btVector3& invInertiaBDiag, + const btScalar invMassB, + const btVector3& linvelB,const btVector3& angvelB, + const btVector3& rel_posA2, + + btScalar depthA, const btVector3& normalA, + const btVector3& rel_posB1,const btVector3& rel_posB2, + btScalar depthB, const btVector3& normalB, + btScalar& imp0,btScalar& imp1); + +/* + void resolveAngularConstraint( const btMatrix3x3& invInertiaAWS, + const btScalar invMassA, + const btVector3& linvelA,const btVector3& angvelA, + const btVector3& rel_posA1, + const btMatrix3x3& invInertiaBWS, + const btScalar invMassB, + const btVector3& linvelB,const btVector3& angvelB, + const btVector3& rel_posA2, + + btScalar depthA, const btVector3& normalA, + const btVector3& rel_posB1,const btVector3& rel_posB2, + btScalar depthB, const btVector3& normalB, + btScalar& imp0,btScalar& imp1); + +*/ + +}; + +#endif //SOLVE_2LINEAR_CONSTRAINT_H diff --git a/bullet/src/BulletDynamics/ConstraintSolver/btSolverBody.h b/bullet/src/BulletDynamics/ConstraintSolver/btSolverBody.h new file mode 100644 index 0000000..2dda865 --- /dev/null +++ b/bullet/src/BulletDynamics/ConstraintSolver/btSolverBody.h @@ -0,0 +1,71 @@ +/* +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. +*/ + +#ifndef BT_SOLVER_BODY_H +#define BT_SOLVER_BODY_H + +class btRigidBody; +#include "LinearMath/btVector3.h" +#include "LinearMath/btMatrix3x3.h" + + + + +ATTRIBUTE_ALIGNED16 (struct) btSolverBody +{ + btVector3 m_centerOfMassPosition; + btVector3 m_linearVelocity; + btVector3 m_angularVelocity; + btRigidBody* m_originalBody; + float m_invMass; + float m_friction; + float m_angularFactor; + + inline void getVelocityInLocalPoint(const btVector3& rel_pos, btVector3& velocity ) const + { + velocity = m_linearVelocity + m_angularVelocity.cross(rel_pos); + } + + //Optimization for the iterative solver: avoid calculating constant terms involving inertia, normal, relative position + inline void internalApplyImpulse(const btVector3& linearComponent, const btVector3& angularComponent,btScalar impulseMagnitude) + { + m_linearVelocity += linearComponent*impulseMagnitude; + m_angularVelocity += angularComponent*impulseMagnitude*m_angularFactor; + } + + void writebackVelocity() + { + if (m_invMass) + { + m_originalBody->setLinearVelocity(m_linearVelocity); + m_originalBody->setAngularVelocity(m_angularVelocity); + } + } + + void readVelocity() + { + if (m_invMass) + { + m_linearVelocity = m_originalBody->getLinearVelocity(); + m_angularVelocity = m_originalBody->getAngularVelocity(); + } + } + + + + +}; + +#endif //BT_SOLVER_BODY_H diff --git a/bullet/src/BulletDynamics/ConstraintSolver/btSolverConstraint.h b/bullet/src/BulletDynamics/ConstraintSolver/btSolverConstraint.h new file mode 100644 index 0000000..19af30e --- /dev/null +++ b/bullet/src/BulletDynamics/ConstraintSolver/btSolverConstraint.h @@ -0,0 +1,63 @@ + + +/* +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. +*/ + +#ifndef BT_SOLVER_CONSTRAINT_H +#define BT_SOLVER_CONSTRAINT_H + +class btRigidBody; +#include "LinearMath/btVector3.h" +#include "LinearMath/btMatrix3x3.h" + +//#define NO_FRICTION_TANGENTIALS 1 + +///1D constraint along a normal axis between bodyA and bodyB. It can be combined to solve contact and friction constraints. +ATTRIBUTE_ALIGNED16 (struct) btSolverConstraint +{ + btVector3 m_relpos1CrossNormal; + btVector3 m_relpos2CrossNormal; + btVector3 m_contactNormal; + btVector3 m_angularComponentA; + btVector3 m_angularComponentB; + + btScalar m_appliedVelocityImpulse; + int m_solverBodyIdA; + int m_solverBodyIdB; + btScalar m_friction; + btScalar m_restitution; + btScalar m_jacDiagABInv; + btScalar m_penetration; + btScalar m_appliedImpulse; + + int m_constraintType; + int m_frictionIndex; + int m_unusedPadding[2]; + + enum btSolverConstraintType + { + BT_SOLVER_CONTACT_1D = 0, + BT_SOLVER_FRICTION_1D + }; +}; + + + + + + +#endif //BT_SOLVER_CONSTRAINT_H + + diff --git a/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.cpp b/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.cpp new file mode 100644 index 0000000..8bc69de --- /dev/null +++ b/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.cpp @@ -0,0 +1,56 @@ +/* +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. +*/ + + +#include "btTypedConstraint.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" + +static btRigidBody s_fixed(0, 0,0); + +btTypedConstraint::btTypedConstraint(btTypedConstraintType type) +: m_constraintType (type), +m_userConstraintType(-1), +m_userConstraintId(-1), +m_rbA(s_fixed), +m_rbB(s_fixed), +m_appliedImpulse(btScalar(0.)) +{ + s_fixed.setMassProps(btScalar(0.),btVector3(btScalar(0.),btScalar(0.),btScalar(0.))); +} +btTypedConstraint::btTypedConstraint(btTypedConstraintType type, btRigidBody& rbA) +: m_constraintType (type), +m_userConstraintType(-1), +m_userConstraintId(-1), +m_rbA(rbA), +m_rbB(s_fixed), +m_appliedImpulse(btScalar(0.)) +{ + s_fixed.setMassProps(btScalar(0.),btVector3(btScalar(0.),btScalar(0.),btScalar(0.))); + +} + + +btTypedConstraint::btTypedConstraint(btTypedConstraintType type, btRigidBody& rbA,btRigidBody& rbB) +: m_constraintType (type), +m_userConstraintType(-1), +m_userConstraintId(-1), +m_rbA(rbA), +m_rbB(rbB), +m_appliedImpulse(btScalar(0.)) +{ + s_fixed.setMassProps(btScalar(0.),btVector3(btScalar(0.),btScalar(0.),btScalar(0.))); + +} + diff --git a/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.h b/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.h new file mode 100644 index 0000000..be09860 --- /dev/null +++ b/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.h @@ -0,0 +1,112 @@ +/* +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. +*/ + +#ifndef TYPED_CONSTRAINT_H +#define TYPED_CONSTRAINT_H + +class btRigidBody; +#include "LinearMath/btScalar.h" + +enum btTypedConstraintType +{ + POINT2POINT_CONSTRAINT_TYPE, + HINGE_CONSTRAINT_TYPE, + CONETWIST_CONSTRAINT_TYPE, + D6_CONSTRAINT_TYPE, + VEHICLE_CONSTRAINT_TYPE +}; + +///TypedConstraint is the baseclass for Bullet constraints and vehicles +class btTypedConstraint +{ + int m_userConstraintType; + int m_userConstraintId; + + btTypedConstraintType m_constraintType; + + btTypedConstraint& operator=(btTypedConstraint& other) + { + btAssert(0); + (void) other; + return *this; + } + +protected: + btRigidBody& m_rbA; + btRigidBody& m_rbB; + btScalar m_appliedImpulse; + + +public: + + btTypedConstraint(btTypedConstraintType type); + virtual ~btTypedConstraint() {}; + btTypedConstraint(btTypedConstraintType type, btRigidBody& rbA); + + btTypedConstraint(btTypedConstraintType type, btRigidBody& rbA,btRigidBody& rbB); + + virtual void buildJacobian() = 0; + + virtual void solveConstraint(btScalar timeStep) = 0; + + const btRigidBody& getRigidBodyA() const + { + return m_rbA; + } + const btRigidBody& getRigidBodyB() const + { + return m_rbB; + } + + btRigidBody& getRigidBodyA() + { + return m_rbA; + } + btRigidBody& getRigidBodyB() + { + return m_rbB; + } + + int getUserConstraintType() const + { + return m_userConstraintType ; + } + + void setUserConstraintType(int userConstraintType) + { + m_userConstraintType = userConstraintType; + }; + + void setUserConstraintId(int uid) + { + m_userConstraintId = uid; + } + + int getUserConstraintId() const + { + return m_userConstraintId; + } + btScalar getAppliedImpulse() const + { + return m_appliedImpulse; + } + + btTypedConstraintType getConstraintType () const + { + return m_constraintType; + } +}; + +#endif //TYPED_CONSTRAINT_H diff --git a/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp b/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp new file mode 100644 index 0000000..c3ec671 --- /dev/null +++ b/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp @@ -0,0 +1,960 @@ +/* +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. +*/ + + +#include "btDiscreteDynamicsWorld.h" + +//collision detection +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/BroadphaseCollision/btSimpleBroadphase.h" +#include "BulletCollision/CollisionShapes/btCollisionShape.h" +#include "BulletCollision/CollisionDispatch/btSimulationIslandManager.h" +#include + +//rigidbody & constraints +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h" +#include "BulletDynamics/ConstraintSolver/btContactSolverInfo.h" +#include "BulletDynamics/ConstraintSolver/btTypedConstraint.h" + +//for debug rendering +#include "BulletCollision/CollisionShapes/btBoxShape.h" +#include "BulletCollision/CollisionShapes/btCapsuleShape.h" +#include "BulletCollision/CollisionShapes/btCompoundShape.h" +#include "BulletCollision/CollisionShapes/btConeShape.h" +#include "BulletCollision/CollisionShapes/btConvexTriangleMeshShape.h" +#include "BulletCollision/CollisionShapes/btCylinderShape.h" +#include "BulletCollision/CollisionShapes/btMultiSphereShape.h" +#include "BulletCollision/CollisionShapes/btPolyhedralConvexShape.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" +#include "BulletCollision/CollisionShapes/btTriangleCallback.h" +#include "BulletCollision/CollisionShapes/btTriangleMeshShape.h" +#include "LinearMath/btIDebugDraw.h" + + + +//vehicle +#include "BulletDynamics/Vehicle/btRaycastVehicle.h" +#include "BulletDynamics/Vehicle/btVehicleRaycaster.h" +#include "BulletDynamics/Vehicle/btWheelInfo.h" +#include "LinearMath/btIDebugDraw.h" +#include "LinearMath/btQuickprof.h" +#include "LinearMath/btMotionState.h" + + + + + +btDiscreteDynamicsWorld::btDiscreteDynamicsWorld(btDispatcher* dispatcher,btBroadphaseInterface* pairCache,btConstraintSolver* constraintSolver) +:btDynamicsWorld(dispatcher,pairCache), +m_constraintSolver(constraintSolver? constraintSolver: new btSequentialImpulseConstraintSolver), +m_debugDrawer(0), +m_gravity(0,-10,0), +m_localTime(btScalar(1.)/btScalar(60.)), +m_profileTimings(0) +{ + m_islandManager = new btSimulationIslandManager(); + m_ownsIslandManager = true; + m_ownsConstraintSolver = (constraintSolver==0); +} + + +btDiscreteDynamicsWorld::~btDiscreteDynamicsWorld() +{ + //only delete it when we created it + if (m_ownsIslandManager) + delete m_islandManager; + if (m_ownsConstraintSolver) + delete m_constraintSolver; +} + +void btDiscreteDynamicsWorld::saveKinematicState(btScalar timeStep) +{ + + for (int i=0;igetActivationState() != ISLAND_SLEEPING) + { + if (body->isKinematicObject()) + { + //to calculate velocities next frame + body->saveKinematicState(timeStep); + } + } + } + } +} + +void btDiscreteDynamicsWorld::synchronizeMotionStates() +{ + //debug vehicle wheels + + + { + //todo: iterate over awake simulation islands! + for ( int i=0;igetDebugMode() & btIDebugDraw::DBG_DrawWireframe) + { + btVector3 color(btScalar(255.),btScalar(255.),btScalar(255.)); + switch(colObj->getActivationState()) + { + case ACTIVE_TAG: + color = btVector3(btScalar(255.),btScalar(255.),btScalar(255.)); break; + case ISLAND_SLEEPING: + color = btVector3(btScalar(0.),btScalar(255.),btScalar(0.));break; + case WANTS_DEACTIVATION: + color = btVector3(btScalar(0.),btScalar(255.),btScalar(255.));break; + case DISABLE_DEACTIVATION: + color = btVector3(btScalar(255.),btScalar(0.),btScalar(0.));break; + case DISABLE_SIMULATION: + color = btVector3(btScalar(255.),btScalar(255.),btScalar(0.));break; + default: + { + color = btVector3(btScalar(255.),btScalar(0.),btScalar(0.)); + } + }; + + debugDrawObject(colObj->getWorldTransform(),colObj->getCollisionShape(),color); + } + btRigidBody* body = btRigidBody::upcast(colObj); + if (body && body->getMotionState() && !body->isStaticOrKinematicObject()) + { + //we need to call the update at least once, even for sleeping objects + //otherwise the 'graphics' transform never updates properly + //so todo: add 'dirty' flag + //if (body->getActivationState() != ISLAND_SLEEPING) + { + btTransform interpolatedTransform; + btTransformUtil::integrateTransform(body->getInterpolationWorldTransform(), + body->getInterpolationLinearVelocity(),body->getInterpolationAngularVelocity(),m_localTime,interpolatedTransform); + body->getMotionState()->setWorldTransform(interpolatedTransform); + } + } + } + } + + if (getDebugDrawer() && getDebugDrawer()->getDebugMode() & btIDebugDraw::DBG_DrawWireframe) + { + for ( int i=0;im_vehicles.size();i++) + { + for (int v=0;vgetNumWheels();v++) + { + btVector3 wheelColor(0,255,255); + if (m_vehicles[i]->getWheelInfo(v).m_raycastInfo.m_isInContact) + { + wheelColor.setValue(0,0,255); + } else + { + wheelColor.setValue(255,0,255); + } + + //synchronize the wheels with the (interpolated) chassis worldtransform + m_vehicles[i]->updateWheelTransform(v,true); + + btVector3 wheelPosWS = m_vehicles[i]->getWheelInfo(v).m_worldTransform.getOrigin(); + + btVector3 axle = btVector3( + m_vehicles[i]->getWheelInfo(v).m_worldTransform.getBasis()[0][m_vehicles[i]->getRightAxis()], + m_vehicles[i]->getWheelInfo(v).m_worldTransform.getBasis()[1][m_vehicles[i]->getRightAxis()], + m_vehicles[i]->getWheelInfo(v).m_worldTransform.getBasis()[2][m_vehicles[i]->getRightAxis()]); + + + //m_vehicles[i]->getWheelInfo(v).m_raycastInfo.m_wheelAxleWS + //debug wheels (cylinders) + m_debugDrawer->drawLine(wheelPosWS,wheelPosWS+axle,wheelColor); + m_debugDrawer->drawLine(wheelPosWS,m_vehicles[i]->getWheelInfo(v).m_raycastInfo.m_contactPointWS,wheelColor); + + } + } + } + +} + + +int btDiscreteDynamicsWorld::stepSimulation( btScalar timeStep,int maxSubSteps, btScalar fixedTimeStep) +{ + int numSimulationSubSteps = 0; + + if (maxSubSteps) + { + //fixed timestep with interpolation + m_localTime += timeStep; + if (m_localTime >= fixedTimeStep) + { + numSimulationSubSteps = int( m_localTime / fixedTimeStep); + m_localTime -= numSimulationSubSteps * fixedTimeStep; + } + } else + { + //variable timestep + fixedTimeStep = timeStep; + m_localTime = timeStep; + if (btFuzzyZero(timeStep)) + { + numSimulationSubSteps = 0; + maxSubSteps = 0; + } else + { + numSimulationSubSteps = 1; + maxSubSteps = 1; + } + } + + //process some debugging flags + if (getDebugDrawer()) + { + gDisableDeactivation = (getDebugDrawer()->getDebugMode() & btIDebugDraw::DBG_NoDeactivation) != 0; + } + if (numSimulationSubSteps) + { + + saveKinematicState(fixedTimeStep); + + //clamp the number of substeps, to prevent simulation grinding spiralling down to a halt + int clampedSimulationSteps = (numSimulationSubSteps > maxSubSteps)? maxSubSteps : numSimulationSubSteps; + + for (int i=0;isetGravity(gravity); + } + } +} + + +void btDiscreteDynamicsWorld::removeRigidBody(btRigidBody* body) +{ + removeCollisionObject(body); +} + +void btDiscreteDynamicsWorld::addRigidBody(btRigidBody* body) +{ + if (!body->isStaticOrKinematicObject()) + { + body->setGravity(m_gravity); + } + + if (body->getCollisionShape()) + { + bool isDynamic = !(body->isStaticObject() || body->isKinematicObject()); + short collisionFilterGroup = isDynamic? short(btBroadphaseProxy::DefaultFilter) : short(btBroadphaseProxy::StaticFilter); + short collisionFilterMask = isDynamic? short(btBroadphaseProxy::AllFilter) : short(btBroadphaseProxy::AllFilter ^ btBroadphaseProxy::StaticFilter); + + addCollisionObject(body,collisionFilterGroup,collisionFilterMask); + } +} + +void btDiscreteDynamicsWorld::addRigidBody(btRigidBody* body, short group, short mask) +{ + if (!body->isStaticOrKinematicObject()) + { + body->setGravity(m_gravity); + } + + if (body->getCollisionShape()) + { + addCollisionObject(body,group,mask); + } +} + + +void btDiscreteDynamicsWorld::updateVehicles(btScalar timeStep) +{ + BEGIN_PROFILE("updateVehicles"); + + for ( int i=0;iupdateVehicle( timeStep); + } + END_PROFILE("updateVehicles"); +} + +void btDiscreteDynamicsWorld::updateActivationState(btScalar timeStep) +{ + BEGIN_PROFILE("updateActivationState"); + + for ( int i=0;iupdateDeactivation(timeStep); + + if (body->wantsSleeping()) + { + if (body->isStaticOrKinematicObject()) + { + body->setActivationState(ISLAND_SLEEPING); + } else + { + if (body->getActivationState() == ACTIVE_TAG) + body->setActivationState( WANTS_DEACTIVATION ); + } + } else + { + if (body->getActivationState() != DISABLE_DEACTIVATION) + body->setActivationState( ACTIVE_TAG ); + } + } + } + END_PROFILE("updateActivationState"); +} + +void btDiscreteDynamicsWorld::addConstraint(btTypedConstraint* constraint,bool disableCollisionsBetweenLinkedBodies) +{ + m_constraints.push_back(constraint); + if (disableCollisionsBetweenLinkedBodies) + { + constraint->getRigidBodyA().addConstraintRef(constraint); + constraint->getRigidBodyB().addConstraintRef(constraint); + } +} + +void btDiscreteDynamicsWorld::removeConstraint(btTypedConstraint* constraint) +{ + m_constraints.remove(constraint); + constraint->getRigidBodyA().removeConstraintRef(constraint); + constraint->getRigidBodyB().removeConstraintRef(constraint); +} + +void btDiscreteDynamicsWorld::addVehicle(btRaycastVehicle* vehicle) +{ + m_vehicles.push_back(vehicle); +} + +void btDiscreteDynamicsWorld::removeVehicle(btRaycastVehicle* vehicle) +{ + m_vehicles.remove(vehicle); +} + +inline int btGetConstraintIslandId(const btTypedConstraint* lhs) +{ + int islandId; + + const btCollisionObject& rcolObj0 = lhs->getRigidBodyA(); + const btCollisionObject& rcolObj1 = lhs->getRigidBodyB(); + islandId= rcolObj0.getIslandTag()>=0?rcolObj0.getIslandTag():rcolObj1.getIslandTag(); + return islandId; + +} + + +class btSortConstraintOnIslandPredicate +{ + public: + + bool operator() ( const btTypedConstraint* lhs, const btTypedConstraint* rhs ) + { + int rIslandId0,lIslandId0; + rIslandId0 = btGetConstraintIslandId(rhs); + lIslandId0 = btGetConstraintIslandId(lhs); + return lIslandId0 < rIslandId0; + } +}; + + + + +void btDiscreteDynamicsWorld::solveConstraints(btContactSolverInfo& solverInfo) +{ + + struct InplaceSolverIslandCallback : public btSimulationIslandManager::IslandCallback + { + + btContactSolverInfo& m_solverInfo; + btConstraintSolver* m_solver; + btTypedConstraint** m_sortedConstraints; + int m_numConstraints; + btIDebugDraw* m_debugDrawer; + btStackAlloc* m_stackAlloc; + + + InplaceSolverIslandCallback( + btContactSolverInfo& solverInfo, + btConstraintSolver* solver, + btTypedConstraint** sortedConstraints, + int numConstraints, + btIDebugDraw* debugDrawer, + btStackAlloc* stackAlloc) + :m_solverInfo(solverInfo), + m_solver(solver), + m_sortedConstraints(sortedConstraints), + m_numConstraints(numConstraints), + m_debugDrawer(debugDrawer), + m_stackAlloc(stackAlloc) + { + + } + + InplaceSolverIslandCallback& operator=(InplaceSolverIslandCallback& other) + { + btAssert(0); + (void)other; + return *this; + } + virtual void ProcessIsland(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifolds,int numManifolds, int islandId) + { + //also add all non-contact constraints/joints for this island + btTypedConstraint** startConstraint = 0; + int numCurConstraints = 0; + int i; + + //find the first constraint for this island + for (i=0;isolveGroup( bodies,numBodies,manifolds, numManifolds,startConstraint,numCurConstraints,m_solverInfo,m_debugDrawer,m_stackAlloc); + } + + }; + + //sorted version of all btTypedConstraint, based on islandId + btAlignedObjectArray sortedConstraints; + sortedConstraints.resize( m_constraints.size()); + int i; + for (i=0;iprepareSolve(getCollisionWorld()->getNumCollisionObjects(), getCollisionWorld()->getDispatcher()->getNumManifolds()); + + /// solve all the constraints for this island + m_islandManager->buildAndProcessIslands(getCollisionWorld()->getDispatcher(),getCollisionWorld()->getCollisionObjectArray(),&solverCallback); + + m_constraintSolver->allSolved(solverInfo, m_debugDrawer, m_stackAlloc); +} + + + + +void btDiscreteDynamicsWorld::calculateSimulationIslands() +{ + BEGIN_PROFILE("calculateSimulationIslands"); + + getSimulationIslandManager()->updateActivationState(getCollisionWorld(),getCollisionWorld()->getDispatcher()); + + { + int i; + int numConstraints = int(m_constraints.size()); + for (i=0;i< numConstraints ; i++ ) + { + btTypedConstraint* constraint = m_constraints[i]; + + const btRigidBody* colObj0 = &constraint->getRigidBodyA(); + const btRigidBody* colObj1 = &constraint->getRigidBodyB(); + + if (((colObj0) && ((colObj0)->mergesSimulationIslands())) && + ((colObj1) && ((colObj1)->mergesSimulationIslands()))) + { + if (colObj0->isActive() || colObj1->isActive()) + { + + getSimulationIslandManager()->getUnionFind().unite((colObj0)->getIslandTag(), + (colObj1)->getIslandTag()); + } + } + } + } + + //Store the island id in each body + getSimulationIslandManager()->storeIslandActivationState(getCollisionWorld()); + + END_PROFILE("calculateSimulationIslands"); + +} + + +void btDiscreteDynamicsWorld::updateAabbs() +{ + BEGIN_PROFILE("updateAabbs"); + + btVector3 colorvec(1,0,0); + btTransform predictedTrans; + for ( int i=0;iIsActive() && (!body->IsStatic())) + { + btPoint3 minAabb,maxAabb; + colObj->getCollisionShape()->getAabb(colObj->getWorldTransform(), minAabb,maxAabb); + btBroadphaseInterface* bp = (btBroadphaseInterface*)m_broadphasePairCache; + + //moving objects should be moderately sized, probably something wrong if not + if ( colObj->isStaticObject() || ((maxAabb-minAabb).length2() < btScalar(1e12))) + { + bp->setAabb(body->getBroadphaseHandle(),minAabb,maxAabb); + } else + { + //something went wrong, investigate + //this assert is unwanted in 3D modelers (danger of loosing work) + body->setActivationState(DISABLE_SIMULATION); + + static bool reportMe = true; + if (reportMe && m_debugDrawer) + { + reportMe = false; + m_debugDrawer->reportErrorWarning("Overflow in AABB, object removed from simulation"); + m_debugDrawer->reportErrorWarning("If you can reproduce this, please email bugs@continuousphysics.com\n"); + m_debugDrawer->reportErrorWarning("Please include above information, your Platform, version of OS.\n"); + m_debugDrawer->reportErrorWarning("Thanks.\n"); + } + + + } + if (m_debugDrawer && (m_debugDrawer->getDebugMode() & btIDebugDraw::DBG_DrawAabb)) + { + m_debugDrawer->drawAabb(minAabb,maxAabb,colorvec); + } + } + } + } + + END_PROFILE("updateAabbs"); +} + +void btDiscreteDynamicsWorld::integrateTransforms(btScalar timeStep) +{ + BEGIN_PROFILE("integrateTransforms"); + btTransform predictedTrans; + for ( int i=0;iisActive() && (!body->isStaticOrKinematicObject())) + { + body->predictIntegratedTransform(timeStep, predictedTrans); + body->proceedToTransform( predictedTrans); + } + } + } + END_PROFILE("integrateTransforms"); +} + + + +void btDiscreteDynamicsWorld::predictUnconstraintMotion(btScalar timeStep) +{ + BEGIN_PROFILE("predictUnconstraintMotion"); + for ( int i=0;iisStaticOrKinematicObject()) + { + if (body->isActive()) + { + body->applyForces( timeStep); + body->integrateVelocities( timeStep); + body->predictIntegratedTransform(timeStep,body->getInterpolationWorldTransform()); + } + } + } + } + END_PROFILE("predictUnconstraintMotion"); +} + + +void btDiscreteDynamicsWorld::startProfiling(btScalar timeStep) +{ + (void)timeStep; + #ifdef USE_QUICKPROF + + + //toggle btProfiler + if ( m_debugDrawer && m_debugDrawer->getDebugMode() & btIDebugDraw::DBG_ProfileTimings) + { + if (!m_profileTimings) + { + m_profileTimings = 1; + // To disable profiling, simply comment out the following line. + static int counter = 0; + + char filename[128]; + sprintf(filename,"quickprof_bullet_timings%i.csv",counter++); + btProfiler::init(filename, btProfiler::BLOCK_CYCLE_SECONDS);//BLOCK_TOTAL_MICROSECONDS + } else + { + btProfiler::endProfilingCycle(); + } + + } else + { + if (m_profileTimings) + { + btProfiler::endProfilingCycle(); + + m_profileTimings = 0; + btProfiler::destroy(); + } + } +#endif //USE_QUICKPROF +} + + + + + + +class DebugDrawcallback : public btTriangleCallback, public btInternalTriangleIndexCallback +{ + btIDebugDraw* m_debugDrawer; + btVector3 m_color; + btTransform m_worldTrans; + +public: + + DebugDrawcallback(btIDebugDraw* debugDrawer,const btTransform& worldTrans,const btVector3& color) : + m_debugDrawer(debugDrawer), + m_color(color), + m_worldTrans(worldTrans) + { + } + + virtual void internalProcessTriangleIndex(btVector3* triangle,int partId,int triangleIndex) + { + processTriangle(triangle,partId,triangleIndex); + } + + virtual void processTriangle(btVector3* triangle,int partId, int triangleIndex) + { + (void)partId; + (void)triangleIndex; + + btVector3 wv0,wv1,wv2; + wv0 = m_worldTrans*triangle[0]; + wv1 = m_worldTrans*triangle[1]; + wv2 = m_worldTrans*triangle[2]; + m_debugDrawer->drawLine(wv0,wv1,m_color); + m_debugDrawer->drawLine(wv1,wv2,m_color); + m_debugDrawer->drawLine(wv2,wv0,m_color); + } +}; + +void btDiscreteDynamicsWorld::debugDrawSphere(btScalar radius, const btTransform& transform, const btVector3& color) +{ + btVector3 start = transform.getOrigin(); + + const btVector3 xoffs = transform.getBasis() * btVector3(radius,0,0); + const btVector3 yoffs = transform.getBasis() * btVector3(0,radius,0); + const btVector3 zoffs = transform.getBasis() * btVector3(0,0,radius); + + // XY + getDebugDrawer()->drawLine(start-xoffs, start+yoffs, color); + getDebugDrawer()->drawLine(start+yoffs, start+xoffs, color); + getDebugDrawer()->drawLine(start+xoffs, start-yoffs, color); + getDebugDrawer()->drawLine(start-yoffs, start-xoffs, color); + + // XZ + getDebugDrawer()->drawLine(start-xoffs, start+zoffs, color); + getDebugDrawer()->drawLine(start+zoffs, start+xoffs, color); + getDebugDrawer()->drawLine(start+xoffs, start-zoffs, color); + getDebugDrawer()->drawLine(start-zoffs, start-xoffs, color); + + // YZ + getDebugDrawer()->drawLine(start-yoffs, start+zoffs, color); + getDebugDrawer()->drawLine(start+zoffs, start+yoffs, color); + getDebugDrawer()->drawLine(start+yoffs, start-zoffs, color); + getDebugDrawer()->drawLine(start-zoffs, start-yoffs, color); +} + +void btDiscreteDynamicsWorld::debugDrawObject(const btTransform& worldTransform, const btCollisionShape* shape, const btVector3& color) +{ + // Draw a small simplex at the center of the object + { + btVector3 start = worldTransform.getOrigin(); + getDebugDrawer()->drawLine(start, start+worldTransform.getBasis() * btVector3(1,0,0), btVector3(1,0,0)); + getDebugDrawer()->drawLine(start, start+worldTransform.getBasis() * btVector3(0,1,0), btVector3(0,1,0)); + getDebugDrawer()->drawLine(start, start+worldTransform.getBasis() * btVector3(0,0,1), btVector3(0,0,1)); + } + + if (shape->getShapeType() == COMPOUND_SHAPE_PROXYTYPE) + { + const btCompoundShape* compoundShape = static_cast(shape); + for (int i=compoundShape->getNumChildShapes()-1;i>=0;i--) + { + btTransform childTrans = compoundShape->getChildTransform(i); + const btCollisionShape* colShape = compoundShape->getChildShape(i); + debugDrawObject(worldTransform*childTrans,colShape,color); + } + + } else + { + switch (shape->getShapeType()) + { + + case SPHERE_SHAPE_PROXYTYPE: + { + const btSphereShape* sphereShape = static_cast(shape); + btScalar radius = sphereShape->getMargin();//radius doesn't include the margin, so draw with margin + + debugDrawSphere(radius, worldTransform, color); + break; + } + case MULTI_SPHERE_SHAPE_PROXYTYPE: + { + const btMultiSphereShape* multiSphereShape = static_cast(shape); + + for (int i = multiSphereShape->getSphereCount()-1; i>=0;i--) + { + btTransform childTransform = worldTransform; + childTransform.getOrigin() += multiSphereShape->getSpherePosition(i); + debugDrawSphere(multiSphereShape->getSphereRadius(i), childTransform, color); + } + + break; + } + case CAPSULE_SHAPE_PROXYTYPE: + { + const btCapsuleShape* capsuleShape = static_cast(shape); + + btScalar radius = capsuleShape->getRadius(); + btScalar halfHeight = capsuleShape->getHalfHeight(); + + // Draw the ends + { + btTransform childTransform = worldTransform; + childTransform.getOrigin() = worldTransform * btVector3(0,halfHeight,0); + debugDrawSphere(radius, childTransform, color); + } + + { + btTransform childTransform = worldTransform; + childTransform.getOrigin() = worldTransform * btVector3(0,-halfHeight,0); + debugDrawSphere(radius, childTransform, color); + } + + // Draw some additional lines + btVector3 start = worldTransform.getOrigin(); + getDebugDrawer()->drawLine(start+worldTransform.getBasis() * btVector3(-radius,halfHeight,0),start+worldTransform.getBasis() * btVector3(-radius,-halfHeight,0), color); + getDebugDrawer()->drawLine(start+worldTransform.getBasis() * btVector3(radius,halfHeight,0),start+worldTransform.getBasis() * btVector3(radius,-halfHeight,0), color); + getDebugDrawer()->drawLine(start+worldTransform.getBasis() * btVector3(0,halfHeight,-radius),start+worldTransform.getBasis() * btVector3(0,-halfHeight,-radius), color); + getDebugDrawer()->drawLine(start+worldTransform.getBasis() * btVector3(0,halfHeight,radius),start+worldTransform.getBasis() * btVector3(0,-halfHeight,radius), color); + + break; + } + case CONE_SHAPE_PROXYTYPE: + { + const btConeShape* coneShape = static_cast(shape); + btScalar radius = coneShape->getRadius();//+coneShape->getMargin(); + btScalar height = coneShape->getHeight();//+coneShape->getMargin(); + btVector3 start = worldTransform.getOrigin(); + getDebugDrawer()->drawLine(start+worldTransform.getBasis() * btVector3(btScalar(0.),btScalar(0.),btScalar(0.5)*height),start+worldTransform.getBasis() * btVector3(radius,btScalar(0.),btScalar(-0.5)*height),color); + getDebugDrawer()->drawLine(start+worldTransform.getBasis() * btVector3(btScalar(0.),btScalar(0.),btScalar(0.5)*height),start+worldTransform.getBasis() * btVector3(-radius,btScalar(0.),btScalar(-0.5)*height),color); + getDebugDrawer()->drawLine(start+worldTransform.getBasis() * btVector3(btScalar(0.),btScalar(0.),btScalar(0.5)*height),start+worldTransform.getBasis() * btVector3(btScalar(0.),radius,btScalar(-0.5)*height),color); + getDebugDrawer()->drawLine(start+worldTransform.getBasis() * btVector3(btScalar(0.),btScalar(0.),btScalar(0.5)*height),start+worldTransform.getBasis() * btVector3(btScalar(0.),-radius,btScalar(-0.5)*height),color); + break; + + } + case CYLINDER_SHAPE_PROXYTYPE: + { + const btCylinderShape* cylinder = static_cast(shape); + int upAxis = cylinder->getUpAxis(); + btScalar radius = cylinder->getRadius(); + btScalar halfHeight = cylinder->getHalfExtents()[upAxis]; + btVector3 start = worldTransform.getOrigin(); + btVector3 offsetHeight(0,0,0); + offsetHeight[upAxis] = halfHeight; + btVector3 offsetRadius(0,0,0); + offsetRadius[(upAxis+1)%3] = radius; + getDebugDrawer()->drawLine(start+worldTransform.getBasis() * (offsetHeight+offsetRadius),start+worldTransform.getBasis() * (-offsetHeight+offsetRadius),color); + getDebugDrawer()->drawLine(start+worldTransform.getBasis() * (offsetHeight-offsetRadius),start+worldTransform.getBasis() * (-offsetHeight-offsetRadius),color); + break; + } + default: + { + + if (shape->isConcave()) + { + btConcaveShape* concaveMesh = (btConcaveShape*) shape; + + //todo pass camera, for some culling + btVector3 aabbMax(btScalar(1e30),btScalar(1e30),btScalar(1e30)); + btVector3 aabbMin(btScalar(-1e30),btScalar(-1e30),btScalar(-1e30)); + + DebugDrawcallback drawCallback(getDebugDrawer(),worldTransform,color); + concaveMesh->processAllTriangles(&drawCallback,aabbMin,aabbMax); + + } + + if (shape->getShapeType() == CONVEX_TRIANGLEMESH_SHAPE_PROXYTYPE) + { + btConvexTriangleMeshShape* convexMesh = (btConvexTriangleMeshShape*) shape; + //todo: pass camera for some culling + btVector3 aabbMax(btScalar(1e30),btScalar(1e30),btScalar(1e30)); + btVector3 aabbMin(btScalar(-1e30),btScalar(-1e30),btScalar(-1e30)); + //DebugDrawcallback drawCallback; + DebugDrawcallback drawCallback(getDebugDrawer(),worldTransform,color); + convexMesh->getStridingMesh()->InternalProcessAllTriangles(&drawCallback,aabbMin,aabbMax); + } + + + /// for polyhedral shapes + if (shape->isPolyhedral()) + { + btPolyhedralConvexShape* polyshape = (btPolyhedralConvexShape*) shape; + + int i; + for (i=0;igetNumEdges();i++) + { + btPoint3 a,b; + polyshape->getEdge(i,a,b); + btVector3 wa = worldTransform * a; + btVector3 wb = worldTransform * b; + getDebugDrawer()->drawLine(wa,wb,color); + + } + + + } + } + } + } +} + + +void btDiscreteDynamicsWorld::setConstraintSolver(btConstraintSolver* solver) +{ + if (m_ownsConstraintSolver) + { + delete m_constraintSolver; + } + m_ownsConstraintSolver = false; + m_constraintSolver = solver; +} + +btConstraintSolver* btDiscreteDynamicsWorld::getConstraintSolver() +{ + return m_constraintSolver; +} + + +int btDiscreteDynamicsWorld::getNumConstraints() const +{ + return int(m_constraints.size()); +} +btTypedConstraint* btDiscreteDynamicsWorld::getConstraint(int index) +{ + return m_constraints[index]; +} +const btTypedConstraint* btDiscreteDynamicsWorld::getConstraint(int index) const +{ + return m_constraints[index]; +} diff --git a/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h b/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h new file mode 100644 index 0000000..ca4a15b --- /dev/null +++ b/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h @@ -0,0 +1,159 @@ +/* +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. +*/ + +#ifndef BT_DISCRETE_DYNAMICS_WORLD_H +#define BT_DISCRETE_DYNAMICS_WORLD_H + +#include "btDynamicsWorld.h" + +class btDispatcher; +class btOverlappingPairCache; +class btConstraintSolver; +class btSimulationIslandManager; +class btTypedConstraint; +#include "BulletDynamics/ConstraintSolver/btContactSolverInfo.h" + +class btRaycastVehicle; +class btIDebugDraw; +#include "LinearMath/btAlignedObjectArray.h" + + +///btDiscreteDynamicsWorld provides discrete rigid body simulation +///those classes replace the obsolete CcdPhysicsEnvironment/CcdPhysicsController +class btDiscreteDynamicsWorld : public btDynamicsWorld +{ +protected: + + btConstraintSolver* m_constraintSolver; + + btSimulationIslandManager* m_islandManager; + + btAlignedObjectArray m_constraints; + + btIDebugDraw* m_debugDrawer; + + btVector3 m_gravity; + + //for variable timesteps + btScalar m_localTime; + //for variable timesteps + + bool m_ownsIslandManager; + bool m_ownsConstraintSolver; + + btContactSolverInfo m_solverInfo; + + + btAlignedObjectArray m_vehicles; + + int m_profileTimings; + + void predictUnconstraintMotion(btScalar timeStep); + + void integrateTransforms(btScalar timeStep); + + void calculateSimulationIslands(); + + void solveConstraints(btContactSolverInfo& solverInfo); + + void updateActivationState(btScalar timeStep); + + void updateVehicles(btScalar timeStep); + + void startProfiling(btScalar timeStep); + + virtual void internalSingleStepSimulation( btScalar timeStep); + + void synchronizeMotionStates(); + + void saveKinematicState(btScalar timeStep); + + void debugDrawSphere(btScalar radius, const btTransform& transform, const btVector3& color); + +public: + + + ///this btDiscreteDynamicsWorld constructor gets created objects from the user, and will not delete those + btDiscreteDynamicsWorld(btDispatcher* dispatcher,btBroadphaseInterface* pairCache,btConstraintSolver* constraintSolver); + + virtual ~btDiscreteDynamicsWorld(); + + ///if maxSubSteps > 0, it will interpolate motion between fixedTimeStep's + virtual int stepSimulation( btScalar timeStep,int maxSubSteps=1, btScalar fixedTimeStep=btScalar(1.)/btScalar(60.)); + + virtual void updateAabbs(); + + void addConstraint(btTypedConstraint* constraint, bool disableCollisionsBetweenLinkedBodies=false); + + void removeConstraint(btTypedConstraint* constraint); + + void addVehicle(btRaycastVehicle* vehicle); + + void removeVehicle(btRaycastVehicle* vehicle); + + btSimulationIslandManager* getSimulationIslandManager() + { + return m_islandManager; + } + + const btSimulationIslandManager* getSimulationIslandManager() const + { + return m_islandManager; + } + + btCollisionWorld* getCollisionWorld() + { + return this; + } + + virtual void setDebugDrawer(btIDebugDraw* debugDrawer) + { + m_debugDrawer = debugDrawer; + } + + virtual btIDebugDraw* getDebugDrawer() + { + return m_debugDrawer; + } + + virtual void setGravity(const btVector3& gravity); + + virtual void addRigidBody(btRigidBody* body); + + virtual void addRigidBody(btRigidBody* body, short group, short mask); + + virtual void removeRigidBody(btRigidBody* body); + + void debugDrawObject(const btTransform& worldTransform, const btCollisionShape* shape, const btVector3& color); + + virtual void setConstraintSolver(btConstraintSolver* solver); + + virtual btConstraintSolver* getConstraintSolver(); + + virtual int getNumConstraints() const; + + virtual btTypedConstraint* getConstraint(int index) ; + + virtual const btTypedConstraint* getConstraint(int index) const; + + btContactSolverInfo& getSolverInfo() + { + return m_solverInfo; + } + + +}; + +#endif //BT_DISCRETE_DYNAMICS_WORLD_H diff --git a/bullet/src/BulletDynamics/Dynamics/btDynamicsWorld.h b/bullet/src/BulletDynamics/Dynamics/btDynamicsWorld.h new file mode 100644 index 0000000..8d1e9b1 --- /dev/null +++ b/bullet/src/BulletDynamics/Dynamics/btDynamicsWorld.h @@ -0,0 +1,80 @@ +/* +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. +*/ + +#ifndef BT_DYNAMICS_WORLD_H +#define BT_DYNAMICS_WORLD_H + +#include "BulletCollision/CollisionDispatch/btCollisionWorld.h" +class btTypedConstraint; +class btRaycastVehicle; +class btConstraintSolver; + + +///btDynamicsWorld is the baseclass for several dynamics implementation, basic, discrete, parallel, and continuous +class btDynamicsWorld : public btCollisionWorld +{ + public: + + + btDynamicsWorld(btDispatcher* dispatcher,btBroadphaseInterface* broadphase) + :btCollisionWorld(dispatcher,broadphase) + { + } + + virtual ~btDynamicsWorld() + { + } + + ///stepSimulation proceeds the simulation over timeStep units + ///if maxSubSteps > 0, it will interpolate time steps + virtual int stepSimulation( btScalar timeStep,int maxSubSteps=1, btScalar fixedTimeStep=btScalar(1.)/btScalar(60.))=0; + + virtual void updateAabbs() = 0; + + virtual void addConstraint(btTypedConstraint* constraint, bool disableCollisionsBetweenLinkedBodies=false) { (void)constraint;}; + + virtual void removeConstraint(btTypedConstraint* constraint) {(void)constraint;}; + + virtual void addVehicle(btRaycastVehicle* vehicle) {(void)vehicle;}; + + virtual void removeVehicle(btRaycastVehicle* vehicle) {(void)vehicle;}; + + + virtual void setDebugDrawer(btIDebugDraw* debugDrawer) = 0; + + virtual btIDebugDraw* getDebugDrawer() = 0; + + //once a rigidbody is added to the dynamics world, it will get this gravity assigned + //existing rigidbodies in the world get gravity assigned too, during this method + virtual void setGravity(const btVector3& gravity) = 0; + + virtual void addRigidBody(btRigidBody* body) = 0; + + virtual void removeRigidBody(btRigidBody* body) = 0; + + virtual void setConstraintSolver(btConstraintSolver* solver) = 0; + + virtual btConstraintSolver* getConstraintSolver() = 0; + + virtual int getNumConstraints() const { return 0; } + + virtual btTypedConstraint* getConstraint(int index) { (void)index; return 0; } + + virtual const btTypedConstraint* getConstraint(int index) const { (void)index; return 0; } + +}; + +#endif //BT_DYNAMICS_WORLD_H + diff --git a/bullet/src/BulletDynamics/Dynamics/btRigidBody.cpp b/bullet/src/BulletDynamics/Dynamics/btRigidBody.cpp new file mode 100644 index 0000000..e4ec9df --- /dev/null +++ b/bullet/src/BulletDynamics/Dynamics/btRigidBody.cpp @@ -0,0 +1,351 @@ +/* +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. +*/ + +#include "btRigidBody.h" +#include "BulletCollision/CollisionShapes/btConvexShape.h" +#include "LinearMath/btMinMax.h" +#include "LinearMath/btTransformUtil.h" +#include "LinearMath/btMotionState.h" +#include "BulletDynamics/ConstraintSolver/btTypedConstraint.h" + +btScalar gLinearAirDamping = btScalar(1.); +//'temporarily' global variables +btScalar gDeactivationTime = btScalar(2.); +bool gDisableDeactivation = false; + +btScalar gLinearSleepingThreshold = btScalar(0.8); +btScalar gAngularSleepingThreshold = btScalar(1.0); +static int uniqueId = 0; + +btRigidBody::btRigidBody(btScalar mass, btMotionState* motionState, btCollisionShape* collisionShape, const btVector3& localInertia,btScalar linearDamping,btScalar angularDamping,btScalar friction,btScalar restitution) +: + m_linearVelocity(btScalar(0.0), btScalar(0.0), btScalar(0.0)), + m_angularVelocity(btScalar(0.),btScalar(0.),btScalar(0.)), + m_angularFactor(btScalar(1.)), + m_gravity(btScalar(0.0), btScalar(0.0), btScalar(0.0)), + m_totalForce(btScalar(0.0), btScalar(0.0), btScalar(0.0)), + m_totalTorque(btScalar(0.0), btScalar(0.0), btScalar(0.0)), + m_linearDamping(btScalar(0.)), + m_angularDamping(btScalar(0.5)), + m_linearSleepingThreshold(gLinearSleepingThreshold), + m_angularSleepingThreshold(gAngularSleepingThreshold), + m_optionalMotionState(motionState), + m_contactSolverType(0), + m_frictionSolverType(0) +{ + + if (motionState) + { + motionState->getWorldTransform(m_worldTransform); + } else + { + m_worldTransform = btTransform::getIdentity(); + } + + m_interpolationWorldTransform = m_worldTransform; + m_interpolationLinearVelocity.setValue(0,0,0); + m_interpolationAngularVelocity.setValue(0,0,0); + + //moved to btCollisionObject + m_friction = friction; + m_restitution = restitution; + + m_collisionShape = collisionShape; + m_debugBodyId = uniqueId++; + + //m_internalOwner is to allow upcasting from collision object to rigid body + m_internalOwner = this; + + setMassProps(mass, localInertia); + setDamping(linearDamping, angularDamping); + updateInertiaTensor(); + +} + +#ifdef OBSOLETE_MOTIONSTATE_LESS +btRigidBody::btRigidBody( btScalar mass,const btTransform& worldTransform,btCollisionShape* collisionShape,const btVector3& localInertia,btScalar linearDamping,btScalar angularDamping,btScalar friction,btScalar restitution) +: + m_gravity(btScalar(0.0), btScalar(0.0), btScalar(0.0)), + m_totalForce(btScalar(0.0), btScalar(0.0), btScalar(0.0)), + m_totalTorque(btScalar(0.0), btScalar(0.0), btScalar(0.0)), + m_linearVelocity(btScalar(0.0), btScalar(0.0), btScalar(0.0)), + m_angularVelocity(btScalar(0.),btScalar(0.),btScalar(0.)), + m_linearSleepingThreshold(gLinearSleepingThreshold), + m_angularSleepingThreshold(gAngularSleepingThreshold), + m_linearDamping(btScalar(0.)), + m_angularDamping(btScalar(0.5)), + m_optionalMotionState(0), + m_contactSolverType(0), + m_frictionSolverType(0) + +{ + + m_worldTransform = worldTransform; + m_interpolationWorldTransform = m_worldTransform; + m_interpolationLinearVelocity.setValue(0,0,0); + m_interpolationAngularVelocity.setValue(0,0,0); + + //moved to btCollisionObject + m_friction = friction; + m_restitution = restitution; + + m_collisionShape = collisionShape; + m_debugBodyId = uniqueId++; + + //m_internalOwner is to allow upcasting from collision object to rigid body + m_internalOwner = this; + + setMassProps(mass, localInertia); + setDamping(linearDamping, angularDamping); + updateInertiaTensor(); + +} + +#endif //OBSOLETE_MOTIONSTATE_LESS + + + + +#define EXPERIMENTAL_JITTER_REMOVAL 1 +#ifdef EXPERIMENTAL_JITTER_REMOVAL +//Bullet 2.20b has experimental damping code to reduce jitter just before objects fall asleep/deactivate +//doesn't work very well yet (value 0 disabled this damping) +//note there this influences deactivation thresholds! +btScalar gClippedAngvelThresholdSqr = btScalar(0.01); +btScalar gClippedLinearThresholdSqr = btScalar(0.01); +#endif //EXPERIMENTAL_JITTER_REMOVAL + +btScalar gJitterVelocityDampingFactor = btScalar(0.7); + +void btRigidBody::predictIntegratedTransform(btScalar timeStep,btTransform& predictedTransform) +{ + +#ifdef EXPERIMENTAL_JITTER_REMOVAL + //if (wantsSleeping()) + { + //clip to avoid jitter + if ((m_angularVelocity.length2() < gClippedAngvelThresholdSqr) && + (m_linearVelocity.length2() < gClippedLinearThresholdSqr)) + { + m_angularVelocity *= gJitterVelocityDampingFactor; + m_linearVelocity *= gJitterVelocityDampingFactor; + } + } + +#endif //EXPERIMENTAL_JITTER_REMOVAL + + btTransformUtil::integrateTransform(m_worldTransform,m_linearVelocity,m_angularVelocity,timeStep,predictedTransform); +} + +void btRigidBody::saveKinematicState(btScalar timeStep) +{ + //todo: clamp to some (user definable) safe minimum timestep, to limit maximum angular/linear velocities + if (timeStep != btScalar(0.)) + { + //if we use motionstate to synchronize world transforms, get the new kinematic/animated world transform + if (getMotionState()) + getMotionState()->getWorldTransform(m_worldTransform); + btVector3 linVel,angVel; + + btTransformUtil::calculateVelocity(m_interpolationWorldTransform,m_worldTransform,timeStep,m_linearVelocity,m_angularVelocity); + m_interpolationLinearVelocity = m_linearVelocity; + m_interpolationAngularVelocity = m_angularVelocity; + m_interpolationWorldTransform = m_worldTransform; + //printf("angular = %f %f %f\n",m_angularVelocity.getX(),m_angularVelocity.getY(),m_angularVelocity.getZ()); + } +} + +void btRigidBody::getAabb(btVector3& aabbMin,btVector3& aabbMax) const +{ + getCollisionShape()->getAabb(m_worldTransform,aabbMin,aabbMax); +} + + + + +void btRigidBody::setGravity(const btVector3& acceleration) +{ + if (m_inverseMass != btScalar(0.0)) + { + m_gravity = acceleration * (btScalar(1.0) / m_inverseMass); + } +} + + + + + + +void btRigidBody::setDamping(btScalar lin_damping, btScalar ang_damping) +{ + m_linearDamping = GEN_clamped(lin_damping, (btScalar)btScalar(0.0), (btScalar)btScalar(1.0)); + m_angularDamping = GEN_clamped(ang_damping, (btScalar)btScalar(0.0), (btScalar)btScalar(1.0)); +} + + + +#include + + +void btRigidBody::applyForces(btScalar step) +{ + if (isStaticOrKinematicObject()) + return; + + applyCentralForce(m_gravity); + + m_linearVelocity *= GEN_clamped((btScalar(1.) - step * gLinearAirDamping * m_linearDamping), (btScalar)btScalar(0.0), (btScalar)btScalar(1.0)); + m_angularVelocity *= GEN_clamped((btScalar(1.) - step * m_angularDamping), (btScalar)btScalar(0.0), (btScalar)btScalar(1.0)); + +#define FORCE_VELOCITY_DAMPING 1 +#ifdef FORCE_VELOCITY_DAMPING + btScalar speed = m_linearVelocity.length(); + if (speed < m_linearDamping) + { + btScalar dampVel = btScalar(0.005); + if (speed > dampVel) + { + btVector3 dir = m_linearVelocity.normalized(); + m_linearVelocity -= dir * dampVel; + } else + { + m_linearVelocity.setValue(btScalar(0.),btScalar(0.),btScalar(0.)); + } + } + + btScalar angSpeed = m_angularVelocity.length(); + if (angSpeed < m_angularDamping) + { + btScalar angDampVel = btScalar(0.005); + if (angSpeed > angDampVel) + { + btVector3 dir = m_angularVelocity.normalized(); + m_angularVelocity -= dir * angDampVel; + } else + { + m_angularVelocity.setValue(btScalar(0.),btScalar(0.),btScalar(0.)); + } + } +#endif //FORCE_VELOCITY_DAMPING + +} + +void btRigidBody::proceedToTransform(const btTransform& newTrans) +{ + setCenterOfMassTransform( newTrans ); +} + + +void btRigidBody::setMassProps(btScalar mass, const btVector3& inertia) +{ + if (mass == btScalar(0.)) + { + m_collisionFlags |= btCollisionObject::CF_STATIC_OBJECT; + m_inverseMass = btScalar(0.); + } else + { + m_collisionFlags &= (~btCollisionObject::CF_STATIC_OBJECT); + m_inverseMass = btScalar(1.0) / mass; + } + + m_invInertiaLocal.setValue(inertia.x() != btScalar(0.0) ? btScalar(1.0) / inertia.x(): btScalar(0.0), + inertia.y() != btScalar(0.0) ? btScalar(1.0) / inertia.y(): btScalar(0.0), + inertia.z() != btScalar(0.0) ? btScalar(1.0) / inertia.z(): btScalar(0.0)); + +} + + + +void btRigidBody::updateInertiaTensor() +{ + m_invInertiaTensorWorld = m_worldTransform.getBasis().scaled(m_invInertiaLocal) * m_worldTransform.getBasis().transpose(); +} + + +void btRigidBody::integrateVelocities(btScalar step) +{ + if (isStaticOrKinematicObject()) + return; + + m_linearVelocity += m_totalForce * (m_inverseMass * step); + m_angularVelocity += m_invInertiaTensorWorld * m_totalTorque * step; + +#define MAX_ANGVEL SIMD_HALF_PI + /// clamp angular velocity. collision calculations will fail on higher angular velocities + btScalar angvel = m_angularVelocity.length(); + if (angvel*step > MAX_ANGVEL) + { + m_angularVelocity *= (MAX_ANGVEL/step) /angvel; + } + + clearForces(); +} + +btQuaternion btRigidBody::getOrientation() const +{ + btQuaternion orn; + m_worldTransform.getBasis().getRotation(orn); + return orn; +} + + +void btRigidBody::setCenterOfMassTransform(const btTransform& xform) +{ + + if (isStaticOrKinematicObject()) + { + m_interpolationWorldTransform = m_worldTransform; + } else + { + m_interpolationWorldTransform = xform; + } + m_interpolationLinearVelocity = getLinearVelocity(); + m_interpolationAngularVelocity = getAngularVelocity(); + m_worldTransform = xform; + updateInertiaTensor(); +} + + +bool btRigidBody::checkCollideWithOverride(btCollisionObject* co) +{ + btRigidBody* otherRb = btRigidBody::upcast(co); + if (!otherRb) + return true; + + for (int i = 0; i < m_constraintRefs.size(); ++i) + { + btTypedConstraint* c = m_constraintRefs[i]; + if (&c->getRigidBodyA() == otherRb || &c->getRigidBodyB() == otherRb) + return false; + } + + return true; +} + +void btRigidBody::addConstraintRef(btTypedConstraint* c) +{ + int index = m_constraintRefs.findLinearSearch(c); + if (index == m_constraintRefs.size()) + m_constraintRefs.push_back(c); + + m_checkCollideWith = true; +} + +void btRigidBody::removeConstraintRef(btTypedConstraint* c) +{ + m_constraintRefs.remove(c); + m_checkCollideWith = m_constraintRefs.size() > 0; +} + diff --git a/bullet/src/BulletDynamics/Dynamics/btRigidBody.h b/bullet/src/BulletDynamics/Dynamics/btRigidBody.h new file mode 100644 index 0000000..3b7e2d6 --- /dev/null +++ b/bullet/src/BulletDynamics/Dynamics/btRigidBody.h @@ -0,0 +1,385 @@ +/* +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. +*/ + +#ifndef RIGIDBODY_H +#define RIGIDBODY_H + +#include "LinearMath/btAlignedObjectArray.h" +#include "LinearMath/btPoint3.h" +#include "LinearMath/btTransform.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" + +class btCollisionShape; +class btMotionState; +class btTypedConstraint; + + +extern btScalar gLinearAirDamping; + +extern btScalar gDeactivationTime; +extern bool gDisableDeactivation; +extern btScalar gLinearSleepingThreshold; +extern btScalar gAngularSleepingThreshold; + + +/// btRigidBody class for btRigidBody Dynamics +/// +class btRigidBody : public btCollisionObject +{ + + btMatrix3x3 m_invInertiaTensorWorld; + btVector3 m_linearVelocity; + btVector3 m_angularVelocity; + btScalar m_inverseMass; + btScalar m_angularFactor; + + btVector3 m_gravity; + btVector3 m_invInertiaLocal; + btVector3 m_totalForce; + btVector3 m_totalTorque; + + btScalar m_linearDamping; + btScalar m_angularDamping; + + btScalar m_linearSleepingThreshold; + btScalar m_angularSleepingThreshold; + + + //m_optionalMotionState allows to automatic synchronize the world transform for active objects + btMotionState* m_optionalMotionState; + + //keep track of typed constraints referencing this rigid body + btAlignedObjectArray m_constraintRefs; + +public: + +#ifdef OBSOLETE_MOTIONSTATE_LESS + //not supported, please use btMotionState + btRigidBody(btScalar mass, const btTransform& worldTransform, btCollisionShape* collisionShape, const btVector3& localInertia=btVector3(0,0,0),btScalar linearDamping=btScalar(0.),btScalar angularDamping=btScalar(0.),btScalar friction=btScalar(0.5),btScalar restitution=btScalar(0.)); +#endif //OBSOLETE_MOTIONSTATE_LESS + + btRigidBody(btScalar mass, btMotionState* motionState, btCollisionShape* collisionShape, const btVector3& localInertia=btVector3(0,0,0),btScalar linearDamping=btScalar(0.),btScalar angularDamping=btScalar(0.),btScalar friction=btScalar(0.5),btScalar restitution=btScalar(0.)); + + virtual ~btRigidBody() + { + //No constraints should point to this rigidbody + //Remove constraints from the dynamics world before you delete the related rigidbodies. + btAssert(m_constraintRefs.size()==0); + } + + + void proceedToTransform(const btTransform& newTrans); + + ///to keep collision detection and dynamics separate we don't store a rigidbody pointer + ///but a rigidbody is derived from btCollisionObject, so we can safely perform an upcast + static const btRigidBody* upcast(const btCollisionObject* colObj) + { + return (const btRigidBody*)colObj->getInternalOwner(); + } + static btRigidBody* upcast(btCollisionObject* colObj) + { + return (btRigidBody*)colObj->getInternalOwner(); + } + + /// continuous collision detection needs prediction + void predictIntegratedTransform(btScalar step, btTransform& predictedTransform) ; + + void saveKinematicState(btScalar step); + + + void applyForces(btScalar step); + + void setGravity(const btVector3& acceleration); + + const btVector3& getGravity() const + { + return m_gravity; + } + + void setDamping(btScalar lin_damping, btScalar ang_damping); + + inline const btCollisionShape* getCollisionShape() const { + return m_collisionShape; + } + + inline btCollisionShape* getCollisionShape() { + return m_collisionShape; + } + + void setMassProps(btScalar mass, const btVector3& inertia); + + btScalar getInvMass() const { return m_inverseMass; } + const btMatrix3x3& getInvInertiaTensorWorld() const { + return m_invInertiaTensorWorld; + } + + void integrateVelocities(btScalar step); + + void setCenterOfMassTransform(const btTransform& xform); + + void applyCentralForce(const btVector3& force) + { + m_totalForce += force; + } + + const btVector3& getInvInertiaDiagLocal() + { + return m_invInertiaLocal; + }; + + void setInvInertiaDiagLocal(const btVector3& diagInvInertia) + { + m_invInertiaLocal = diagInvInertia; + } + + void setSleepingThresholds(btScalar linear,btScalar angular) + { + m_linearSleepingThreshold = linear; + m_angularSleepingThreshold = angular; + } + + void applyTorque(const btVector3& torque) + { + m_totalTorque += torque; + } + + void applyForce(const btVector3& force, const btVector3& rel_pos) + { + applyCentralForce(force); + applyTorque(rel_pos.cross(force)); + } + + void applyCentralImpulse(const btVector3& impulse) + { + m_linearVelocity += impulse * m_inverseMass; + } + + void applyTorqueImpulse(const btVector3& torque) + { + m_angularVelocity += m_invInertiaTensorWorld * torque; + } + + void applyImpulse(const btVector3& impulse, const btVector3& rel_pos) + { + if (m_inverseMass != btScalar(0.)) + { + applyCentralImpulse(impulse); + if (m_angularFactor) + { + applyTorqueImpulse(rel_pos.cross(impulse)*m_angularFactor); + } + } + } + + //Optimization for the iterative solver: avoid calculating constant terms involving inertia, normal, relative position + inline void internalApplyImpulse(const btVector3& linearComponent, const btVector3& angularComponent,btScalar impulseMagnitude) + { + if (m_inverseMass != btScalar(0.)) + { + m_linearVelocity += linearComponent*impulseMagnitude; + if (m_angularFactor) + { + m_angularVelocity += angularComponent*impulseMagnitude*m_angularFactor; + } + } + } + + void clearForces() + { + m_totalForce.setValue(btScalar(0.0), btScalar(0.0), btScalar(0.0)); + m_totalTorque.setValue(btScalar(0.0), btScalar(0.0), btScalar(0.0)); + } + + void updateInertiaTensor(); + + const btPoint3& getCenterOfMassPosition() const { + return m_worldTransform.getOrigin(); + } + btQuaternion getOrientation() const; + + const btTransform& getCenterOfMassTransform() const { + return m_worldTransform; + } + const btVector3& getLinearVelocity() const { + return m_linearVelocity; + } + const btVector3& getAngularVelocity() const { + return m_angularVelocity; + } + + + inline void setLinearVelocity(const btVector3& lin_vel) + { + assert (m_collisionFlags != btCollisionObject::CF_STATIC_OBJECT); + m_linearVelocity = lin_vel; + } + + inline void setAngularVelocity(const btVector3& ang_vel) { + assert (m_collisionFlags != btCollisionObject::CF_STATIC_OBJECT); + { + m_angularVelocity = ang_vel; + } + } + + btVector3 getVelocityInLocalPoint(const btVector3& rel_pos) const + { + //we also calculate lin/ang velocity for kinematic objects + return m_linearVelocity + m_angularVelocity.cross(rel_pos); + + //for kinematic objects, we could also use use: + // return (m_worldTransform(rel_pos) - m_interpolationWorldTransform(rel_pos)) / m_kinematicTimeStep; + } + + void translate(const btVector3& v) + { + m_worldTransform.getOrigin() += v; + } + + + void getAabb(btVector3& aabbMin,btVector3& aabbMax) const; + + + + + + inline btScalar computeImpulseDenominator(const btPoint3& pos, const btVector3& normal) const + { + btVector3 r0 = pos - getCenterOfMassPosition(); + + btVector3 c0 = (r0).cross(normal); + + btVector3 vec = (c0 * getInvInertiaTensorWorld()).cross(r0); + + return m_inverseMass + normal.dot(vec); + + } + + inline btScalar computeAngularImpulseDenominator(const btVector3& axis) const + { + btVector3 vec = axis * getInvInertiaTensorWorld(); + return axis.dot(vec); + } + + inline void updateDeactivation(btScalar timeStep) + { + if ( (getActivationState() == ISLAND_SLEEPING) || (getActivationState() == DISABLE_DEACTIVATION)) + return; + + if ((getLinearVelocity().length2() < m_linearSleepingThreshold*m_linearSleepingThreshold) && + (getAngularVelocity().length2() < m_angularSleepingThreshold*m_angularSleepingThreshold)) + { + m_deactivationTime += timeStep; + } else + { + m_deactivationTime=btScalar(0.); + setActivationState(0); + } + + } + + inline bool wantsSleeping() + { + + if (getActivationState() == DISABLE_DEACTIVATION) + return false; + + //disable deactivation + if (gDisableDeactivation || (gDeactivationTime == btScalar(0.))) + return false; + + if ( (getActivationState() == ISLAND_SLEEPING) || (getActivationState() == WANTS_DEACTIVATION)) + return true; + + if (m_deactivationTime> gDeactivationTime) + { + return true; + } + return false; + } + + + + const btBroadphaseProxy* getBroadphaseProxy() const + { + return m_broadphaseHandle; + } + btBroadphaseProxy* getBroadphaseProxy() + { + return m_broadphaseHandle; + } + void setNewBroadphaseProxy(btBroadphaseProxy* broadphaseProxy) + { + m_broadphaseHandle = broadphaseProxy; + } + + //btMotionState allows to automatic synchronize the world transform for active objects + btMotionState* getMotionState() + { + return m_optionalMotionState; + } + const btMotionState* getMotionState() const + { + return m_optionalMotionState; + } + void setMotionState(btMotionState* motionState) + { + m_optionalMotionState = motionState; + if (m_optionalMotionState) + motionState->getWorldTransform(m_worldTransform); + } + + //for experimental overriding of friction/contact solver func + int m_contactSolverType; + int m_frictionSolverType; + + void setAngularFactor(btScalar angFac) + { + m_angularFactor = angFac; + } + btScalar getAngularFactor() const + { + return m_angularFactor; + } + + //is this rigidbody added to a btCollisionWorld/btDynamicsWorld/btBroadphase? + bool isInWorld() const + { + return (getBroadphaseProxy() != 0); + } + + virtual bool checkCollideWithOverride(btCollisionObject* co); + + void addConstraintRef(btTypedConstraint* c); + void removeConstraintRef(btTypedConstraint* c); + + btTypedConstraint* getConstraintRef(int index) + { + return m_constraintRefs[index]; + } + + int getNumConstraintRefs() + { + return m_constraintRefs.size(); + } + + + int m_debugBodyId; +}; + + + +#endif + diff --git a/bullet/src/BulletDynamics/Dynamics/btSimpleDynamicsWorld.cpp b/bullet/src/BulletDynamics/Dynamics/btSimpleDynamicsWorld.cpp new file mode 100644 index 0000000..29d0b58 --- /dev/null +++ b/bullet/src/BulletDynamics/Dynamics/btSimpleDynamicsWorld.cpp @@ -0,0 +1,217 @@ +/* +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. +*/ + +#include "btSimpleDynamicsWorld.h" +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/BroadphaseCollision/btSimpleBroadphase.h" +#include "BulletCollision/CollisionShapes/btCollisionShape.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h" +#include "BulletDynamics/ConstraintSolver/btContactSolverInfo.h" + + +/* + Make sure this dummy function never changes so that it + can be used by probes that are checking whether the + library is actually installed. +*/ +extern "C" void btBulletDynamicsProbe () {} + + + + +btSimpleDynamicsWorld::btSimpleDynamicsWorld(btDispatcher* dispatcher,btBroadphaseInterface* pairCache,btConstraintSolver* constraintSolver) +:btDynamicsWorld(dispatcher,pairCache), +m_constraintSolver(constraintSolver), +m_ownsConstraintSolver(false), +m_debugDrawer(0), +m_gravity(0,0,-10) +{ + +} + + +btSimpleDynamicsWorld::~btSimpleDynamicsWorld() +{ + if (m_ownsConstraintSolver) + delete m_constraintSolver; +} + +int btSimpleDynamicsWorld::stepSimulation( btScalar timeStep,int maxSubSteps, btScalar fixedTimeStep) +{ + (void)fixedTimeStep; + (void)maxSubSteps; + + + ///apply gravity, predict motion + predictUnconstraintMotion(timeStep); + + btDispatcherInfo& dispatchInfo = getDispatchInfo(); + dispatchInfo.m_timeStep = timeStep; + dispatchInfo.m_stepCount = 0; + dispatchInfo.m_debugDraw = getDebugDrawer(); + + ///perform collision detection + performDiscreteCollisionDetection(); + + ///solve contact constraints + int numManifolds = m_dispatcher1->getNumManifolds(); + if (numManifolds) + { + btPersistentManifold** manifoldPtr = ((btCollisionDispatcher*)m_dispatcher1)->getInternalManifoldPointer(); + + btContactSolverInfo infoGlobal; + infoGlobal.m_timeStep = timeStep; + m_constraintSolver->prepareSolve(0,numManifolds); + m_constraintSolver->solveGroup(0,0,manifoldPtr, numManifolds,0,0,infoGlobal,m_debugDrawer, m_stackAlloc); + m_constraintSolver->allSolved(infoGlobal,m_debugDrawer, m_stackAlloc); + } + + ///integrate transforms + integrateTransforms(timeStep); + + updateAabbs(); + + synchronizeMotionStates(); + + return 1; + +} + + +void btSimpleDynamicsWorld::setGravity(const btVector3& gravity) +{ + m_gravity = gravity; + for ( int i=0;isetGravity(gravity); + } + } +} + +void btSimpleDynamicsWorld::removeRigidBody(btRigidBody* body) +{ + removeCollisionObject(body); +} + +void btSimpleDynamicsWorld::addRigidBody(btRigidBody* body) +{ + body->setGravity(m_gravity); + + if (body->getCollisionShape()) + { + addCollisionObject(body); + } +} + +void btSimpleDynamicsWorld::updateAabbs() +{ + btTransform predictedTrans; + for ( int i=0;iisActive() && (!body->isStaticObject())) + { + btPoint3 minAabb,maxAabb; + colObj->getCollisionShape()->getAabb(colObj->getWorldTransform(), minAabb,maxAabb); + btBroadphaseInterface* bp = getBroadphase(); + bp->setAabb(body->getBroadphaseHandle(),minAabb,maxAabb); + } + } + } +} + +void btSimpleDynamicsWorld::integrateTransforms(btScalar timeStep) +{ + btTransform predictedTrans; + for ( int i=0;iisActive() && (!body->isStaticObject())) + { + body->predictIntegratedTransform(timeStep, predictedTrans); + body->proceedToTransform( predictedTrans); + } + } + } +} + + + +void btSimpleDynamicsWorld::predictUnconstraintMotion(btScalar timeStep) +{ + for ( int i=0;iisStaticObject()) + { + if (body->isActive()) + { + body->applyForces( timeStep); + body->integrateVelocities( timeStep); + body->predictIntegratedTransform(timeStep,body->getInterpolationWorldTransform()); + } + } + } + } +} + + +void btSimpleDynamicsWorld::synchronizeMotionStates() +{ + //todo: iterate over awake simulation islands! + for ( int i=0;igetMotionState()) + { + if (body->getActivationState() != ISLAND_SLEEPING) + { + body->getMotionState()->setWorldTransform(body->getWorldTransform()); + } + } + } + +} + + +void btSimpleDynamicsWorld::setConstraintSolver(btConstraintSolver* solver) +{ + if (m_ownsConstraintSolver) + { + delete m_constraintSolver; + } + m_ownsConstraintSolver = false; + m_constraintSolver = solver; +} + +btConstraintSolver* btSimpleDynamicsWorld::getConstraintSolver() +{ + return m_constraintSolver; +} diff --git a/bullet/src/BulletDynamics/Dynamics/btSimpleDynamicsWorld.h b/bullet/src/BulletDynamics/Dynamics/btSimpleDynamicsWorld.h new file mode 100644 index 0000000..90b6d8f --- /dev/null +++ b/bullet/src/BulletDynamics/Dynamics/btSimpleDynamicsWorld.h @@ -0,0 +1,84 @@ +/* +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. +*/ + +#ifndef BT_SIMPLE_DYNAMICS_WORLD_H +#define BT_SIMPLE_DYNAMICS_WORLD_H + +#include "btDynamicsWorld.h" + +class btDispatcher; +class btOverlappingPairCache; +class btConstraintSolver; + +///btSimpleDynamicsWorld demonstrates very basic usage of Bullet rigid body dynamics +///It can be used for basic simulations, and as a starting point for porting Bullet +///btSimpleDynamicsWorld lacks object deactivation, island management and other concepts. +///For more complicated simulations, btDiscreteDynamicsWorld and btContinuousDynamicsWorld are recommended +///those classes replace the obsolete CcdPhysicsEnvironment/CcdPhysicsController +class btSimpleDynamicsWorld : public btDynamicsWorld +{ +protected: + + btConstraintSolver* m_constraintSolver; + + bool m_ownsConstraintSolver; + + btIDebugDraw* m_debugDrawer; + + void predictUnconstraintMotion(btScalar timeStep); + + void integrateTransforms(btScalar timeStep); + + btVector3 m_gravity; + +public: + + + + ///this btSimpleDynamicsWorld constructor creates dispatcher, broadphase pairCache and constraintSolver + btSimpleDynamicsWorld(btDispatcher* dispatcher,btBroadphaseInterface* pairCache,btConstraintSolver* constraintSolver); + + virtual ~btSimpleDynamicsWorld(); + + ///maxSubSteps/fixedTimeStep for interpolation is currently ignored for btSimpleDynamicsWorld, use btDiscreteDynamicsWorld instead + virtual int stepSimulation( btScalar timeStep,int maxSubSteps=1, btScalar fixedTimeStep=btScalar(1.)/btScalar(60.)); + + virtual void setDebugDrawer(btIDebugDraw* debugDrawer) + { + m_debugDrawer = debugDrawer; + }; + + virtual btIDebugDraw* getDebugDrawer() + { + return m_debugDrawer; + } + + virtual void setGravity(const btVector3& gravity); + + virtual void addRigidBody(btRigidBody* body); + + virtual void removeRigidBody(btRigidBody* body); + + virtual void updateAabbs(); + + void synchronizeMotionStates(); + + virtual void setConstraintSolver(btConstraintSolver* solver); + + virtual btConstraintSolver* getConstraintSolver(); + +}; + +#endif //BT_SIMPLE_DYNAMICS_WORLD_H diff --git a/bullet/src/BulletDynamics/Vehicle/btRaycastVehicle.cpp b/bullet/src/BulletDynamics/Vehicle/btRaycastVehicle.cpp new file mode 100644 index 0000000..ec008ab --- /dev/null +++ b/bullet/src/BulletDynamics/Vehicle/btRaycastVehicle.cpp @@ -0,0 +1,734 @@ +/* + * Copyright (c) 2005 Erwin Coumans http://continuousphysics.com/Bullet/ + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies. + * Erwin Coumans makes no representations about the suitability + * of this software for any purpose. + * It is provided "as is" without express or implied warranty. +*/ + +#include "LinearMath/btVector3.h" +#include "btRaycastVehicle.h" + +#include "BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.h" +#include "BulletDynamics/ConstraintSolver/btJacobianEntry.h" +#include "LinearMath/btQuaternion.h" +#include "BulletDynamics/Dynamics/btDynamicsWorld.h" +#include "btVehicleRaycaster.h" +#include "btWheelInfo.h" +#include "LinearMath/btMinMax.h" + + +#include "BulletDynamics/ConstraintSolver/btContactConstraint.h" + + + +static btRigidBody s_fixedObject( 0,0,0); + +btRaycastVehicle::btRaycastVehicle(const btVehicleTuning& tuning,btRigidBody* chassis, btVehicleRaycaster* raycaster ) +: btTypedConstraint(VEHICLE_CONSTRAINT_TYPE), +m_vehicleRaycaster(raycaster), +m_pitchControl(btScalar(0.)) +{ + m_chassisBody = chassis; + m_indexRightAxis = 0; + m_indexUpAxis = 2; + m_indexForwardAxis = 1; + defaultInit(tuning); +} + + +void btRaycastVehicle::defaultInit(const btVehicleTuning& tuning) +{ + (void)tuning; + m_currentVehicleSpeedKmHour = btScalar(0.); + m_steeringValue = btScalar(0.); + +} + + + +btRaycastVehicle::~btRaycastVehicle() +{ +} + + +// +// basically most of the code is general for 2 or 4 wheel vehicles, but some of it needs to be reviewed +// +btWheelInfo& btRaycastVehicle::addWheel( const btVector3& connectionPointCS, const btVector3& wheelDirectionCS0,const btVector3& wheelAxleCS, btScalar suspensionRestLength, btScalar wheelRadius,const btVehicleTuning& tuning, bool isFrontWheel) +{ + + btWheelInfoConstructionInfo ci; + + ci.m_chassisConnectionCS = connectionPointCS; + ci.m_wheelDirectionCS = wheelDirectionCS0; + ci.m_wheelAxleCS = wheelAxleCS; + ci.m_suspensionRestLength = suspensionRestLength; + ci.m_wheelRadius = wheelRadius; + ci.m_suspensionStiffness = tuning.m_suspensionStiffness; + ci.m_wheelsDampingCompression = tuning.m_suspensionCompression; + ci.m_wheelsDampingRelaxation = tuning.m_suspensionDamping; + ci.m_frictionSlip = tuning.m_frictionSlip; + ci.m_bIsFrontWheel = isFrontWheel; + ci.m_maxSuspensionTravelCm = tuning.m_maxSuspensionTravelCm; + + m_wheelInfo.push_back( btWheelInfo(ci)); + + btWheelInfo& wheel = m_wheelInfo[getNumWheels()-1]; + + updateWheelTransformsWS( wheel , false ); + updateWheelTransform(getNumWheels()-1,false); + return wheel; +} + + + + +const btTransform& btRaycastVehicle::getWheelTransformWS( int wheelIndex ) const +{ + assert(wheelIndex < getNumWheels()); + const btWheelInfo& wheel = m_wheelInfo[wheelIndex]; + return wheel.m_worldTransform; + +} + +void btRaycastVehicle::updateWheelTransform( int wheelIndex , bool interpolatedTransform) +{ + + btWheelInfo& wheel = m_wheelInfo[ wheelIndex ]; + updateWheelTransformsWS(wheel,interpolatedTransform); + btVector3 up = -wheel.m_raycastInfo.m_wheelDirectionWS; + const btVector3& right = wheel.m_raycastInfo.m_wheelAxleWS; + btVector3 fwd = up.cross(right); + fwd = fwd.normalize(); +// up = right.cross(fwd); +// up.normalize(); + + //rotate around steering over de wheelAxleWS + btScalar steering = wheel.m_steering; + + btQuaternion steeringOrn(up,steering);//wheel.m_steering); + btMatrix3x3 steeringMat(steeringOrn); + + btQuaternion rotatingOrn(right,-wheel.m_rotation); + btMatrix3x3 rotatingMat(rotatingOrn); + + btMatrix3x3 basis2( + right[0],fwd[0],up[0], + right[1],fwd[1],up[1], + right[2],fwd[2],up[2] + ); + + wheel.m_worldTransform.setBasis(steeringMat * rotatingMat * basis2); + wheel.m_worldTransform.setOrigin( + wheel.m_raycastInfo.m_hardPointWS + wheel.m_raycastInfo.m_wheelDirectionWS * wheel.m_raycastInfo.m_suspensionLength + ); +} + +void btRaycastVehicle::resetSuspension() +{ + + int i; + for (i=0;igetMotionState())) + { + getRigidBody()->getMotionState()->getWorldTransform(chassisTrans); + } + + wheel.m_raycastInfo.m_hardPointWS = chassisTrans( wheel.m_chassisConnectionPointCS ); + wheel.m_raycastInfo.m_wheelDirectionWS = chassisTrans.getBasis() * wheel.m_wheelDirectionCS ; + wheel.m_raycastInfo.m_wheelAxleWS = chassisTrans.getBasis() * wheel.m_wheelAxleCS; +} + +btScalar btRaycastVehicle::rayCast(btWheelInfo& wheel) +{ + updateWheelTransformsWS( wheel,false); + + + btScalar depth = -1; + + btScalar raylen = wheel.getSuspensionRestLength()+wheel.m_wheelsRadius; + + btVector3 rayvector = wheel.m_raycastInfo.m_wheelDirectionWS * (raylen); + const btVector3& source = wheel.m_raycastInfo.m_hardPointWS; + wheel.m_raycastInfo.m_contactPointWS = source + rayvector; + const btVector3& target = wheel.m_raycastInfo.m_contactPointWS; + + btScalar param = btScalar(0.); + + btVehicleRaycaster::btVehicleRaycasterResult rayResults; + + assert(m_vehicleRaycaster); + + void* object = m_vehicleRaycaster->castRay(source,target,rayResults); + + wheel.m_raycastInfo.m_groundObject = 0; + + if (object) + { + param = rayResults.m_distFraction; + depth = raylen * rayResults.m_distFraction; + wheel.m_raycastInfo.m_contactNormalWS = rayResults.m_hitNormalInWorld; + wheel.m_raycastInfo.m_isInContact = true; + + wheel.m_raycastInfo.m_groundObject = &s_fixedObject;//todo for driving on dynamic/movable objects!; + //wheel.m_raycastInfo.m_groundObject = object; + + + btScalar hitDistance = param*raylen; + wheel.m_raycastInfo.m_suspensionLength = hitDistance - wheel.m_wheelsRadius; + //clamp on max suspension travel + + btScalar minSuspensionLength = wheel.getSuspensionRestLength() - wheel.m_maxSuspensionTravelCm*btScalar(0.01); + btScalar maxSuspensionLength = wheel.getSuspensionRestLength()+ wheel.m_maxSuspensionTravelCm*btScalar(0.01); + if (wheel.m_raycastInfo.m_suspensionLength < minSuspensionLength) + { + wheel.m_raycastInfo.m_suspensionLength = minSuspensionLength; + } + if (wheel.m_raycastInfo.m_suspensionLength > maxSuspensionLength) + { + wheel.m_raycastInfo.m_suspensionLength = maxSuspensionLength; + } + + wheel.m_raycastInfo.m_contactPointWS = rayResults.m_hitPointInWorld; + + btScalar denominator= wheel.m_raycastInfo.m_contactNormalWS.dot( wheel.m_raycastInfo.m_wheelDirectionWS ); + + btVector3 chassis_velocity_at_contactPoint; + btVector3 relpos = wheel.m_raycastInfo.m_contactPointWS-getRigidBody()->getCenterOfMassPosition(); + + chassis_velocity_at_contactPoint = getRigidBody()->getVelocityInLocalPoint(relpos); + + btScalar projVel = wheel.m_raycastInfo.m_contactNormalWS.dot( chassis_velocity_at_contactPoint ); + + if ( denominator >= btScalar(-0.1)) + { + wheel.m_suspensionRelativeVelocity = btScalar(0.0); + wheel.m_clippedInvContactDotSuspension = btScalar(1.0) / btScalar(0.1); + } + else + { + btScalar inv = btScalar(-1.) / denominator; + wheel.m_suspensionRelativeVelocity = projVel * inv; + wheel.m_clippedInvContactDotSuspension = inv; + } + + } else + { + //put wheel info as in rest position + wheel.m_raycastInfo.m_suspensionLength = wheel.getSuspensionRestLength(); + wheel.m_suspensionRelativeVelocity = btScalar(0.0); + wheel.m_raycastInfo.m_contactNormalWS = - wheel.m_raycastInfo.m_wheelDirectionWS; + wheel.m_clippedInvContactDotSuspension = btScalar(1.0); + } + + return depth; +} + + +const btTransform& btRaycastVehicle::getChassisWorldTransform() const +{ + /*if (getRigidBody()->getMotionState()) + { + btTransform chassisWorldTrans; + getRigidBody()->getMotionState()->getWorldTransform(chassisWorldTrans); + return chassisWorldTrans; + } + */ + + + return getRigidBody()->getCenterOfMassTransform(); +} + + +void btRaycastVehicle::updateVehicle( btScalar step ) +{ + { + for (int i=0;igetLinearVelocity().length(); + + const btTransform& chassisTrans = getChassisWorldTransform(); + + btVector3 forwardW ( + chassisTrans.getBasis()[0][m_indexForwardAxis], + chassisTrans.getBasis()[1][m_indexForwardAxis], + chassisTrans.getBasis()[2][m_indexForwardAxis]); + + if (forwardW.dot(getRigidBody()->getLinearVelocity()) < btScalar(0.)) + { + m_currentVehicleSpeedKmHour *= btScalar(-1.); + } + + // + // simulate suspension + // + + int i=0; + for (i=0;i gMaxSuspensionForce) + { + suspensionForce = gMaxSuspensionForce; + } + btVector3 impulse = wheel.m_raycastInfo.m_contactNormalWS * suspensionForce * step; + btVector3 relpos = wheel.m_raycastInfo.m_contactPointWS - getRigidBody()->getCenterOfMassPosition(); + + getRigidBody()->applyImpulse(impulse, relpos); + + } + + + + updateFriction( step); + + + for (i=0;igetCenterOfMassPosition(); + btVector3 vel = getRigidBody()->getVelocityInLocalPoint( relpos ); + + if (wheel.m_raycastInfo.m_isInContact) + { + const btTransform& chassisWorldTransform = getChassisWorldTransform(); + + btVector3 fwd ( + chassisWorldTransform.getBasis()[0][m_indexForwardAxis], + chassisWorldTransform.getBasis()[1][m_indexForwardAxis], + chassisWorldTransform.getBasis()[2][m_indexForwardAxis]); + + btScalar proj = fwd.dot(wheel.m_raycastInfo.m_contactNormalWS); + fwd -= wheel.m_raycastInfo.m_contactNormalWS * proj; + + btScalar proj2 = fwd.dot(vel); + + wheel.m_deltaRotation = (proj2 * step) / (wheel.m_wheelsRadius); + wheel.m_rotation += wheel.m_deltaRotation; + + } else + { + wheel.m_rotation += wheel.m_deltaRotation; + } + + wheel.m_deltaRotation *= btScalar(0.99);//damping of rotation when not in contact + + } + + + +} + + +void btRaycastVehicle::setSteeringValue(btScalar steering,int wheel) +{ + assert(wheel>=0 && wheel < getNumWheels()); + + btWheelInfo& wheelInfo = getWheelInfo(wheel); + wheelInfo.m_steering = steering; +} + + + +btScalar btRaycastVehicle::getSteeringValue(int wheel) const +{ + return getWheelInfo(wheel).m_steering; +} + + +void btRaycastVehicle::applyEngineForce(btScalar force, int wheel) +{ + assert(wheel>=0 && wheel < getNumWheels()); + btWheelInfo& wheelInfo = getWheelInfo(wheel); + wheelInfo.m_engineForce = force; +} + + +const btWheelInfo& btRaycastVehicle::getWheelInfo(int index) const +{ + btAssert((index >= 0) && (index < getNumWheels())); + + return m_wheelInfo[index]; +} + +btWheelInfo& btRaycastVehicle::getWheelInfo(int index) +{ + btAssert((index >= 0) && (index < getNumWheels())); + + return m_wheelInfo[index]; +} + +void btRaycastVehicle::setBrake(btScalar brake,int wheelIndex) +{ + btAssert((wheelIndex >= 0) && (wheelIndex < getNumWheels())); + getWheelInfo(wheelIndex).m_brake = brake; +} + + +void btRaycastVehicle::updateSuspension(btScalar deltaTime) +{ + (void)deltaTime; + + btScalar chassisMass = btScalar(1.) / m_chassisBody->getInvMass(); + + for (int w_it=0; w_itcomputeImpulseDenominator(frictionPosWorld,frictionDirectionWorld); + btScalar denom1 = body1->computeImpulseDenominator(frictionPosWorld,frictionDirectionWorld); + btScalar relaxation = 1.f; + m_jacDiagABInv = relaxation/(denom0+denom1); + } + + + +}; + +btScalar calcRollingFriction(btWheelContactPoint& contactPoint) +{ + + btScalar j1=0.f; + + const btVector3& contactPosWorld = contactPoint.m_frictionPositionWorld; + + btVector3 rel_pos1 = contactPosWorld - contactPoint.m_body0->getCenterOfMassPosition(); + btVector3 rel_pos2 = contactPosWorld - contactPoint.m_body1->getCenterOfMassPosition(); + + btScalar maxImpulse = contactPoint.m_maxImpulse; + + btVector3 vel1 = contactPoint.m_body0->getVelocityInLocalPoint(rel_pos1); + btVector3 vel2 = contactPoint.m_body1->getVelocityInLocalPoint(rel_pos2); + btVector3 vel = vel1 - vel2; + + btScalar vrel = contactPoint.m_frictionDirectionWorld.dot(vel); + + // calculate j that moves us to zero relative velocity + j1 = -vrel * contactPoint.m_jacDiagABInv; + GEN_set_min(j1, maxImpulse); + GEN_set_max(j1, -maxImpulse); + + return j1; +} + + + + +btScalar sideFrictionStiffness2 = btScalar(1.0); +void btRaycastVehicle::updateFriction(btScalar timeStep) +{ + + //calculate the impulse, so that the wheels don't move sidewards + int numWheel = getNumWheels(); + if (!numWheel) + return; + + + btVector3* forwardWS = new btVector3[numWheel]; + btVector3* axle = new btVector3[numWheel]; + btScalar* forwardImpulse = new btScalar[numWheel]; + btScalar* sideImpulse = new btScalar[numWheel]; + + int numWheelsOnGround = 0; + + + //collapse all those loops into one! + for (int i=0;i maximpSquared) + { + sliding = true; + + btScalar factor = maximp / btSqrt(impulseSquared); + + m_wheelInfo[wheel].m_skidInfo *= factor; + } + } + + } + } + + + + + if (sliding) + { + for (int wheel = 0;wheel < getNumWheels(); wheel++) + { + if (sideImpulse[wheel] != btScalar(0.)) + { + if (m_wheelInfo[wheel].m_skidInfo< btScalar(1.)) + { + forwardImpulse[wheel] *= m_wheelInfo[wheel].m_skidInfo; + sideImpulse[wheel] *= m_wheelInfo[wheel].m_skidInfo; + } + } + } + } + + // apply the impulses + { + for (int wheel = 0;wheelgetCenterOfMassPosition(); + + if (forwardImpulse[wheel] != btScalar(0.)) + { + m_chassisBody->applyImpulse(forwardWS[wheel]*(forwardImpulse[wheel]),rel_pos); + } + if (sideImpulse[wheel] != btScalar(0.)) + { + class btRigidBody* groundObject = (class btRigidBody*) m_wheelInfo[wheel].m_raycastInfo.m_groundObject; + + btVector3 rel_pos2 = wheelInfo.m_raycastInfo.m_contactPointWS - + groundObject->getCenterOfMassPosition(); + + + btVector3 sideImp = axle[wheel] * sideImpulse[wheel]; + + rel_pos[2] *= wheelInfo.m_rollInfluence; + m_chassisBody->applyImpulse(sideImp,rel_pos); + + //apply friction impulse on the ground + groundObject->applyImpulse(-sideImp,rel_pos2); + } + } + } + + delete []forwardWS; + delete [] axle; + delete[]forwardImpulse; + delete[] sideImpulse; +} + + +void* btDefaultVehicleRaycaster::castRay(const btVector3& from,const btVector3& to, btVehicleRaycasterResult& result) +{ +// RayResultCallback& resultCallback; + + btCollisionWorld::ClosestRayResultCallback rayCallback(from,to); + + m_dynamicsWorld->rayTest(from, to, rayCallback); + + if (rayCallback.HasHit()) + { + + btRigidBody* body = btRigidBody::upcast(rayCallback.m_collisionObject); + if (body) + { + result.m_hitPointInWorld = rayCallback.m_hitPointWorld; + result.m_hitNormalInWorld = rayCallback.m_hitNormalWorld; + result.m_hitNormalInWorld.normalize(); + result.m_distFraction = rayCallback.m_closestHitFraction; + return body; + } + } + return 0; +} diff --git a/bullet/src/BulletDynamics/Vehicle/btRaycastVehicle.h b/bullet/src/BulletDynamics/Vehicle/btRaycastVehicle.h new file mode 100644 index 0000000..8c08b38 --- /dev/null +++ b/bullet/src/BulletDynamics/Vehicle/btRaycastVehicle.h @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2005 Erwin Coumans http://continuousphysics.com/Bullet/ + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies. + * Erwin Coumans makes no representations about the suitability + * of this software for any purpose. + * It is provided "as is" without express or implied warranty. +*/ +#ifndef RAYCASTVEHICLE_H +#define RAYCASTVEHICLE_H + +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "BulletDynamics/ConstraintSolver/btTypedConstraint.h" +#include "btVehicleRaycaster.h" +class btDynamicsWorld; +#include "LinearMath/btAlignedObjectArray.h" +#include "btWheelInfo.h" + +class btVehicleTuning; + +///rayCast vehicle, very special constraint that turn a rigidbody into a vehicle. +class btRaycastVehicle : public btTypedConstraint +{ +public: + class btVehicleTuning + { + public: + + btVehicleTuning() + :m_suspensionStiffness(btScalar(5.88)), + m_suspensionCompression(btScalar(0.83)), + m_suspensionDamping(btScalar(0.88)), + m_maxSuspensionTravelCm(btScalar(500.)), + m_frictionSlip(btScalar(10.5)) + { + } + btScalar m_suspensionStiffness; + btScalar m_suspensionCompression; + btScalar m_suspensionDamping; + btScalar m_maxSuspensionTravelCm; + btScalar m_frictionSlip; + + }; +private: + + btScalar m_tau; + btScalar m_damping; + btVehicleRaycaster* m_vehicleRaycaster; + btScalar m_pitchControl; + btScalar m_steeringValue; + btScalar m_currentVehicleSpeedKmHour; + + btRigidBody* m_chassisBody; + + int m_indexRightAxis; + int m_indexUpAxis; + int m_indexForwardAxis; + + void defaultInit(const btVehicleTuning& tuning); + +public: + + //constructor to create a car from an existing rigidbody + btRaycastVehicle(const btVehicleTuning& tuning,btRigidBody* chassis, btVehicleRaycaster* raycaster ); + + virtual ~btRaycastVehicle() ; + + + const btTransform& getChassisWorldTransform() const; + + btScalar rayCast(btWheelInfo& wheel); + + virtual void updateVehicle(btScalar step); + + void resetSuspension(); + + btScalar getSteeringValue(int wheel) const; + + void setSteeringValue(btScalar steering,int wheel); + + + void applyEngineForce(btScalar force, int wheel); + + const btTransform& getWheelTransformWS( int wheelIndex ) const; + + void updateWheelTransform( int wheelIndex, bool interpolatedTransform = true ); + + void setRaycastWheelInfo( int wheelIndex , bool isInContact, const btVector3& hitPoint, const btVector3& hitNormal,btScalar depth); + + btWheelInfo& addWheel( const btVector3& connectionPointCS0, const btVector3& wheelDirectionCS0,const btVector3& wheelAxleCS,btScalar suspensionRestLength,btScalar wheelRadius,const btVehicleTuning& tuning, bool isFrontWheel); + + inline int getNumWheels() const { + return int (m_wheelInfo.size()); + } + + btAlignedObjectArray m_wheelInfo; + + + const btWheelInfo& getWheelInfo(int index) const; + + btWheelInfo& getWheelInfo(int index); + + void updateWheelTransformsWS(btWheelInfo& wheel , bool interpolatedTransform = true); + + + void setBrake(btScalar brake,int wheelIndex); + + void setPitchControl(btScalar pitch) + { + m_pitchControl = pitch; + } + + void updateSuspension(btScalar deltaTime); + + void updateFriction(btScalar timeStep); + + + + inline btRigidBody* getRigidBody() + { + return m_chassisBody; + } + + const btRigidBody* getRigidBody() const + { + return m_chassisBody; + } + + inline int getRightAxis() const + { + return m_indexRightAxis; + } + inline int getUpAxis() const + { + return m_indexUpAxis; + } + + inline int getForwardAxis() const + { + return m_indexForwardAxis; + } + + + ///Worldspace forward vector + btVector3 getForwardVector() const + { + const btTransform& chassisTrans = getChassisWorldTransform(); + + btVector3 forwardW ( + chassisTrans.getBasis()[0][m_indexForwardAxis], + chassisTrans.getBasis()[1][m_indexForwardAxis], + chassisTrans.getBasis()[2][m_indexForwardAxis]); + + return forwardW; + } + + ///Velocity of vehicle (positive if velocity vector has same direction as foward vector) + btScalar getCurrentSpeedKmHour() const + { + return m_currentVehicleSpeedKmHour; + } + + virtual void setCoordinateSystem(int rightIndex,int upIndex,int forwardIndex) + { + m_indexRightAxis = rightIndex; + m_indexUpAxis = upIndex; + m_indexForwardAxis = forwardIndex; + } + + virtual void buildJacobian() + { + //not yet + } + + virtual void solveConstraint(btScalar timeStep) + { + (void)timeStep; + //not yet + } + + +}; + +class btDefaultVehicleRaycaster : public btVehicleRaycaster +{ + btDynamicsWorld* m_dynamicsWorld; +public: + btDefaultVehicleRaycaster(btDynamicsWorld* world) + :m_dynamicsWorld(world) + { + } + + virtual void* castRay(const btVector3& from,const btVector3& to, btVehicleRaycasterResult& result); + +}; + + +#endif //RAYCASTVEHICLE_H + diff --git a/bullet/src/BulletDynamics/Vehicle/btVehicleRaycaster.h b/bullet/src/BulletDynamics/Vehicle/btVehicleRaycaster.h new file mode 100644 index 0000000..7c830bd --- /dev/null +++ b/bullet/src/BulletDynamics/Vehicle/btVehicleRaycaster.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005 Erwin Coumans http://continuousphysics.com/Bullet/ + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies. + * Erwin Coumans makes no representations about the suitability + * of this software for any purpose. + * It is provided "as is" without express or implied warranty. +*/ +#ifndef VEHICLE_RAYCASTER_H +#define VEHICLE_RAYCASTER_H + +#include "LinearMath/btVector3.h" + +/// btVehicleRaycaster is provides interface for between vehicle simulation and raycasting +struct btVehicleRaycaster +{ +virtual ~btVehicleRaycaster() +{ +} + struct btVehicleRaycasterResult + { + btVehicleRaycasterResult() :m_distFraction(btScalar(-1.)){}; + btVector3 m_hitPointInWorld; + btVector3 m_hitNormalInWorld; + btScalar m_distFraction; + }; + + virtual void* castRay(const btVector3& from,const btVector3& to, btVehicleRaycasterResult& result) = 0; + +}; + +#endif //VEHICLE_RAYCASTER_H + diff --git a/bullet/src/BulletDynamics/Vehicle/btWheelInfo.cpp b/bullet/src/BulletDynamics/Vehicle/btWheelInfo.cpp new file mode 100644 index 0000000..e51c0a5 --- /dev/null +++ b/bullet/src/BulletDynamics/Vehicle/btWheelInfo.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2005 Erwin Coumans http://continuousphysics.com/Bullet/ + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies. + * Erwin Coumans makes no representations about the suitability + * of this software for any purpose. + * It is provided "as is" without express or implied warranty. +*/ +#include "btWheelInfo.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" // for pointvelocity + + +btScalar btWheelInfo::getSuspensionRestLength() const +{ + + return m_suspensionRestLength1; + +} + +void btWheelInfo::updateWheel(const btRigidBody& chassis,RaycastInfo& raycastInfo) +{ + (void)raycastInfo; + + + if (m_raycastInfo.m_isInContact) + + { + btScalar project= m_raycastInfo.m_contactNormalWS.dot( m_raycastInfo.m_wheelDirectionWS ); + btVector3 chassis_velocity_at_contactPoint; + btVector3 relpos = m_raycastInfo.m_contactPointWS - chassis.getCenterOfMassPosition(); + chassis_velocity_at_contactPoint = chassis.getVelocityInLocalPoint( relpos ); + btScalar projVel = m_raycastInfo.m_contactNormalWS.dot( chassis_velocity_at_contactPoint ); + if ( project >= btScalar(-0.1)) + { + m_suspensionRelativeVelocity = btScalar(0.0); + m_clippedInvContactDotSuspension = btScalar(1.0) / btScalar(0.1); + } + else + { + btScalar inv = btScalar(-1.) / project; + m_suspensionRelativeVelocity = projVel * inv; + m_clippedInvContactDotSuspension = inv; + } + + } + + else // Not in contact : position wheel in a nice (rest length) position + { + m_raycastInfo.m_suspensionLength = this->getSuspensionRestLength(); + m_suspensionRelativeVelocity = btScalar(0.0); + m_raycastInfo.m_contactNormalWS = -m_raycastInfo.m_wheelDirectionWS; + m_clippedInvContactDotSuspension = btScalar(1.0); + } +} diff --git a/bullet/src/BulletDynamics/Vehicle/btWheelInfo.h b/bullet/src/BulletDynamics/Vehicle/btWheelInfo.h new file mode 100644 index 0000000..a785fd0 --- /dev/null +++ b/bullet/src/BulletDynamics/Vehicle/btWheelInfo.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2005 Erwin Coumans http://continuousphysics.com/Bullet/ + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies. + * Erwin Coumans makes no representations about the suitability + * of this software for any purpose. + * It is provided "as is" without express or implied warranty. +*/ +#ifndef WHEEL_INFO_H +#define WHEEL_INFO_H + +#include "LinearMath/btVector3.h" +#include "LinearMath/btTransform.h" + +class btRigidBody; + +struct btWheelInfoConstructionInfo +{ + btVector3 m_chassisConnectionCS; + btVector3 m_wheelDirectionCS; + btVector3 m_wheelAxleCS; + btScalar m_suspensionRestLength; + btScalar m_maxSuspensionTravelCm; + btScalar m_wheelRadius; + + btScalar m_suspensionStiffness; + btScalar m_wheelsDampingCompression; + btScalar m_wheelsDampingRelaxation; + btScalar m_frictionSlip; + bool m_bIsFrontWheel; + +}; + +/// btWheelInfo contains information per wheel about friction and suspension. +struct btWheelInfo +{ + struct RaycastInfo + { + //set by raycaster + btVector3 m_contactNormalWS;//contactnormal + btVector3 m_contactPointWS;//raycast hitpoint + btScalar m_suspensionLength; + btVector3 m_hardPointWS;//raycast starting point + btVector3 m_wheelDirectionWS; //direction in worldspace + btVector3 m_wheelAxleWS; // axle in worldspace + bool m_isInContact; + void* m_groundObject; //could be general void* ptr + }; + + RaycastInfo m_raycastInfo; + + btTransform m_worldTransform; + + btVector3 m_chassisConnectionPointCS; //const + btVector3 m_wheelDirectionCS;//const + btVector3 m_wheelAxleCS; // const or modified by steering + btScalar m_suspensionRestLength1;//const + btScalar m_maxSuspensionTravelCm; + btScalar getSuspensionRestLength() const; + btScalar m_wheelsRadius;//const + btScalar m_suspensionStiffness;//const + btScalar m_wheelsDampingCompression;//const + btScalar m_wheelsDampingRelaxation;//const + btScalar m_frictionSlip; + btScalar m_steering; + btScalar m_rotation; + btScalar m_deltaRotation; + btScalar m_rollInfluence; + + btScalar m_engineForce; + + btScalar m_brake; + + bool m_bIsFrontWheel; + + void* m_clientInfo;//can be used to store pointer to sync transforms... + + btWheelInfo(btWheelInfoConstructionInfo& ci) + + { + + m_suspensionRestLength1 = ci.m_suspensionRestLength; + m_maxSuspensionTravelCm = ci.m_maxSuspensionTravelCm; + + m_wheelsRadius = ci.m_wheelRadius; + m_suspensionStiffness = ci.m_suspensionStiffness; + m_wheelsDampingCompression = ci.m_wheelsDampingCompression; + m_wheelsDampingRelaxation = ci.m_wheelsDampingRelaxation; + m_chassisConnectionPointCS = ci.m_chassisConnectionCS; + m_wheelDirectionCS = ci.m_wheelDirectionCS; + m_wheelAxleCS = ci.m_wheelAxleCS; + m_frictionSlip = ci.m_frictionSlip; + m_steering = btScalar(0.); + m_engineForce = btScalar(0.); + m_rotation = btScalar(0.); + m_deltaRotation = btScalar(0.); + m_brake = btScalar(0.); + m_rollInfluence = btScalar(0.1); + m_bIsFrontWheel = ci.m_bIsFrontWheel; + + } + + void updateWheel(const btRigidBody& chassis,RaycastInfo& raycastInfo); + + btScalar m_clippedInvContactDotSuspension; + btScalar m_suspensionRelativeVelocity; + //calculated by suspension + btScalar m_wheelsSuspensionForce; + btScalar m_skidInfo; + +}; + +#endif //WHEEL_INFO_H + diff --git a/bullet/src/LinearMath/btAabbUtil2.h b/bullet/src/LinearMath/btAabbUtil2.h new file mode 100644 index 0000000..04b84f0 --- /dev/null +++ b/bullet/src/LinearMath/btAabbUtil2.h @@ -0,0 +1,127 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / 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. +*/ + + + +#ifndef AABB_UTIL2 +#define AABB_UTIL2 + +#include "btVector3.h" +#include "btSimdMinMax.h" + + +#define btMin(a,b) ((a < b ? a : b)) +#define btMax(a,b) ((a > b ? a : b)) + + +/// conservative test for overlap between two aabbs +SIMD_FORCE_INLINE bool TestAabbAgainstAabb2(const btVector3 &aabbMin1, const btVector3 &aabbMax1, + const btVector3 &aabbMin2, const btVector3 &aabbMax2) +{ + bool overlap = true; + overlap = (aabbMin1[0] > aabbMax2[0] || aabbMax1[0] < aabbMin2[0]) ? false : overlap; + overlap = (aabbMin1[2] > aabbMax2[2] || aabbMax1[2] < aabbMin2[2]) ? false : overlap; + overlap = (aabbMin1[1] > aabbMax2[1] || aabbMax1[1] < aabbMin2[1]) ? false : overlap; + return overlap; +} + +/// conservative test for overlap between triangle and aabb +SIMD_FORCE_INLINE bool TestTriangleAgainstAabb2(const btVector3 *vertices, + const btVector3 &aabbMin, const btVector3 &aabbMax) +{ + const btVector3 &p1 = vertices[0]; + const btVector3 &p2 = vertices[1]; + const btVector3 &p3 = vertices[2]; + + if (btMin(btMin(p1[0], p2[0]), p3[0]) > aabbMax[0]) return false; + if (btMax(btMax(p1[0], p2[0]), p3[0]) < aabbMin[0]) return false; + + if (btMin(btMin(p1[2], p2[2]), p3[2]) > aabbMax[2]) return false; + if (btMax(btMax(p1[2], p2[2]), p3[2]) < aabbMin[2]) return false; + + if (btMin(btMin(p1[1], p2[1]), p3[1]) > aabbMax[1]) return false; + if (btMax(btMax(p1[1], p2[1]), p3[1]) < aabbMin[1]) return false; + return true; +} + + +SIMD_FORCE_INLINE int btOutcode(const btVector3& p,const btVector3& halfExtent) +{ + return (p.getX() < -halfExtent.getX() ? 0x01 : 0x0) | + (p.getX() > halfExtent.getX() ? 0x08 : 0x0) | + (p.getY() < -halfExtent.getY() ? 0x02 : 0x0) | + (p.getY() > halfExtent.getY() ? 0x10 : 0x0) | + (p.getZ() < -halfExtent.getZ() ? 0x4 : 0x0) | + (p.getZ() > halfExtent.getZ() ? 0x20 : 0x0); +} + + +SIMD_FORCE_INLINE bool btRayAabb(const btVector3& rayFrom, + const btVector3& rayTo, + const btVector3& aabbMin, + const btVector3& aabbMax, + btScalar& param, btVector3& normal) +{ + btVector3 aabbHalfExtent = (aabbMax-aabbMin)* btScalar(0.5); + btVector3 aabbCenter = (aabbMax+aabbMin)* btScalar(0.5); + btVector3 source = rayFrom - aabbCenter; + btVector3 target = rayTo - aabbCenter; + int sourceOutcode = btOutcode(source,aabbHalfExtent); + int targetOutcode = btOutcode(target,aabbHalfExtent); + if ((sourceOutcode & targetOutcode) == 0x0) + { + btScalar lambda_enter = btScalar(0.0); + btScalar lambda_exit = param; + btVector3 r = target - source; + int i; + btScalar normSign = 1; + btVector3 hitNormal(0,0,0); + int bit=1; + + for (int j=0;j<2;j++) + { + for (i = 0; i != 3; ++i) + { + if (sourceOutcode & bit) + { + btScalar lambda = (-source[i] - aabbHalfExtent[i]*normSign) / r[i]; + if (lambda_enter <= lambda) + { + lambda_enter = lambda; + hitNormal.setValue(0,0,0); + hitNormal[i] = normSign; + } + } + else if (targetOutcode & bit) + { + btScalar lambda = (-source[i] - aabbHalfExtent[i]*normSign) / r[i]; + btSetMin(lambda_exit, lambda); + } + bit<<=1; + } + normSign = btScalar(-1.); + } + if (lambda_enter <= lambda_exit) + { + param = lambda_enter; + normal = hitNormal; + return true; + } + } + return false; +} + + +#endif + diff --git a/bullet/src/LinearMath/btAlignedAllocator.cpp b/bullet/src/LinearMath/btAlignedAllocator.cpp new file mode 100644 index 0000000..fec5073 --- /dev/null +++ b/bullet/src/LinearMath/btAlignedAllocator.cpp @@ -0,0 +1,70 @@ +/* +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. +*/ + +#include "btAlignedAllocator.h" + + +#if defined (BT_HAS_ALIGNED_ALOCATOR) + +#include +void* btAlignedAlloc (int size, int alignment) +{ + return _aligned_malloc(size,alignment); +} + +void btAlignedFree (void* ptr) +{ + _aligned_free(ptr); +} + +#else + +#ifdef __CELLOS_LV2__ + +#include + +int numAllocs = 0; +int numFree = 0; + +void* btAlignedAlloc (int size, int alignment) +{ + numAllocs++; + return memalign(alignment, size); +} + +void btAlignedFree (void* ptr) +{ + numFree++; + free(ptr); +} + +#else +///todo +///will add some multi-platform version that works without _aligned_malloc/_aligned_free + +void* btAlignedAlloc (int size, int alignment) +{ + return new char[size]; +} + +void btAlignedFree (void* ptr) +{ + delete [] (char*) ptr; +} +#endif // + +#endif + + diff --git a/bullet/src/LinearMath/btAlignedAllocator.h b/bullet/src/LinearMath/btAlignedAllocator.h new file mode 100644 index 0000000..ea1acf8 --- /dev/null +++ b/bullet/src/LinearMath/btAlignedAllocator.h @@ -0,0 +1,80 @@ +/* +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. +*/ + +#ifndef BT_ALIGNED_ALLOCATOR +#define BT_ALIGNED_ALLOCATOR + +///we probably replace this with our own aligned memory allocator +///so we replace _aligned_malloc and _aligned_free with our own +///that is better portable and more predictable + +#include "btScalar.h" + +void* btAlignedAlloc (int size, int alignment); + +void btAlignedFree (void* ptr); + + +typedef int size_type; + + +template < typename T , unsigned Alignment > +class btAlignedAllocator { + + typedef btAlignedAllocator< T , Alignment > self_type; + +public: + + //just going down a list: + btAlignedAllocator() {} + /* + btAlignedAllocator( const self_type & ) {} + */ + + template < typename Other > + btAlignedAllocator( const btAlignedAllocator< Other , Alignment > & ) {} + + typedef const T* const_pointer; + typedef const T& const_reference; + typedef T* pointer; + typedef T& reference; + typedef T value_type; + + pointer address ( reference ref ) const { return &ref; } + const_pointer address ( const_reference ref ) const { return &ref; } + pointer allocate ( size_type n , const_pointer * hint = 0 ) { + (void)hint; + return reinterpret_cast< pointer >(btAlignedAlloc( sizeof(value_type) * n , Alignment )); + } + void construct ( pointer ptr , const value_type & value ) { new (ptr) value_type( value ); } + void deallocate( pointer ptr ) { + btAlignedFree( reinterpret_cast< void * >( ptr ) ); + } + void destroy ( pointer ptr ) { ptr->~value_type(); } + + + template < typename O > struct rebind { + typedef btAlignedAllocator< O , Alignment > other; + }; + template < typename O > + self_type & operator=( const btAlignedAllocator< O , Alignment > & ) { return *this; } + + friend bool operator==( const self_type & , const self_type & ) { return true; } +}; + + + +#endif //BT_ALIGNED_ALLOCATOR + diff --git a/bullet/src/LinearMath/btAlignedObjectArray.h b/bullet/src/LinearMath/btAlignedObjectArray.h new file mode 100644 index 0000000..ac86b54 --- /dev/null +++ b/bullet/src/LinearMath/btAlignedObjectArray.h @@ -0,0 +1,367 @@ +/* +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. +*/ + + +#ifndef BT_OBJECT_ARRAY__ +#define BT_OBJECT_ARRAY__ + +#include "btScalar.h" // has definitions like SIMD_FORCE_INLINE +#include "btAlignedAllocator.h" + +///If the platform doesn't support placement new, you can disable BT_USE_PLACEMENT_NEW +///then the btAlignedObjectArray doesn't support objects with virtual methods, and non-trivial constructors/destructors +///You can enable BT_USE_MEMCPY, then swapping elements in the array will use memcpy instead of operator= +///see discussion here: http://continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=1231 and +///http://www.continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=1240 + +#define BT_USE_PLACEMENT_NEW 1 +//#define BT_USE_MEMCPY 1 //disable, because it is cumbersome to find out for each platform where memcpy is defined. It can be in or or otherwise... + +#ifdef BT_USE_MEMCPY +#include +#include +#endif //BT_USE_MEMCPY + +#ifdef BT_USE_PLACEMENT_NEW +#include //for placement new +#endif //BT_USE_PLACEMENT_NEW + + +///btAlignedObjectArray uses a subset of the stl::vector interface for its methods +///It is developed to replace stl::vector to avoid STL alignment issues to add SIMD/SSE data +template +//template +class btAlignedObjectArray +{ + btAlignedAllocator m_allocator; + + int m_size; + int m_capacity; + T* m_data; + + protected: + SIMD_FORCE_INLINE int allocSize(int size) + { + return (size ? size*2 : 1); + } + SIMD_FORCE_INLINE void copy(int start,int end, T* dest) + { + int i; + for (i=start;i size()) + { + reserve(newsize); + } +#ifdef BT_USE_PLACEMENT_NEW + for (int i=curSize;i + void downHeap(T *pArr, int k, int n,L CompareFunc) + { + /* PRE: a[k+1..N] is a heap */ + /* POST: a[k..N] is a heap */ + + T temp = pArr[k - 1]; + /* k has child(s) */ + while (k <= n/2) + { + int child = 2*k; + + if ((child < n) && CompareFunc(pArr[child - 1] , pArr[child])) + { + child++; + } + /* pick larger child */ + if (CompareFunc(temp , pArr[child - 1])) + { + /* move child up */ + pArr[k - 1] = pArr[child - 1]; + k = child; + } + else + { + break; + } + } + pArr[k - 1] = temp; + } /*downHeap*/ + + void swap(int index0,int index1) + { +#ifdef BT_USE_MEMCPY + char temp[sizeof(T)]; + memcpy(temp,&m_data[index0],sizeof(T)); + memcpy(&m_data[index0],&m_data[index1],sizeof(T)); + memcpy(&m_data[index1],temp,sizeof(T)); +#else + T temp = m_data[index0]; + m_data[index0] = m_data[index1]; + m_data[index1] = temp; +#endif //BT_USE_PLACEMENT_NEW + + } + + template + void heapSort(L CompareFunc) + { + /* sort a[0..N-1], N.B. 0 to N-1 */ + int k; + int n = m_size; + for (k = n/2; k > 0; k--) + { + downHeap(m_data, k, n, CompareFunc); + } + + /* a[1..N] is now a heap */ + while ( n>=1 ) + { + swap(0,n-1); /* largest of a[0..n-1] */ + + + n = n - 1; + /* restore a[1..i-1] heap */ + downHeap(m_data, 1, n, CompareFunc); + } + } + + ///non-recursive binary search, assumes sorted array + int findBinarySearch(const T& key) const + { + int first = 0; + int last = size(); + + //assume sorted array + while (first <= last) { + int mid = (first + last) / 2; // compute mid point. + if (key > m_data[mid]) + first = mid + 1; // repeat search in top half. + else if (key < m_data[mid]) + last = mid - 1; // repeat search in bottom half. + else + return mid; // found it. return position ///// + } + return size(); // failed to find key + } + + + int findLinearSearch(const T& key) const + { + int index=size(); + int i; + + for (i=0;i& planeEquations, const btVector3& point, btScalar margin) +{ + int numbrushes = planeEquations.size(); + for (int i=0;ibtScalar(0.)) + { + return false; + } + } + return true; + +} + + +bool btGeometryUtil::areVerticesBehindPlane(const btVector3& planeNormal, const btAlignedObjectArray& vertices, btScalar margin) +{ + int numvertices = vertices.size(); + for (int i=0;ibtScalar(0.)) + { + return false; + } + } + return true; +} + +bool notExist(const btVector3& planeEquation,const btAlignedObjectArray& planeEquations) +{ + int numbrushes = planeEquations.size(); + for (int i=0;i btScalar(0.999)) + { + return false; + } + } + return true; +} + +void btGeometryUtil::getPlaneEquationsFromVertices(btAlignedObjectArray& vertices, btAlignedObjectArray& planeEquationsOut ) +{ + const int numvertices = vertices.size(); + // brute force: + for (int i=0;i btScalar(0.0001)) + { + planeEquation.normalize(); + if (notExist(planeEquation,planeEquationsOut)) + { + planeEquation[3] = -planeEquation.dot(N1); + + //check if inside, and replace supportingVertexOut if needed + if (areVerticesBehindPlane(planeEquation,vertices,btScalar(0.01))) + { + planeEquationsOut.push_back(planeEquation); + } + } + } + normalSign = btScalar(-1.); + } + + } + } + } + +} + +void btGeometryUtil::getVerticesFromPlaneEquations(const btAlignedObjectArray& planeEquations , btAlignedObjectArray& verticesOut ) +{ + const int numbrushes = planeEquations.size(); + // brute force: + for (int i=0;i btScalar(0.0001) ) && + ( n3n1.length2() > btScalar(0.0001) ) && + ( n1n2.length2() > btScalar(0.0001) ) ) + { + //point P out of 3 plane equations: + + // d1 ( N2 * N3 ) + d2 ( N3 * N1 ) + d3 ( N1 * N2 ) + //P = ------------------------------------------------------------------------- + // N1 . ( N2 * N3 ) + + + btScalar quotient = (N1.dot(n2n3)); + if (btFabs(quotient) > btScalar(0.000001)) + { + quotient = btScalar(-1.) / quotient; + n2n3 *= N1[3]; + n3n1 *= N2[3]; + n1n2 *= N3[3]; + btVector3 potentialVertex = n2n3; + potentialVertex += n3n1; + potentialVertex += n1n2; + potentialVertex *= quotient; + + //check if inside, and replace supportingVertexOut if needed + if (isPointInsidePlanes(planeEquations,potentialVertex,btScalar(0.01))) + { + verticesOut.push_back(potentialVertex); + } + } + } + } + } + } +} + diff --git a/bullet/src/LinearMath/btGeometryUtil.h b/bullet/src/LinearMath/btGeometryUtil.h new file mode 100644 index 0000000..d8d3c19 --- /dev/null +++ b/bullet/src/LinearMath/btGeometryUtil.h @@ -0,0 +1,41 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / 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. +*/ + + +#ifndef BT_GEOMETRY_UTIL_H +#define BT_GEOMETRY_UTIL_H + +#include "btVector3.h" +#include "btAlignedObjectArray.h" + +class btGeometryUtil +{ + public: + + + static void getPlaneEquationsFromVertices(btAlignedObjectArray& vertices, btAlignedObjectArray& planeEquationsOut ); + + static void getVerticesFromPlaneEquations(const btAlignedObjectArray& planeEquations , btAlignedObjectArray& verticesOut ); + + static bool isInside(const btAlignedObjectArray& vertices, const btVector3& planeNormal, btScalar margin); + + static bool isPointInsidePlanes(const btAlignedObjectArray& planeEquations, const btVector3& point, btScalar margin); + + static bool areVerticesBehindPlane(const btVector3& planeNormal, const btAlignedObjectArray& vertices, btScalar margin); + +}; + + +#endif //BT_GEOMETRY_UTIL_H + diff --git a/bullet/src/LinearMath/btIDebugDraw.h b/bullet/src/LinearMath/btIDebugDraw.h new file mode 100644 index 0000000..f5b80c8 --- /dev/null +++ b/bullet/src/LinearMath/btIDebugDraw.h @@ -0,0 +1,100 @@ +/* +Copyright (c) 2005 Gino van den Bergen / Erwin Coumans http://continuousphysics.com + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef IDEBUG_DRAW__H +#define IDEBUG_DRAW__H + +#include "btVector3.h" + + +class btIDebugDraw +{ + public: + + enum DebugDrawModes + { + DBG_NoDebug=0, + DBG_DrawWireframe = 1, + DBG_DrawAabb=2, + DBG_DrawFeaturesText=4, + DBG_DrawContactPoints=8, + DBG_NoDeactivation=16, + DBG_NoHelpText = 32, + DBG_DrawText=64, + DBG_ProfileTimings = 128, + DBG_EnableSatComparison = 256, + DBG_DisableBulletLCP = 512, + DBG_EnableCCD = 1024, + DBG_MAX_DEBUG_DRAW_MODE + }; + + virtual ~btIDebugDraw() {}; + + virtual void drawLine(const btVector3& from,const btVector3& to,const btVector3& color)=0; + + virtual void drawContactPoint(const btVector3& PointOnB,const btVector3& normalOnB,btScalar distance,int lifeTime,const btVector3& color)=0; + + virtual void reportErrorWarning(const char* warningString) = 0; + + virtual void setDebugMode(int debugMode) =0; + + virtual int getDebugMode() const = 0; + + inline void drawAabb(const btVector3& from,const btVector3& to,const btVector3& color) + { + + btVector3 halfExtents = (to-from)* 0.5f; + btVector3 center = (to+from) *0.5f; + int i,j; + + btVector3 edgecoord(1.f,1.f,1.f),pa,pb; + for (i=0;i<4;i++) + { + for (j=0;j<3;j++) + { + pa = btVector3(edgecoord[0]*halfExtents[0], edgecoord[1]*halfExtents[1], + edgecoord[2]*halfExtents[2]); + pa+=center; + + int othercoord = j%3; + edgecoord[othercoord]*=-1.f; + pb = btVector3(edgecoord[0]*halfExtents[0], edgecoord[1]*halfExtents[1], + edgecoord[2]*halfExtents[2]); + pb+=center; + + drawLine(pa,pb,color); + } + edgecoord = btVector3(-1.f,-1.f,-1.f); + if (i<3) + edgecoord[i]*=-1.f; + } + } +}; + + +#endif //IDEBUG_DRAW__H + diff --git a/bullet/src/LinearMath/btList.h b/bullet/src/LinearMath/btList.h new file mode 100644 index 0000000..c0d1d9d --- /dev/null +++ b/bullet/src/LinearMath/btList.h @@ -0,0 +1,73 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / 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. +*/ + + + +#ifndef GEN_LIST_H +#define GEN_LIST_H + +class btGEN_Link { +public: + btGEN_Link() : m_next(0), m_prev(0) {} + btGEN_Link(btGEN_Link *next, btGEN_Link *prev) : m_next(next), m_prev(prev) {} + + btGEN_Link *getNext() const { return m_next; } + btGEN_Link *getPrev() const { return m_prev; } + + bool isHead() const { return m_prev == 0; } + bool isTail() const { return m_next == 0; } + + void insertBefore(btGEN_Link *link) { + m_next = link; + m_prev = link->m_prev; + m_next->m_prev = this; + m_prev->m_next = this; + } + + void insertAfter(btGEN_Link *link) { + m_next = link->m_next; + m_prev = link; + m_next->m_prev = this; + m_prev->m_next = this; + } + + void remove() { + m_next->m_prev = m_prev; + m_prev->m_next = m_next; + } + +private: + btGEN_Link *m_next; + btGEN_Link *m_prev; +}; + +class btGEN_List { +public: + btGEN_List() : m_head(&m_tail, 0), m_tail(0, &m_head) {} + + btGEN_Link *getHead() const { return m_head.getNext(); } + btGEN_Link *getTail() const { return m_tail.getPrev(); } + + void addHead(btGEN_Link *link) { link->insertAfter(&m_head); } + void addTail(btGEN_Link *link) { link->insertBefore(&m_tail); } + +private: + btGEN_Link m_head; + btGEN_Link m_tail; +}; + +#endif + + + diff --git a/bullet/src/LinearMath/btMatrix3x3.h b/bullet/src/LinearMath/btMatrix3x3.h new file mode 100644 index 0000000..e4dd9bc --- /dev/null +++ b/bullet/src/LinearMath/btMatrix3x3.h @@ -0,0 +1,410 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / 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. +*/ + + +#ifndef btMatrix3x3_H +#define btMatrix3x3_H + +#include "btScalar.h" + +#include "btVector3.h" +#include "btQuaternion.h" + + +class btMatrix3x3 { + public: + btMatrix3x3 () {} + +// explicit btMatrix3x3(const btScalar *m) { setFromOpenGLSubMatrix(m); } + + explicit btMatrix3x3(const btQuaternion& q) { setRotation(q); } + /* + template + Matrix3x3(const btScalar& yaw, const btScalar& pitch, const btScalar& roll) + { + setEulerYPR(yaw, pitch, roll); + } + */ + btMatrix3x3(const btScalar& xx, const btScalar& xy, const btScalar& xz, + const btScalar& yx, const btScalar& yy, const btScalar& yz, + const btScalar& zx, const btScalar& zy, const btScalar& zz) + { + setValue(xx, xy, xz, + yx, yy, yz, + zx, zy, zz); + } + + SIMD_FORCE_INLINE btMatrix3x3 (const btMatrix3x3& other) + { + m_el[0] = other.m_el[0]; + m_el[1] = other.m_el[1]; + m_el[2] = other.m_el[2]; + } + + SIMD_FORCE_INLINE btMatrix3x3& operator=(const btMatrix3x3& other) + { + m_el[0] = other.m_el[0]; + m_el[1] = other.m_el[1]; + m_el[2] = other.m_el[2]; + return *this; + } + + SIMD_FORCE_INLINE btVector3 getColumn(int i) const + { + return btVector3(m_el[0][i],m_el[1][i],m_el[2][i]); + } + + + + SIMD_FORCE_INLINE const btVector3& getRow(int i) const + { + return m_el[i]; + } + + + SIMD_FORCE_INLINE btVector3& operator[](int i) + { + btFullAssert(0 <= i && i < 3); + return m_el[i]; + } + + SIMD_FORCE_INLINE const btVector3& operator[](int i) const + { + btFullAssert(0 <= i && i < 3); + return m_el[i]; + } + + btMatrix3x3& operator*=(const btMatrix3x3& m); + + + void setFromOpenGLSubMatrix(const btScalar *m) + { + m_el[0].setValue(m[0],m[4],m[8]); + m_el[1].setValue(m[1],m[5],m[9]); + m_el[2].setValue(m[2],m[6],m[10]); + + } + + void setValue(const btScalar& xx, const btScalar& xy, const btScalar& xz, + const btScalar& yx, const btScalar& yy, const btScalar& yz, + const btScalar& zx, const btScalar& zy, const btScalar& zz) + { + m_el[0].setValue(xx,xy,xz); + m_el[1].setValue(yx,yy,yz); + m_el[2].setValue(zx,zy,zz); + } + + void setRotation(const btQuaternion& q) + { + btScalar d = q.length2(); + btFullAssert(d != btScalar(0.0)); + btScalar s = btScalar(2.0) / d; + btScalar xs = q.x() * s, ys = q.y() * s, zs = q.z() * s; + btScalar wx = q.w() * xs, wy = q.w() * ys, wz = q.w() * zs; + btScalar xx = q.x() * xs, xy = q.x() * ys, xz = q.x() * zs; + btScalar yy = q.y() * ys, yz = q.y() * zs, zz = q.z() * zs; + setValue(btScalar(1.0) - (yy + zz), xy - wz, xz + wy, + xy + wz, btScalar(1.0) - (xx + zz), yz - wx, + xz - wy, yz + wx, btScalar(1.0) - (xx + yy)); + } + + + + void setEulerYPR(const btScalar& yaw, const btScalar& pitch, const btScalar& roll) + { + + btScalar cy(btCos(yaw)); + btScalar sy(btSin(yaw)); + btScalar cp(btCos(pitch)); + btScalar sp(btSin(pitch)); + btScalar cr(btCos(roll)); + btScalar sr(btSin(roll)); + btScalar cc = cy * cr; + btScalar cs = cy * sr; + btScalar sc = sy * cr; + btScalar ss = sy * sr; + setValue(cc - sp * ss, -cs - sp * sc, -sy * cp, + cp * sr, cp * cr, -sp, + sc + sp * cs, -ss + sp * cc, cy * cp); + + } + + /** + * setEulerZYX + * @param euler a const reference to a btVector3 of euler angles + * These angles are used to produce a rotation matrix. The euler + * angles are applied in ZYX order. I.e a vector is first rotated + * about X then Y and then Z + **/ + + void setEulerZYX(btScalar eulerX,btScalar eulerY,btScalar eulerZ) { + btScalar ci ( btCos(eulerX)); + btScalar cj ( btCos(eulerY)); + btScalar ch ( btCos(eulerZ)); + btScalar si ( btSin(eulerX)); + btScalar sj ( btSin(eulerY)); + btScalar sh ( btSin(eulerZ)); + btScalar cc = ci * ch; + btScalar cs = ci * sh; + btScalar sc = si * ch; + btScalar ss = si * sh; + + setValue(cj * ch, sj * sc - cs, sj * cc + ss, + cj * sh, sj * ss + cc, sj * cs - sc, + -sj, cj * si, cj * ci); + } + + void setIdentity() + { + setValue(btScalar(1.0), btScalar(0.0), btScalar(0.0), + btScalar(0.0), btScalar(1.0), btScalar(0.0), + btScalar(0.0), btScalar(0.0), btScalar(1.0)); + } + + void getOpenGLSubMatrix(btScalar *m) const + { + m[0] = btScalar(m_el[0].x()); + m[1] = btScalar(m_el[1].x()); + m[2] = btScalar(m_el[2].x()); + m[3] = btScalar(0.0); + m[4] = btScalar(m_el[0].y()); + m[5] = btScalar(m_el[1].y()); + m[6] = btScalar(m_el[2].y()); + m[7] = btScalar(0.0); + m[8] = btScalar(m_el[0].z()); + m[9] = btScalar(m_el[1].z()); + m[10] = btScalar(m_el[2].z()); + m[11] = btScalar(0.0); + } + + void getRotation(btQuaternion& q) const + { + btScalar trace = m_el[0].x() + m_el[1].y() + m_el[2].z(); + btScalar temp[4]; + + if (trace > btScalar(0.0)) + { + btScalar s = btSqrt(trace + btScalar(1.0)); + temp[3]=(s * btScalar(0.5)); + s = btScalar(0.5) / s; + + temp[0]=((m_el[2].y() - m_el[1].z()) * s); + temp[1]=((m_el[0].z() - m_el[2].x()) * s); + temp[2]=((m_el[1].x() - m_el[0].y()) * s); + } + else + { + int i = m_el[0].x() < m_el[1].y() ? + (m_el[1].y() < m_el[2].z() ? 2 : 1) : + (m_el[0].x() < m_el[2].z() ? 2 : 0); + int j = (i + 1) % 3; + int k = (i + 2) % 3; + + btScalar s = btSqrt(m_el[i][i] - m_el[j][j] - m_el[k][k] + btScalar(1.0)); + temp[i] = s * btScalar(0.5); + s = btScalar(0.5) / s; + + temp[3] = (m_el[k][j] - m_el[j][k]) * s; + temp[j] = (m_el[j][i] + m_el[i][j]) * s; + temp[k] = (m_el[k][i] + m_el[i][k]) * s; + } + q.setValue(temp[0],temp[1],temp[2],temp[3]); + } + + void getEuler(btScalar& yaw, btScalar& pitch, btScalar& roll) const + { + + if (btScalar(m_el[1].z()) < btScalar(1)) + { + if (btScalar(m_el[1].z()) > -btScalar(1)) + { + yaw = btScalar(btAtan2(m_el[1].x(), m_el[0].x())); + pitch = btScalar(btAsin(-m_el[1].y())); + roll = btScalar(btAtan2(m_el[2].y(), m_el[2].z())); + } + else + { + yaw = btScalar(-btAtan2(-m_el[0].y(), m_el[0].z())); + pitch = SIMD_HALF_PI; + roll = btScalar(0.0); + } + } + else + { + yaw = btScalar(btAtan2(-m_el[0].y(), m_el[0].z())); + pitch = -SIMD_HALF_PI; + roll = btScalar(0.0); + } + } + + + + + btMatrix3x3 scaled(const btVector3& s) const + { + return btMatrix3x3(m_el[0].x() * s.x(), m_el[0].y() * s.y(), m_el[0].z() * s.z(), + m_el[1].x() * s.x(), m_el[1].y() * s.y(), m_el[1].z() * s.z(), + m_el[2].x() * s.x(), m_el[2].y() * s.y(), m_el[2].z() * s.z()); + } + + btScalar determinant() const; + btMatrix3x3 adjoint() const; + btMatrix3x3 absolute() const; + btMatrix3x3 transpose() const; + btMatrix3x3 inverse() const; + + btMatrix3x3 transposeTimes(const btMatrix3x3& m) const; + btMatrix3x3 timesTranspose(const btMatrix3x3& m) const; + + SIMD_FORCE_INLINE btScalar tdotx(const btVector3& v) const + { + return m_el[0].x() * v.x() + m_el[1].x() * v.y() + m_el[2].x() * v.z(); + } + SIMD_FORCE_INLINE btScalar tdoty(const btVector3& v) const + { + return m_el[0].y() * v.x() + m_el[1].y() * v.y() + m_el[2].y() * v.z(); + } + SIMD_FORCE_INLINE btScalar tdotz(const btVector3& v) const + { + return m_el[0].z() * v.x() + m_el[1].z() * v.y() + m_el[2].z() * v.z(); + } + + + + protected: + btScalar cofac(int r1, int c1, int r2, int c2) const + { + return m_el[r1][c1] * m_el[r2][c2] - m_el[r1][c2] * m_el[r2][c1]; + } + + btVector3 m_el[3]; + }; + + SIMD_FORCE_INLINE btMatrix3x3& + btMatrix3x3::operator*=(const btMatrix3x3& m) + { + setValue(m.tdotx(m_el[0]), m.tdoty(m_el[0]), m.tdotz(m_el[0]), + m.tdotx(m_el[1]), m.tdoty(m_el[1]), m.tdotz(m_el[1]), + m.tdotx(m_el[2]), m.tdoty(m_el[2]), m.tdotz(m_el[2])); + return *this; + } + + SIMD_FORCE_INLINE btScalar + btMatrix3x3::determinant() const + { + return triple((*this)[0], (*this)[1], (*this)[2]); + } + + + SIMD_FORCE_INLINE btMatrix3x3 + btMatrix3x3::absolute() const + { + return btMatrix3x3( + btFabs(m_el[0].x()), btFabs(m_el[0].y()), btFabs(m_el[0].z()), + btFabs(m_el[1].x()), btFabs(m_el[1].y()), btFabs(m_el[1].z()), + btFabs(m_el[2].x()), btFabs(m_el[2].y()), btFabs(m_el[2].z())); + } + + SIMD_FORCE_INLINE btMatrix3x3 + btMatrix3x3::transpose() const + { + return btMatrix3x3(m_el[0].x(), m_el[1].x(), m_el[2].x(), + m_el[0].y(), m_el[1].y(), m_el[2].y(), + m_el[0].z(), m_el[1].z(), m_el[2].z()); + } + + SIMD_FORCE_INLINE btMatrix3x3 + btMatrix3x3::adjoint() const + { + return btMatrix3x3(cofac(1, 1, 2, 2), cofac(0, 2, 2, 1), cofac(0, 1, 1, 2), + cofac(1, 2, 2, 0), cofac(0, 0, 2, 2), cofac(0, 2, 1, 0), + cofac(1, 0, 2, 1), cofac(0, 1, 2, 0), cofac(0, 0, 1, 1)); + } + + SIMD_FORCE_INLINE btMatrix3x3 + btMatrix3x3::inverse() const + { + btVector3 co(cofac(1, 1, 2, 2), cofac(1, 2, 2, 0), cofac(1, 0, 2, 1)); + btScalar det = (*this)[0].dot(co); + btFullAssert(det != btScalar(0.0)); + btScalar s = btScalar(1.0) / det; + return btMatrix3x3(co.x() * s, cofac(0, 2, 2, 1) * s, cofac(0, 1, 1, 2) * s, + co.y() * s, cofac(0, 0, 2, 2) * s, cofac(0, 2, 1, 0) * s, + co.z() * s, cofac(0, 1, 2, 0) * s, cofac(0, 0, 1, 1) * s); + } + + SIMD_FORCE_INLINE btMatrix3x3 + btMatrix3x3::transposeTimes(const btMatrix3x3& m) const + { + return btMatrix3x3( + m_el[0].x() * m[0].x() + m_el[1].x() * m[1].x() + m_el[2].x() * m[2].x(), + m_el[0].x() * m[0].y() + m_el[1].x() * m[1].y() + m_el[2].x() * m[2].y(), + m_el[0].x() * m[0].z() + m_el[1].x() * m[1].z() + m_el[2].x() * m[2].z(), + m_el[0].y() * m[0].x() + m_el[1].y() * m[1].x() + m_el[2].y() * m[2].x(), + m_el[0].y() * m[0].y() + m_el[1].y() * m[1].y() + m_el[2].y() * m[2].y(), + m_el[0].y() * m[0].z() + m_el[1].y() * m[1].z() + m_el[2].y() * m[2].z(), + m_el[0].z() * m[0].x() + m_el[1].z() * m[1].x() + m_el[2].z() * m[2].x(), + m_el[0].z() * m[0].y() + m_el[1].z() * m[1].y() + m_el[2].z() * m[2].y(), + m_el[0].z() * m[0].z() + m_el[1].z() * m[1].z() + m_el[2].z() * m[2].x()); + } + + SIMD_FORCE_INLINE btMatrix3x3 + btMatrix3x3::timesTranspose(const btMatrix3x3& m) const + { + return btMatrix3x3( + m_el[0].dot(m[0]), m_el[0].dot(m[1]), m_el[0].dot(m[2]), + m_el[1].dot(m[0]), m_el[1].dot(m[1]), m_el[1].dot(m[2]), + m_el[2].dot(m[0]), m_el[2].dot(m[1]), m_el[2].dot(m[2])); + + } + + SIMD_FORCE_INLINE btVector3 + operator*(const btMatrix3x3& m, const btVector3& v) + { + return btVector3(m[0].dot(v), m[1].dot(v), m[2].dot(v)); + } + + + SIMD_FORCE_INLINE btVector3 + operator*(const btVector3& v, const btMatrix3x3& m) + { + return btVector3(m.tdotx(v), m.tdoty(v), m.tdotz(v)); + } + + SIMD_FORCE_INLINE btMatrix3x3 + operator*(const btMatrix3x3& m1, const btMatrix3x3& m2) + { + return btMatrix3x3( + m2.tdotx( m1[0]), m2.tdoty( m1[0]), m2.tdotz( m1[0]), + m2.tdotx( m1[1]), m2.tdoty( m1[1]), m2.tdotz( m1[1]), + m2.tdotx( m1[2]), m2.tdoty( m1[2]), m2.tdotz( m1[2])); + } + +/* + SIMD_FORCE_INLINE btMatrix3x3 btMultTransposeLeft(const btMatrix3x3& m1, const btMatrix3x3& m2) { + return btMatrix3x3( + m1[0][0] * m2[0][0] + m1[1][0] * m2[1][0] + m1[2][0] * m2[2][0], + m1[0][0] * m2[0][1] + m1[1][0] * m2[1][1] + m1[2][0] * m2[2][1], + m1[0][0] * m2[0][2] + m1[1][0] * m2[1][2] + m1[2][0] * m2[2][2], + m1[0][1] * m2[0][0] + m1[1][1] * m2[1][0] + m1[2][1] * m2[2][0], + m1[0][1] * m2[0][1] + m1[1][1] * m2[1][1] + m1[2][1] * m2[2][1], + m1[0][1] * m2[0][2] + m1[1][1] * m2[1][2] + m1[2][1] * m2[2][2], + m1[0][2] * m2[0][0] + m1[1][2] * m2[1][0] + m1[2][2] * m2[2][0], + m1[0][2] * m2[0][1] + m1[1][2] * m2[1][1] + m1[2][2] * m2[2][1], + m1[0][2] * m2[0][2] + m1[1][2] * m2[1][2] + m1[2][2] * m2[2][2]); +} +*/ + + +#endif diff --git a/bullet/src/LinearMath/btMinMax.h b/bullet/src/LinearMath/btMinMax.h new file mode 100644 index 0000000..f46b2f3 --- /dev/null +++ b/bullet/src/LinearMath/btMinMax.h @@ -0,0 +1,69 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / 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. +*/ + + + +#ifndef GEN_MINMAX_H +#define GEN_MINMAX_H + +template +SIMD_FORCE_INLINE const T& GEN_min(const T& a, const T& b) +{ + return b < a ? b : a; +} + +template +SIMD_FORCE_INLINE const T& GEN_max(const T& a, const T& b) +{ + return a < b ? b : a; +} + +template +SIMD_FORCE_INLINE const T& GEN_clamped(const T& a, const T& lb, const T& ub) +{ + return a < lb ? lb : (ub < a ? ub : a); +} + +template +SIMD_FORCE_INLINE void GEN_set_min(T& a, const T& b) +{ + if (b < a) + { + a = b; + } +} + +template +SIMD_FORCE_INLINE void GEN_set_max(T& a, const T& b) +{ + if (a < b) + { + a = b; + } +} + +template +SIMD_FORCE_INLINE void GEN_clamp(T& a, const T& lb, const T& ub) +{ + if (a < lb) + { + a = lb; + } + else if (ub < a) + { + a = ub; + } +} + +#endif diff --git a/bullet/src/LinearMath/btMotionState.h b/bullet/src/LinearMath/btMotionState.h new file mode 100644 index 0000000..e788b3d --- /dev/null +++ b/bullet/src/LinearMath/btMotionState.h @@ -0,0 +1,40 @@ +/* +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. +*/ + +#ifndef BT_MOTIONSTATE_H +#define BT_MOTIONSTATE_H + +#include "btTransform.h" + +///btMotionState allows the dynamics world to synchronize the updated world transforms with graphics +///For optimizations, potentially only moving objects get synchronized (using setWorldPosition/setWorldOrientation) +class btMotionState +{ + public: + + virtual ~btMotionState() + { + + } + + virtual void getWorldTransform(btTransform& worldTrans ) const =0; + + //Bullet only calls the update of worldtransform for active objects + virtual void setWorldTransform(const btTransform& worldTrans)=0; + + +}; + +#endif //BT_MOTIONSTATE_H diff --git a/bullet/src/LinearMath/btPoint3.h b/bullet/src/LinearMath/btPoint3.h new file mode 100644 index 0000000..41df4ea --- /dev/null +++ b/bullet/src/LinearMath/btPoint3.h @@ -0,0 +1,24 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / 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. +*/ + + + +#ifndef btPoint3_H +#define btPoint3_H + +#include "btVector3.h" + +typedef btVector3 btPoint3; + +#endif diff --git a/bullet/src/LinearMath/btQuadWord.h b/bullet/src/LinearMath/btQuadWord.h new file mode 100644 index 0000000..b5d6cb0 --- /dev/null +++ b/bullet/src/LinearMath/btQuadWord.h @@ -0,0 +1,130 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / 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. +*/ + + +#ifndef SIMD_QUADWORD_H +#define SIMD_QUADWORD_H + +#include "btScalar.h" +#include "btSimdMinMax.h" + +class btQuadWordStorage +{ +protected: + btScalar m_x; + btScalar m_y; + btScalar m_z; + btScalar m_unusedW; +}; + + +///btQuadWord is base-class for vectors, points +class btQuadWord : public btQuadWordStorage +{ + public: + +// SIMD_FORCE_INLINE btScalar& operator[](int i) { return (&m_x)[i]; } +// SIMD_FORCE_INLINE const btScalar& operator[](int i) const { return (&m_x)[i]; } + + SIMD_FORCE_INLINE const btScalar& getX() const { return m_x; } + + SIMD_FORCE_INLINE const btScalar& getY() const { return m_y; } + + SIMD_FORCE_INLINE const btScalar& getZ() const { return m_z; } + + SIMD_FORCE_INLINE void setX(btScalar x) { m_x = x;}; + + SIMD_FORCE_INLINE void setY(btScalar y) { m_y = y;}; + + SIMD_FORCE_INLINE void setZ(btScalar z) { m_z = z;}; + + SIMD_FORCE_INLINE void setW(btScalar w) { m_unusedW = w;}; + + SIMD_FORCE_INLINE const btScalar& x() const { return m_x; } + + SIMD_FORCE_INLINE const btScalar& y() const { return m_y; } + + SIMD_FORCE_INLINE const btScalar& z() const { return m_z; } + + SIMD_FORCE_INLINE const btScalar& w() const { return m_unusedW; } + + + SIMD_FORCE_INLINE operator btScalar *() { return &m_x; } + SIMD_FORCE_INLINE operator const btScalar *() const { return &m_x; } + + SIMD_FORCE_INLINE void setValue(const btScalar& x, const btScalar& y, const btScalar& z) + { + m_x=x; + m_y=y; + m_z=z; + m_unusedW = 0.f; + } + +/* void getValue(btScalar *m) const + { + m[0] = m_x; + m[1] = m_y; + m[2] = m_z; + } +*/ + SIMD_FORCE_INLINE void setValue(const btScalar& x, const btScalar& y, const btScalar& z,const btScalar& w) + { + m_x=x; + m_y=y; + m_z=z; + m_unusedW=w; + } + + SIMD_FORCE_INLINE btQuadWord() + // :m_x(btScalar(0.)),m_y(btScalar(0.)),m_z(btScalar(0.)),m_unusedW(btScalar(0.)) + { + } + + SIMD_FORCE_INLINE btQuadWord(const btQuadWordStorage& q) + { + *((btQuadWordStorage*)this) = q; + } + + SIMD_FORCE_INLINE btQuadWord(const btScalar& x, const btScalar& y, const btScalar& z) + { + m_x = x, m_y = y, m_z = z, m_unusedW = 0.0f; + } + + SIMD_FORCE_INLINE btQuadWord(const btScalar& x, const btScalar& y, const btScalar& z,const btScalar& w) + { + m_x = x, m_y = y, m_z = z, m_unusedW = w; + } + + + SIMD_FORCE_INLINE void setMax(const btQuadWord& other) + { + btSetMax(m_x, other.m_x); + btSetMax(m_y, other.m_y); + btSetMax(m_z, other.m_z); + btSetMax(m_unusedW, other.m_unusedW); + } + + SIMD_FORCE_INLINE void setMin(const btQuadWord& other) + { + btSetMin(m_x, other.m_x); + btSetMin(m_y, other.m_y); + btSetMin(m_z, other.m_z); + btSetMin(m_unusedW, other.m_unusedW); + } + + + +}; + +#endif //SIMD_QUADWORD_H diff --git a/bullet/src/LinearMath/btQuaternion.h b/bullet/src/LinearMath/btQuaternion.h new file mode 100644 index 0000000..4c67697 --- /dev/null +++ b/bullet/src/LinearMath/btQuaternion.h @@ -0,0 +1,321 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / 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. +*/ + + + +#ifndef SIMD__QUATERNION_H_ +#define SIMD__QUATERNION_H_ + +#include "btVector3.h" + +class btQuaternion : public btQuadWord { +public: + btQuaternion() {} + + // template + // explicit Quaternion(const btScalar *v) : Tuple4(v) {} + + btQuaternion(const btScalar& x, const btScalar& y, const btScalar& z, const btScalar& w) + : btQuadWord(x, y, z, w) + {} + + btQuaternion(const btVector3& axis, const btScalar& angle) + { + setRotation(axis, angle); + } + + btQuaternion(const btScalar& yaw, const btScalar& pitch, const btScalar& roll) + { + setEuler(yaw, pitch, roll); + } + + void setRotation(const btVector3& axis, const btScalar& angle) + { + btScalar d = axis.length(); + assert(d != btScalar(0.0)); + btScalar s = btSin(angle * btScalar(0.5)) / d; + setValue(axis.x() * s, axis.y() * s, axis.z() * s, + btCos(angle * btScalar(0.5))); + } + + void setEuler(const btScalar& yaw, const btScalar& pitch, const btScalar& roll) + { + btScalar halfYaw = btScalar(yaw) * btScalar(0.5); + btScalar halfPitch = btScalar(pitch) * btScalar(0.5); + btScalar halfRoll = btScalar(roll) * btScalar(0.5); + btScalar cosYaw = btCos(halfYaw); + btScalar sinYaw = btSin(halfYaw); + btScalar cosPitch = btCos(halfPitch); + btScalar sinPitch = btSin(halfPitch); + btScalar cosRoll = btCos(halfRoll); + btScalar sinRoll = btSin(halfRoll); + setValue(cosRoll * sinPitch * cosYaw + sinRoll * cosPitch * sinYaw, + cosRoll * cosPitch * sinYaw - sinRoll * sinPitch * cosYaw, + sinRoll * cosPitch * cosYaw - cosRoll * sinPitch * sinYaw, + cosRoll * cosPitch * cosYaw + sinRoll * sinPitch * sinYaw); + } + + btQuaternion& operator+=(const btQuaternion& q) + { + m_x += q.x(); m_y += q.y(); m_z += q.z(); m_unusedW += q.m_unusedW; + return *this; + } + + btQuaternion& operator-=(const btQuaternion& q) + { + m_x -= q.x(); m_y -= q.y(); m_z -= q.z(); m_unusedW -= q.m_unusedW; + return *this; + } + + btQuaternion& operator*=(const btScalar& s) + { + m_x *= s; m_y *= s; m_z *= s; m_unusedW *= s; + return *this; + } + + + btQuaternion& operator*=(const btQuaternion& q) + { + setValue(m_unusedW * q.x() + m_x * q.m_unusedW + m_y * q.z() - m_z * q.y(), + m_unusedW * q.y() + m_y * q.m_unusedW + m_z * q.x() - m_x * q.z(), + m_unusedW * q.z() + m_z * q.m_unusedW + m_x * q.y() - m_y * q.x(), + m_unusedW * q.m_unusedW - m_x * q.x() - m_y * q.y() - m_z * q.z()); + return *this; + } + + btScalar dot(const btQuaternion& q) const + { + return m_x * q.x() + m_y * q.y() + m_z * q.z() + m_unusedW * q.m_unusedW; + } + + btScalar length2() const + { + return dot(*this); + } + + btScalar length() const + { + return btSqrt(length2()); + } + + btQuaternion& normalize() + { + return *this /= length(); + } + + SIMD_FORCE_INLINE btQuaternion + operator*(const btScalar& s) const + { + return btQuaternion(x() * s, y() * s, z() * s, m_unusedW * s); + } + + + + btQuaternion operator/(const btScalar& s) const + { + assert(s != btScalar(0.0)); + return *this * (btScalar(1.0) / s); + } + + + btQuaternion& operator/=(const btScalar& s) + { + assert(s != btScalar(0.0)); + return *this *= btScalar(1.0) / s; + } + + + btQuaternion normalized() const + { + return *this / length(); + } + + btScalar angle(const btQuaternion& q) const + { + btScalar s = btSqrt(length2() * q.length2()); + assert(s != btScalar(0.0)); + return btAcos(dot(q) / s); + } + + btScalar getAngle() const + { + btScalar s = btScalar(2.) * btAcos(m_unusedW); + return s; + } + + + + btQuaternion inverse() const + { + return btQuaternion(m_x, m_y, m_z, -m_unusedW); + } + + SIMD_FORCE_INLINE btQuaternion + operator+(const btQuaternion& q2) const + { + const btQuaternion& q1 = *this; + return btQuaternion(q1.x() + q2.x(), q1.y() + q2.y(), q1.z() + q2.z(), q1.m_unusedW + q2.m_unusedW); + } + + SIMD_FORCE_INLINE btQuaternion + operator-(const btQuaternion& q2) const + { + const btQuaternion& q1 = *this; + return btQuaternion(q1.x() - q2.x(), q1.y() - q2.y(), q1.z() - q2.z(), q1.m_unusedW - q2.m_unusedW); + } + + SIMD_FORCE_INLINE btQuaternion operator-() const + { + const btQuaternion& q2 = *this; + return btQuaternion( - q2.x(), - q2.y(), - q2.z(), - q2.m_unusedW); + } + + SIMD_FORCE_INLINE btQuaternion farthest( const btQuaternion& qd) const + { + btQuaternion diff,sum; + diff = *this - qd; + sum = *this + qd; + if( diff.dot(diff) > sum.dot(sum) ) + return qd; + return (-qd); + } + + btQuaternion slerp(const btQuaternion& q, const btScalar& t) const + { + btScalar theta = angle(q); + if (theta != btScalar(0.0)) + { + btScalar d = btScalar(1.0) / btSin(theta); + btScalar s0 = btSin((btScalar(1.0) - t) * theta); + btScalar s1 = btSin(t * theta); + return btQuaternion((m_x * s0 + q.x() * s1) * d, + (m_y * s0 + q.y() * s1) * d, + (m_z * s0 + q.z() * s1) * d, + (m_unusedW * s0 + q.m_unusedW * s1) * d); + } + else + { + return *this; + } + } + + SIMD_FORCE_INLINE const btScalar& getW() const { return m_unusedW; } + + +}; + + + +SIMD_FORCE_INLINE btQuaternion +operator-(const btQuaternion& q) +{ + return btQuaternion(-q.x(), -q.y(), -q.z(), -q.w()); +} + + + + +SIMD_FORCE_INLINE btQuaternion +operator*(const btQuaternion& q1, const btQuaternion& q2) { + return btQuaternion(q1.w() * q2.x() + q1.x() * q2.w() + q1.y() * q2.z() - q1.z() * q2.y(), + q1.w() * q2.y() + q1.y() * q2.w() + q1.z() * q2.x() - q1.x() * q2.z(), + q1.w() * q2.z() + q1.z() * q2.w() + q1.x() * q2.y() - q1.y() * q2.x(), + q1.w() * q2.w() - q1.x() * q2.x() - q1.y() * q2.y() - q1.z() * q2.z()); +} + +SIMD_FORCE_INLINE btQuaternion +operator*(const btQuaternion& q, const btVector3& w) +{ + return btQuaternion( q.w() * w.x() + q.y() * w.z() - q.z() * w.y(), + q.w() * w.y() + q.z() * w.x() - q.x() * w.z(), + q.w() * w.z() + q.x() * w.y() - q.y() * w.x(), + -q.x() * w.x() - q.y() * w.y() - q.z() * w.z()); +} + +SIMD_FORCE_INLINE btQuaternion +operator*(const btVector3& w, const btQuaternion& q) +{ + return btQuaternion( w.x() * q.w() + w.y() * q.z() - w.z() * q.y(), + w.y() * q.w() + w.z() * q.x() - w.x() * q.z(), + w.z() * q.w() + w.x() * q.y() - w.y() * q.x(), + -w.x() * q.x() - w.y() * q.y() - w.z() * q.z()); +} + +SIMD_FORCE_INLINE btScalar +dot(const btQuaternion& q1, const btQuaternion& q2) +{ + return q1.dot(q2); +} + + +SIMD_FORCE_INLINE btScalar +length(const btQuaternion& q) +{ + return q.length(); +} + +SIMD_FORCE_INLINE btScalar +angle(const btQuaternion& q1, const btQuaternion& q2) +{ + return q1.angle(q2); +} + + +SIMD_FORCE_INLINE btQuaternion +inverse(const btQuaternion& q) +{ + return q.inverse(); +} + +SIMD_FORCE_INLINE btQuaternion +slerp(const btQuaternion& q1, const btQuaternion& q2, const btScalar& t) +{ + return q1.slerp(q2, t); +} + +SIMD_FORCE_INLINE btVector3 +quatRotate(const btQuaternion& rotation, const btVector3& v) +{ + btQuaternion q = rotation * v; + q *= rotation.inverse(); + return btVector3(q.getX(),q.getY(),q.getZ()); +} + +SIMD_FORCE_INLINE btQuaternion +shortestArcQuat(const btVector3& v0, const btVector3& v1) // Game Programming Gems 2.10. make sure v0,v1 are normalized +{ + btVector3 c = v0.cross(v1); + btScalar d = v0.dot(v1); + + if (d < -1.0 + SIMD_EPSILON) + return btQuaternion(0.0f,1.0f,0.0f,0.0f); // just pick any vector + + btScalar s = btSqrt((1.0f + d) * 2.0f); + btScalar rs = 1.0f / s; + + return btQuaternion(c.getX()*rs,c.getY()*rs,c.getZ()*rs,s * 0.5f); +} + +SIMD_FORCE_INLINE btQuaternion +shortestArcQuatNormalize(btVector3 v0,btVector3 v1) +{ + v0.normalize(); + v1.normalize(); + return shortestArcQuat(v0,v1); +} + +#endif + + + diff --git a/bullet/src/LinearMath/btQuickprof.cpp b/bullet/src/LinearMath/btQuickprof.cpp new file mode 100644 index 0000000..2ec36d8 --- /dev/null +++ b/bullet/src/LinearMath/btQuickprof.cpp @@ -0,0 +1,38 @@ +/* +Copyright (c) 2006 Tyler Streeter + +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. + +*/ + + +// Please visit the project website (http://quickprof.sourceforge.net) +// for usage instructions. + +// Credits: The Clock class was inspired by the Timer classes in +// Ogre (www.ogre3d.org). + +#include "LinearMath/btQuickprof.h" + +#ifdef USE_QUICKPROF + +// Note: We must declare these private static variables again here to +// avoid link errors. +bool btProfiler::mEnabled = false; +btClock btProfiler::mClock; +unsigned long int btProfiler::mCurrentCycleStartMicroseconds = 0; +unsigned long int btProfiler::mLastCycleDurationMicroseconds = 0; +std::map btProfiler::mProfileBlocks; +std::ofstream btProfiler::mOutputFile; +bool btProfiler::mFirstFileOutput = true; +btProfiler::BlockTimingMethod btProfiler::mFileOutputMethod; +unsigned long int btProfiler::mCycleNumber = 0; +#endif //USE_QUICKPROF diff --git a/bullet/src/LinearMath/btQuickprof.h b/bullet/src/LinearMath/btQuickprof.h new file mode 100644 index 0000000..436b539 --- /dev/null +++ b/bullet/src/LinearMath/btQuickprof.h @@ -0,0 +1,712 @@ +/* +Copyright (c) 2006 Tyler Streeter + +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. + +*/ + +// Please visit the project website (http://quickprof.sourceforge.net) +// for usage instructions. + +// Credits: The Clock class was inspired by the Timer classes in +// Ogre (www.ogre3d.org). + +#ifndef QUICK_PROF_H +#define QUICK_PROF_H + +#include "btScalar.h" + +//#define USE_QUICKPROF 1 +//Don't use quickprof for now, because it contains STL. TODO: replace STL by Bullet container classes. + + +//if you don't need btClock, you can comment next line +#define USE_BT_CLOCK 1 + +#ifdef USE_BT_CLOCK +#ifdef __CELLOS_LV2__ +#include +#include +typedef uint64_t __int64; +#endif + +#if defined (SUNOS) || defined (__SUNOS__) + #include +#endif + +#if defined(WIN32) || defined(_WIN32) + + #define USE_WINDOWS_TIMERS + #define WIN32_LEAN_AND_MEAN + #define NOWINRES + #define NOMCX + #define NOIME +#ifdef _XBOX + #include +#else + #include +#endif + #include + +#else + #include +#endif + +#define mymin(a,b) (a > b ? a : b) + +/// basic clock +class btClock + { + public: + btClock() + { +#ifdef USE_WINDOWS_TIMERS + QueryPerformanceFrequency(&mClockFrequency); +#endif + reset(); + } + + ~btClock() + { + } + + /// Resets the initial reference time. + void reset() + { +#ifdef USE_WINDOWS_TIMERS + QueryPerformanceCounter(&mStartTime); + mStartTick = GetTickCount(); + mPrevElapsedTime = 0; +#else +#ifdef __CELLOS_LV2__ + + typedef uint64_t __int64; + typedef __int64 ClockSize; + ClockSize newTime; + __asm __volatile__( "mftb %0" : "=r" (newTime) : : "memory"); + mStartTime = newTime; +#else + gettimeofday(&mStartTime, 0); +#endif + +#endif + } + + /// Returns the time in ms since the last call to reset or since + /// the btClock was created. + unsigned long int getTimeMilliseconds() + { +#ifdef USE_WINDOWS_TIMERS + LARGE_INTEGER currentTime; + QueryPerformanceCounter(¤tTime); + LONGLONG elapsedTime = currentTime.QuadPart - + mStartTime.QuadPart; + + // Compute the number of millisecond ticks elapsed. + unsigned long msecTicks = (unsigned long)(1000 * elapsedTime / + mClockFrequency.QuadPart); + + // Check for unexpected leaps in the Win32 performance counter. + // (This is caused by unexpected data across the PCI to ISA + // bridge, aka south bridge. See Microsoft KB274323.) + unsigned long elapsedTicks = GetTickCount() - mStartTick; + signed long msecOff = (signed long)(msecTicks - elapsedTicks); + if (msecOff < -100 || msecOff > 100) + { + // Adjust the starting time forwards. + LONGLONG msecAdjustment = mymin(msecOff * + mClockFrequency.QuadPart / 1000, elapsedTime - + mPrevElapsedTime); + mStartTime.QuadPart += msecAdjustment; + elapsedTime -= msecAdjustment; + + // Recompute the number of millisecond ticks elapsed. + msecTicks = (unsigned long)(1000 * elapsedTime / + mClockFrequency.QuadPart); + } + + // Store the current elapsed time for adjustments next time. + mPrevElapsedTime = elapsedTime; + + return msecTicks; +#else + +#ifdef __CELLOS_LV2__ + __int64 freq=sys_time_get_timebase_frequency(); + double dFreq=((double) freq) / 1000.0; + typedef uint64_t __int64; + typedef __int64 ClockSize; + ClockSize newTime; + __asm __volatile__( "mftb %0" : "=r" (newTime) : : "memory"); + + return (newTime-mStartTime) / dFreq; +#else + + struct timeval currentTime; + gettimeofday(¤tTime, 0); + return (currentTime.tv_sec - mStartTime.tv_sec) * 1000 + + (currentTime.tv_usec - mStartTime.tv_usec) / 1000; +#endif //__CELLOS_LV2__ +#endif + } + + /// Returns the time in us since the last call to reset or since + /// the Clock was created. + unsigned long int getTimeMicroseconds() + { +#ifdef USE_WINDOWS_TIMERS + LARGE_INTEGER currentTime; + QueryPerformanceCounter(¤tTime); + LONGLONG elapsedTime = currentTime.QuadPart - + mStartTime.QuadPart; + + // Compute the number of millisecond ticks elapsed. + unsigned long msecTicks = (unsigned long)(1000 * elapsedTime / + mClockFrequency.QuadPart); + + // Check for unexpected leaps in the Win32 performance counter. + // (This is caused by unexpected data across the PCI to ISA + // bridge, aka south bridge. See Microsoft KB274323.) + unsigned long elapsedTicks = GetTickCount() - mStartTick; + signed long msecOff = (signed long)(msecTicks - elapsedTicks); + if (msecOff < -100 || msecOff > 100) + { + // Adjust the starting time forwards. + LONGLONG msecAdjustment = mymin(msecOff * + mClockFrequency.QuadPart / 1000, elapsedTime - + mPrevElapsedTime); + mStartTime.QuadPart += msecAdjustment; + elapsedTime -= msecAdjustment; + } + + // Store the current elapsed time for adjustments next time. + mPrevElapsedTime = elapsedTime; + + // Convert to microseconds. + unsigned long usecTicks = (unsigned long)(1000000 * elapsedTime / + mClockFrequency.QuadPart); + + return usecTicks; +#else + +#ifdef __CELLOS_LV2__ + __int64 freq=sys_time_get_timebase_frequency(); + double dFreq=((double) freq)/ 1000000.0; + typedef uint64_t __int64; + typedef __int64 ClockSize; + ClockSize newTime; + __asm __volatile__( "mftb %0" : "=r" (newTime) : : "memory"); + + return (newTime-mStartTime) / dFreq; +#else + + struct timeval currentTime; + gettimeofday(¤tTime, 0); + return (currentTime.tv_sec - mStartTime.tv_sec) * 1000000 + + (currentTime.tv_usec - mStartTime.tv_usec); +#endif//__CELLOS_LV2__ +#endif + } + + private: +#ifdef USE_WINDOWS_TIMERS + LARGE_INTEGER mClockFrequency; + DWORD mStartTick; + LONGLONG mPrevElapsedTime; + LARGE_INTEGER mStartTime; +#else +#ifdef __CELLOS_LV2__ + uint64_t mStartTime; +#else + struct timeval mStartTime; +#endif +#endif //__CELLOS_LV2__ + + }; + +#endif //USE_BT_CLOCK + + +#ifdef USE_QUICKPROF + + +#include +#include +#include +#include + + + + +namespace hidden +{ + /// A simple data structure representing a single timed block + /// of code. + struct ProfileBlock + { + ProfileBlock() + { + currentBlockStartMicroseconds = 0; + currentCycleTotalMicroseconds = 0; + lastCycleTotalMicroseconds = 0; + totalMicroseconds = 0; + } + + /// The starting time (in us) of the current block update. + unsigned long int currentBlockStartMicroseconds; + + /// The accumulated time (in us) spent in this block during the + /// current profiling cycle. + unsigned long int currentCycleTotalMicroseconds; + + /// The accumulated time (in us) spent in this block during the + /// past profiling cycle. + unsigned long int lastCycleTotalMicroseconds; + + /// The total accumulated time (in us) spent in this block. + unsigned long int totalMicroseconds; + }; + +}; + +/// A static class that manages timing for a set of profiling blocks. +class btProfiler +{ +public: + /// A set of ways to retrieve block timing data. + enum BlockTimingMethod + { + /// The total time spent in the block (in seconds) since the + /// profiler was initialized. + BLOCK_TOTAL_SECONDS, + + /// The total time spent in the block (in ms) since the + /// profiler was initialized. + BLOCK_TOTAL_MILLISECONDS, + + /// The total time spent in the block (in us) since the + /// profiler was initialized. + BLOCK_TOTAL_MICROSECONDS, + + /// The total time spent in the block, as a % of the total + /// elapsed time since the profiler was initialized. + BLOCK_TOTAL_PERCENT, + + /// The time spent in the block (in seconds) in the most recent + /// profiling cycle. + BLOCK_CYCLE_SECONDS, + + /// The time spent in the block (in ms) in the most recent + /// profiling cycle. + BLOCK_CYCLE_MILLISECONDS, + + /// The time spent in the block (in us) in the most recent + /// profiling cycle. + BLOCK_CYCLE_MICROSECONDS, + + /// The time spent in the block (in seconds) in the most recent + /// profiling cycle, as a % of the total cycle time. + BLOCK_CYCLE_PERCENT + }; + + /// Initializes the profiler. This must be called first. If this is + /// never called, the profiler is effectively disabled; all other + /// functions will return immediately. The first parameter + /// is the name of an output data file; if this string is not empty, + /// data will be saved on every profiling cycle; if this string is + /// empty, no data will be saved to a file. The second parameter + /// determines which timing method is used when printing data to the + /// output file. + inline static void init(const std::string outputFilename="", + BlockTimingMethod outputMethod=BLOCK_CYCLE_MILLISECONDS); + + /// Cleans up allocated memory. + inline static void destroy(); + + /// Begins timing the named block of code. + inline static void beginBlock(const std::string& name); + + /// Updates the accumulated time spent in the named block by adding + /// the elapsed time since the last call to startBlock for this block + /// name. + inline static void endBlock(const std::string& name); + + /// Returns the time spent in the named block according to the + /// given timing method. See comments on BlockTimingMethod for details. + inline static double getBlockTime(const std::string& name, + BlockTimingMethod method=BLOCK_CYCLE_MILLISECONDS); + + /// Defines the end of a profiling cycle. Use this regularly if you + /// want to generate detailed timing information. This must not be + /// called within a timing block. + inline static void endProfilingCycle(); + + /// A helper function that creates a string of statistics for + /// each timing block. This is mainly for printing an overall + /// summary to the command line. + inline static std::string createStatsString( + BlockTimingMethod method=BLOCK_TOTAL_PERCENT); + +//private: + inline btProfiler(); + + inline ~btProfiler(); + + /// Prints an error message to standard output. + inline static void printError(const std::string& msg) + { + //btAssert(0); + std::cout << "[QuickProf error] " << msg << std::endl; + } + + /// Determines whether the profiler is enabled. + static bool mEnabled; + + /// The clock used to time profile blocks. + static btClock mClock; + + /// The starting time (in us) of the current profiling cycle. + static unsigned long int mCurrentCycleStartMicroseconds; + + /// The duration (in us) of the most recent profiling cycle. + static unsigned long int mLastCycleDurationMicroseconds; + + /// Internal map of named profile blocks. + static std::map mProfileBlocks; + + /// The data file used if this feature is enabled in 'init.' + static std::ofstream mOutputFile; + + /// Tracks whether we have begun print data to the output file. + static bool mFirstFileOutput; + + /// The method used when printing timing data to an output file. + static BlockTimingMethod mFileOutputMethod; + + /// The number of the current profiling cycle. + static unsigned long int mCycleNumber; +}; + + +btProfiler::btProfiler() +{ + // This never gets called because a btProfiler instance is never + // created. +} + +btProfiler::~btProfiler() +{ + // This never gets called because a btProfiler instance is never + // created. +} + +void btProfiler::init(const std::string outputFilename, + BlockTimingMethod outputMethod) +{ + mEnabled = true; + + if (!outputFilename.empty()) + { + mOutputFile.open(outputFilename.c_str()); + } + + mFileOutputMethod = outputMethod; + + mClock.reset(); + + // Set the start time for the first cycle. + mCurrentCycleStartMicroseconds = mClock.getTimeMicroseconds(); +} + +void btProfiler::destroy() +{ + if (!mEnabled) + { + return; + } + + if (mOutputFile.is_open()) + { + mOutputFile.close(); + } + + // Destroy all ProfileBlocks. + while (!mProfileBlocks.empty()) + { + delete (*mProfileBlocks.begin()).second; + mProfileBlocks.erase(mProfileBlocks.begin()); + } +} + +void btProfiler::beginBlock(const std::string& name) +{ + if (!mEnabled) + { + return; + } + + if (name.empty()) + { + printError("Cannot allow unnamed profile blocks."); + return; + } + + hidden::ProfileBlock* block = mProfileBlocks[name]; + + if (!block) + { + // Create a new ProfileBlock. + mProfileBlocks[name] = new hidden::ProfileBlock(); + block = mProfileBlocks[name]; + } + + // We do this at the end to get more accurate results. + block->currentBlockStartMicroseconds = mClock.getTimeMicroseconds(); +} + +void btProfiler::endBlock(const std::string& name) +{ + if (!mEnabled) + { + return; + } + + // We do this at the beginning to get more accurate results. + unsigned long int endTick = mClock.getTimeMicroseconds(); + + hidden::ProfileBlock* block = mProfileBlocks[name]; + + if (!block) + { + // The named block does not exist. Print an error. + printError("The profile block named '" + name + + "' does not exist."); + return; + } + + unsigned long int blockDuration = endTick - + block->currentBlockStartMicroseconds; + block->currentCycleTotalMicroseconds += blockDuration; + block->totalMicroseconds += blockDuration; +} + +double btProfiler::getBlockTime(const std::string& name, + BlockTimingMethod method) +{ + if (!mEnabled) + { + return 0; + } + + hidden::ProfileBlock* block = mProfileBlocks[name]; + + if (!block) + { + // The named block does not exist. Print an error. + printError("The profile block named '" + name + + "' does not exist."); + return 0; + } + + double result = 0; + + switch(method) + { + case BLOCK_TOTAL_SECONDS: + result = (double)block->totalMicroseconds * (double)0.000001; + break; + case BLOCK_TOTAL_MILLISECONDS: + result = (double)block->totalMicroseconds * (double)0.001; + break; + case BLOCK_TOTAL_MICROSECONDS: + result = (double)block->totalMicroseconds; + break; + case BLOCK_TOTAL_PERCENT: + { + double timeSinceInit = (double)mClock.getTimeMicroseconds(); + if (timeSinceInit <= 0) + { + result = 0; + } + else + { + result = 100.0 * (double)block->totalMicroseconds / + timeSinceInit; + } + break; + } + case BLOCK_CYCLE_SECONDS: + result = (double)block->lastCycleTotalMicroseconds * + (double)0.000001; + break; + case BLOCK_CYCLE_MILLISECONDS: + result = (double)block->lastCycleTotalMicroseconds * + (double)0.001; + break; + case BLOCK_CYCLE_MICROSECONDS: + result = (double)block->lastCycleTotalMicroseconds; + break; + case BLOCK_CYCLE_PERCENT: + { + if (0 == mLastCycleDurationMicroseconds) + { + // We have not yet finished a cycle, so just return zero + // percent to avoid a divide by zero error. + result = 0; + } + else + { + result = 100.0 * (double)block->lastCycleTotalMicroseconds / + mLastCycleDurationMicroseconds; + } + break; + } + default: + break; + } + + return result; +} + +void btProfiler::endProfilingCycle() +{ + if (!mEnabled) + { + return; + } + + // Store the duration of the cycle that just finished. + mLastCycleDurationMicroseconds = mClock.getTimeMicroseconds() - + mCurrentCycleStartMicroseconds; + + // For each block, update data for the cycle that just finished. + std::map::iterator iter; + for (iter = mProfileBlocks.begin(); iter != mProfileBlocks.end(); ++iter) + { + hidden::ProfileBlock* block = (*iter).second; + block->lastCycleTotalMicroseconds = + block->currentCycleTotalMicroseconds; + block->currentCycleTotalMicroseconds = 0; + } + + if (mOutputFile.is_open()) + { + // Print data to the output file. + if (mFirstFileOutput) + { + // On the first iteration, print a header line that shows the + // names of each profiling block. + mOutputFile << "#cycle, "; + + for (iter = mProfileBlocks.begin(); iter != mProfileBlocks.end(); + ++iter) + { + mOutputFile << (*iter).first << ", "; + } + + mOutputFile << std::endl; + mFirstFileOutput = false; + } + + mOutputFile << mCycleNumber << ", "; + + for (iter = mProfileBlocks.begin(); iter != mProfileBlocks.end(); + ++iter) + { + mOutputFile << getBlockTime((*iter).first, mFileOutputMethod) + << ", "; + } + + mOutputFile << std::endl; + } + + ++mCycleNumber; + mCurrentCycleStartMicroseconds = mClock.getTimeMicroseconds(); +} + +std::string btProfiler::createStatsString(BlockTimingMethod method) +{ + if (!mEnabled) + { + return ""; + } + + std::string s; + std::string suffix; + + switch(method) + { + case BLOCK_TOTAL_SECONDS: + suffix = "s"; + break; + case BLOCK_TOTAL_MILLISECONDS: + suffix = "ms"; + break; + case BLOCK_TOTAL_MICROSECONDS: + suffix = "us"; + break; + case BLOCK_TOTAL_PERCENT: + { + suffix = "%"; + break; + } + case BLOCK_CYCLE_SECONDS: + suffix = "s"; + break; + case BLOCK_CYCLE_MILLISECONDS: + suffix = "ms"; + break; + case BLOCK_CYCLE_MICROSECONDS: + suffix = "us"; + break; + case BLOCK_CYCLE_PERCENT: + { + suffix = "%"; + break; + } + default: + break; + } + + std::map::iterator iter; + for (iter = mProfileBlocks.begin(); iter != mProfileBlocks.end(); ++iter) + { + if (iter != mProfileBlocks.begin()) + { + s += "\n"; + } + + char blockTime[64]; + sprintf(blockTime, "%lf", getBlockTime((*iter).first, method)); + + s += (*iter).first; + s += ": "; + s += blockTime; + s += " "; + s += suffix; + } + + return s; +} + + +#define BEGIN_PROFILE(a) btProfiler::beginBlock(a) +#define END_PROFILE(a) btProfiler::endBlock(a) + +#else //USE_QUICKPROF +#define BEGIN_PROFILE(a) +#define END_PROFILE(a) + +#endif //USE_QUICKPROF + +#endif //QUICK_PROF_H + + diff --git a/bullet/src/LinearMath/btRandom.h b/bullet/src/LinearMath/btRandom.h new file mode 100644 index 0000000..ebd6c39 --- /dev/null +++ b/bullet/src/LinearMath/btRandom.h @@ -0,0 +1,42 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / 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. +*/ + + + +#ifndef GEN_RANDOM_H +#define GEN_RANDOM_H + +#ifdef MT19937 + +#include +#include + +#define GEN_RAND_MAX UINT_MAX + +SIMD_FORCE_INLINE void GEN_srand(unsigned int seed) { init_genrand(seed); } +SIMD_FORCE_INLINE unsigned int GEN_rand() { return genrand_int32(); } + +#else + +#include + +#define GEN_RAND_MAX RAND_MAX + +SIMD_FORCE_INLINE void GEN_srand(unsigned int seed) { srand(seed); } +SIMD_FORCE_INLINE unsigned int GEN_rand() { return rand(); } + +#endif + +#endif + diff --git a/bullet/src/LinearMath/btScalar.h b/bullet/src/LinearMath/btScalar.h new file mode 100644 index 0000000..3b6409c --- /dev/null +++ b/bullet/src/LinearMath/btScalar.h @@ -0,0 +1,192 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / 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. +*/ + + + +#ifndef SIMD___SCALAR_H +#define SIMD___SCALAR_H + +#include + +#include +#include +#include + +#ifdef WIN32 + + #if defined(__MINGW32__) || defined(__CYGWIN__) || (defined (_MSC_VER) && _MSC_VER < 1300) + #define SIMD_FORCE_INLINE inline + #define ATTRIBUTE_ALIGNED16(a) a + #else + #define BT_HAS_ALIGNED_ALOCATOR + #pragma warning(disable:4530) + #pragma warning(disable:4996) + #pragma warning(disable:4786) + #define SIMD_FORCE_INLINE __forceinline + #define ATTRIBUTE_ALIGNED16(a) __declspec(align(16)) a + #ifdef _XBOX + #define BT_USE_VMX128 + + #include + #define BT_HAVE_NATIVE_FSEL + #define btFsel(a,b,c) __fsel((a),(b),(c)) + #else + #define BT_USE_SSE + #endif + #endif //__MINGW32__ + + #include + #define btAssert assert + //btFullAssert is optional, slows down a lot + #define btFullAssert(x) +#else + +#if defined (__CELLOS_LV2__) + #define SIMD_FORCE_INLINE inline + #define ATTRIBUTE_ALIGNED16(a) a __attribute__ ((aligned (16))) + #ifndef assert + #include + #endif + #define btAssert assert + //btFullAssert is optional, slows down a lot + #define btFullAssert(x) +#else + + //non-windows systems + + #define SIMD_FORCE_INLINE inline + #define ATTRIBUTE_ALIGNED16(a) a + #ifndef assert + #include + #endif + #define btAssert assert + //btFullAssert is optional, slows down a lot + #define btFullAssert(x) +#endif //__CELLOS_LV2__ +#endif + +/// older compilers (gcc 3.x) and Sun needs double version of sqrt etc. +/// exclude Apple Intel (i's assumed to be a Macbook or new Intel Dual Core Processor) +#if defined (__sun) || defined (__sun__) || defined (__sparc) || (defined (__APPLE__) && ! defined (__i386__)) +//use slow double float precision operation on those platforms +#ifndef BT_USE_DOUBLE_PRECISION +#define BT_FORCE_DOUBLE_FUNCTIONS +#endif +#endif + +#if defined(BT_USE_DOUBLE_PRECISION) +typedef double btScalar; +#else +typedef float btScalar; +#endif + + +#if defined(BT_USE_DOUBLE_PRECISION) || defined(BT_FORCE_DOUBLE_FUNCTIONS) + +SIMD_FORCE_INLINE btScalar btSqrt(btScalar x) { return sqrt(x); } +SIMD_FORCE_INLINE btScalar btFabs(btScalar x) { return fabs(x); } +SIMD_FORCE_INLINE btScalar btCos(btScalar x) { return cos(x); } +SIMD_FORCE_INLINE btScalar btSin(btScalar x) { return sin(x); } +SIMD_FORCE_INLINE btScalar btTan(btScalar x) { return tan(x); } +SIMD_FORCE_INLINE btScalar btAcos(btScalar x) { return acos(x); } +SIMD_FORCE_INLINE btScalar btAsin(btScalar x) { return asin(x); } +SIMD_FORCE_INLINE btScalar btAtan(btScalar x) { return atan(x); } +SIMD_FORCE_INLINE btScalar btAtan2(btScalar x, btScalar y) { return atan2(x, y); } +SIMD_FORCE_INLINE btScalar btExp(btScalar x) { return exp(x); } +SIMD_FORCE_INLINE btScalar btLog(btScalar x) { return log(x); } +SIMD_FORCE_INLINE btScalar btPow(btScalar x,btScalar y) { return pow(x,y); } + +#else + +SIMD_FORCE_INLINE btScalar btSqrt(btScalar x) { return sqrtf(x); } +SIMD_FORCE_INLINE btScalar btFabs(btScalar x) { return fabsf(x); } +SIMD_FORCE_INLINE btScalar btCos(btScalar x) { return cosf(x); } +SIMD_FORCE_INLINE btScalar btSin(btScalar x) { return sinf(x); } +SIMD_FORCE_INLINE btScalar btTan(btScalar x) { return tanf(x); } +SIMD_FORCE_INLINE btScalar btAcos(btScalar x) { return acosf(x); } +SIMD_FORCE_INLINE btScalar btAsin(btScalar x) { return asinf(x); } +SIMD_FORCE_INLINE btScalar btAtan(btScalar x) { return atanf(x); } +SIMD_FORCE_INLINE btScalar btAtan2(btScalar x, btScalar y) { return atan2f(x, y); } +SIMD_FORCE_INLINE btScalar btExp(btScalar x) { return expf(x); } +SIMD_FORCE_INLINE btScalar btLog(btScalar x) { return logf(x); } +SIMD_FORCE_INLINE btScalar btPow(btScalar x,btScalar y) { return powf(x,y); } + +#endif + +#define SIMD_2_PI btScalar(6.283185307179586232) +#define SIMD_PI (SIMD_2_PI * btScalar(0.5)) +#define SIMD_HALF_PI (SIMD_2_PI * btScalar(0.25)) +#define SIMD_RADS_PER_DEG (SIMD_2_PI / btScalar(360.0)) +#define SIMD_DEGS_PER_RAD (btScalar(360.0) / SIMD_2_PI) + +#ifdef BT_USE_DOUBLE_PRECISION +#define SIMD_EPSILON DBL_EPSILON +#define SIMD_INFINITY DBL_MAX +#else +#define SIMD_EPSILON FLT_EPSILON +#define SIMD_INFINITY FLT_MAX +#endif + +SIMD_FORCE_INLINE btScalar btAtan2Fast(btScalar y, btScalar x) +{ + btScalar coeff_1 = SIMD_PI / 4.0f; + btScalar coeff_2 = 3.0f * coeff_1; + btScalar abs_y = btFabs(y); + btScalar angle; + if (x >= 0.0f) { + btScalar r = (x - abs_y) / (x + abs_y); + angle = coeff_1 - coeff_1 * r; + } else { + btScalar r = (x + abs_y) / (abs_y - x); + angle = coeff_2 - coeff_1 * r; + } + return (y < 0.0f) ? -angle : angle; +} + +SIMD_FORCE_INLINE bool btFuzzyZero(btScalar x) { return btFabs(x) < SIMD_EPSILON; } + +SIMD_FORCE_INLINE bool btEqual(btScalar a, btScalar eps) { + return (((a) <= eps) && !((a) < -eps)); +} +SIMD_FORCE_INLINE bool btGreaterEqual (btScalar a, btScalar eps) { + return (!((a) <= eps)); +} + +/*SIMD_FORCE_INLINE btScalar btCos(btScalar x) { return cosf(x); } +SIMD_FORCE_INLINE btScalar btSin(btScalar x) { return sinf(x); } +SIMD_FORCE_INLINE btScalar btTan(btScalar x) { return tanf(x); } +SIMD_FORCE_INLINE btScalar btAcos(btScalar x) { return acosf(x); } +SIMD_FORCE_INLINE btScalar btAsin(btScalar x) { return asinf(x); } +SIMD_FORCE_INLINE btScalar btAtan(btScalar x) { return atanf(x); } +SIMD_FORCE_INLINE btScalar btAtan2(btScalar x, btScalar y) { return atan2f(x, y); } +*/ + +SIMD_FORCE_INLINE int btIsNegative(btScalar x) { + return x < btScalar(0.0) ? 1 : 0; +} + +SIMD_FORCE_INLINE btScalar btRadians(btScalar x) { return x * SIMD_RADS_PER_DEG; } +SIMD_FORCE_INLINE btScalar btDegrees(btScalar x) { return x * SIMD_DEGS_PER_RAD; } + +#define BT_DECLARE_HANDLE(name) typedef struct name##__ { int unused; } *name + +#ifndef btFsel +SIMD_FORCE_INLINE btScalar btFsel(btScalar a, btScalar b, btScalar c) +{ + return a >= 0 ? b : c; +} +#endif +#define btFsels(a,b,c) (btScalar)btFsel(a,b,c) + +#endif //SIMD___SCALAR_H diff --git a/bullet/src/LinearMath/btSimdMinMax.h b/bullet/src/LinearMath/btSimdMinMax.h new file mode 100644 index 0000000..550746a --- /dev/null +++ b/bullet/src/LinearMath/btSimdMinMax.h @@ -0,0 +1,76 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / 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. +*/ + + + +#ifndef SIMD_MINMAX_H +#define SIMD_MINMAX_H +#include "btScalar.h" + +template +SIMD_FORCE_INLINE const T& btMin(const T& a, const T& b) { + return b < a ? b : a; +} + +template +SIMD_FORCE_INLINE const T& btMax(const T& a, const T& b) { + return a < b ? b : a; +} + +template +SIMD_FORCE_INLINE void btSetMin(T& a, const T& b) { + if (a > b) a = b; +} + +template +SIMD_FORCE_INLINE void btSetMax(T& a, const T& b) { + if (a < b) a = b; +} + +// Specialize on float/double for platforms that have btFsel natively +#ifdef BT_HAVE_NATIVE_FSEL +SIMD_FORCE_INLINE float btMin( float a, float b) { + return (float)btFsel((a-b), b, a); +} + +SIMD_FORCE_INLINE float btMax( float a, float b) { + return (float)btFsel((a-b), a, b); +} + +SIMD_FORCE_INLINE void btSetMin(float& a, float b) { + a = (float)btFsel((a-b), b, a); +} + +SIMD_FORCE_INLINE void btSetMax(float& a, float b) { + a = (float)btFsel((a-b), a, b); +} + +SIMD_FORCE_INLINE double btMin( double a, double b) { + return btFsel((a-b), b, a); +} + +SIMD_FORCE_INLINE double btMax( double a, double b) { + return btFsel((a-b), a, b); +} + +SIMD_FORCE_INLINE void btSetMin(double& a, double b) { + a = btFsel((a-b), b, a); +} + +SIMD_FORCE_INLINE void btSetMax(double& a, double b) { + a = btFsel((a-b), a, b); +} +#endif + +#endif diff --git a/bullet/src/LinearMath/btStackAlloc.h b/bullet/src/LinearMath/btStackAlloc.h new file mode 100644 index 0000000..8a3eed6 --- /dev/null +++ b/bullet/src/LinearMath/btStackAlloc.h @@ -0,0 +1,106 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / 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. +*/ + +/* +StackAlloc extracted from GJK-EPA collision solver by Nathanael Presson +Nov.2006 +*/ + +#ifndef BT_STACK_ALLOC +#define BT_STACK_ALLOC + +#include "btScalar.h" //for btAssert + +struct btBlock +{ + btBlock* previous; + unsigned char* address; +}; + +///StackAlloc provides some fast stack-based memory allocator (LIFO last-in first-out) +class btStackAlloc +{ +public: + + btStackAlloc(unsigned int size) { ctor();create(size); } + ~btStackAlloc() { destroy(); } + + inline void create(unsigned int size) + { + destroy(); + data = new unsigned char[size]; + totalsize = size; + } + inline void destroy() + { + btAssert(usedsize==0); + //Raise(L"StackAlloc is still in use"); + + if(usedsize==0) + { + if(!ischild) delete[] data; + data = 0; + usedsize = 0; + } + + } + unsigned char* allocate(unsigned int size) + { + const unsigned int nus(usedsize+size); + if(nusprevious = current; + pb->address = data+usedsize; + current = pb; + return(pb); + } + inline void endBlock(btBlock* block) + { + btAssert(block==current); + //Raise(L"Unmatched blocks"); + if(block==current) + { + current = block->previous; + usedsize = (unsigned int)((block->address-data)-sizeof(btBlock)); + } + } + +private: + void ctor() + { + data = 0; + totalsize = 0; + usedsize = 0; + current = 0; + ischild = false; + } + unsigned char* data; + unsigned int totalsize; + unsigned int usedsize; + btBlock* current; + bool ischild; +}; + +#endif //BT_STACK_ALLOC diff --git a/bullet/src/LinearMath/btTransform.h b/bullet/src/LinearMath/btTransform.h new file mode 100644 index 0000000..2e95e99 --- /dev/null +++ b/bullet/src/LinearMath/btTransform.h @@ -0,0 +1,206 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / 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. +*/ + + + +#ifndef btTransform_H +#define btTransform_H + +#include "btVector3.h" +#include "btMatrix3x3.h" + + +///btTransform supports rigid transforms (only translation and rotation, no scaling/shear) +class btTransform { + + +public: + + + btTransform() {} + + explicit SIMD_FORCE_INLINE btTransform(const btQuaternion& q, + const btVector3& c = btVector3(btScalar(0), btScalar(0), btScalar(0))) + : m_basis(q), + m_origin(c) + {} + + explicit SIMD_FORCE_INLINE btTransform(const btMatrix3x3& b, + const btVector3& c = btVector3(btScalar(0), btScalar(0), btScalar(0))) + : m_basis(b), + m_origin(c) + {} + + SIMD_FORCE_INLINE btTransform (const btTransform& other) + : m_basis(other.m_basis), + m_origin(other.m_origin) + { + } + + SIMD_FORCE_INLINE btTransform& operator=(const btTransform& other) + { + m_basis = other.m_basis; + m_origin = other.m_origin; + return *this; + } + + + SIMD_FORCE_INLINE void mult(const btTransform& t1, const btTransform& t2) { + m_basis = t1.m_basis * t2.m_basis; + m_origin = t1(t2.m_origin); + } + +/* void multInverseLeft(const btTransform& t1, const btTransform& t2) { + btVector3 v = t2.m_origin - t1.m_origin; + m_basis = btMultTransposeLeft(t1.m_basis, t2.m_basis); + m_origin = v * t1.m_basis; + } + */ + + + SIMD_FORCE_INLINE btVector3 operator()(const btVector3& x) const + { + return btVector3(m_basis[0].dot(x) + m_origin.x(), + m_basis[1].dot(x) + m_origin.y(), + m_basis[2].dot(x) + m_origin.z()); + } + + SIMD_FORCE_INLINE btVector3 operator*(const btVector3& x) const + { + return (*this)(x); + } + + SIMD_FORCE_INLINE btMatrix3x3& getBasis() { return m_basis; } + SIMD_FORCE_INLINE const btMatrix3x3& getBasis() const { return m_basis; } + + SIMD_FORCE_INLINE btVector3& getOrigin() { return m_origin; } + SIMD_FORCE_INLINE const btVector3& getOrigin() const { return m_origin; } + + btQuaternion getRotation() const { + btQuaternion q; + m_basis.getRotation(q); + return q; + } + template + void setValue(const Scalar2 *m) + { + m_basis.setValue(m); + m_origin.setValue(&m[12]); + } + + + void setFromOpenGLMatrix(const btScalar *m) + { + m_basis.setFromOpenGLSubMatrix(m); + m_origin.setValue(m[12],m[13],m[14]); + } + + void getOpenGLMatrix(btScalar *m) const + { + m_basis.getOpenGLSubMatrix(m); + m[12] = m_origin.x(); + m[13] = m_origin.y(); + m[14] = m_origin.z(); + m[15] = btScalar(1.0); + } + + SIMD_FORCE_INLINE void setOrigin(const btVector3& origin) + { + m_origin = origin; + } + + SIMD_FORCE_INLINE btVector3 invXform(const btVector3& inVec) const; + + + + SIMD_FORCE_INLINE void setBasis(const btMatrix3x3& basis) + { + m_basis = basis; + } + + SIMD_FORCE_INLINE void setRotation(const btQuaternion& q) + { + m_basis.setRotation(q); + } + + + + void setIdentity() + { + m_basis.setIdentity(); + m_origin.setValue(btScalar(0.0), btScalar(0.0), btScalar(0.0)); + } + + + btTransform& operator*=(const btTransform& t) + { + m_origin += m_basis * t.m_origin; + m_basis *= t.m_basis; + return *this; + } + + btTransform inverse() const + { + btMatrix3x3 inv = m_basis.transpose(); + return btTransform(inv, inv * -m_origin); + } + + btTransform inverseTimes(const btTransform& t) const; + + btTransform operator*(const btTransform& t) const; + + static btTransform getIdentity() + { + btTransform tr; + tr.setIdentity(); + return tr; + } + +private: + + btMatrix3x3 m_basis; + btVector3 m_origin; +}; + + +SIMD_FORCE_INLINE btVector3 +btTransform::invXform(const btVector3& inVec) const +{ + btVector3 v = inVec - m_origin; + return (m_basis.transpose() * v); +} + +SIMD_FORCE_INLINE btTransform +btTransform::inverseTimes(const btTransform& t) const +{ + btVector3 v = t.getOrigin() - m_origin; + return btTransform(m_basis.transposeTimes(t.m_basis), + v * m_basis); +} + +SIMD_FORCE_INLINE btTransform +btTransform::operator*(const btTransform& t) const +{ + return btTransform(m_basis * t.m_basis, + (*this)(t.m_origin)); +} + + + +#endif + + + + + diff --git a/bullet/src/LinearMath/btTransformUtil.h b/bullet/src/LinearMath/btTransformUtil.h new file mode 100644 index 0000000..055b2ea --- /dev/null +++ b/bullet/src/LinearMath/btTransformUtil.h @@ -0,0 +1,138 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / 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. +*/ + + +#ifndef SIMD_TRANSFORM_UTIL_H +#define SIMD_TRANSFORM_UTIL_H + +#include "btTransform.h" +#define ANGULAR_MOTION_THRESHOLD btScalar(0.5)*SIMD_HALF_PI + + + +#define SIMDSQRT12 btScalar(0.7071067811865475244008443621048490) + +#define btRecipSqrt(x) ((btScalar)(btScalar(1.0)/btSqrt(btScalar(x)))) /* reciprocal square root */ + +inline btVector3 btAabbSupport(const btVector3& halfExtents,const btVector3& supportDir) +{ + return btVector3(supportDir.x() < btScalar(0.0) ? -halfExtents.x() : halfExtents.x(), + supportDir.y() < btScalar(0.0) ? -halfExtents.y() : halfExtents.y(), + supportDir.z() < btScalar(0.0) ? -halfExtents.z() : halfExtents.z()); +} + + +inline void btPlaneSpace1 (const btVector3& n, btVector3& p, btVector3& q) +{ + if (btFabs(n.z()) > SIMDSQRT12) { + // choose p in y-z plane + btScalar a = n[1]*n[1] + n[2]*n[2]; + btScalar k = btRecipSqrt (a); + p.setValue(0,-n[2]*k,n[1]*k); + // set q = n x p + q.setValue(a*k,-n[0]*p[2],n[0]*p[1]); + } + else { + // choose p in x-y plane + btScalar a = n.x()*n.x() + n.y()*n.y(); + btScalar k = btRecipSqrt (a); + p.setValue(-n.y()*k,n.x()*k,0); + // set q = n x p + q.setValue(-n.z()*p.y(),n.z()*p.x(),a*k); + } +} + + + +/// Utils related to temporal transforms +class btTransformUtil +{ + +public: + + static void integrateTransform(const btTransform& curTrans,const btVector3& linvel,const btVector3& angvel,btScalar timeStep,btTransform& predictedTransform) + { + predictedTransform.setOrigin(curTrans.getOrigin() + linvel * timeStep); +// #define QUATERNION_DERIVATIVE + #ifdef QUATERNION_DERIVATIVE + btQuaternion predictedOrn = curTrans.getRotation(); + predictedOrn += (angvel * predictedOrn) * (timeStep * btScalar(0.5)); + predictedOrn.normalize(); + #else + //exponential map + btVector3 axis; + btScalar fAngle = angvel.length(); + //limit the angular motion + if (fAngle*timeStep > ANGULAR_MOTION_THRESHOLD) + { + fAngle = ANGULAR_MOTION_THRESHOLD / timeStep; + } + + if ( fAngle < btScalar(0.001) ) + { + // use Taylor's expansions of sync function + axis = angvel*( btScalar(0.5)*timeStep-(timeStep*timeStep*timeStep)*(btScalar(0.020833333333))*fAngle*fAngle ); + } + else + { + // sync(fAngle) = sin(c*fAngle)/t + axis = angvel*( btSin(btScalar(0.5)*fAngle*timeStep)/fAngle ); + } + btQuaternion dorn (axis.x(),axis.y(),axis.z(),btCos( fAngle*timeStep*btScalar(0.5) )); + btQuaternion orn0 = curTrans.getRotation(); + + btQuaternion predictedOrn = dorn * orn0; + predictedOrn.normalize(); + #endif + predictedTransform.setRotation(predictedOrn); + } + + static void calculateVelocity(const btTransform& transform0,const btTransform& transform1,btScalar timeStep,btVector3& linVel,btVector3& angVel) + { + linVel = (transform1.getOrigin() - transform0.getOrigin()) / timeStep; + btVector3 axis; + btScalar angle; + calculateDiffAxisAngle(transform0,transform1,axis,angle); + angVel = axis * angle / timeStep; + } + + static void calculateDiffAxisAngle(const btTransform& transform0,const btTransform& transform1,btVector3& axis,btScalar& angle) + { + + #ifdef USE_QUATERNION_DIFF + btQuaternion orn0 = transform0.getRotation(); + btQuaternion orn1a = transform1.getRotation(); + btQuaternion orn1 = orn0.farthest(orn1a); + btQuaternion dorn = orn1 * orn0.inverse(); +#else + btMatrix3x3 dmat = transform1.getBasis() * transform0.getBasis().inverse(); + btQuaternion dorn; + dmat.getRotation(dorn); +#endif//USE_QUATERNION_DIFF + + angle = dorn.getAngle(); + axis = btVector3(dorn.x(),dorn.y(),dorn.z()); + axis[3] = btScalar(0.); + //check for axis length + btScalar len = axis.length2(); + if (len < SIMD_EPSILON*SIMD_EPSILON) + axis = btVector3(btScalar(1.),btScalar(0.),btScalar(0.)); + else + axis /= btSqrt(len); + } + +}; + +#endif //SIMD_TRANSFORM_UTIL_H + diff --git a/bullet/src/LinearMath/btVector3.h b/bullet/src/LinearMath/btVector3.h new file mode 100644 index 0000000..53746b5 --- /dev/null +++ b/bullet/src/LinearMath/btVector3.h @@ -0,0 +1,406 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / 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. +*/ + + + +#ifndef SIMD__VECTOR3_H +#define SIMD__VECTOR3_H + +#include "btQuadWord.h" + +///btVector3 can be used to represent 3D points and vectors. +///It has an un-used w component to suit 16-byte alignment when btVector3 is stored in containers. This extra component can be used by derived classes (Quaternion?) or by user +///Ideally, this class should be replaced by a platform optimized SIMD version that keeps the data in registers +class btVector3 : public btQuadWord { + +public: + SIMD_FORCE_INLINE btVector3() {} + + SIMD_FORCE_INLINE btVector3(const btQuadWordStorage& q) + : btQuadWord(q) + { + } + + + SIMD_FORCE_INLINE btVector3(const btScalar& x, const btScalar& y, const btScalar& z) + :btQuadWord(x,y,z,btScalar(0.)) + { + } + +// SIMD_FORCE_INLINE btVector3(const btScalar& x, const btScalar& y, const btScalar& z,const btScalar& w) +// : btQuadWord(x,y,z,w) +// { +// } + + + + SIMD_FORCE_INLINE btVector3& operator+=(const btVector3& v) + { + m_x += v.x(); m_y += v.y(); m_z += v.z(); + return *this; + } + + + + SIMD_FORCE_INLINE btVector3& operator-=(const btVector3& v) + { + m_x -= v.x(); m_y -= v.y(); m_z -= v.z(); + return *this; + } + + SIMD_FORCE_INLINE btVector3& operator*=(const btScalar& s) + { + m_x *= s; m_y *= s; m_z *= s; + return *this; + } + + SIMD_FORCE_INLINE btVector3& operator/=(const btScalar& s) + { + btFullAssert(s != btScalar(0.0)); + return *this *= btScalar(1.0) / s; + } + + SIMD_FORCE_INLINE btScalar dot(const btVector3& v) const + { + return m_x * v.x() + m_y * v.y() + m_z * v.z(); + } + + SIMD_FORCE_INLINE btScalar length2() const + { + return dot(*this); + } + + SIMD_FORCE_INLINE btScalar length() const + { + return btSqrt(length2()); + } + + SIMD_FORCE_INLINE btScalar distance2(const btVector3& v) const; + + SIMD_FORCE_INLINE btScalar distance(const btVector3& v) const; + + SIMD_FORCE_INLINE btVector3& normalize() + { + return *this /= length(); + } + + SIMD_FORCE_INLINE btVector3 normalized() const; + + SIMD_FORCE_INLINE btVector3 rotate( const btVector3& wAxis, const btScalar angle ); + + SIMD_FORCE_INLINE btScalar angle(const btVector3& v) const + { + btScalar s = btSqrt(length2() * v.length2()); + btFullAssert(s != btScalar(0.0)); + return btAcos(dot(v) / s); + } + + SIMD_FORCE_INLINE btVector3 absolute() const + { + return btVector3( + btFabs(m_x), + btFabs(m_y), + btFabs(m_z)); + } + + SIMD_FORCE_INLINE btVector3 cross(const btVector3& v) const + { + return btVector3( + m_y * v.z() - m_z * v.y(), + m_z * v.x() - m_x * v.z(), + m_x * v.y() - m_y * v.x()); + } + + SIMD_FORCE_INLINE btScalar triple(const btVector3& v1, const btVector3& v2) const + { + return m_x * (v1.y() * v2.z() - v1.z() * v2.y()) + + m_y * (v1.z() * v2.x() - v1.x() * v2.z()) + + m_z * (v1.x() * v2.y() - v1.y() * v2.x()); + } + + SIMD_FORCE_INLINE int minAxis() const + { + return m_x < m_y ? (m_x < m_z ? 0 : 2) : (m_y < m_z ? 1 : 2); + } + + SIMD_FORCE_INLINE int maxAxis() const + { + return m_x < m_y ? (m_y < m_z ? 2 : 1) : (m_x < m_z ? 2 : 0); + } + + SIMD_FORCE_INLINE int furthestAxis() const + { + return absolute().minAxis(); + } + + SIMD_FORCE_INLINE int closestAxis() const + { + return absolute().maxAxis(); + } + + SIMD_FORCE_INLINE void setInterpolate3(const btVector3& v0, const btVector3& v1, btScalar rt) + { + btScalar s = btScalar(1.0) - rt; + m_x = s * v0.x() + rt * v1.x(); + m_y = s * v0.y() + rt * v1.y(); + m_z = s * v0.z() + rt * v1.z(); + //don't do the unused w component + // m_co[3] = s * v0[3] + rt * v1[3]; + } + + SIMD_FORCE_INLINE btVector3 lerp(const btVector3& v, const btScalar& t) const + { + return btVector3(m_x + (v.x() - m_x) * t, + m_y + (v.y() - m_y) * t, + m_z + (v.z() - m_z) * t); + } + + + SIMD_FORCE_INLINE btVector3& operator*=(const btVector3& v) + { + m_x *= v.x(); m_y *= v.y(); m_z *= v.z(); + return *this; + } + + + +}; + +SIMD_FORCE_INLINE btVector3 +operator+(const btVector3& v1, const btVector3& v2) +{ + return btVector3(v1.x() + v2.x(), v1.y() + v2.y(), v1.z() + v2.z()); +} + +SIMD_FORCE_INLINE btVector3 +operator*(const btVector3& v1, const btVector3& v2) +{ + return btVector3(v1.x() * v2.x(), v1.y() * v2.y(), v1.z() * v2.z()); +} + +SIMD_FORCE_INLINE btVector3 +operator-(const btVector3& v1, const btVector3& v2) +{ + return btVector3(v1.x() - v2.x(), v1.y() - v2.y(), v1.z() - v2.z()); +} + +SIMD_FORCE_INLINE btVector3 +operator-(const btVector3& v) +{ + return btVector3(-v.x(), -v.y(), -v.z()); +} + +SIMD_FORCE_INLINE btVector3 +operator*(const btVector3& v, const btScalar& s) +{ + return btVector3(v.x() * s, v.y() * s, v.z() * s); +} + +SIMD_FORCE_INLINE btVector3 +operator*(const btScalar& s, const btVector3& v) +{ + return v * s; +} + +SIMD_FORCE_INLINE btVector3 +operator/(const btVector3& v, const btScalar& s) +{ + btFullAssert(s != btScalar(0.0)); + return v * (btScalar(1.0) / s); +} + +SIMD_FORCE_INLINE btVector3 +operator/(const btVector3& v1, const btVector3& v2) +{ + return btVector3(v1.x() / v2.x(),v1.y() / v2.y(),v1.z() / v2.z()); +} + +SIMD_FORCE_INLINE btScalar +dot(const btVector3& v1, const btVector3& v2) +{ + return v1.dot(v2); +} + + + +SIMD_FORCE_INLINE btScalar +distance2(const btVector3& v1, const btVector3& v2) +{ + return v1.distance2(v2); +} + + +SIMD_FORCE_INLINE btScalar +distance(const btVector3& v1, const btVector3& v2) +{ + return v1.distance(v2); +} + +SIMD_FORCE_INLINE btScalar +angle(const btVector3& v1, const btVector3& v2) +{ + return v1.angle(v2); +} + +SIMD_FORCE_INLINE btVector3 +cross(const btVector3& v1, const btVector3& v2) +{ + return v1.cross(v2); +} + +SIMD_FORCE_INLINE btScalar +triple(const btVector3& v1, const btVector3& v2, const btVector3& v3) +{ + return v1.triple(v2, v3); +} + +SIMD_FORCE_INLINE btVector3 +lerp(const btVector3& v1, const btVector3& v2, const btScalar& t) +{ + return v1.lerp(v2, t); +} + + +SIMD_FORCE_INLINE bool operator==(const btVector3& p1, const btVector3& p2) +{ + return p1.x() == p2.x() && p1.y() == p2.y() && p1.z() == p2.z(); +} + +SIMD_FORCE_INLINE btScalar btVector3::distance2(const btVector3& v) const +{ + return (v - *this).length2(); +} + +SIMD_FORCE_INLINE btScalar btVector3::distance(const btVector3& v) const +{ + return (v - *this).length(); +} + +SIMD_FORCE_INLINE btVector3 btVector3::normalized() const +{ + return *this / length(); +} + +SIMD_FORCE_INLINE btVector3 btVector3::rotate( const btVector3& wAxis, const btScalar angle ) +{ + // wAxis must be a unit lenght vector + + btVector3 o = wAxis * wAxis.dot( *this ); + btVector3 x = *this - o; + btVector3 y; + + y = wAxis.cross( *this ); + + return ( o + x * btCos( angle ) + y * btSin( angle ) ); +} + +class btVector4 : public btVector3 +{ +public: + + SIMD_FORCE_INLINE btVector4() {} + + + SIMD_FORCE_INLINE btVector4(const btScalar& x, const btScalar& y, const btScalar& z,const btScalar& w) + : btVector3(x,y,z) + { + m_unusedW = w; + } + + + SIMD_FORCE_INLINE btVector4 absolute4() const + { + return btVector4( + btFabs(m_x), + btFabs(m_y), + btFabs(m_z), + btFabs(m_unusedW)); + } + + + + btScalar getW() const { return m_unusedW;} + + + SIMD_FORCE_INLINE int maxAxis4() const + { + int maxIndex = -1; + btScalar maxVal = btScalar(-1e30); + if (m_x > maxVal) + { + maxIndex = 0; + maxVal = m_x; + } + if (m_y > maxVal) + { + maxIndex = 1; + maxVal = m_y; + } + if (m_z > maxVal) + { + maxIndex = 2; + maxVal = m_z; + } + if (m_unusedW > maxVal) + { + maxIndex = 3; + maxVal = m_unusedW; + } + + + + + return maxIndex; + + } + + + SIMD_FORCE_INLINE int minAxis4() const + { + int minIndex = -1; + btScalar minVal = btScalar(1e30); + if (m_x < minVal) + { + minIndex = 0; + minVal = m_x; + } + if (m_y < minVal) + { + minIndex = 1; + minVal = m_y; + } + if (m_z < minVal) + { + minIndex = 2; + minVal = m_z; + } + if (m_unusedW < minVal) + { + minIndex = 3; + minVal = m_unusedW; + } + + return minIndex; + + } + + + SIMD_FORCE_INLINE int closestAxis4() const + { + return absolute4().maxAxis4(); + } + +}; + +#endif //SIMD__VECTOR3_H diff --git a/bullet/src/btBulletCollisionCommon.h b/bullet/src/btBulletCollisionCommon.h new file mode 100644 index 0000000..482f217 --- /dev/null +++ b/bullet/src/btBulletCollisionCommon.h @@ -0,0 +1,62 @@ +/* +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. +*/ + +#ifndef BULLET_COLLISION_COMMON_H +#define BULLET_COLLISION_COMMON_H + +///Common headerfile includes for Bullet Collision Detection + +///Bullet's btCollisionWorld and btCollisionObject definitions +#include "BulletCollision/CollisionDispatch/btCollisionWorld.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" + +///Collision Shapes +#include "BulletCollision/CollisionShapes/btBoxShape.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" +#include "BulletCollision/CollisionShapes/btCapsuleShape.h" +#include "BulletCollision/CollisionShapes/btCylinderShape.h" +#include "BulletCollision/CollisionShapes/btConeShape.h" +#include "BulletCollision/CollisionShapes/btStaticPlaneShape.h" +#include "BulletCollision/CollisionShapes/btConvexHullShape.h" +#include "BulletCollision/CollisionShapes/btTriangleMesh.h" +#include "BulletCollision/CollisionShapes/btConvexTriangleMeshShape.h" +#include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h" +#include "BulletCollision/CollisionShapes/btTriangleMeshShape.h" +#include "BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h" +#include "BulletCollision/CollisionShapes/btCompoundShape.h" +#include "BulletCollision/CollisionShapes/btTetrahedronShape.h" +#include "BulletCollision/CollisionShapes/btEmptyShape.h" +#include "BulletCollision/CollisionShapes/btMultiSphereShape.h" +#include "BulletCollision/CollisionShapes/btUniformScalingShape.h" + +///Narrowphase Collision Detector +#include "BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.h" + +///Dispatching and generation of collision pairs (broadphase) +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/BroadphaseCollision/btSimpleBroadphase.h" +#include "BulletCollision/BroadphaseCollision/btAxisSweep3.h" + + +///Math library & Utils +#include "LinearMath/btQuaternion.h" +#include "LinearMath/btTransform.h" +#include "LinearMath/btDefaultMotionState.h" +#include "LinearMath/btQuickprof.h" +#include "LinearMath/btIDebugDraw.h" + +#endif //BULLET_COLLISION_COMMON_H + diff --git a/bullet/src/btBulletDynamicsCommon.h b/bullet/src/btBulletDynamicsCommon.h new file mode 100644 index 0000000..af9561a --- /dev/null +++ b/bullet/src/btBulletDynamicsCommon.h @@ -0,0 +1,42 @@ +/* +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. +*/ + +#ifndef BULLET_DYNAMICS_COMMON_H +#define BULLET_DYNAMICS_COMMON_H + +///Common headerfile includes for Bullet Dynamics, including Collision Detection +#include "btBulletCollisionCommon.h" + +#include "BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h" +#include "BulletDynamics/Dynamics/btSimpleDynamicsWorld.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" + +#include "BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h" +#include "BulletDynamics/ConstraintSolver/btHingeConstraint.h" +#include "BulletDynamics/ConstraintSolver/btConeTwistConstraint.h" +#include "BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h" + + +#include "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h" +///Vehicle simulation, with wheel contact simulated by raycasts +#include "BulletDynamics/Vehicle/btRaycastVehicle.h" + + + + + + +#endif //BULLET_DYNAMICS_COMMON_H + diff --git a/corona/Makefile b/corona/Makefile new file mode 100644 index 0000000..4888e81 --- /dev/null +++ b/corona/Makefile @@ -0,0 +1,16 @@ +include ../Makefile.common + +OBJ = $(patsubst %.cpp,%.o,$(wildcard src/*.cpp)) +LIBNAME = libcorona.a +CXXFLAGS += -Isrc -I../libpng -I../zlib -I../physfs +all: $(LIBNAME) + +$(LIBNAME): $(OBJ) + @echo Creating archive $@ + @ar -csru $@ $(OBJ) + @echo + +clean: + -@$(RM) src$(SLASH)*.o + -@$(RM) src$(SLASH)*.d + -@$(RM) $(LIBNAME) diff --git a/corona/src/Convert.cpp b/corona/src/Convert.cpp new file mode 100644 index 0000000..325f984 --- /dev/null +++ b/corona/src/Convert.cpp @@ -0,0 +1,275 @@ +/** + * @file + * @todo allow conversions from direct color images to + * palettized images + */ +#include +#include +#include "corona.h" +#include "Debug.h" +#include "SimpleImage.h" +#include "Utility.h" + + +namespace corona { + + Image* ExpandPalette(Image* image) { + COR_GUARD("ExpandPalette()"); + + // assert isPalettized(image->getFormat()) + + const int width = image->getWidth(); + const int height = image->getHeight(); + const byte* in = (byte*)image->getPixels(); + const PixelFormat palette_format = image->getPaletteFormat(); + const int pixel_size = GetPixelSize(palette_format); + const byte* palette = (byte*)image->getPalette(); + + byte* pixels = new byte[width * height * pixel_size]; + byte* out = pixels; + for (int i = 0; i < width * height; ++i) { + memcpy(out, palette + (*in) * pixel_size, pixel_size); + out += pixel_size; + ++in; + } + delete image; + return new SimpleImage(width, height, palette_format, pixels); + } + + + struct FormatDesc { + FormatDesc(int r, int g, int b, int a, bool ha) { + r_shift = r; + g_shift = g; + b_shift = b; + a_shift = a; + has_alpha = ha; + } + + // shifts are in bytes from the right + // In the case of RGBA, r_shift is 0, g_shift is 1, ... + int r_shift; + int g_shift; + int b_shift; + int a_shift; + bool has_alpha; + }; + + + #define DEFINE_DESC(format, desc) \ + case format: { \ + COR_LOG(#format); \ + static FormatDesc format##_desc desc; \ + return &format##_desc; \ + } + + FormatDesc* GetDescription(PixelFormat format) { + // assert isDirect(image->getFormat()) + + switch (format) { + DEFINE_DESC(PF_R8G8B8A8, (0, 1, 2, 3, true)); + DEFINE_DESC(PF_R8G8B8, (0, 1, 2, 0, false)); + DEFINE_DESC(PF_B8G8R8A8, (2, 1, 0, 3, true)); + DEFINE_DESC(PF_B8G8R8, (2, 1, 0, 0, false)); + default: return 0; + } + } + + + bool ConvertPixels(byte* out, PixelFormat out_format, + const byte* in, PixelFormat in_format, + int pixel_count) + { + const FormatDesc* out_desc = GetDescription(out_format); + const FormatDesc* in_desc = GetDescription(in_format); + if (!out_desc || !in_desc) { + return false; + } + + const int out_size = GetPixelSize(out_format); + const int in_size = GetPixelSize(in_format); + + for (int i = 0; i < pixel_count; ++i) { + out[out_desc->r_shift] = in[in_desc->r_shift]; + out[out_desc->g_shift] = in[in_desc->g_shift]; + out[out_desc->b_shift] = in[in_desc->b_shift]; + + if (out_desc->has_alpha) { + if (in_desc->has_alpha) { + out[out_desc->a_shift] = in[in_desc->a_shift]; + } else { + out[out_desc->a_shift] = 255; + } + } + + in += in_size; + out += out_size; + } + + return true; + } + + + Image* DirectConversion(Image* image, PixelFormat target_format) { + COR_GUARD("DirectConversion()"); + + // assert isDirect(image->getFormat()) + + const int width = image->getWidth(); + const int height = image->getHeight(); + const PixelFormat source_format = image->getFormat(); + const byte* in = (byte*)image->getPixels(); + + if (source_format == target_format) { + return image; + } + + const int target_size = GetPixelSize(target_format); + byte* out_pixels = new byte[width * height * target_size]; + if (!ConvertPixels(out_pixels, target_format, + in, source_format, + width * height)) + { + delete[] out_pixels; + delete image; + return 0; + } + + delete image; + return new SimpleImage(width, height, target_format, out_pixels); + } + + + namespace hidden { + + COR_EXPORT(Image*) CorConvertImage( + Image* image, + PixelFormat target_format) + { + COR_GUARD("CorConvertImage"); + + // if we don't have an image, user doesn't care about format, or + // the formats match, don't do any conversion. + if (!image || + target_format == PF_DONTCARE || + target_format == image->getFormat()) + { + return image; + } + + COR_LOG("Doing the conversion..."); + + // if we have a palettized image, convert it to a direct color + // image and then convert that + if (IsPalettized(image->getFormat())) { + image = ExpandPalette(image); + } + + return DirectConversion(image, target_format); + } + + + COR_EXPORT(Image*) CorConvertPalette( + Image* image, + PixelFormat palette_format) + { + // do we need to convert? + if (!image || + palette_format == PF_DONTCARE || + image->getPaletteFormat() == palette_format) + { + return image; + } + + // do we have invalid data? + if (!IsPalettized(image->getFormat()) || + !IsDirect(palette_format)) + { + delete image; + return 0; + } + + const int width = image->getWidth(); + const int height = image->getHeight(); + const PixelFormat format = image->getFormat(); + const int palette_size = image->getPaletteSize(); + + // the palette indices don't change, so just make a copy + const int image_size = width * height * GetPixelSize(format); + byte* pixels = new byte[image_size]; + memcpy(pixels, image->getPixels(), image_size); + + byte* new_palette = new byte[ + palette_size * GetPixelSize(palette_format)]; + + if (!ConvertPixels(new_palette, palette_format, + (byte*)image->getPalette(), image->getPaletteFormat(), + palette_size)) + { + delete image; + delete[] pixels; + delete[] new_palette; + return 0; + } + + delete image; + return new SimpleImage( + width, height, format, pixels, + new_palette, palette_size, palette_format); + } + + COR_EXPORT(Image*) CorFlipImage( + Image* image, + int coordinate_axis) + { + COR_GUARD("CorFlipImage"); + + // if we don't have an image, don't flip. + if (!image) { + return 0; + } + + COR_LOG("Doing the flip..."); + + const int width = image->getWidth(); + const int height = image->getHeight(); + byte* pixels = (byte*)image->getPixels(); + const PixelFormat pixel_format = image->getFormat(); + const int pixel_size = GetPixelSize(pixel_format); + + // flip about the X axis + if (coordinate_axis & CA_X) { + + byte* row = new byte[width * pixel_size]; + for (int h = 0; h < height / 2; ++h) { + byte* top = pixels + h * width * pixel_size; + byte* bot = pixels + (height - h - 1) * width * pixel_size; + memcpy(row, top, width * pixel_size); + memcpy(top, bot, width * pixel_size); + memcpy(bot, row, width * pixel_size); + } + delete[] row; + + } + + // flip about the Y axis + if (coordinate_axis & CA_Y) { + + for (int h = 0; h < height; ++h) { + byte* row = pixels + h * width * pixel_size; + for (int w = 0; w < width / 2; ++w) { + byte* left = row + w * pixel_size; + byte* right = row + (width - w - 1) * pixel_size; + for (int b = 0; b < pixel_size; ++b) { + std::swap(left[b], right[b]); + } + } + } + + } + + return image; + } + } + +} diff --git a/corona/src/Corona.cpp b/corona/src/Corona.cpp new file mode 100644 index 0000000..3b773b8 --- /dev/null +++ b/corona/src/Corona.cpp @@ -0,0 +1,321 @@ +#include +#include +#include +#include +#include +#include "corona.h" +#include "MemoryFile.h" +#include "Open.h" +#include "Save.h" +#include "SimpleImage.h" + + +namespace corona { + namespace hidden { + + /////////////////////////////////////////////////////////////////////////// + + COR_EXPORT(const char*) CorGetVersion() { + return "1.0.2"; + } + + /////////////////////////////////////////////////////////////////////////// + + class FFDImpl : public FileFormatDesc { + public: + FFDImpl(FileFormat format, const char* description, const char* exts) { + m_format = format; + m_description = description; + + const char* ext = exts; + while (*ext) { + m_extensions.push_back(ext); + ext += strlen(ext) + 1; + } + } + + FileFormat getFormat() { return m_format; } + const char* getDescription() { return m_description.c_str(); } + size_t getExtensionCount() { return m_extensions.size(); } + const char* getExtension(size_t i) { return m_extensions[i].c_str(); } + + private: + FileFormat m_format; + std::string m_description; + std::vector m_extensions; + }; + + FFDImpl ffPNG (FF_PNG, "PNG Files", "png\0"); + FFDImpl ffJPEG(FF_JPEG, "JPEG Files", "jpeg\0jpg\0"); + FFDImpl ffPCX (FF_PCX, "PCX Files", "pcx\0"); + FFDImpl ffBMP (FF_BMP, "BMP Files", "bmp\0"); + FFDImpl ffTGA (FF_TGA, "TGA Files", "tga\0"); + FFDImpl ffGIF (FF_GIF, "GIF Files", "gif\0"); + + const int MAX_FORMAT_COUNT = 64; + FileFormatDesc** g_read_formats = 0; + FileFormatDesc** g_write_formats = 0; + FileFormatDesc* g_read_array[MAX_FORMAT_COUNT + 1] = {0}; + FileFormatDesc* g_write_array[MAX_FORMAT_COUNT + 1] = {0}; + + + COR_EXPORT(FileFormatDesc**) CorGetSupportedReadFormats() { + if (!g_read_formats) { + g_read_formats = g_read_array; + FileFormatDesc** f = g_read_formats; + *f++ = &ffPNG; + *f++ = &ffPCX; + *f++ = &ffBMP; + *f++ = &ffTGA; + } + return g_read_formats; + } + + + COR_EXPORT(FileFormatDesc**) CorGetSupportedWriteFormats() { + if (!g_write_formats) { + g_write_formats = g_write_array; + FileFormatDesc** f = g_write_formats; + *f++ = &ffPNG; + *f++ = &ffTGA; + } + return g_write_formats; + } + + /////////////////////////////////////////////////////////////////////////// + + COR_EXPORT(Image*) CorCreateImage( + int width, + int height, + PixelFormat format) + { + return CreateImage(width, height, format, 0); + } + + /////////////////////////////////////////////////////////////////////////// + + COR_EXPORT(Image*) CorCreateImageWithPixels( + int width, + int height, + PixelFormat format, + void* pixels) + { + // this function only supports creation of non-palettized images + if (!IsDirect(format)) { + return 0; + } + + int size = width * height * GetPixelSize(format); + byte* p = new byte[size]; + if (pixels) { + memcpy(p, pixels, size); + } else { + memset(p, 0, size); + } + return new SimpleImage(width, height, format, p); + } + + /////////////////////////////////////////////////////////////////////////// + + COR_EXPORT(Image*) CorCreatePalettizedImage( + int width, + int height, + PixelFormat format, + int palette_size, + PixelFormat palette_format) + { + // only support creation of palettized images + if (!IsPalettized(format) || !IsDirect(palette_format)) { + return 0; + } + + // make sure the palette is the right size + if (palette_size != GetPaletteSize(format)) { + return 0; + } + + int size = width * height * GetPixelSize(format); + byte* pixels = new byte[size]; + memset(pixels, 0, size); + + int palette_bytes = palette_size * GetPixelSize(palette_format); + byte* palette = new byte[palette_bytes]; + memset(palette, 0, palette_bytes); + + return new SimpleImage(width, height, format, pixels, + palette, palette_size, palette_format); + } + + /////////////////////////////////////////////////////////////////////////// + + COR_EXPORT(Image*) CorCloneImage( + Image* source, + PixelFormat format) + { + if (!source) { + // we need an image to clone :) + return 0; + } + + const int width = source->getWidth(); + const int height = source->getHeight(); + const PixelFormat source_format = source->getFormat(); + + const int source_pixel_size = GetPixelSize(source_format); + if (source_pixel_size == 0) { + // unknown pixel size? + return 0; + } + + // duplicate the image + int image_size = width * height * source_pixel_size; + byte* pixels = new byte[image_size]; + memcpy(pixels, source->getPixels(), image_size); + + if (IsPalettized(source_format)) { + // clone palette + int palette_size = source->getPaletteSize(); + PixelFormat palette_format = source->getPaletteFormat(); + int palette_bytes = palette_size * GetPixelSize(palette_format); + byte* palette = new byte[palette_bytes]; + memcpy(palette, source->getPalette(), palette_bytes); + Image* image = new SimpleImage(width, height, source_format, pixels, + palette, palette_size, palette_format); + return ConvertImage(image, format); + } + + Image* image = new SimpleImage(width, height, source_format, pixels); + return ConvertImage(image, format); + } + + /////////////////////////////////////////////////////////////////////////// + + COR_EXPORT(Image*) CorOpenImage( + const char* filename, + FileFormat file_format) + { + if (!filename) { + return 0; + } + + std::auto_ptr file(OpenFile(filename, false)); + return CorOpenImageFromFile(file.get(), file_format); + } + + /////////////////////////////////////////////////////////////////////////// + + COR_EXPORT(Image*) CorOpenImageFromFile( + File* file, + FileFormat file_format) + { + if (!file) { + return 0; + } + +#define TRY_TYPE(type) \ + { \ + Image* image = CorOpenImageFromFile(file, (type)); \ + if (image) { return image; } \ + } + + file->seek(0, File::BEGIN); + switch (file_format) { + case FF_AUTODETECT: { + TRY_TYPE(FF_PNG); + TRY_TYPE(FF_PCX); + TRY_TYPE(FF_BMP); + TRY_TYPE(FF_TGA); + return 0; + } + + case FF_PNG: return OpenPNG(file); + case FF_PCX: return OpenPCX(file); + case FF_BMP: return OpenBMP(file); + case FF_TGA: return OpenTGA(file); + default: return 0; + } + } + + /////////////////////////////////////////////////////////////////////////// + + int strcmp_ci(const char* a, const char* b) { + while (*a && *b) { + const int diff = tolower(*a) - tolower(*b); + if (diff != 0) { + return diff; + } + ++a; + ++b; + } + return tolower(*a) - tolower(*b); + } + + bool ends_with(const char* str, const char* ext) { + const int str_len = strlen(str); + const int ext_len = strlen(ext); + return (str_len >= ext_len && + strcmp_ci(str + str_len - ext_len, ext) == 0); + } + + COR_EXPORT(bool) CorSaveImage( + const char* filename, + FileFormat file_format, + Image* image) + { + if (!filename) { + return false; + } + + if (file_format == FF_AUTODETECT) { + if (ends_with(filename, ".png")) { + file_format = FF_PNG; + } else if (ends_with(filename, ".tga")) { + file_format = FF_TGA; + } else { + return false; + } + } + + std::auto_ptr file(OpenFile(filename, true)); + return CorSaveImageToFile(file.get(), file_format, image); + } + + /////////////////////////////////////////////////////////////////////////// + + COR_EXPORT(bool) CorSaveImageToFile( + File* file, + FileFormat file_format, + Image* image) + { + if (!file || !image) { + return false; + } + + switch (file_format) { + case FF_PNG: return SavePNG(file, image); + case FF_JPEG: return false; + case FF_PCX: return false; + case FF_BMP: return false; + case FF_TGA: return SaveTGA(file, image); + case FF_GIF: return false; + default: return false; + } + } + + /////////////////////////////////////////////////////////////////////////// + + COR_EXPORT(int) CorGetPixelSize(PixelFormat format) { + switch (format) { + case PF_R8G8B8A8: return 4; + case PF_R8G8B8: return 3; + case PF_B8G8R8A8: return 4; + case PF_B8G8R8: return 3; + case PF_I8: return 1; + default: return 0; + } + } + + /////////////////////////////////////////////////////////////////////////// + + } +} diff --git a/corona/src/Debug.cpp b/corona/src/Debug.cpp new file mode 100644 index 0000000..caf062b --- /dev/null +++ b/corona/src/Debug.cpp @@ -0,0 +1,52 @@ +#include "Debug.h" + +#ifdef CORONA_DEBUG + + +FILE* Log::handle; +int Log::indent_count; + + +//////////////////////////////////////////////////////////////////////////////// + +void +Log::Write(const char* str) +{ + EnsureOpen(); + if (handle) { + std::string s(std::string(indent_count * 2, ' ') + str + "\n"); + fputs(s.c_str(), handle); + fflush(handle); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +void +Log::EnsureOpen() +{ + if (!handle) { +#ifdef WIN32 + handle = 0; //fopen("C:/corona_debug.log", "w"); +#else + //std::string home(getenv("HOME")); + handle = 0; //fopen((home + "/corona_debug.log").c_str(), "w"); +#endif + atexit(Close); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +void +Log::Close() +{ + if (handle) { + fclose(handle); + } +} + +//////////////////////////////////////////////////////////////////////////////// + + +#endif diff --git a/corona/src/Debug.h b/corona/src/Debug.h new file mode 100644 index 0000000..e6fae2e --- /dev/null +++ b/corona/src/Debug.h @@ -0,0 +1,70 @@ +#ifndef DEBUG_HPP +#define DEBUG_HPP + + +#ifdef _DEBUG +# ifndef CORONA_DEBUG +# define CORONA_DEBUG +# endif +#endif + + +#ifdef CORONA_DEBUG + + #include + #include + + class Log { + public: + static void Write(const char* str); + static void IncrementIndent() { ++indent_count; } + static void DecrementIndent() { --indent_count; } + + private: + static void EnsureOpen(); + static void Close(); + + private: + static FILE* handle; + static int indent_count; + }; + + + class Guard { + public: + Guard(const char* label) + : m_label(label) { + Write("+"); + Log::IncrementIndent(); + } + + ~Guard() { + Log::DecrementIndent(); + Write("-"); + } + + void Write(const char* prefix) { + Log::Write((prefix + m_label).c_str()); + } + + private: + std::string m_label; + }; + + + #define COR_GUARD(label) Guard guard_obj__(label) + #define COR_LOG(label) (Log::Write(label)) + #define COR_IF_DEBUG if (true) + #define COR_ASSERT(condition, label) if (!(condition)) { __asm int 3 } + +#else + + #define COR_GUARD(label) + #define COR_LOG(label) + #define COR_IF_DEBUG if (false) + #define COR_ASSERT(condition, label) + +#endif + + +#endif diff --git a/corona/src/DefaultFileSystem.cpp b/corona/src/DefaultFileSystem.cpp new file mode 100644 index 0000000..bc8fdfe --- /dev/null +++ b/corona/src/DefaultFileSystem.cpp @@ -0,0 +1,55 @@ +//#include +#include +#include "Utility.h" + + +namespace corona { + + class CFile : public DLLImplementation { + public: + CFile(PHYSFS_File* file) { + m_file = file; + } + + ~CFile() { + PHYSFS_close(m_file); + } + + int COR_CALL read(void* buffer, int size) { + return (int)PHYSFS_read(m_file, buffer, 1, size ); + } + + int COR_CALL write(const void* buffer, int size) { + return (int)PHYSFS_write(m_file, buffer, 1, size ); + } + + bool COR_CALL seek(int position, SeekMode mode) { + PHYSFS_uint64 pos; + switch (mode) { + case BEGIN: pos = position; break; + case CURRENT: pos = PHYSFS_tell(m_file) + position; break; + case END: pos = PHYSFS_fileLength( m_file ) + position; break; + default: return false; + } + return PHYSFS_seek(m_file, pos) != 0; + } + + int COR_CALL tell() { + return (int)PHYSFS_tell(m_file); + } + + private: + PHYSFS_File* m_file; + }; + + + COR_EXPORT(File*) CorOpenFile(const char* filename, bool writeable) { + PHYSFS_File* file; + if( writeable ) + file = PHYSFS_openWrite(filename); + else + file = PHYSFS_openRead(filename); + return (file ? new CFile(file) : 0); + } + +} diff --git a/corona/src/MemoryFile.cpp b/corona/src/MemoryFile.cpp new file mode 100644 index 0000000..359bd11 --- /dev/null +++ b/corona/src/MemoryFile.cpp @@ -0,0 +1,93 @@ +#include +#include "MemoryFile.h" +#include "Utility.h" + +namespace corona { + + COR_EXPORT(File*) CorCreateMemoryFile(const void* buffer, int size) { + if (size && !buffer) { + return 0; + } + if (size < 0) { + return 0; + } + + return new MemoryFile(buffer, size); + } + + + int getNextPowerOfTwo(int value) { + int i = 1; + while (i < value) { + i *= 2; + } + return i; + } + + MemoryFile::MemoryFile(const void* buffer, int size) { + m_capacity = getNextPowerOfTwo(size); + m_size = size; + m_buffer = new byte[m_capacity]; + memcpy(m_buffer, buffer, size); + + m_position = 0; + } + + MemoryFile::~MemoryFile() { + delete[] m_buffer; + } + + int COR_CALL MemoryFile::read(void* buffer, int size) { + int real_read = std::min((m_size - m_position), size); + memcpy(buffer, m_buffer + m_position, real_read); + m_position += real_read; + return real_read; + } + + int COR_CALL MemoryFile::write(const void* buffer, int size) { + ensureSize(m_position + size); + memcpy(m_buffer + m_position, buffer, size); + m_position += size; + return size; + } + + bool COR_CALL MemoryFile::seek(int position, SeekMode mode) { + int real_pos; + switch (mode) { + case BEGIN: real_pos = position; break; + case CURRENT: real_pos = m_position + position; break; + case END: real_pos = m_size + position; break; + default: return false; + } + + if (real_pos < 0 || real_pos > m_size) { + m_position = 0; + return false; + } else { + m_position = real_pos; + return true; + } + } + + int COR_CALL MemoryFile::tell() { + return m_position; + } + + void MemoryFile::ensureSize(int min_size) { + bool realloc_needed = false; + while (m_capacity < min_size) { + m_capacity *= 2; + realloc_needed = true; + } + + if (realloc_needed) { + byte* new_buffer = new byte[m_capacity]; + memcpy(new_buffer, m_buffer, m_size); + delete[] m_buffer; + m_buffer = new_buffer; + } + + m_size = min_size; + } + +}; diff --git a/corona/src/MemoryFile.h b/corona/src/MemoryFile.h new file mode 100644 index 0000000..dfd0d3f --- /dev/null +++ b/corona/src/MemoryFile.h @@ -0,0 +1,36 @@ +#ifndef MEMORY_FILE_H +#define MEMORY_FILE_H + + +#include "corona.h" +#include "Types.h" +#include "Utility.h" + + +namespace corona { + + class MemoryFile : public DLLImplementation { + public: + MemoryFile(const void* buffer, int size); + ~MemoryFile(); + + int COR_CALL read(void* buffer, int size); + int COR_CALL write(const void* buffer, int size); + bool COR_CALL seek(int position, SeekMode mode); + int COR_CALL tell(); + + private: + void ensureSize(int min_size); + + byte* m_buffer; + int m_position; + int m_size; + + /// The actual size of m_buffer. Always a power of two. + int m_capacity; + }; + +} + + +#endif diff --git a/corona/src/Open.h b/corona/src/Open.h new file mode 100644 index 0000000..fc3400e --- /dev/null +++ b/corona/src/Open.h @@ -0,0 +1,16 @@ +#ifndef CORONA_OPEN_H +#define CORONA_OPEN_H + + +#include "corona.h" + + +namespace corona { + Image* OpenBMP (File* file); // OpenBMP.cpp + Image* OpenPCX (File* file); // OpenPCX.cpp + Image* OpenPNG (File* file); // OpenPNG.cpp + Image* OpenTGA (File* file); // OpenTGA.cpp +} + + +#endif diff --git a/corona/src/OpenBMP.cpp b/corona/src/OpenBMP.cpp new file mode 100644 index 0000000..1553df0 --- /dev/null +++ b/corona/src/OpenBMP.cpp @@ -0,0 +1,678 @@ +// format information gleaned from +// http://www.daubnet.com/formats/BMP.html +// and +// http://www.edm2.com/0107/os2bmp.html +// +// If you have Visual Studio.NET: +// ms-help://MS.VSCC/MS.MSDNVS/gdi/bitmaps_7c36.htm + +#include +#include "corona.h" +#include "SimpleImage.h" +#include "Utility.h" + + +namespace corona { + + struct Header { + bool os2; + + int file_size; + int data_offset; + int width; + int height; + int bpp; + int compression; + + int pitch; // number of bytes in each scanline + int image_size; + + auto_array palette; + int palette_size; + + // for bitfield specification... + // /*- 16-bit only -*/ + u32 bf_red_mask, bf_red_shift, bf_red_rshift; + u32 bf_green_mask, bf_green_shift, bf_green_rshift; + u32 bf_blue_mask, bf_blue_shift, bf_blue_rshift; + }; + + + bool ReadHeader(File* file, Header& h); + bool ReadInfoHeader(File* file, Header& h); + bool ReadPalette(File* file, Header& h); + Image* DecodeBitmap(File* file, const Header& h); + + + Image* OpenBMP(File* file) { + Header h; + if (ReadHeader(file, h) && + ReadInfoHeader(file, h) && + ReadPalette(file, h)) { + + return DecodeBitmap(file, h); + + } else { + + return 0; + + } + } + + + bool ReadHeader(File* file, Header& h) { + byte header[14]; + if (file->read(header, 14) != 14) { + return false; + } + + // check signature + if (header[0] != 'B' || header[1] != 'M') { + return false; + } + + h.file_size = read32_le(header + 2); + h.data_offset = read32_le(header + 10); + return true; + } + + + bool ReadInfoHeader(File* file, Header& h) { + + const int HEADER_READ_SIZE = 24; + + // read the only part of the header we need + byte header[HEADER_READ_SIZE]; + if (file->read(header, HEADER_READ_SIZE) != HEADER_READ_SIZE) { + return false; + } + + int size = read32_le(header + 0); + int width; + int height; + int planes; + int bpp; + int compression; + int image_size; + + if (size < 40) { // assume OS/2 bitmap + if (size < 12) { + return false; + } + + h.os2 = true; + width = read16_le(header + 4); + height = read16_le(header + 6); + planes = read16_le(header + 8); + bpp = read16_le(header + 10); + compression = 0; + image_size = 0; + + } else { + + h.os2 = false; + width = read32_le(header + 4); + height = read32_le(header + 8); + planes = read16_le(header + 12); + bpp = read16_le(header + 14); + compression = read32_le(header + 16); + image_size = read32_le(header + 20); + + } + + // sanity check the info header + if (planes != 1) { + return false; + } + + // adjust image_size + // (if compression == 0 or 3, manually calculate image size) + int line_size = 0; + if (compression == 0 || compression == 3) { + line_size = (width * bpp + 7) / 8; + line_size = (line_size + 3) / 4 * 4; // 32-bit-aligned + image_size = line_size * height; + } + + h.width = width; + h.height = height; + h.bpp = bpp; + h.compression = compression; + h.pitch = line_size; + h.image_size = image_size; + + // jump forward (backward in the OS/2 case :) to the palette data + file->seek(size - HEADER_READ_SIZE, File::CURRENT); + + return true; + } + + // count the number of consecutive zeroes on the right side of a + // binary number + // 0x00F0 will return 4 + int count_right_zeroes(u32 n) { + int total = 0; + u32 c = 1; + while ((total < 32) && ((n & c) == 0)) { + c <<= 1; + ++total; + } + return total; + } + + // count the number of ones in a binary number + // 0x00F1 will return 5 + int count_ones(u32 n) { + int total = 0; + u32 c = 1; + for (int i = 0; i < 32; ++i) { + if (n & c) { + ++total; + } + c <<= 1; + } + return total; + } + + bool ReadPalette(File* file, Header& h) { + + // initialize bit masks/shifts... just in case + h.bf_red_mask = h.bf_red_shift = h.bf_red_rshift = 0; + h.bf_green_mask = h.bf_green_shift = h.bf_green_rshift = 0; + h.bf_blue_mask = h.bf_blue_shift = h.bf_blue_rshift = 0; + + // if we're not a palettized image, don't even read a palette + if (h.bpp > 8) { + h.palette_size = 0; + + // do we have bitfields? + if (h.compression == 3) { + + auto_array bitfields(new byte[12]); + if (file->read(bitfields, 12) != 12) { + return false; + } + + h.bf_red_mask = read32_le((byte*)bitfields); + h.bf_green_mask = read32_le((byte*)bitfields + 4); + h.bf_blue_mask = read32_le((byte*)bitfields + 8); + + // calculate shifts + h.bf_red_shift = count_right_zeroes(h.bf_red_mask); + h.bf_green_shift = count_right_zeroes(h.bf_green_mask); + h.bf_blue_shift = count_right_zeroes(h.bf_blue_mask); + h.bf_red_rshift = 8 - count_ones(h.bf_red_mask); + h.bf_green_rshift = 8 - count_ones(h.bf_green_mask); + h.bf_blue_rshift = 8 - count_ones(h.bf_blue_mask); + + // otherwise, set default bitfield entries + } else { + + if (h.bpp == 16) { + + h.bf_red_mask = 0x7C00; + h.bf_red_shift = 10; + h.bf_red_rshift = 3; + + h.bf_green_mask = 0x03E0; + h.bf_green_shift = 5; + h.bf_green_rshift = 3; + + h.bf_blue_mask = 0x001F; + h.bf_blue_shift = 0; + h.bf_blue_rshift = 3; + + } else if (h.bpp == 32) { + + // these don't need any rshift + h.bf_red_mask = 0x00FF0000; h.bf_red_shift = 16; + h.bf_green_mask = 0x0000FF00; h.bf_green_shift = 8; + h.bf_blue_mask = 0x000000FF; h.bf_blue_shift = 0; + + } + } + + return true; + } + + if (h.bpp <= 8) { + h.palette_size = 1 << h.bpp; + } else { + h.palette_size = 0; + return true; + } + h.palette = new BGR[h.palette_size]; + + // read the BMP color table + const int buffer_size = h.palette_size * (h.os2 ? 3 : 4); + auto_array buffer(new byte[buffer_size]); + if (file->read(buffer, buffer_size) != buffer_size) { + return false; + } + + byte* in = buffer; + BGR* out = h.palette; + for (int i = 0; i < h.palette_size; ++i) { + out->blue = *in++; + out->green = *in++; + out->red = *in++; + if (!h.os2) { + ++in; // skip alpha + } + ++out; + } + + return true; + } + + + bool advance(int& x, int& y, const Header& h) { + if (++x >= h.width) { + x = 0; + if (++y >= h.height) { + return false; + } + } + return true; + } + + Image* ReadBitmap1(const byte* raster_data, const Header& h) { + auto_array pixels(new byte[h.width * h.height]); + + auto_array palette(new BGR[256]); + memset(palette, 0, 256 * sizeof(BGR)); + memcpy(palette, h.palette, h.palette_size * sizeof(BGR)); + + for (int i = 0; i < h.height; ++i) { + const byte* in = raster_data + i * h.pitch; + byte* out = pixels + (h.height - i - 1) * h.width; + + int mask = 128; + for (int j = 0; j < h.width; ++j) { + *out++ = (*in & mask) > 0; + + mask >>= 1; + if (mask == 0) { + ++in; + mask = 128; + } + } + } + + return new SimpleImage(h.width, h.height, PF_I8, pixels.release(), + (byte*)palette.release(), 256, PF_B8G8R8); + } + + Image* ReadBitmap4(const byte* raster_data, const Header& h) { + auto_array pixels(new byte[h.width * h.height]); + + auto_array palette(new BGR[256]); + memset(palette, 0, 256 * sizeof(BGR)); + memcpy(palette, h.palette, h.palette_size * sizeof(BGR)); + + for (int i = 0; i < h.height; ++i) { + const byte* in = raster_data + i * h.pitch; + byte* out = pixels + (h.height - i - 1) * h.width; + + for (int j = 0; j < h.width / 2; ++j) { + *out++ = (*in >> 4); + *out++ = (*in & 0x0F); + + ++in; + } + + if (h.width % 2) { + *out++ = (*in >> 4); + } + } + + return new SimpleImage(h.width, h.height, PF_I8, pixels.release(), + (byte*)palette.release(), 256, PF_B8G8R8); + } + + Image* ReadBitmapRLE4(const byte* raster_data, const Header& h) { + auto_array pixels(new byte[h.width * h.height]); + + auto_array palette(new BGR[256]); + memset(palette, 0, 256 * sizeof(BGR)); + memcpy(palette, h.palette, h.palette_size * sizeof(BGR)); + + // by default, we have an empty bitmap + memset(pixels, 0, h.width * h.height); + + // we read the image from the bottom down, and then flip it when + // we're done + int x = 0; + int y = 0; + + const byte* in = raster_data; + while (in - raster_data < h.image_size - 1) { + byte n = *in++; + byte c = *in++; + + if (n == 0) { // escape code + + if (c == 0) { // end of line + x = 0; + + //++y; // XXXaegis uhhh... uhhh... :) it works this way... + + if (y >= h.height) { + // did we go too far? + break; + } + } else if (c == 1) { // end of bitmap + break; + } else if (c == 2) { // delta + + // do we have enough space? + if (in - raster_data >= h.image_size - 1) { + break; + } + + // I have no idea how I'm supposed to do this... + // Let's take a guess! + int dx = *in++; + int dy = *in++; + x = (x + dx) % h.width; + y += dy + (x + dx) / h.width; + if (y >= h.height) { + // if we went too far, stop now + break; + } + + } else { // read uncompressed + + // the input raster data is padded on DWORD boundaries + // c == num_pixels + int num_bytes = (c + 3) / 4 * 2; + + // make sure we have enough space + if (in - raster_data > h.image_size - num_bytes) { + break; + } + + // nasty decoding loop... + int i = 0; + int j = 0; + while (true) { + byte l = (in[j] & 0xF0) >> 4; + byte r = (in[j] & 0x0F); + ++j; + + pixels[y * h.width + x] = l; + if (!advance(x, y, h) || ++i >= c) { + break; + } + + pixels[y * h.width + x] = r; + if (!advance(x, y, h) || ++i >= c) { + break; + } + } + // make SURE we move forward the right number of bytes + in += num_bytes; + } + + } else { + + // a less nasty decoding loop... + byte lc = (c & 0xF0) >> 4; + byte rc = c & 0x0F; + + int i = 0; + while (true) { + pixels[y * h.width + x] = lc; + if (!advance(x, y, h) || ++i >= n) { + break; + } + + pixels[y * h.width + x] = rc; + if (!advance(x, y, h) || ++i >= n) { + break; + } + } + + } // end if + } // end while + + // flippy flippy! + int pitch = h.width; + auto_array row(new byte[pitch]); + for (int i = 0; i < h.height / 2; ++i) { + int j = h.height - i - 1; + memcpy((byte*)row, pixels + i * pitch, pitch); + memcpy(pixels + i * pitch, pixels + j * pitch, pitch); + memcpy(pixels + j * pitch, (byte*)row, pitch); + } + + return new SimpleImage(h.width, h.height, PF_I8, pixels.release(), + (byte*)palette.release(), 256, PF_B8G8R8); + } + + Image* ReadBitmap8(const byte* raster_data, const Header& h) { + auto_array pixels(new byte[h.width * h.height]); + + auto_array palette(new BGR[256]); + memset(palette, 0, 256 * sizeof(BGR)); + memcpy(palette, h.palette, h.palette_size * sizeof(BGR)); + + for (int i = 0; i < h.height; ++i) { + const byte* in = raster_data + i * h.pitch; + byte* out = pixels + (h.height - i - 1) * h.width; + + for (int j = 0; j < h.width; ++j) { + *out++ = *in++; + } + } + + return new SimpleImage(h.width, h.height, PF_I8, pixels.release(), + (byte*)palette.release(), 256, PF_B8G8R8); + } + + Image* ReadBitmapRLE8(const byte* raster_data, const Header& h) { + auto_array pixels(new byte[h.width * h.height]); + + auto_array palette(new BGR[256]); + memset(palette, 0, 256 * sizeof(BGR)); + memcpy(palette, h.palette, h.palette_size * sizeof(BGR)); + + // by default, we have an empty bitmap + memset(pixels, 0, h.width * h.height); + + // we read the image from the bottom down, and then flip it when + // we're done + int x = 0; + int y = 0; + + const byte* in = raster_data; + while (in - raster_data < h.image_size - 1) { + byte n = *in++; + byte c = *in++; + + if (n == 0) { // escape code + + if (c == 0) { // end of line + x = 0; + + //++y; // XXXaegis uhhh... uhhh... :) it works this way... + + if (y >= h.height) { + // did we go too far? + break; + } + } else if (c == 1) { // end of bitmap + break; + } else if (c == 2) { // delta + + // do we have enough space? + if (in - raster_data >= h.image_size - 1) { + break; + } + + // I have no idea how I'm supposed to do this... + // Let's take a guess! + int dx = *in++; + int dy = *in++; + x = (x + dx) % h.width; + y += dy + (x + dx) / h.width; + if (y >= h.height) { + // if we went too far, stop now + break; + } + + } else { // read uncompressed + + // c == num_pixels + int num_bytes = (c + 1) / 2 * 2; + + // make sure we have enough space + if (in - raster_data > h.image_size - num_bytes) { + break; + } + + // decoding loop... + int i = 0; + int j = 0; + while (true) { + pixels[y * h.width + x] = in[j++]; + if (!advance(x, y, h) || ++i >= c) { + break; + } + } + // make SURE we move forward the right number of bytes + in += num_bytes; + } + + } else { + + int i = 0; + while (true) { + pixels[y * h.width + x] = c; + if (!advance(x, y, h) || ++i >= n) { + break; + } + } + + } // end if + } // end while + + // flippy flippy! + int pitch = h.width; + auto_array row(new byte[pitch]); + for (int i = 0; i < h.height / 2; ++i) { + int j = h.height - i - 1; + memcpy((byte*)row, pixels + i * pitch, pitch); + memcpy(pixels + i * pitch, pixels + j * pitch, pitch); + memcpy(pixels + j * pitch, (byte*)row, pitch); + } + + return new SimpleImage(h.width, h.height, PF_I8, pixels.release(), + (byte*)palette.release(), 256, PF_B8G8R8); + } + + Image* ReadBitmap16(const byte* raster_data, const Header& h) { + auto_array pixels(new RGB[h.width * h.height]); + + for (int i = 0; i < h.height; ++i) { + const byte* in = raster_data + i * h.pitch; + RGB* out = pixels + (h.height - i - 1) * h.width; + + for (int j = 0; j < h.width; ++j) { + int clr = read16_le(in); + in += 2; + +#define C16(C) \ + (byte)( ((clr & h.bf_##C##_mask) >> h.bf_##C##_shift) << h.bf_##C##_rshift); + + out->red = C16(red); + out->green = C16(green); + out->blue = C16(blue); + ++out; + +#undef C16 + } + } + + return new SimpleImage(h.width, h.height, PF_R8G8B8, + (byte*)pixels.release()); + } + + Image* ReadBitmap24(const byte* raster_data, const Header& h) { + auto_array pixels(new BGR[h.width * h.height]); + + for (int i = 0; i < h.height; ++i) { + const byte* in = raster_data + i * h.pitch; + BGR* out = pixels + (h.height - i - 1) * h.width; + + for (int j = 0; j < h.width; ++j) { + out->blue = *in++; + out->green = *in++; + out->red = *in++; + ++out; + } + } + + return new SimpleImage(h.width, h.height, PF_B8G8R8, + (byte*)pixels.release()); + } + + Image* ReadBitmap32(const byte* raster_data, const Header& h) { + auto_array pixels(new RGB[h.width * h.height]); + + for (int i = 0; i < h.height; ++i) { + const byte* in = raster_data + i * h.pitch; + RGB* out = pixels + (h.height - i - 1) * h.width; + + for (int j = 0; j < h.width; ++j) { + u32 pixel = read32_le(in); + in += 4; + out->red = (byte)((pixel & h.bf_red_mask) >> h.bf_red_shift); + out->green = (byte)((pixel & h.bf_green_mask) >> h.bf_green_shift); + out->blue = (byte)((pixel & h.bf_blue_mask) >> h.bf_blue_shift); + ++out; + } + } + + return new SimpleImage(h.width, h.height, PF_R8G8B8, + (byte*)pixels.release()); + } + + Image* DecodeBitmap(File* file, const Header& h) { + + if (!file->seek(h.data_offset, File::BEGIN)) { + return 0; + } + + // the raster data stored in the file + auto_array raster_data(new byte[h.image_size]); + if (file->read(raster_data, h.image_size) != h.image_size) { + return 0; + } + + // the output pixel buffer (parameter to new SimpleImage) + auto_array pixels(new byte[h.width * h.height * 3]); + + typedef Image* (*Decoder)(const byte* raster_data, const Header& h); + + Decoder decoder = 0; + + if (h.bpp == 1 && h.compression == 0) { decoder = ReadBitmap1; } + else if (h.bpp == 4 && h.compression == 0) { decoder = ReadBitmap4; } + else if (h.bpp == 4 && h.compression == 2) { decoder = ReadBitmapRLE4; } + else if (h.bpp == 8 && h.compression == 0) { decoder = ReadBitmap8; } + else if (h.bpp == 8 && h.compression == 1) { decoder = ReadBitmapRLE8; } + else if (h.bpp == 16 && (h.compression == 0 || + h.compression == 3)) { decoder = ReadBitmap16; } + else if (h.bpp == 24 && h.compression == 0) { decoder = ReadBitmap24; } + else if (h.bpp == 32 && (h.compression == 0 || + h.compression == 3)) { decoder = ReadBitmap32; } + + if (decoder) { + return decoder(raster_data.get(), h); + } else { + return 0; + } + } + +} diff --git a/corona/src/OpenPCX.cpp b/corona/src/OpenPCX.cpp new file mode 100644 index 0000000..8a30044 --- /dev/null +++ b/corona/src/OpenPCX.cpp @@ -0,0 +1,190 @@ +#include +#include +#include "Debug.h" +#include "Open.h" +#include "SimpleImage.h" +#include "Utility.h" + + +namespace corona { + + /* + struct PCX_HEADER { + byte manufacturer; + byte version; + byte encoding; + byte bits_per_pixel; + word xmin; + word ymin; + word xmax; + word ymax; + word hdpi; + word vdpi; + byte colormap[48]; + byte reserved; + byte num_planes; + word bytes_per_line; + word palette_info; + word h_screen_size; + word v_screen_size; + byte filler[54]; + }; + */ + + + ////////////////////////////////////////////////////////////////////////////// + + bool ReadScanline(File* file, int scansize, byte* scanline) { + byte* out = scanline; + while (out - scanline < scansize) { + + // read a byte! + byte data; + if (file->read(&data, 1) != 1) { + return false; + } + + if ((data & 0xC0) != 0xC0) { // non RLE + *out++ = data; + } else { // RLE + + // read the repeated byte + int numbytes = data & 0x3F; + if (file->read(&data, 1) != 1) { + return false; + } + + while (numbytes-- && out - scanline < scansize) { + *out++ = data; + } + + } + } + + return true; + } + + ////////////////////////////////////////////////////////////////////////////// + + Image* OpenPCX(File* file) { + COR_GUARD("OpenPCX"); + + // read the header block + byte pcx_header[128]; + int read = file->read(pcx_header, 128); + if (read != 128) { + return 0; + } + + // parse the header... + //int manufacturer = pcx_header[0]; + //int version = pcx_header[1]; + int encoding = pcx_header[2]; + int bpp = pcx_header[3]; + int xmin = read16_le(pcx_header + 4); + int ymin = read16_le(pcx_header + 6); + int xmax = read16_le(pcx_header + 8); + int ymax = read16_le(pcx_header + 10); + int num_planes = pcx_header[65]; + int bytes_per_line = read16_le(pcx_header + 66); + + + // verify the header + + // we only support RLE encoding + if (encoding != 1) { + COR_LOG("Unsupported encoding"); + return 0; + } + + COR_IF_DEBUG { + char str[100]; + sprintf(str, "bits per pixel - %d", bpp); + COR_LOG(str); + sprintf(str, "bytes per line - %d", bytes_per_line); + COR_LOG(str); + } + + // we only support 8 bits per pixel + if (bpp != 8) { + COR_LOG("Unsupported bpp"); + return 0; + } + + // create the image structure + int width = xmax - xmin + 1; + int height = ymax - ymin + 1; + + auto_array scanline(new byte[bytes_per_line]); + auto_array pixels(new byte[width * height * 3]); + + // decode the pixel data + + if (num_planes == 1) { // 256 colors + + auto_array palette(new RGB[256]); + auto_array image(new byte[width * height]); + + // read all of the scanlines + for (int iy = 0; iy < height; ++iy) { + if (!ReadScanline(file, bytes_per_line, scanline)) { + COR_LOG("Failure reading scanline"); + return 0; + } + memcpy((byte*)image + iy * width, scanline, width); + } + + // seek back from the end 769 bytes + if (!file->seek(-769, File::END)) { + COR_LOG("Failure seeking to palette"); + return 0; + } + + // do we have a palette? + byte has_palette; + if (file->read(&has_palette, 1) != 1 || has_palette != 12) { + COR_LOG("Failure testing for existence of palette"); + return 0; + } + + // read palette + if (file->read(palette, 3 * 256) != 3 * 256) { + COR_LOG("Failure reading palette"); + return 0; + } + + return new SimpleImage(width, height, PF_I8, image.release(), + (byte*)palette.release(), 256, PF_R8G8B8); + + } else if (num_planes == 3) { // 24-bit color + + auto_array scanline(new byte[3 * bytes_per_line]); + + byte* out = pixels; + for (int iy = 0; iy < height; ++iy) { + if (!ReadScanline(file, 3 * bytes_per_line, scanline)) { + COR_LOG("Failure reading scanline"); + return 0; + } + + byte* r = scanline; + byte* g = scanline + bytes_per_line; + byte* b = scanline + bytes_per_line * 2; + for (int ix = 0; ix < width; ++ix) { + *out++ = *r++; + *out++ = *g++; + *out++ = *b++; + } + } + + return new SimpleImage(width, height, PF_R8G8B8, pixels.release()); + + } else { + COR_LOG("Unknown number of planes"); + return 0; + } + } + + ////////////////////////////////////////////////////////////////////////////// + +} diff --git a/corona/src/OpenPNG.cpp b/corona/src/OpenPNG.cpp new file mode 100644 index 0000000..1ecac1c --- /dev/null +++ b/corona/src/OpenPNG.cpp @@ -0,0 +1,255 @@ +/** + * @todo use our own longjmp instead of libpng's. this way we don't need + * to use PNG_SETJMP_SUPPORTED in Windows, and don't depend on + * png_ptr->jmpbuf in older versions of libpng. + */ + + +#include +#include "Debug.h" +#include "Open.h" +#include "SimpleImage.h" +#include "Utility.h" + + +namespace corona { + + typedef unsigned char byte; + + + ////////////////////////////////////////////////////////////////////////////// + + void PNG_read_function(png_structp png_ptr, + png_bytep data, + png_size_t length) { + File* file = (File*)png_get_io_ptr(png_ptr); + if (file->read(data, length) != int(length)) { + png_error(png_ptr, "Read error"); + } + } + + ////////////////////////////////////////////////////////////////////////////// + + void PNG_warning_function(png_structp png_ptr, png_const_charp error) { + // no warnings + } + + ////////////////////////////////////////////////////////////////////////////// + + void PNG_error_function(png_structp png_ptr, png_const_charp warning) { + // copied from libpng's pngerror.cpp, but without the fprintf + jmp_buf jmpbuf; + memcpy(jmpbuf, png_ptr->jmpbuf, sizeof(jmp_buf)); + longjmp(jmpbuf, 1); + } + + ////////////////////////////////////////////////////////////////////////////// + + void fill_palette(png_structp png, png_infop info, png_color palette[256]) { + + COR_GUARD("fill_palette"); + + // by default, the palette is greyscale + for (int i = 0; i < 256; ++i) { + palette[i].red = i; + palette[i].green = i; + palette[i].blue = i; + } + + // do we have a palette and is it big enough? + png_colorp png_palette; + int num_palette = 0; + png_get_PLTE(png, info, &png_palette, &num_palette); + + COR_IF_DEBUG { + char str[80]; + sprintf(str, "palette size: %d", num_palette); + COR_LOG(str); + } + + if (num_palette >= 256) { + +#if 0 + COR_IF_DEBUG { + for (int i = 0; i < 256; ++i) { + char str[80]; + sprintf(str, "r(%d) g(%d) b(%d)", + int(palette[i].red), + int(palette[i].green), + int(palette[i].blue)); + COR_LOG(str); + } + } +#endif + + memcpy(palette, png_palette, 256 * sizeof(png_color)); + } + } + + ////////////////////////////////////////////////////////////////////////////// + + Image* OpenPNG(File* file) { + + COR_GUARD("OpenPNG"); + + // verify PNG signature + byte sig[8]; + file->read(sig, 8); + if (png_sig_cmp(sig, 0, 8)) { + return 0; + } + + COR_LOG("Signature verified"); + + // read struct + png_structp png_ptr = png_create_read_struct( + PNG_LIBPNG_VER_STRING, + NULL, NULL, NULL); + if (!png_ptr) { + return 0; + } + + COR_LOG("png struct created"); + + // info struct + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, NULL, NULL); + return 0; + } + + COR_LOG("info struct created"); + + // the PNG error function calls longjmp(png_ptr->jmpbuf) + if (setjmp(png_jmpbuf(png_ptr))) { + COR_LOG("Error loading PNG"); + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + return 0; + } + + COR_LOG("setjmp() succeeded"); + + // set the error function + png_set_error_fn(png_ptr, 0, PNG_error_function, PNG_warning_function); + + // read the image + png_set_read_fn(png_ptr, file, PNG_read_function); + png_set_sig_bytes(png_ptr, 8); // we already read 8 bytes for the sig + // always give us 8-bit samples (strip 16-bit and expand <8-bit) + int png_transform = PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_EXPAND; + png_read_png(png_ptr, info_ptr, png_transform, NULL); + + COR_LOG("PNG read"); + + if (!png_get_rows(png_ptr, info_ptr)) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + return 0; + } + + int width = png_get_image_width(png_ptr, info_ptr); + int height = png_get_image_height(png_ptr, info_ptr); + byte* pixels = 0; // allocate when we know the format + PixelFormat format; + byte* palette = 0; + PixelFormat palette_format; + + // decode based on pixel format + int bit_depth = png_get_bit_depth(png_ptr, info_ptr); + int num_channels = png_get_channels(png_ptr, info_ptr); + png_bytepp row_pointers = png_get_rows(png_ptr, info_ptr); + + // 32-bit RGBA + if (bit_depth == 8 && num_channels == 4) { + COR_LOG("32-bit RGBA: bit_depth = 8 && num_channels = 4"); + + format = PF_R8G8B8A8; + pixels = new byte[width * height * 4]; + for (int i = 0; i < height; ++i) { + memcpy(pixels + i * width * 4, row_pointers[i], width * 4); + } + + // 24-bit RGB + } else if (bit_depth == 8 && num_channels == 3) { + COR_LOG("24-bit RGB: bit_depth = 8 && num_channels = 3"); + + format = PF_R8G8B8; + pixels = new byte[width * height * 3]; + for (int i = 0; i < height; ++i) { + memcpy(pixels + i * width * 3, row_pointers[i], width * 3); + } + + // palettized or greyscale with alpha + } else if (bit_depth == 8 && (num_channels == 2 || num_channels == 1)) { + png_color png_palette[256]; + fill_palette(png_ptr, info_ptr, png_palette); + + if (num_channels == 2) { + COR_LOG("bit_depth = 8 && num_channels = 2"); + + format = PF_R8G8B8A8; + pixels = new byte[width * height * 4]; + byte* out = pixels; + + for (int i = 0; i < height; ++i) { + byte* in = row_pointers[i]; + for (int j = 0; j < width; ++j) { + byte c = *in++; + *out++ = png_palette[c].red; + *out++ = png_palette[c].green; + *out++ = png_palette[c].blue; + *out++ = *in++; // alpha + } + } + + } else { // (num_channels == 1) + COR_LOG("bit_depth = 8 && num_channels = 1"); + + pixels = new byte[width * height]; + format = PF_I8; + palette = new byte[256 * 4]; + palette_format = PF_R8G8B8A8; + + + // get the transparent palette flags + png_bytep trans; + int num_trans = 0; + png_color_16p trans_values; // XXX not used - should be? + png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values); + + // copy the palette from the PNG + for (int i = 0; i < 256; ++i) { + palette[i * 4 + 0] = png_palette[i].red; + palette[i * 4 + 1] = png_palette[i].green; + palette[i * 4 + 2] = png_palette[i].blue; + palette[i * 4 + 3] = 255; + } + // apply transparency to palette entries + for (int i = 0; i < num_trans; ++i) { + palette[trans[i] * 4 + 3] = 0; + } + + byte* out = pixels; + for (int i = 0; i < height; ++i) { + memcpy(out, row_pointers[i], width); + out += width; + } + } + + } else { // unknown format + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + return 0; + } + + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + + if (palette) { + return new SimpleImage(width, height, format, pixels, + palette, 256, palette_format); + } else { + return new SimpleImage(width, height, format, pixels); + } + } + + ////////////////////////////////////////////////////////////////////////////// + +} diff --git a/corona/src/OpenTGA.cpp b/corona/src/OpenTGA.cpp new file mode 100644 index 0000000..c651568 --- /dev/null +++ b/corona/src/OpenTGA.cpp @@ -0,0 +1,147 @@ +#include +#include "Debug.h" +#include "Open.h" +#include "SimpleImage.h" +#include "Utility.h" + + +namespace corona { + + Image* OpenTGA(File* file) { + COR_GUARD("OpenTGA"); + + // read header + byte header[18]; + if (file->read(header, 18) != 18) { + return 0; + } + + // decode header + int id_length = header[0]; + int cm_type = header[1]; + int image_type = header[2]; + //int cm_first = read16_le(header + 3); + int cm_length = read16_le(header + 5); + int cm_entry_size = header[7]; // in bits + //int x_origin = read16_le(header + 8); + //int y_origin = read16_le(header + 10); + int width = read16_le(header + 12); + int height = read16_le(header + 14); + int pixel_depth = header[16]; + int image_descriptor = header[17]; + + bool mirrored = (image_descriptor & (1 << 4)) != 0; // left-to-right? + bool flipped = (image_descriptor & (1 << 5)) == 0; // bottom-to-top? + + /* + * image types + * 0 = no image data + * 1 = uncompressed, color-mapped + * 2 = uncompressed, true-color + * 3 = uncompressed, black and white + * 9 = RLE, color-mapped + * 10 = RLE, true-color + * 11 = RLE, black and white + */ + + // make sure we support the image + if (image_type != 2 || (pixel_depth != 24 && pixel_depth != 32)) { + return 0; + } + + // skip image id + byte unused[255]; + if (file->read(unused, id_length) != id_length) { + return 0; + } + + // skip color map + if (cm_type != 0) { + // allocate color map + int cm_entry_bytes = (cm_entry_size + 7) / 8; + int cm_size = cm_entry_bytes * cm_length; + auto_array color_map(new byte[cm_size]); + if (file->read(color_map, cm_size) != cm_size) { + return 0; + } + } + + // read image data + PixelFormat format; + auto_array pixels; + if (pixel_depth == 24) { + + COR_LOG("24-bit image"); + + format = PF_B8G8R8; + int image_size = width * height * 3; + pixels = new byte[image_size]; + if (file->read(pixels, image_size) != image_size) { + return 0; + } + + } else if (pixel_depth == 32) { + + COR_LOG("32-bit image"); + + format = PF_B8G8R8A8; + int image_size = width * height * 4; + pixels = new byte[image_size]; + if (file->read(pixels, image_size) != image_size) { + return 0; + } + + } else { + return 0; + } + + // reverse each row + if (mirrored) { + COR_LOG("Image is mirrored"); + + const int bpp = pixel_depth / 8; // bytes per pixel + for (int y = 0; y < height; ++y) { + + // points to the first pixel of the row + byte* start = pixels.get() + y * width * bpp; + // points to the last pixel of the row + byte* end = start + (width - 1) * bpp; + + while (start < end) { + for (int b = 0; b < bpp; ++b) { + std::swap(start[b], end[b]); + } + start += bpp; + end -= bpp; + } + } + } + + // reverse rows as a whole + if (flipped) { + COR_LOG("Image is flipped"); + + const int bpp = pixel_depth / 8; // bytes per pixel + const int row_size = width * bpp; + auto_array temp(new byte[row_size]); // for the swap + + // points to the beginning of the first row + byte* start = pixels.get(); + + // points to the beginning of the last row + byte* end = start + (height - 1) * width * bpp; + + while (start < end) { + memcpy(temp.get(), start, row_size); + memcpy(start, end, row_size); + memcpy(end, temp.get(), row_size); + + start += row_size; + end -= row_size; + } + } + + return new SimpleImage(width, height, format, pixels.release()); + } + +} diff --git a/corona/src/Save.h b/corona/src/Save.h new file mode 100644 index 0000000..c01ba92 --- /dev/null +++ b/corona/src/Save.h @@ -0,0 +1,16 @@ +#ifndef CORONA_SAVE_H +#define CORONA_SAVE_H + + +#include "corona.h" + + +namespace corona { +#ifndef NO_PNG + bool SavePNG(File* file, Image* image); // SavePNG.cpp +#endif + bool SaveTGA(File* file, Image* image); // SaveTGA.cpp +} + + +#endif diff --git a/corona/src/SavePNG.cpp b/corona/src/SavePNG.cpp new file mode 100644 index 0000000..4efb0a4 --- /dev/null +++ b/corona/src/SavePNG.cpp @@ -0,0 +1,168 @@ +#include +#include +#include "Debug.h" +#include "Save.h" +#include "Types.h" + + +namespace corona { + + void PNG_write(png_structp png_ptr, png_bytep data, png_size_t length) { + File* file = (File*)png_get_io_ptr(png_ptr); + if (file->write(data, length) != int(length)) { + png_error(png_ptr, "Write error"); + } + } + + void PNG_flush(png_structp png_ptr) { + // assume that files always flush + } + + bool SavePNG(File* file, Image* image) { + COR_GUARD("SavePNG"); + + if (!image) { + return false; + } + + // If the image format isn't supported directly by this function, + // clone to a supported format and try to save with that. + switch (image->getFormat()) { + case PF_R8G8B8A8: + case PF_R8G8B8: + case PF_I8: + break; + default: { + COR_LOG("Unsupported pixel format... cloning"); + std::auto_ptr cloned(CloneImage(image, PF_R8G8B8A8)); + return SavePNG(file, cloned.get()); + } + } + + // create write struct + png_structp png_ptr = png_create_write_struct( + PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { + return false; + } + + // error handling! + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_write_struct(&png_ptr, NULL); + return false; + } + + // create info struct + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_write_struct(&png_ptr, NULL); + return false; + } + + int width = image->getWidth(); + int height = image->getHeight(); + + // set image characteristics + png_set_write_fn(png_ptr, file, PNG_write, PNG_flush); + + int color_format = 0; // png output format + int color_format_bpp = 0; // png bytes per pixel + bool color_format_paletted = false; // png palette needed flag + + // figure out output format + switch (image->getFormat()) { + case PF_R8G8B8A8: + color_format = PNG_COLOR_TYPE_RGB_ALPHA; + color_format_bpp = 4; + break; + case PF_R8G8B8: + color_format = PNG_COLOR_TYPE_RGB; + color_format_bpp = 3; + break; + case PF_I8: + color_format = PNG_COLOR_TYPE_PALETTE; + color_format_bpp = 1; + color_format_paletted = true; + break; + default: + // Unsupported format. This should already be taken care of + // by the test at the beginning of this function. + png_destroy_write_struct(&png_ptr, &info_ptr); + return false; + } + + png_set_IHDR( + png_ptr, info_ptr, + width, height, + 8, + color_format, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + png_color* png_palette = 0; + if (color_format_paletted) { + COR_LOG("Saving palettized image..."); + + int image_palette_format = image->getPaletteFormat(); // palette format + int image_palette_size = image->getPaletteSize(); // palette size + + // allocate png palette and get pointer to image palette + png_palette = (png_color*)png_malloc( + png_ptr, sizeof(png_color) * image_palette_size); + byte* image_palette = (byte*)image->getPalette(); + + + if (image_palette_format == PF_R8G8B8) { + // 24 bit source palette + for (int i = 0; i < image_palette_size; i++) { + // copy entry directly + png_palette[i].red = *image_palette++; + png_palette[i].green = *image_palette++; + png_palette[i].blue = *image_palette++; + } + } else if (image_palette_format == PF_R8G8B8A8) { + // 32 bit source palette + for (int i = 0; i < image_palette_size; i++) { + // copy entry, skip alpha + png_palette[i].red = *image_palette++; + png_palette[i].green = *image_palette++; + png_palette[i].blue = *image_palette++; + image_palette++; + } + } + // write palette + png_set_PLTE(png_ptr, info_ptr, png_palette, image_palette_size); + } + + byte* pixels = (byte*)image->getPixels(); + + // build rows + void** rows = (void**)png_malloc(png_ptr, sizeof(void*) * height); + for (int i = 0; i < height; ++i) { + rows[i] = png_malloc(png_ptr, color_format_bpp * width); + memcpy(rows[i], pixels, color_format_bpp * width); + pixels += width * color_format_bpp; + } + png_set_rows(png_ptr, info_ptr, (png_bytepp)rows); + info_ptr->valid |= PNG_INFO_IDAT; + + // actually write the image + png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); + + // clean up memory + for (int i = 0; i < height; ++i) { + png_free(png_ptr, rows[i]); + } + png_free(png_ptr, rows); + + if (png_palette) { + png_free(png_ptr, png_palette); + } + + png_destroy_write_struct(&png_ptr, &info_ptr); + + return true; + } + +} diff --git a/corona/src/SaveTGA.cpp b/corona/src/SaveTGA.cpp new file mode 100644 index 0000000..790c8ef --- /dev/null +++ b/corona/src/SaveTGA.cpp @@ -0,0 +1,49 @@ +#include +#include +#include "Debug.h" +#include "Save.h" +#include "Utility.h" + + +namespace corona { + + bool SaveTGA(File* file, Image* source) { + COR_GUARD("SaveTGA"); + + std::auto_ptr image(CloneImage(source, PF_B8G8R8A8)); + if (!image.get()) { + return false; + } + + const int width = image->getWidth(); + const int height = image->getHeight(); + + // generate the header + byte header[18]; + header[0] = 0; // id_length + header[1] = 0; // cm_type + header[2] = 2; // image_type (uncompressed true color) + write16_le(header + 3, 0); // cm_first + write16_le(header + 5, 0); // cm_length + header[7] = 0; // cm_entry_size + write16_le(header + 8, 0); // x_origin + write16_le(header + 10, 0); // y_origin + write16_le(header + 12, width); + write16_le(header + 14, height); + header[16] = 32; // pixel_depth + header[17] = (1 << 5) | 7; // origin at upper/left, 8 bits of alpha data + + if (file->write(header, 18) != 18) { + return false; + } + + // write pixels + const int data_size = width * height * 4; + if (file->write(image->getPixels(), data_size) != data_size) { + return false; + } + + return true; + } + +} diff --git a/corona/src/SimpleImage.h b/corona/src/SimpleImage.h new file mode 100644 index 0000000..1110ac1 --- /dev/null +++ b/corona/src/SimpleImage.h @@ -0,0 +1,101 @@ +#ifndef CORONA_SIMPLE_IMAGE_H +#define CORONA_SIMPLE_IMAGE_H + + +#include "corona.h" +#include "Types.h" +#include "Utility.h" + + +namespace corona { + + /** + * Basic, flat, simple image. Has a width, a height, a pixel + * format, and a 2D array of pixels (one-byte packing). + * + * The constructor takes a pixel buffer (and optionally a palette) + * which it then owns and delete[]'s when the image is destroyed. + */ + class SimpleImage : public DLLImplementation { + public: + + /** + * Creates a new image, setting all properties. + * + * @param width width of the new image + * @param height height of the new image + * @param format format that the pixels are stored in + * @param pixels pixel buffer that the SimpleImage takes + ownership of. it should be + width*height*sizeof(pixel) bytes. + * @param palette palette color buffer + * @param palette_size number of entries in palette + * @param palette_format color format palette is stored as + */ + SimpleImage(int width, + int height, + PixelFormat format, + byte* pixels, + byte* palette = 0, + int palette_size = 0, + PixelFormat palette_format = PF_DONTCARE) { + + m_width = width; + m_height = height; + m_format = format; + m_pixels = pixels; + m_palette = palette; + m_palette_size = palette_size; + m_palette_format = palette_format; + } + + /** + * Destroys the image, freeing the owned pixel buffer and palette. + */ + ~SimpleImage() { + delete[] m_pixels; + delete[] m_palette; + } + + int COR_CALL getWidth() { + return m_width; + } + + int COR_CALL getHeight() { + return m_height; + } + + PixelFormat COR_CALL getFormat() { + return m_format; + } + + void* COR_CALL getPixels() { + return m_pixels; + } + + void* COR_CALL getPalette() { + return m_palette; + } + + int COR_CALL getPaletteSize() { + return m_palette_size; + } + + PixelFormat COR_CALL getPaletteFormat() { + return m_palette_format; + } + + private: + int m_width; + int m_height; + PixelFormat m_format; + byte* m_pixels; + byte* m_palette; + int m_palette_size; + PixelFormat m_palette_format; + }; + +} + + +#endif diff --git a/corona/src/Types.h b/corona/src/Types.h new file mode 100644 index 0000000..88d189b --- /dev/null +++ b/corona/src/Types.h @@ -0,0 +1,27 @@ +#ifndef CORONA_TYPES_H +#define CORONA_TYPES_H + + +namespace corona { + + // VC++-specific types + #ifdef _MSC_VER + + typedef unsigned char byte; + typedef unsigned __int16 u16; + typedef unsigned __int32 u32; + + // reasonable defaults + // should work on any 32-bit platform + #else + + typedef unsigned char byte; + typedef unsigned short u16; + typedef unsigned long u32; + + #endif + +} + + +#endif diff --git a/corona/src/Utility.h b/corona/src/Utility.h new file mode 100644 index 0000000..59b7407 --- /dev/null +++ b/corona/src/Utility.h @@ -0,0 +1,127 @@ +#ifndef CORONA_UTILITY_H +#define CORONA_UTILITY_H + + +#include +#include "corona.h" +#include "Types.h" + + +#define COR_EXPORT(ret) COR_FUNCTION(ret) + + +#if defined(_MSC_VER) && _MSC_VER <= 1200 + +// define our own std::min and std::max in VC6 +namespace std { + template + T min(T a, T b) { + return a < b ? a : b; + } + template + T max(T a, T b) { + return a > b ? a : b; + } +} + +#endif + + +namespace corona { + + + template + class auto_array { + public: + explicit auto_array(T* initial = 0) { + array = initial; + } + + ~auto_array() { + delete[] array; + } + + operator T*() const { + return array; + } + + T* get() const { + return array; + } + + T* release() { + T* old = array; + array = 0; + return old; + } + + auto_array& operator=(T* a) { + if (array != a) { + delete array; + array = a; + } + return *this; + } + + private: + T* array; + }; + + + inline u16 read16_le(const byte* b) { + return b[0] + (b[1] << 8); + } + + inline void write16_le(byte* b, u16 value) { + b[0] = value & 0xFF; + b[1] = value >> 8; + } + + inline u16 read16_be(const byte* b) { + return (b[0] << 8) + b[1]; + } + + inline void write16_be(byte* b, u16 value) { + b[0] = value >> 8; + b[1] = value & 0xFF; + } + + inline u32 read32_le(const byte* b) { + return read16_le(b) + (read16_le(b + 2) << 16); + } + + inline u32 read32_be(const byte* b) { + return (read16_be(b) << 16) + read16_be(b + 2); + } + + + struct RGB { + byte red; + byte green; + byte blue; + }; + + struct RGBA { + byte red; + byte green; + byte blue; + byte alpha; + }; + + struct BGR { + byte blue; + byte green; + byte red; + }; + + struct BGRA { + byte blue; + byte green; + byte red; + byte alpha; + }; + +} + + +#endif diff --git a/corona/src/corona.h b/corona/src/corona.h new file mode 100644 index 0000000..7c7f349 --- /dev/null +++ b/corona/src/corona.h @@ -0,0 +1,728 @@ +/** + * Corona Image I/O Library + * Version 1.0.2 + * (c) 2003 Chad Austin + * + * This API uses principles explained at + * http://aegisknight.org/cppinterface.html + * + * This code licensed under the terms of the zlib license. See + * license.txt. + * + * + * Note: When compiling this header in gcc, you may want to use the + * -Wno-non-virtual-dtor flag to get rid of those annoying "class has + * virtual functions but no virtual destructor" warnings. + */ + + +#ifndef CORONA_H +#define CORONA_H + + +#ifndef __cplusplus +#error Corona requires C++ +#endif + + +#include + + +// DLLs in Windows should use the standard calling convention +#ifndef COR_CALL +# if defined(WIN32) || defined(_WIN32) +# define COR_CALL __stdcall +# else +# define COR_CALL +# endif +#endif + +// Export functions from the DLL +#ifndef COR_DECL +# if defined(WIN32) || defined(_WIN32) +# ifdef CORONA_EXPORTS +# define COR_DECL __declspec(dllexport) +# else +# define COR_DECL +# endif +# else +# define COR_DECL +# endif +#endif + + +// evil "identifier is too long in debug information" warning +#ifdef _MSC_VER +#pragma warning(disable : 4786) +#endif + + +#define COR_FUNCTION(ret) extern "C" COR_DECL ret COR_CALL + + +namespace corona { + + /** + * File formats supported for reading or writing. + */ + enum FileFormat { + FF_AUTODETECT = 0x0100, + FF_PNG = 0x0101, + FF_JPEG = 0x0102, + FF_PCX = 0x0103, + FF_BMP = 0x0104, + FF_TGA = 0x0105, + FF_GIF = 0x0106, + }; + + /** + * Pixel format specifications. Pixel data can be packed in one of + * the following ways. + */ + enum PixelFormat { + PF_DONTCARE = 0x0200, /**< special format used when specifying a + desired pixel format */ + PF_R8G8B8A8 = 0x0201, /**< RGBA, channels have eight bits of precision */ + PF_R8G8B8 = 0x0202, /**< RGB, channels have eight bits of precision */ + PF_I8 = 0x0203, /**< Palettized, 8-bit indices into palette */ + PF_B8G8R8A8 = 0x0204, /**< BGRA, channels have eight bits of precision */ + PF_B8G8R8 = 0x0205, /**< BGR, channels have eight bits of precision */ + }; + + /** + * Axis specifications. The image can be flipped along the following + * axes. + */ + enum CoordinateAxis { + CA_X = 0x0001, + CA_Y = 0x0002, + }; + + /** + * A helper class for DLL-compatible interfaces. Derive your cross-DLL + * interfaces from this class. + * + * When deriving from this class, do not declare a virtual destructor + * on your interface. + */ + class DLLInterface { + private: + /** + * Destroy the object, freeing all associated memory. This is + * the same as a destructor. + */ + virtual void COR_CALL destroy() = 0; + + public: + /** + * "delete image" should actually call image->destroy(), thus putting the + * burden of calling the destructor and freeing the memory on the image + * object, and thus on Corona's side of the DLL boundary. + */ + void operator delete(void* p) { + if (p) { + DLLInterface* i = static_cast(p); + i->destroy(); + } + } + }; + + + /** + * A helper class for DLL-compatible interface implementations. Derive + * your implementations from DLLImplementation. + */ + template + class DLLImplementation : public Interface { + public: + /** + * So the implementation can put its destruction logic in the destructor, + * as natural C++ code does. + */ + virtual ~DLLImplementation() { } + + /** + * Call the destructor in a Win32 ABI-compatible way. + */ + virtual void COR_CALL destroy() { + delete this; + } + + /** + * So destroy()'s "delete this" doesn't go into an infinite loop, + * calling the interface's operator delete, which calls destroy()... + */ + void operator delete(void* p) { + ::operator delete(p); + } + }; + + + /** + * An image object represents a rectangular collections of pixels. + * They have a width, a height, and a pixel format. Images cannot + * be resized. + */ + class Image : public DLLInterface { + public: + /** + * Get image width. + * @return image width + */ + virtual int COR_CALL getWidth() = 0; + + /** + * Get image height. + * @return image height + */ + virtual int COR_CALL getHeight() = 0; + + /** + * Get pixel format. + * @return pixel format + */ + virtual PixelFormat COR_CALL getFormat() = 0; + + /** + * Get pixel buffer. The pixels are packed in the format defined + * by the image's pixel format. + * + * @return pointer to first element in pixel buffer + */ + virtual void* COR_CALL getPixels() = 0; + + /** + * Get the palette. Pixels are packed in the format defined by + * getPaletteFormat(). + * + * @return pointer to first palette entry + */ + virtual void* COR_CALL getPalette() = 0; + + /** + * Get the number of entries in the palette. + * + * @return number of palette entries + */ + virtual int COR_CALL getPaletteSize() = 0; + + /** + * Get the format of the colors in the palette. + * + * @return pixel format of palette entries + */ + virtual PixelFormat COR_CALL getPaletteFormat() = 0; + }; + + + /** + * Represents a random-access file, usually stored on a disk. Files + * are always binary: that is, they do no end-of-line + * transformations. File objects are roughly analogous to ANSI C + * FILE* objects. + */ + class File : public DLLInterface { + public: + + /** + * The different ways you can seek within a file. + */ + enum SeekMode { + BEGIN, /**< relative to the beginning of the file */ + CURRENT, /**< relative to the current position in the file */ + END /**< relative to the end of the file: position should + be negative*/ + }; + + /** + * Read size bytes from the file, storing them in buffer. + * + * @param buffer buffer to read into + * @param size number of bytes to read + * + * @return number of bytes successfully read + */ + virtual int COR_CALL read(void* buffer, int size) = 0; + + /** + * Write size bytes from buffer to the file. + * + * @param buffer buffer that contains the data to write + * @param size number of bytes to write + * + * @return number of bytes successfully written + */ + virtual int COR_CALL write(const void* buffer, int size) = 0; + + /** + * Jump to a new position in the file, using the specified seek + * mode. Remember: if mode is END, the position must be negative, + * to seek backwards from the end of the file into its contents. + * If the seek fails, the current position is undefined. + * + * @param position position relative to the mode + * @param mode where to seek from in the file + * + * @return true on success, false otherwise + */ + virtual bool COR_CALL seek(int position, SeekMode mode) = 0; + + /** + * Get current position within the file. + * + * @return current position + */ + virtual int COR_CALL tell() = 0; + }; + + + /// Describes a file format that Corona supports. + class FileFormatDesc { + protected: + virtual ~FileFormatDesc() { } + + public: + /// Actual FileFormat being described. + virtual FileFormat getFormat() = 0; + + /// Short description of format, such as "PNG Files" or "JPEG Files" + virtual const char* getDescription() = 0; + + /// @{ + /// List of supported extensions, such as {"bmp", "rle", "dib"} + virtual size_t getExtensionCount() = 0; + virtual const char* getExtension(size_t i) = 0; + /// @} + }; + + + /// PRIVATE API - for internal use only + namespace hidden { + + // these are extern "C" so we don't mangle the names + + + // API information + + COR_FUNCTION(const char*) CorGetVersion(); + + COR_FUNCTION(FileFormatDesc**) CorGetSupportedReadFormats(); + COR_FUNCTION(FileFormatDesc**) CorGetSupportedWriteFormats(); + + // creation + + COR_FUNCTION(Image*) CorCreateImage( + int width, + int height, + PixelFormat format); + + COR_FUNCTION(Image*) CorCreateImageWithPixels( + int width, + int height, + PixelFormat format, + void* pixels); + + COR_FUNCTION(Image*) CorCreatePalettizedImage( + int width, + int height, + PixelFormat format, // must be a palettized format + int palette_size, + PixelFormat palette_format); + + COR_FUNCTION(Image*) CorCloneImage( + Image* source, + PixelFormat format); + + // loading + + COR_FUNCTION(Image*) CorOpenImage( + const char* filename, + FileFormat file_format); + + COR_FUNCTION(Image*) CorOpenImageFromFile( + File* file, + FileFormat file_format); + + // saving + + COR_FUNCTION(bool) CorSaveImage( + const char* filename, + FileFormat file_format, + Image* image); + + COR_FUNCTION(bool) CorSaveImageToFile( + File* file, + FileFormat file_format, + Image* image); + + // conversion + + COR_FUNCTION(Image*) CorConvertImage( + Image* image, + PixelFormat format); + + COR_FUNCTION(Image*) CorConvertPalette( + Image* image, + PixelFormat palette_format); + + COR_FUNCTION(Image*) CorFlipImage( + Image* image, + int coordinate_axis); + + // files + + COR_FUNCTION(File*) CorOpenFile(const char* name, bool writeable); + COR_FUNCTION(File*) CorCreateMemoryFile(const void* buffer, int size); + + // utility + + COR_FUNCTION(int) CorGetPixelSize(PixelFormat format); + } + + + /* PUBLIC API */ + + + /** + * Return the Corona version string. + * + * @return Corona version information + */ + inline const char* GetVersion() { + return hidden::CorGetVersion(); + } + + + /** + * Returns a null-terminated array of FileFormatDesc* pointers that + * describe the file formats Corona can read. The array is owned by + * Corona, so do not delete it when you are done using it. + */ + inline FileFormatDesc** GetSupportedReadFormats() { + return hidden::CorGetSupportedReadFormats(); + } + + /** + * Returns a null-terminated array of FileFormatDesc* pointers that + * describe the file formats Corona can write. The array is owned + * by Corona, so do not delete it when you are done using it. + */ + inline FileFormatDesc** GetSupportedWriteFormats() { + return hidden::CorGetSupportedWriteFormats(); + } + + + /** + * Create a new, blank image with a specified width, height, and + * format. If pixels is specified, Corona uses them to initialize + * the contents of the image. Corona does *not* take ownership of + * the pixel memory, so the caller is responsible for cleaning up + * after itself. If pixels is not specified, the new image is + * filled with zeroes. + * + * @param width width of the new image + * @param height height of the new image + * @param format format the pixels are stored in, cannot be PF_DONTCARE + * @param pixels pixel buffer used to initialize the new image + * + * @return newly created blank image + */ + inline Image* CreateImage( + int width, + int height, + PixelFormat format, + void* pixels = 0) + { + return hidden::CorCreateImageWithPixels(width, height, format, pixels); + } + + /** + * Create a new, blank image with a specified width, height, format, + * and palette. + * + * @param width width of image + * @param height height of image + * @param format format of palette indices, should be PF_I8 + * @param palette_size number of colors in palette + * @param palette_format pixel format of palette entries + */ + inline Image* CreateImage( + int width, + int height, + PixelFormat format, + int palette_size, + PixelFormat palette_format) + { + return hidden::CorCreatePalettizedImage( + width, height, format, + palette_size, palette_format); + } + + /** + * Create a new image from an old one. If format is specified, the + * new image is converted to that pixel format. If format is not + * specified, the new image simply uses the same format as the + * source. If the image could not be cloned or the pixel format is + * invalid, CloneImage returns 0. + * + * @param source image to clone + * @param format format the new image is stored in, defaults to PF_DONTCARE + * + * @return new image cloned from the source, 0 if failure + */ + inline Image* CloneImage( + Image* source, + PixelFormat format = PF_DONTCARE) + { + return hidden::CorCloneImage(source, format); + } + + /** + * Opens an image from the default filesystem. This function simply + * forwards the call to OpenImage(file, file_format, pixel_format) + * with a standard C library file. + * + * See OpenImage(fs, filename, file_format, pixel_format) for more + * information. + * + * @param filename image filename to open + * @param file_format file format the image is stored in, or FF_AUTODETECT + * to try all loaders + * @param pixel_format desired pixel format, or PF_DONTCARE to use image's + * native format + * + * @return the image loaded from the disk, or 0 if it cannot be opened + */ + inline Image* OpenImage( + const char* filename, + PixelFormat pixel_format = PF_DONTCARE, + FileFormat file_format = FF_AUTODETECT) + { + return hidden::CorConvertImage( + hidden::CorOpenImage(filename, file_format), + pixel_format); + } + + /** + * Opens an image from the specified file. + * + * If file_format is FF_AUTODETECT, the loader tries + * to load each format until it finds one that succeeds. Otherwise, + * it tries the specific loader specified. + * + * If pixel_format is PF_DONTCARE, the new image object has the + * pixel format closest to the image's format on disk. Otherwise, + * the pixels are converted to the specified format before the image + * is returned. + * + * @param file name of the file that contains the image + * @param file_format file format the image is stored in, or FF_AUTODETECT + * to try all loaders + * @param pixel_format desired pixel format, or PF_DONTCARE to use image's + * native format + * + * @return the image loaded from the file, or 0 if it cannot be opened + */ + inline Image* OpenImage( + File* file, + PixelFormat pixel_format = PF_DONTCARE, + FileFormat file_format = FF_AUTODETECT) + { + return hidden::CorConvertImage( + hidden::CorOpenImageFromFile(file, file_format), + pixel_format); + } + + /// For compatibility. This function may be deprecated. + inline Image* OpenImage( + const char* filename, + FileFormat file_format, + PixelFormat pixel_format = PF_DONTCARE) + { + return OpenImage(filename, pixel_format, file_format); + } + + /// For compatibility. This function may be deprecated. + inline Image* OpenImage( + File* file, + FileFormat file_format, + PixelFormat pixel_format = PF_DONTCARE) + { + return OpenImage(file, pixel_format, file_format); + } + + /** + * Saves an image to a file in the default filesystem. This + * function simply calls SaveImage(file, file_format, image) + * with a standard C library file. + * + * See SaveImage(fs, filename, file_format, image) for more information. + * + * @param filename name of the file to save the image to + * @param file_format file format in which to save image. if FF_AUTODETECT, + * SaveImage guesses the type from the file extension + * @param image image to save + * + * @return true if save succeeds, false otherwise + */ + inline bool SaveImage( + const char* filename, + FileFormat file_format, + Image* image) + { + return hidden::CorSaveImage(filename, file_format, image); + } + + /** + * Saves an image to the specified file. This function saves image + * to a file of type file_format. If file_format is not a supported + * output type, the function fails. As of now, Corona only supports + * saving images of type FF_PNG and FF_TGA. + * + * @note This function may create the file even if the save does not + * succeed, so users of this library should remove the file after + * the call to SaveImage(). + * + * @param file file in which to save the image + * @param file_format file format in which to save image -- must not be + * FF_AUTODETECT + * @param image image to save + * + * @return true if the save succeeds, false otherwise + */ + inline bool SaveImage( + File* file, + FileFormat file_format, + Image* image) + { + return hidden::CorSaveImageToFile(file, file_format, image); + } + + /** + * Converts an image from one format to another, destroying the old + * image. If source is 0, the function returns 0. If format is + * PF_DONTCARE or the source and target formats match, returns the + * unmodified source image. If a valid conversion is not found, + * ConvertImage destroys the old image and returns 0. For example, + * ConvertImage does not support creating a palettized image from a + * direct color image yet. + * + * @param source image to convert + * @param format desired format -- can be PF_DONTCARE + * + * @return valid image object if conversion succeeds, 0 otherwise + */ + inline Image* ConvertImage(Image* source, PixelFormat format) { + return hidden::CorConvertImage(source, format); + } + + /** + * Converts the palette of a palettized image from one format to + * another, destroying the old image. If the source is 0, the + * palette_format is PF_DONTCARE, or the source and target formats + * match, the function returns the unmodified source image. If a + * valid conversion is not found or invalid inputs are given (such + * as a direct-color source image), this function destroys the old + * image and returns 0. + * + * @param source palettized image to convert + * @param palette_format desired pixel format of palette + * + * @return valid image object if conversion succeeds, 0 otherwise + */ + inline Image* ConvertPalette(Image* source, PixelFormat palette_format) { + return hidden::CorConvertPalette(source, palette_format); + } + + /** + * Flips the pixels in the image around the given axis. + * + * @param source image to flip + * @param coordinate_axis Axis around which to flip. Both CA_X and CA_Y + * can be specified by ORing them together. + * + * @return the image passed in + */ + inline Image* FlipImage(Image* source, int coordinate_axis) { + return hidden::CorFlipImage(source, coordinate_axis); + } + + /** + * Returns a default File implementation. + * + * @param filename name of the file on local filesystem + * @param writeable whether the file can be written to + */ + inline File* OpenFile(const char* filename, bool writeable) { + return hidden::CorOpenFile(filename, writeable); + } + + /** + * Creates a File implementation that reads from a buffer in memory. + * It stores a copy of the buffer that is passed in. + * + * The File object does not take ownership of the memory buffer. + * When the file is destroyed, it will not free the memory. + * + * @param buffer Pointer to the beginning of the data. + * @param size Size of the buffer in bytes. + * + * @return 0 if size is non-zero and buffer is null. Otherwise, + * returns a valid File object. + */ + inline File* CreateMemoryFile(const void* buffer, int size) { + return hidden::CorCreateMemoryFile(buffer, size); + } + + /** + * Returns the number of bytes needed to store a pixel of a gixen format. + * + * @param format The format to query. + * + * @return Number of bytes each pixel takes, or 0 if the format is invalid. + */ + inline int GetPixelSize(PixelFormat format) { + return hidden::CorGetPixelSize(format); + } + + /** + * Returns true if the pixel format does not require a palette; that + * is, if each pixel itself contains color data. + * + * @param format The format to query. + * + * @return True if format is direct color, false otherwise. + */ + inline bool IsDirect(PixelFormat format) { + return (format == PF_R8G8B8A8 || format == PF_R8G8B8 || + format == PF_B8G8R8A8 || format == PF_B8G8R8); + } + + /** + * Returns true if the pixel format requires a palette; that + * is, if each pixel is an index into a separate palette. + * + * @param format The format to query. + * + * @return True if format is palettized, false otherwise. + */ + inline bool IsPalettized(PixelFormat format) { + return format == PF_I8; + } + + /** + * Returns the number of color entries in a palette for an image + * of the given format. + * + * @param format The format to query. + * + * @return Number of color entries, or 0 if the format is not palettized. + */ + inline int GetPaletteSize(PixelFormat format) { + return (format == PF_I8 ? 256 : 0); + } + +} + + +#endif diff --git a/engine/AABB.h b/engine/AABB.h new file mode 100644 index 0000000..2562186 --- /dev/null +++ b/engine/AABB.h @@ -0,0 +1,39 @@ +#ifndef BLUECORE_BOUNDING_CUBE_H +#define BLUECORE_BOUNDING_CUBE_H + +#include "Math/Transformation.h" + +namespace BlueCore +{ + + class AABB + { + + public: + Vector3 Min; + Vector3 Max; + + bool overlaps (const AABB &aabb) + { + bool overlap = true; + + overlap = (Min.x > aabb.Max.x || Max.x < aabb.Min.x) ? false : overlap; + overlap = (Min.y > aabb.Max.y || Max.y < aabb.Min.y) ? false : overlap; + overlap = (Min.z > aabb.Max.z || Max.z < aabb.Min.z) ? false : overlap; + + return overlap; + } + + AABB transformed (const Transformation& transformation) + { + AABB aabb; + + Vector3 new_max = transformation.transform (Max); + Vector3 new_min = transformation.transform (Min); + + return aabb; + } + }; +} + +#endif diff --git a/engine/Camera.cpp b/engine/Camera.cpp new file mode 100644 index 0000000..1c4db39 --- /dev/null +++ b/engine/Camera.cpp @@ -0,0 +1,123 @@ +#include "Camera.h" + +#include "Math/Matrix.h" + +#include "GL/glfw.h" + +namespace BlueCore +{ + +//-------------------------------------------------------------------------- +Camera::Camera() +{ +} + +//-------------------------------------------------------------------------- +Camera::~Camera() +{ +} + +//-------------------------------------------------------------------------- +void Camera::setFoV(Scalar fov) +{ + _FoV = fov; +} + +//-------------------------------------------------------------------------- +void Camera::setAspectRatio(Scalar aspect) +{ + _AspectRatio = aspect; +} + +//-------------------------------------------------------------------------- +void Camera::setNearPlane(Scalar near) +{ + _NearPlane = near; +} + +//-------------------------------------------------------------------------- +void Camera::setFarPlane(Scalar far) +{ + _FarPlane = far; +} + +//-------------------------------------------------------------------------- +void Camera::setPosition(const Vector3& position) +{ + _Position = position; +} + +const Vector3& Camera::getPosition() +{ + return _Position; +} + +//-------------------------------------------------------------------------- +void Camera::setRotation(const Quaternion& rotation) +{ + _Rotation = rotation; +} + +//-------------------------------------------------------------------------- +void Camera::setupProjectionMatrix() +{ + Scalar fW, fH; + + fH = tan ( (_FoV / 2) / 180* Pi ) * _NearPlane; + fW = fH * _AspectRatio; + + // setup projectiom matrix + glMatrixMode (GL_PROJECTION ); + glLoadIdentity(); + glFrustum ( -fW, fW, -fH, fH, _NearPlane, _FarPlane ); + + // save variables for frustum culling + /* + _near = nearZ; + _far = farZ; + _hNear = tan ( ( fov / 2 ) / 180 * Pi ) * nearZ; + _wNear = _hNear * aspect; + _hFar = tan ( ( fov / 2 ) / 180 * Pi ) * farZ; + _wFar = _hFar * aspect; + */ +} + +//-------------------------------------------------------------------------- +void Camera::setupViewMatrix() +{ + // set the view matrix + glMatrixMode (GL_MODELVIEW ); + glLoadIdentity(); + Matrix4x4 m(_Rotation, _Rotation.applyInversed(_Position * -1.0) ); + glMultMatrixd ( ( GLdouble * ) &m.m ); + /* + // calculate frustum planes + Vector3 up = q.apply ( Vector3 ( 0.0, 1.0, 0.0 ) ); + Vector3 right = q.apply ( Vector3 ( 1.0, 0.0, 0.0 ) ); + Vector3 d = q.apply ( Vector3 ( 0.0, 0.0, -1.0 ) ); + + Vector3 fc = p + d * _far; + + Vector3 ftl = fc + ( up * _hFar ) - ( right * _wFar ); + Vector3 ftr = fc + ( up * _hFar ) + ( right * _wFar ); + Vector3 fbl = fc - ( up * _hFar ) - ( right * _wFar ); + Vector3 fbr = fc - ( up * _hFar ) + ( right * _wFar ); + + Vector3 nc = p + d * _near; + + Vector3 ntl = nc + ( up * _hNear ) - ( right * _wNear ); + Vector3 ntr = nc + ( up * _hNear ) + ( right * _wNear ); + Vector3 nbl = nc - ( up * _hNear ) - ( right * _wNear ); + Vector3 nbr = nc - ( up * _hNear ) + ( right * _wNear ); + + _frustumPlanes[RightPlane] = Plane ( nbr, fbr, ntr ); + _frustumPlanes[LeftPlane] = Plane ( ntl, fbl, nbl ); + _frustumPlanes[BottomPlane] = Plane ( nbl, fbr, nbr ); + _frustumPlanes[TopPlane] = Plane ( ntr, ftl, ntl ); + _frustumPlanes[FarPlane] = Plane ( ftl, ftr, fbl ); + _frustumPlanes[NearPlane] = Plane ( ntl, nbl, ntr ); + */ +} + +} // namespace BlueCore + diff --git a/engine/Camera.h b/engine/Camera.h new file mode 100644 index 0000000..cabac1e --- /dev/null +++ b/engine/Camera.h @@ -0,0 +1,55 @@ +#ifndef BLUECORE_CAMERA_H +#define BLUECORE_CAMERA_H + +// project includes +#include "Utilities/Referenced.h" +#include "Math/Vector.h" +#include "Math/Quaternion.h" +#include "Math/Plane.h" +//#include "geometry/frustum.h" + +namespace BlueCore { + +class Camera : public Referenced +{ + +private: + + Vector3 _LookAtPoint; + Vector3 _LookAtUp; + bool _LookAt; + Scalar _NearPlane, _FarPlane, _FoV, _AspectRatio; + //Frustum _Frustum; + + Vector3 _Position; + Quaternion _Rotation; + +public: + + Camera(); + ~Camera(); + + void lookAt( const Vector3 &point ); + void lookAt( const Vector3 &point, const Vector3 &up ); + void lookStraight(); + + void setFoV( Scalar fov ); + void setAspectRatio( Scalar aspect ); + void setNearPlane( Scalar near ); + void setFarPlane( Scalar far ); + + void setPosition (const Vector3& position); + const Vector3& getPosition (); + + void setRotation (const Quaternion& rotation); + + void setupViewMatrix (); + void setupProjectionMatrix (); + + //const Frustum &getFrustum() const; +}; + +} // namespace BlueCore + +#endif + diff --git a/engine/FileSystem.h b/engine/FileSystem.h new file mode 100644 index 0000000..bdfd283 --- /dev/null +++ b/engine/FileSystem.h @@ -0,0 +1,57 @@ +#include "Utilities/Referenced.h" + + +class File : public Referenced +{ +public: + bool isOpen (); + unsigned long read (char *buffer, unsigned long size); +}; + + + +class FileSystem : public Referenced +{ +public: + + FileSystem(); + File *openBinary (const std::string& filename); + +private: + + static bool _PhysfsInialized; +}; + + + +FileSystem::FileSystem() +{ + PHYSFS_init (0); + + std::string appdir = PHYSFS_getUserDir(); + appdir += ".bluecore"; + + if( !PHYSFS_setWriteDir(appdir.c_str()) ) + { + if( (PHYSFS_setWriteDir(PHYSFS_getUserDir())) && (PHYSFS_mkdir(".bluecore")) ) + PHYSFS_setWriteDir( appdir.c_str() ); + } + + PHYSFS_addToSearchPath( appdir.c_str(), 0 ); + PHYSFS_addToSearchPath( "data", 1 ); + + char **rc = PHYSFS_enumerateFiles ( "" ); + + for ( char **i = rc; *i != 0; i++ ) + { + std::string filename ( *i ); + + if ( filename.substr ( filename.size() - 4, 4 ) == ".zip" ) + { + PHYSFS_addToSearchPath ( ( "data/" + filename ).c_str(), 1 ); + BlueCore::log << ">>> Using addon: " << filename << endlog; + } + } + + PHYSFS_freeList ( rc ); +} diff --git a/engine/FontManager.cpp b/engine/FontManager.cpp new file mode 100644 index 0000000..c1773f0 --- /dev/null +++ b/engine/FontManager.cpp @@ -0,0 +1,204 @@ +#include "FontManager.h" +#include "RenderDevice.h" + +// project includes +#include "Utilities/Buffer.h" +#include "Utilities/Log.h" +#include "Utilities/format.h" + +// library includes +#include "physfs.h" +#include "FTGLTextureFont.h" + +// system includes +#include + +using namespace std; + +#include "FontManagerDefaultFont.h" + +namespace BlueCore +{ + +//------------------------------------------------------------------------------ +FontManager::FontManager(RenderDevice* device) : + _Device(device) +{ + // create default font + FTFont *ftfont = new FTGLTextureFont ( + DefaultFontBytes, + sizeof ( DefaultFontBytes ), + false ); + ftfont->FaceSize( 16); + _DefaultFont = new Font( ftfont, 0, device ); + + if (_Device.valid()) + _Device->DeviceShutdownSignal.connect(this, + &FontManager::DeviceShutdownSlot); + + clog << ">>> FontManager constructed..."<< endline; +} + +//------------------------------------------------------------------------------ +FontManager::~FontManager() +{ + destroy(); + + clog << ">>> FontManager destructed..."<< endline; +} + +//------------------------------------------------------------------------------ +void FontManager::DeviceShutdownSlot() +{ + destroy(); +} + +//------------------------------------------------------------------------------ +void FontManager::destroy() +{ + FontMap::iterator i; + + for (i = _fonts.begin(); i != _fonts.end(); i++) + { + if (i->second.valid()) + i->second->destroy(); + } + + _DefaultFont = 0; +} + +//------------------------------------------------------------------------------ +Font *FontManager::loadFont(const std::string &filename, int size, bool hinting) +{ + string key = format("%s-%d", filename.c_str(), size); + + // check if this font is already loaded + FontMap::const_iterator result; + result = _fonts.find(key); + + if (result != _fonts.end() && result->second.valid()) + { + return result->second.get(); + } + + Font *font; + + PHYSFS_file *file = PHYSFS_openRead(filename.c_str() ); + + if (file) + { + unsigned int file_size = PHYSFS_fileLength(file); + unsigned char *buffer = new unsigned char[file_size]; + + PHYSFS_read(file, buffer, 1, file_size); + PHYSFS_close(file); + + // create the font + FTFont *ftfont = new FTGLTextureFont ( buffer, file_size, hinting ); + ftfont->FaceSize(size); + + if (ftfont->Error() == 0) + { + font = new Font( ftfont, buffer, _Device.get() ); + clog << ">>> Font '"<< filename << "' ("<< size << ") loaded" + << endline; + } + else + { + delete ftfont; + clog << "!!! Font '"<< filename << "' could not be loaded" + << endline; + font = new Font(); + } + } + else + { + clog << "!!! Font '"<< filename << "' not found"<< endline; + font = new Font(); + } + + _fonts[key] = font; + + return font; +} + +//------------------------------------------------------------------------------ +Font *FontManager::getDefaultFont() +{ + return _DefaultFont.get(); +} + +//------------------------------------------------------------------------------ +Font::Font() +{ + _Font = 0; + _Buffer = 0; + clog << ">>> Font constructed..."<< endline; +} + +//------------------------------------------------------------------------------ +Font::Font(FTFont *font, unsigned char *buffer, RenderDevice* device) : + _Font(font), _Buffer(buffer), _Device(device) +{ + clog << ">>> Font constructed..."<< endline; +} + +//------------------------------------------------------------------------------ +Font::~Font() +{ + destroy(); + clog << ">>> Font destructed..."<< endline; +} + +//------------------------------------------------------------------------------ +void Font::destroy() +{ + delete _Font; + delete [] _Buffer; + clog << ">>> Font destroyed..."<< endline; +} + +//------------------------------------------------------------------------------ +void Font::print(float x, float y, const std::string &text, int halign, + int valign) const +{ + if (_Font == 0) + return; + + if (_Device.valid() == false) + return; + + _Device->begin2D(); + + if (x < 0.0) + x += _Device->getViewportWidth(); + else if (x <= 1.0) + x *= _Device->getViewportWidth(); + + if (halign == 0) + x -= _Font->Advance(text.c_str() ) / 2; + else if (halign == -1) + x -= _Font->Advance(text.c_str() ); + + if (y < 0.0) + y += _Device->getViewportHeight(); + else if (y <= 1.0) + y *= _Device->getViewportHeight(); + + y -= _Font->Descender(); + if (valign == 0) + y -= _Font->LineHeight() / 2; + else if (valign == -1) + y -= _Font->LineHeight(); + + glEnable(GL_TEXTURE_2D); + glPushMatrix(); + glLoadIdentity(); + glTranslatef(x, y, 0); + _Font->Render(text.c_str() ); + glPopMatrix(); + + _Device->end2D(); +} + +} // namespace BlueCore diff --git a/engine/FontManager.h b/engine/FontManager.h new file mode 100644 index 0000000..9a6663c --- /dev/null +++ b/engine/FontManager.h @@ -0,0 +1,70 @@ +#ifndef BLUECORE_FONT_MANAGER_H +#define BLUECORE_FONT_MANAGER_H + +// system includes +#include +#include + +// project includes +#include "Utilities/Referenced.h" + +#include "RenderDevice.h" + +// forward declaratins +class FTFont; + +namespace BlueCore +{ +class Font : public Referenced +{ + FTFont *_Font; + unsigned char *_Buffer; + ref_ptr _Device; + + protected: + + ~Font(); + + public: + + Font(FTFont *font, unsigned char *buffer, RenderDevice *device); + Font(); + + void destroy(); + + void print(float x, float y, const std::string &text, int halign = 0, + int valign = 0) const; +}; + +class FontManager : public Referenced, public sigslot::has_slots<> +{ + + private: + + typedef std::map > FontMap; + + FontMap _fonts; + ref_ptr _DefaultFont; + + void FontDestroyedSlot(Referenced*); + + void DeviceShutdownSlot(); + + void destroy(); + + public: + + FontManager(RenderDevice* device); + ~FontManager(); + + Font *loadFont(const std::string &name, int size, bool hinting = true); + Font *getDefaultFont(); + + private: + + ref_ptr _Device; +}; + +} // namespace BlueCore + +#endif // BLUECORE_FONT_MANAGER_H diff --git a/engine/FontManagerDefaultFont.h b/engine/FontManagerDefaultFont.h new file mode 100644 index 0000000..20efa0b --- /dev/null +++ b/engine/FontManagerDefaultFont.h @@ -0,0 +1,4483 @@ +/* +This is the font "ProggyTiny.ttf" from http://www.proggyfonts.com. + +Copyright (c) 2004, 2005 Tristan Grimmer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +const unsigned char DefaultFontBytes[] = + { + /*[0000]*/ 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x80, + /*[0008]*/ 0x00, 0x03, 0x00, 0x40, 0x4f, 0x53, 0x2f, 0x32, + /*[0010]*/ 0x87, 0xeb, 0x73, 0x10, 0x00, 0x00, 0x01, 0x48, + /*[0018]*/ 0x00, 0x00, 0x00, 0x4e, 0x63, 0x6d, 0x61, 0x70, + /*[0020]*/ 0x02, 0x12, 0x23, 0x75, 0x00, 0x00, 0x03, 0xa0, + /*[0028]*/ 0x00, 0x00, 0x01, 0x52, 0x63, 0x76, 0x74, 0x20, + /*[0030]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xfc, + /*[0038]*/ 0x00, 0x00, 0x00, 0x02, 0x67, 0x6c, 0x79, 0x66, + /*[0040]*/ 0xd3, 0xee, 0x60, 0xd0, 0x00, 0x00, 0x07, 0x04, + /*[0048]*/ 0x00, 0x00, 0x7c, 0xd4, 0x68, 0x65, 0x61, 0x64, + /*[0050]*/ 0xd7, 0x11, 0x68, 0xb2, 0x00, 0x00, 0x00, 0xcc, + /*[0058]*/ 0x00, 0x00, 0x00, 0x36, 0x68, 0x68, 0x65, 0x61, + /*[0060]*/ 0x06, 0xc2, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x04, + /*[0068]*/ 0x00, 0x00, 0x00, 0x24, 0x68, 0x6d, 0x74, 0x78, + /*[0070]*/ 0x58, 0x80, 0x4c, 0x80, 0x00, 0x00, 0x01, 0x98, + /*[0078]*/ 0x00, 0x00, 0x02, 0x06, 0x6c, 0x6f, 0x63, 0x61, + /*[0080]*/ 0x45, 0xf3, 0x64, 0xf4, 0x00, 0x00, 0x05, 0x00, + /*[0088]*/ 0x00, 0x00, 0x02, 0x04, 0x6d, 0x61, 0x78, 0x70, + /*[0090]*/ 0x01, 0xa6, 0x00, 0xba, 0x00, 0x00, 0x01, 0x28, + /*[0098]*/ 0x00, 0x00, 0x00, 0x20, 0x6e, 0x61, 0x6d, 0x65, + /*[00a0]*/ 0x01, 0xfc, 0x6e, 0xbc, 0x00, 0x00, 0x83, 0xd8, + /*[00a8]*/ 0x00, 0x00, 0x01, 0x9b, 0x70, 0x6f, 0x73, 0x74, + /*[00b0]*/ 0xa6, 0xac, 0x83, 0xef, 0x00, 0x00, 0x85, 0x74, + /*[00b8]*/ 0x00, 0x00, 0x05, 0xd2, 0x70, 0x72, 0x65, 0x70, + /*[00c0]*/ 0x69, 0x02, 0x01, 0x12, 0x00, 0x00, 0x04, 0xf4, + /*[00c8]*/ 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, + /*[00d0]*/ 0x00, 0x01, 0x00, 0x00, 0xf6, 0xa4, 0x10, 0xea, + /*[00d8]*/ 0x5f, 0x0f, 0x3c, 0xf5, 0x00, 0x03, 0x08, 0x00, + /*[00e0]*/ 0x00, 0x00, 0x00, 0x00, 0xb7, 0x67, 0x77, 0x84, + /*[00e8]*/ 0x00, 0x00, 0x00, 0x00, 0xbd, 0x92, 0xa9, 0x33, + /*[00f0]*/ 0x00, 0x00, 0xff, 0x00, 0x03, 0x00, 0x04, 0x00, + /*[00f8]*/ 0x00, 0x00, 0x00, 0x06, 0x00, 0x02, 0x00, 0x00, + /*[0100]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + /*[0108]*/ 0x03, 0xc0, 0xfe, 0xc0, 0x00, 0x00, 0x03, 0x00, + /*[0110]*/ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, + /*[0118]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0120]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + /*[0128]*/ 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x70, + /*[0130]*/ 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + /*[0138]*/ 0x00, 0x08, 0x00, 0x40, 0x00, 0x0a, 0x00, 0x00, + /*[0140]*/ 0x00, 0x76, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + /*[0148]*/ 0x00, 0x00, 0x03, 0x00, 0x01, 0x90, 0x00, 0x05, + /*[0150]*/ 0x00, 0x00, 0x02, 0xbc, 0x02, 0x8a, 0x00, 0x00, + /*[0158]*/ 0x00, 0x8f, 0x02, 0xbc, 0x02, 0x8a, 0x00, 0x00, + /*[0160]*/ 0x01, 0xc5, 0x00, 0x32, 0x01, 0x80, 0x00, 0x00, + /*[0168]*/ 0x00, 0x00, 0x04, 0x09, 0x00, 0x00, 0x00, 0x00, + /*[0170]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0178]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0180]*/ 0x00, 0x00, 0x41, 0x6c, 0x74, 0x73, 0x00, 0x40, + /*[0188]*/ 0x00, 0x00, 0x20, 0xac, 0x08, 0x00, 0x00, 0x00, + /*[0190]*/ 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, + /*[0198]*/ 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + /*[01a0]*/ 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + /*[01a8]*/ 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + /*[01b0]*/ 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + /*[01b8]*/ 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + /*[01c0]*/ 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + /*[01c8]*/ 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + /*[01d0]*/ 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + /*[01d8]*/ 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + /*[01e0]*/ 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, + /*[01e8]*/ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x80, + /*[01f0]*/ 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + /*[01f8]*/ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0200]*/ 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0208]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0210]*/ 0x00, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, + /*[0218]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0220]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0228]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0230]*/ 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0238]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0240]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0248]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0250]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + /*[0258]*/ 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + /*[0260]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0268]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0270]*/ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, + /*[0278]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0280]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + /*[0288]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0290]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, + /*[0298]*/ 0x00, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + /*[02a0]*/ 0x03, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x80, + /*[02a8]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + /*[02b0]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + /*[02b8]*/ 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + /*[02c0]*/ 0x01, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x80, + /*[02c8]*/ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + /*[02d0]*/ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + /*[02d8]*/ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + /*[02e0]*/ 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, + /*[02e8]*/ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, + /*[02f0]*/ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, + /*[02f8]*/ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + /*[0300]*/ 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x01, 0x00, + /*[0308]*/ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + /*[0310]*/ 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, + /*[0318]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0320]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0328]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0330]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + /*[0338]*/ 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, + /*[0340]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0348]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0350]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0358]*/ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, + /*[0360]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0368]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0370]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + /*[0378]*/ 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, + /*[0380]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0388]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0390]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0398]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[03a0]*/ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + /*[03a8]*/ 0x00, 0x00, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x00, + /*[03b0]*/ 0x00, 0x00, 0x00, 0x4c, 0x00, 0x03, 0x00, 0x01, + /*[03b8]*/ 0x00, 0x00, 0x00, 0x1c, 0x00, 0x04, 0x00, 0x30, + /*[03c0]*/ 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x02, + /*[03c8]*/ 0x00, 0x00, 0x00, 0x7f, 0x00, 0xff, 0x20, 0xac, + /*[03d0]*/ 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, + /*[03d8]*/ 0x20, 0xac, 0xff, 0xff, 0x00, 0x01, 0x00, 0x01, + /*[03e0]*/ 0xdf, 0xd5, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + /*[03e8]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, + /*[03f0]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[03f8]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0400]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0408]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0410]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0418]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0420]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0428]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0430]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0438]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0440]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0448]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0450]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0458]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0460]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0468]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0470]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0478]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0480]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0488]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0490]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[0498]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[04a0]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[04a8]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[04b0]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[04b8]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[04c0]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[04c8]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, + /*[04d0]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[04d8]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[04e0]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[04e8]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[04f0]*/ 0x00, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x01, 0x8d, + /*[04f8]*/ 0xb8, 0x01, 0xff, 0x85, 0x00, 0x00, 0x00, 0x00, + /*[0500]*/ 0x00, 0x00, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, + /*[0508]*/ 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, + /*[0510]*/ 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, + /*[0518]*/ 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, + /*[0520]*/ 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, + /*[0528]*/ 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, + /*[0530]*/ 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, + /*[0538]*/ 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, + /*[0540]*/ 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0xc6, + /*[0548]*/ 0x00, 0xee, 0x01, 0x50, 0x01, 0xb0, 0x02, 0x04, + /*[0550]*/ 0x02, 0x66, 0x02, 0x7e, 0x02, 0xb8, 0x02, 0xf0, + /*[0558]*/ 0x03, 0x32, 0x03, 0x68, 0x03, 0x7a, 0x03, 0x98, + /*[0560]*/ 0x03, 0xa4, 0x03, 0xe0, 0x04, 0x42, 0x04, 0x7e, + /*[0568]*/ 0x04, 0xd2, 0x05, 0x22, 0x05, 0x74, 0x05, 0xd0, + /*[0570]*/ 0x06, 0x26, 0x06, 0x66, 0x06, 0xc8, 0x07, 0x1e, + /*[0578]*/ 0x07, 0x32, 0x07, 0x4c, 0x07, 0x7c, 0x07, 0xb2, + /*[0580]*/ 0x07, 0xe2, 0x08, 0x1a, 0x08, 0x9e, 0x08, 0xec, + /*[0588]*/ 0x09, 0x5a, 0x09, 0xa6, 0x0a, 0x04, 0x0a, 0x64, + /*[0590]*/ 0x0a, 0xb2, 0x0b, 0x14, 0x0b, 0x78, 0x0b, 0xb8, + /*[0598]*/ 0x0b, 0xfe, 0x0c, 0x52, 0x0c, 0x92, 0x0d, 0x02, + /*[05a0]*/ 0x0d, 0x78, 0x0d, 0xd6, 0x0e, 0x2c, 0x0e, 0x94, + /*[05a8]*/ 0x0e, 0xfa, 0x0f, 0x4e, 0x0f, 0x8e, 0x0f, 0xe8, + /*[05b0]*/ 0x10, 0x32, 0x10, 0x94, 0x10, 0xe4, 0x11, 0x2c, + /*[05b8]*/ 0x11, 0x80, 0x11, 0xcc, 0x12, 0x02, 0x12, 0x4e, + /*[05c0]*/ 0x12, 0x72, 0x12, 0x94, 0x12, 0xa6, 0x12, 0xf4, + /*[05c8]*/ 0x13, 0x54, 0x13, 0x92, 0x13, 0xf4, 0x14, 0x46, + /*[05d0]*/ 0x14, 0x92, 0x14, 0xf2, 0x15, 0x4a, 0x15, 0x78, + /*[05d8]*/ 0x15, 0xba, 0x16, 0x08, 0x16, 0x40, 0x16, 0x96, + /*[05e0]*/ 0x16, 0xde, 0x17, 0x26, 0x17, 0x82, 0x17, 0xde, + /*[05e8]*/ 0x18, 0x14, 0x18, 0x5a, 0x18, 0x9a, 0x18, 0xe2, + /*[05f0]*/ 0x19, 0x1c, 0x19, 0x64, 0x19, 0x9e, 0x19, 0xfa, + /*[05f8]*/ 0x1a, 0x42, 0x1a, 0x7c, 0x1a, 0xb6, 0x1a, 0xee, + /*[0600]*/ 0x1b, 0x14, 0x1b, 0x14, 0x1b, 0x7e, 0x1b, 0x7e, + /*[0608]*/ 0x1b, 0x90, 0x1b, 0xde, 0x1b, 0xfa, 0x1c, 0x10, + /*[0610]*/ 0x1c, 0x5c, 0x1c, 0xbc, 0x1c, 0xd4, 0x1d, 0x18, + /*[0618]*/ 0x1d, 0x7a, 0x1d, 0x9e, 0x1e, 0x0a, 0x1e, 0x0a, + /*[0620]*/ 0x1e, 0x64, 0x1e, 0x64, 0x1e, 0x64, 0x1e, 0x7c, + /*[0628]*/ 0x1e, 0x94, 0x1e, 0xbc, 0x1e, 0xe4, 0x1f, 0x00, + /*[0630]*/ 0x1f, 0x1e, 0x1f, 0x40, 0x1f, 0x5e, 0x1f, 0xb4, + /*[0638]*/ 0x20, 0x0c, 0x20, 0x30, 0x20, 0x86, 0x20, 0x86, + /*[0640]*/ 0x20, 0xe0, 0x21, 0x30, 0x21, 0x30, 0x21, 0x5a, + /*[0648]*/ 0x21, 0xa2, 0x21, 0xfc, 0x22, 0x58, 0x22, 0xae, + /*[0650]*/ 0x22, 0xe2, 0x23, 0x4a, 0x23, 0x5c, 0x23, 0xda, + /*[0658]*/ 0x24, 0x0c, 0x24, 0x4c, 0x24, 0x76, 0x24, 0x76, + /*[0660]*/ 0x24, 0xec, 0x25, 0x0a, 0x25, 0x28, 0x25, 0x76, + /*[0668]*/ 0x25, 0xa6, 0x25, 0xd6, 0x25, 0xea, 0x26, 0x3e, + /*[0670]*/ 0x26, 0xcc, 0x26, 0xda, 0x26, 0xf6, 0x27, 0x28, + /*[0678]*/ 0x27, 0x52, 0x27, 0x92, 0x28, 0x02, 0x28, 0x6e, + /*[0680]*/ 0x28, 0xec, 0x29, 0x24, 0x29, 0x6c, 0x29, 0xb6, + /*[0688]*/ 0x2a, 0x06, 0x2a, 0x5a, 0x2a, 0xa4, 0x2a, 0xf8, + /*[0690]*/ 0x2b, 0x68, 0x2b, 0xc0, 0x2c, 0x20, 0x2c, 0x82, + /*[0698]*/ 0x2c, 0xea, 0x2d, 0x4a, 0x2d, 0x8c, 0x2d, 0xce, + /*[06a0]*/ 0x2e, 0x16, 0x2e, 0x58, 0x2e, 0xc4, 0x2f, 0x32, + /*[06a8]*/ 0x2f, 0x84, 0x2f, 0xd8, 0x30, 0x32, 0x30, 0x90, + /*[06b0]*/ 0x30, 0xe2, 0x31, 0x1c, 0x31, 0x78, 0x31, 0xc8, + /*[06b8]*/ 0x32, 0x18, 0x32, 0x6e, 0x32, 0xbe, 0x33, 0x0e, + /*[06c0]*/ 0x33, 0x60, 0x33, 0xc8, 0x34, 0x20, 0x34, 0x7a, + /*[06c8]*/ 0x34, 0xda, 0x35, 0x3e, 0x35, 0x96, 0x35, 0xfa, + /*[06d0]*/ 0x36, 0x4e, 0x36, 0x98, 0x36, 0xf6, 0x37, 0x54, + /*[06d8]*/ 0x37, 0xb8, 0x38, 0x16, 0x38, 0x48, 0x38, 0x7c, + /*[06e0]*/ 0x38, 0xb6, 0x38, 0xe8, 0x39, 0x42, 0x39, 0xa2, + /*[06e8]*/ 0x39, 0xf4, 0x3a, 0x48, 0x3a, 0xa2, 0x3b, 0x00, + /*[06f0]*/ 0x3b, 0x52, 0x3b, 0x7e, 0x3b, 0xda, 0x3c, 0x2e, + /*[06f8]*/ 0x3c, 0x84, 0x3c, 0xde, 0x3d, 0x32, 0x3d, 0x9c, + /*[0700]*/ 0x3e, 0x02, 0x3e, 0x6a, 0x00, 0x1c, 0x00, 0x00, + /*[0708]*/ 0xff, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x03, + /*[0710]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[0718]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[0720]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[0728]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[0730]*/ 0x00, 0x47, 0x00, 0x4b, 0x00, 0x4f, 0x00, 0x53, + /*[0738]*/ 0x00, 0x57, 0x00, 0x5b, 0x00, 0x5f, 0x00, 0x63, + /*[0740]*/ 0x00, 0x67, 0x00, 0x6b, 0x00, 0x6f, 0x00, 0x00, + /*[0748]*/ 0x11, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[0750]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[0758]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[0760]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[0768]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[0770]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[0778]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[0780]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[0788]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[0790]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[0798]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[07a0]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[07a8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[07b0]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[07b8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x00, + /*[07c0]*/ 0x80, 0x02, 0x00, 0x80, 0xfd, 0x00, 0x80, 0x02, + /*[07c8]*/ 0x00, 0x80, 0xfd, 0x00, 0x80, 0x02, 0x00, 0x80, + /*[07d0]*/ 0xfd, 0x00, 0x80, 0x02, 0x00, 0x80, 0xfd, 0x00, + /*[07d8]*/ 0x80, 0x02, 0x00, 0x80, 0xfd, 0x00, 0x80, 0x02, + /*[07e0]*/ 0x00, 0x80, 0xfd, 0x00, 0x80, 0x02, 0x00, 0x80, + /*[07e8]*/ 0xfd, 0x00, 0x80, 0x02, 0x00, 0x80, 0xfd, 0x00, + /*[07f0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x03, 0x80, + /*[07f8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0800]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0808]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0810]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0818]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0820]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0828]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0830]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0838]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x00, + /*[0840]*/ 0x00, 0x00, 0x01, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[0848]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[0850]*/ 0x00, 0x17, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, + /*[0858]*/ 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[0860]*/ 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[0868]*/ 0x03, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0x80, + /*[0870]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0878]*/ 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0880]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0888]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[0890]*/ 0x00, 0x06, 0x00, 0x80, 0x02, 0x80, 0x02, 0x00, + /*[0898]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[08a0]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x00, + /*[08a8]*/ 0x13, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[08b0]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[08b8]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[08c0]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, + /*[08c8]*/ 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0x03, 0x80, + /*[08d0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[08d8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[08e0]*/ 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[08e8]*/ 0x03, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[08f0]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[08f8]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[0900]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[0908]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x47, 0x00, 0x00, + /*[0910]*/ 0x13, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[0918]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[0920]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[0928]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[0930]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[0938]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[0940]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[0948]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[0950]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[0958]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[0960]*/ 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, + /*[0968]*/ 0xfe, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, + /*[0970]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[0978]*/ 0x80, 0x02, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0980]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0988]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0990]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0998]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[09a0]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x11, 0x00, 0x00, + /*[09a8]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[09b0]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[09b8]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[09c0]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[09c8]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[09d0]*/ 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[09d8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[09e0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[09e8]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[09f0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[09f8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[0a00]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[0a08]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[0a10]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x01, 0x00, + /*[0a18]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, 0xfd, + /*[0a20]*/ 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, + /*[0a28]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0xfd, 0x80, + /*[0a30]*/ 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x03, + /*[0a38]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0a40]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0a48]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0a50]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0a58]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0a60]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, + /*[0a68]*/ 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x03, + /*[0a70]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[0a78]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[0a80]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[0a88]*/ 0x00, 0x37, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, + /*[0a90]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[0a98]*/ 0x33, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[0aa0]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[0aa8]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[0ab0]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[0ab8]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[0ac0]*/ 0x21, 0x35, 0x33, 0x15, 0x80, 0x80, 0x01, 0x80, + /*[0ac8]*/ 0x80, 0xfd, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0ad0]*/ 0xfe, 0x00, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[0ad8]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[0ae0]*/ 0x80, 0xfd, 0x00, 0x80, 0x01, 0x80, 0x80, 0x02, + /*[0ae8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0af0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0af8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0b00]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0b08]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, + /*[0b10]*/ 0x00, 0x00, 0x03, 0x00, 0x03, 0x80, 0x00, 0x03, + /*[0b18]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[0b20]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[0b28]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[0b30]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[0b38]*/ 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[0b40]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[0b48]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[0b50]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[0b58]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[0b60]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[0b68]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[0b70]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[0b78]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[0b80]*/ 0x80, 0xfe, 0x80, 0x80, 0x01, 0x00, 0x80, 0xfe, + /*[0b88]*/ 0x00, 0x80, 0x01, 0x00, 0x80, 0xfe, 0x80, 0x80, + /*[0b90]*/ 0x80, 0xfe, 0x80, 0x80, 0x01, 0x00, 0x80, 0x80, + /*[0b98]*/ 0x80, 0xfd, 0x00, 0x80, 0x01, 0x80, 0x80, 0xfe, + /*[0ba0]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x03, 0x00, + /*[0ba8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0bb0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0bb8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0bc0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0bc8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0bd0]*/ 0x00, 0x03, 0x01, 0x00, 0x02, 0x80, 0x01, 0x80, + /*[0bd8]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[0be0]*/ 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[0be8]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x01, 0x00, + /*[0bf0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x03, 0x80, 0x80, + /*[0bf8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[0c00]*/ 0x00, 0x09, 0x00, 0x80, 0xff, 0x80, 0x02, 0x00, + /*[0c08]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[0c10]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[0c18]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x00, 0x01, 0x35, + /*[0c20]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[0c28]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[0c30]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[0c38]*/ 0x33, 0x1d, 0x01, 0x35, 0x33, 0x1d, 0x01, 0x35, + /*[0c40]*/ 0x33, 0x15, 0x01, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[0c48]*/ 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0c50]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x03, 0x80, 0x80, + /*[0c58]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0c60]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0c68]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0c70]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x80, + /*[0c78]*/ 0xff, 0x80, 0x02, 0x00, 0x04, 0x00, 0x00, 0x03, + /*[0c80]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[0c88]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[0c90]*/ 0x00, 0x00, 0x13, 0x35, 0x33, 0x1d, 0x01, 0x35, + /*[0c98]*/ 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[0ca0]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[0ca8]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[0cb0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[0cb8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0cc0]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0xff, 0x00, 0x80, + /*[0cc8]*/ 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0cd0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0cd8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0ce0]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x0b, 0x00, 0x00, + /*[0ce8]*/ 0x00, 0x80, 0x02, 0x80, 0x03, 0x00, 0x00, 0x03, + /*[0cf0]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[0cf8]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[0d00]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x00, 0x01, 0x35, + /*[0d08]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[0d10]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[0d18]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[0d20]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[0d28]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[0d30]*/ 0x33, 0x15, 0x01, 0x00, 0x80, 0xfe, 0x80, 0x80, + /*[0d38]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[0d40]*/ 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0d48]*/ 0xfe, 0x80, 0x80, 0x02, 0x80, 0x80, 0x80, 0x80, + /*[0d50]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0d58]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0d60]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[0d68]*/ 0x00, 0x09, 0x00, 0x00, 0x00, 0x80, 0x02, 0x80, + /*[0d70]*/ 0x03, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[0d78]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[0d80]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x00, 0x01, 0x35, + /*[0d88]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[0d90]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[0d98]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[0da0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[0da8]*/ 0x33, 0x15, 0x01, 0x00, 0x80, 0x80, 0x80, 0xfe, + /*[0db0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x80, + /*[0db8]*/ 0x80, 0x80, 0x80, 0x02, 0x80, 0x80, 0x80, 0x80, + /*[0dc0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0dc8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0dd0]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x02, 0x00, 0x80, + /*[0dd8]*/ 0xff, 0x80, 0x01, 0x80, 0x00, 0x80, 0x00, 0x03, + /*[0de0]*/ 0x00, 0x07, 0x00, 0x00, 0x21, 0x35, 0x33, 0x15, + /*[0de8]*/ 0x05, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0xff, + /*[0df0]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[0df8]*/ 0x00, 0x05, 0x00, 0x00, 0x01, 0x80, 0x02, 0x80, + /*[0e00]*/ 0x02, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[0e08]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x00, 0x11, 0x35, + /*[0e10]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[0e18]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[0e20]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01, + /*[0e28]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0e30]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x01, 0x01, 0x00, + /*[0e38]*/ 0x00, 0x00, 0x01, 0x80, 0x00, 0x80, 0x00, 0x03, + /*[0e40]*/ 0x00, 0x00, 0x21, 0x35, 0x33, 0x15, 0x01, 0x00, + /*[0e48]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x09, 0x00, 0x00, + /*[0e50]*/ 0xff, 0x80, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[0e58]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[0e60]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[0e68]*/ 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[0e70]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[0e78]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[0e80]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[0e88]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x02, 0x00, + /*[0e90]*/ 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[0e98]*/ 0xff, 0x00, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[0ea0]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0x03, 0x80, 0x80, + /*[0ea8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0eb0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0eb8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0ec0]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, + /*[0ec8]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[0ed0]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[0ed8]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[0ee0]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[0ee8]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[0ef0]*/ 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[0ef8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[0f00]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[0f08]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[0f10]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[0f18]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[0f20]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[0f28]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[0f30]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[0f38]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, 0x80, 0x80, + /*[0f40]*/ 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[0f48]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[0f50]*/ 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, + /*[0f58]*/ 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x03, 0x00, + /*[0f60]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0f68]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0f70]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0f78]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0f80]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0f88]*/ 0x00, 0x0a, 0x00, 0x80, 0x00, 0x00, 0x02, 0x00, + /*[0f90]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[0f98]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[0fa0]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x00, + /*[0fa8]*/ 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[0fb0]*/ 0x31, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[0fb8]*/ 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[0fc0]*/ 0x07, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[0fc8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[0fd0]*/ 0x01, 0x00, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[0fd8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, + /*[0fe0]*/ 0x00, 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, + /*[0fe8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0ff0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[0ff8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1000]*/ 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[1008]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[1010]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[1018]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[1020]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[1028]*/ 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1030]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[1038]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[1040]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1048]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[1050]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1058]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1060]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[1068]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, 0x80, 0x80, + /*[1070]*/ 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0xfe, 0x80, + /*[1078]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1080]*/ 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[1088]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1090]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1098]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[10a0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[10a8]*/ 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[10b0]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[10b8]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[10c0]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[10c8]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, + /*[10d0]*/ 0x13, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[10d8]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[10e0]*/ 0x21, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[10e8]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x1d, + /*[10f0]*/ 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[10f8]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[1100]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1108]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, + /*[1110]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, + /*[1118]*/ 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfe, + /*[1120]*/ 0x00, 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, + /*[1128]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1130]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1138]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1140]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1148]*/ 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[1150]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[1158]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[1160]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[1168]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, + /*[1170]*/ 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[1178]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[1180]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[1188]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[1190]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1198]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[11a0]*/ 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[11a8]*/ 0x01, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0xfe, + /*[11b0]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, + /*[11b8]*/ 0x00, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[11c0]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x03, 0x00, + /*[11c8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[11d0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[11d8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[11e0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[11e8]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, + /*[11f0]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[11f8]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[1200]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[1208]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[1210]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[1218]*/ 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1220]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1228]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[1230]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[1238]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1240]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x1d, 0x01, 0x35, + /*[1248]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[1250]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1258]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[1260]*/ 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, + /*[1268]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, + /*[1270]*/ 0x80, 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[1278]*/ 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1280]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1288]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1290]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1298]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[12a0]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x0f, 0x00, 0x00, + /*[12a8]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[12b0]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[12b8]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[12c0]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[12c8]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x00, 0x01, 0x35, + /*[12d0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[12d8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[12e0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[12e8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[12f0]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[12f8]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[1300]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1308]*/ 0x33, 0x15, 0x01, 0x00, 0x80, 0x80, 0xfe, 0x80, + /*[1310]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1318]*/ 0x80, 0xfe, 0x00, 0x80, 0x01, 0x80, 0x80, 0xfd, + /*[1320]*/ 0x80, 0x80, 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, + /*[1328]*/ 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[1330]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1338]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1340]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1348]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1350]*/ 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[1358]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[1360]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[1368]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[1370]*/ 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1378]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1380]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[1388]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[1390]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[1398]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[13a0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[13a8]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0xff, + /*[13b0]*/ 0x00, 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[13b8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[13c0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[13c8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[13d0]*/ 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[13d8]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[13e0]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[13e8]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[13f0]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[13f8]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x00, 0x13, 0x35, + /*[1400]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1408]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[1410]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[1418]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1420]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[1428]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[1430]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[1438]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1440]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[1448]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[1450]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0xfe, + /*[1458]*/ 0x00, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[1460]*/ 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, + /*[1468]*/ 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1470]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1478]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1480]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1488]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1490]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, + /*[1498]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[14a0]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[14a8]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[14b0]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[14b8]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x00, 0x13, 0x35, + /*[14c0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[14c8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[14d0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[14d8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[14e0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[14e8]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[14f0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[14f8]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[1500]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[1508]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[1510]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0xfe, 0x80, 0x80, + /*[1518]*/ 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1520]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1528]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1530]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1538]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[1540]*/ 0x00, 0x02, 0x01, 0x00, 0x00, 0x80, 0x01, 0x80, + /*[1548]*/ 0x02, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, + /*[1550]*/ 0x01, 0x35, 0x33, 0x15, 0x03, 0x35, 0x33, 0x15, + /*[1558]*/ 0x01, 0x00, 0x80, 0x80, 0x80, 0x02, 0x00, 0x80, + /*[1560]*/ 0x80, 0xfe, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[1568]*/ 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x01, 0x80, + /*[1570]*/ 0x02, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[1578]*/ 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x03, 0x35, + /*[1580]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x01, 0x00, + /*[1588]*/ 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x02, 0x00, + /*[1590]*/ 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1598]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + /*[15a0]*/ 0x00, 0x80, 0x02, 0x80, 0x03, 0x00, 0x00, 0x03, + /*[15a8]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[15b0]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x00, + /*[15b8]*/ 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[15c0]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[15c8]*/ 0x31, 0x35, 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, + /*[15d0]*/ 0x31, 0x35, 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, + /*[15d8]*/ 0x02, 0x00, 0x80, 0xfe, 0x80, 0x80, 0x80, 0xfe, + /*[15e0]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x02, 0x80, + /*[15e8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[15f0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[15f8]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x0a, 0x00, 0x00, + /*[1600]*/ 0x01, 0x00, 0x02, 0x80, 0x02, 0x80, 0x00, 0x03, + /*[1608]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[1610]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[1618]*/ 0x00, 0x27, 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, + /*[1620]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1628]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1630]*/ 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1638]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1640]*/ 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[1648]*/ 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1650]*/ 0x02, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1658]*/ 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, + /*[1660]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1668]*/ 0x00, 0x08, 0x00, 0x00, 0x00, 0x80, 0x02, 0x80, + /*[1670]*/ 0x03, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[1678]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[1680]*/ 0x00, 0x1f, 0x00, 0x00, 0x11, 0x35, 0x33, 0x1d, + /*[1688]*/ 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x1d, + /*[1690]*/ 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1698]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[16a0]*/ 0x05, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[16a8]*/ 0x80, 0xfe, 0x00, 0x80, 0x80, 0xfe, 0x80, 0x80, + /*[16b0]*/ 0x02, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[16b8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[16c0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[16c8]*/ 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[16d0]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[16d8]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[16e0]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x00, 0x13, 0x35, + /*[16e8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[16f0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[16f8]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[1700]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x03, 0x35, + /*[1708]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[1710]*/ 0x80, 0x01, 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, + /*[1718]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x03, 0x00, + /*[1720]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1728]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1730]*/ 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, + /*[1738]*/ 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + /*[1740]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[1748]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[1750]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[1758]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[1760]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x47, 0x00, 0x4b, + /*[1768]*/ 0x00, 0x4f, 0x00, 0x53, 0x00, 0x57, 0x00, 0x5b, + /*[1770]*/ 0x00, 0x5f, 0x00, 0x63, 0x00, 0x00, 0x13, 0x35, + /*[1778]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1780]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[1788]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[1790]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1798]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[17a0]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[17a8]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[17b0]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[17b8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[17c0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x1d, 0x01, 0x35, + /*[17c8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[17d0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[17d8]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, + /*[17e0]*/ 0x80, 0x80, 0x02, 0x00, 0x80, 0xfd, 0x00, 0x80, + /*[17e8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x00, 0x80, + /*[17f0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x00, 0x80, + /*[17f8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x00, 0x80, + /*[1800]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, + /*[1808]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1810]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1818]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1820]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1828]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1830]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1838]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[1840]*/ 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[1848]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[1850]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[1858]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[1860]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x00, 0x01, 0x35, + /*[1868]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[1870]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[1878]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[1880]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1888]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[1890]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[1898]*/ 0x33, 0x15, 0x01, 0x00, 0x80, 0x80, 0x80, 0xff, + /*[18a0]*/ 0x00, 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, + /*[18a8]*/ 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[18b0]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[18b8]*/ 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[18c0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[18c8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[18d0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[18d8]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x14, 0x00, 0x00, + /*[18e0]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[18e8]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[18f0]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[18f8]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[1900]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[1908]*/ 0x00, 0x47, 0x00, 0x4b, 0x00, 0x4f, 0x00, 0x00, + /*[1910]*/ 0x11, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1918]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1920]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[1928]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[1930]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1938]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1940]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[1948]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[1950]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1958]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1960]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, + /*[1968]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[1970]*/ 0xfd, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[1978]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[1980]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1988]*/ 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1990]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1998]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[19a0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[19a8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[19b0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[19b8]*/ 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[19c0]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[19c8]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[19d0]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[19d8]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x00, 0x13, 0x35, + /*[19e0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[19e8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[19f0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[19f8]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[1a00]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[1a08]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1a10]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[1a18]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, + /*[1a20]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[1a28]*/ 0xfe, 0x00, 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, + /*[1a30]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1a38]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1a40]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1a48]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[1a50]*/ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[1a58]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[1a60]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[1a68]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[1a70]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[1a78]*/ 0x00, 0x3f, 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, + /*[1a80]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1a88]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[1a90]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[1a98]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[1aa0]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[1aa8]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[1ab0]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1ab8]*/ 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0xfe, + /*[1ac0]*/ 0x80, 0x80, 0x01, 0x00, 0x80, 0xfe, 0x00, 0x80, + /*[1ac8]*/ 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, + /*[1ad0]*/ 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, + /*[1ad8]*/ 0x80, 0x80, 0x01, 0x00, 0x80, 0xfe, 0x00, 0x80, + /*[1ae0]*/ 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[1ae8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1af0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1af8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1b00]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1b08]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, + /*[1b10]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[1b18]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[1b20]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[1b28]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[1b30]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[1b38]*/ 0x00, 0x47, 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, + /*[1b40]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1b48]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1b50]*/ 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[1b58]*/ 0x07, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1b60]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1b68]*/ 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[1b70]*/ 0x07, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1b78]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1b80]*/ 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[1b88]*/ 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1b90]*/ 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, + /*[1b98]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x03, 0x00, + /*[1ba0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1ba8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1bb0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1bb8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1bc0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1bc8]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, + /*[1bd0]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[1bd8]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[1be0]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[1be8]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[1bf0]*/ 0x00, 0x37, 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, + /*[1bf8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1c00]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1c08]*/ 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[1c10]*/ 0x07, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1c18]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1c20]*/ 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[1c28]*/ 0x07, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[1c30]*/ 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1c38]*/ 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, + /*[1c40]*/ 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[1c48]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1c50]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1c58]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1c60]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[1c68]*/ 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[1c70]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[1c78]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[1c80]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[1c88]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[1c90]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x00, 0x13, 0x35, + /*[1c98]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1ca0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[1ca8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[1cb0]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1cb8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[1cc0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[1cc8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1cd0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1cd8]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[1ce0]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, + /*[1ce8]*/ 0x80, 0x01, 0x00, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[1cf0]*/ 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, + /*[1cf8]*/ 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, 0x03, + /*[1d00]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1d08]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1d10]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1d18]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1d20]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1d28]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, + /*[1d30]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[1d38]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[1d40]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[1d48]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[1d50]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[1d58]*/ 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[1d60]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[1d68]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[1d70]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1d78]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1d80]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[1d88]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[1d90]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[1d98]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x80, 0x01, + /*[1da0]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[1da8]*/ 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[1db0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[1db8]*/ 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, + /*[1dc0]*/ 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0x03, + /*[1dc8]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1dd0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1dd8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1de0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1de8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1df0]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x80, + /*[1df8]*/ 0x00, 0x00, 0x02, 0x00, 0x03, 0x80, 0x00, 0x03, + /*[1e00]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[1e08]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[1e10]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x00, 0x13, 0x35, + /*[1e18]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1e20]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[1e28]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[1e30]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[1e38]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1e40]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, + /*[1e48]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1e50]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x03, 0x00, + /*[1e58]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1e60]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1e68]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1e70]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x0c, 0x00, 0x00, + /*[1e78]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[1e80]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[1e88]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[1e90]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x00, + /*[1e98]*/ 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1ea0]*/ 0x31, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[1ea8]*/ 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[1eb0]*/ 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[1eb8]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1ec0]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1ec8]*/ 0x01, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1ed0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, + /*[1ed8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, + /*[1ee0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1ee8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1ef0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1ef8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[1f00]*/ 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[1f08]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[1f10]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[1f18]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[1f20]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, + /*[1f28]*/ 0x11, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[1f30]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[1f38]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[1f40]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[1f48]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[1f50]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[1f58]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[1f60]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[1f68]*/ 0x00, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0xfe, + /*[1f70]*/ 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[1f78]*/ 0xfe, 0x80, 0x80, 0x01, 0x00, 0x80, 0xfe, 0x00, + /*[1f80]*/ 0x80, 0x01, 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, + /*[1f88]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1f90]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1f98]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1fa0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[1fa8]*/ 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[1fb0]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[1fb8]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[1fc0]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[1fc8]*/ 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[1fd0]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[1fd8]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[1fe0]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1fe8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[1ff0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[1ff8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2000]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x03, + /*[2008]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2010]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2018]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2020]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[2028]*/ 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[2030]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[2038]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[2040]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[2048]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[2050]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x47, 0x00, 0x4b, + /*[2058]*/ 0x00, 0x4f, 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, + /*[2060]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[2068]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[2070]*/ 0x31, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[2078]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[2080]*/ 0x31, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[2088]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[2090]*/ 0x33, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[2098]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[20a0]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[20a8]*/ 0x21, 0x35, 0x33, 0x15, 0x80, 0x01, 0x80, 0x80, + /*[20b0]*/ 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[20b8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[20c0]*/ 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, + /*[20c8]*/ 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, + /*[20d0]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[20d8]*/ 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[20e0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[20e8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[20f0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[20f8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2100]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2108]*/ 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[2110]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[2118]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[2120]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[2128]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[2130]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x47, 0x00, 0x4b, + /*[2138]*/ 0x00, 0x4f, 0x00, 0x53, 0x00, 0x00, 0x11, 0x35, + /*[2140]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[2148]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2150]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[2158]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[2160]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[2168]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[2170]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[2178]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[2180]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[2188]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2190]*/ 0x33, 0x15, 0x80, 0x80, 0x01, 0x00, 0x80, 0xfd, + /*[2198]*/ 0x80, 0x80, 0x80, 0x01, 0x00, 0x80, 0xfd, 0x80, + /*[21a0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[21a8]*/ 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, + /*[21b0]*/ 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x00, + /*[21b8]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x00, 0x80, + /*[21c0]*/ 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[21c8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[21d0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[21d8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[21e0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[21e8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[21f0]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x10, 0x00, 0x00, + /*[21f8]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[2200]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[2208]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[2210]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[2218]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x00, + /*[2220]*/ 0x13, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[2228]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[2230]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[2238]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[2240]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[2248]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[2250]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[2258]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[2260]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, + /*[2268]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[2270]*/ 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[2278]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[2280]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x03, + /*[2288]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2290]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2298]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[22a0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[22a8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[22b0]*/ 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[22b8]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[22c0]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[22c8]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[22d0]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[22d8]*/ 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[22e0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[22e8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[22f0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[22f8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2300]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2308]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[2310]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[2318]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, 0x80, 0x80, + /*[2320]*/ 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[2328]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[2330]*/ 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, + /*[2338]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2340]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2348]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2350]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2358]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, + /*[2360]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[2368]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[2370]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[2378]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[2380]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[2388]*/ 0x00, 0x47, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, + /*[2390]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[2398]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[23a0]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[23a8]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[23b0]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[23b8]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[23c0]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[23c8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[23d0]*/ 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[23d8]*/ 0xfe, 0x00, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[23e0]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[23e8]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[23f0]*/ 0xfd, 0x80, 0x80, 0x01, 0x00, 0x80, 0x80, 0xfe, + /*[23f8]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, + /*[2400]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2408]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2410]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2418]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2420]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2428]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, + /*[2430]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[2438]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[2440]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[2448]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[2450]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[2458]*/ 0x00, 0x47, 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, + /*[2460]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[2468]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[2470]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[2478]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[2480]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[2488]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[2490]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[2498]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[24a0]*/ 0x21, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[24a8]*/ 0xfe, 0x00, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[24b0]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, + /*[24b8]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, 0x80, 0x80, + /*[24c0]*/ 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[24c8]*/ 0x80, 0x01, 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, + /*[24d0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[24d8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[24e0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[24e8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[24f0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[24f8]*/ 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[2500]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[2508]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[2510]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[2518]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[2520]*/ 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2528]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[2530]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[2538]*/ 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2540]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x1d, 0x01, 0x35, + /*[2548]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[2550]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2558]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[2560]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, 0x80, 0x80, + /*[2568]*/ 0xfd, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, + /*[2570]*/ 0x80, 0x80, 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, + /*[2578]*/ 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[2580]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2588]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2590]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2598]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[25a0]*/ 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[25a8]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[25b0]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[25b8]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[25c0]*/ 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[25c8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[25d0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[25d8]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[25e0]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[25e8]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[25f0]*/ 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, + /*[25f8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2600]*/ 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2608]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2610]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2618]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[2620]*/ 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[2628]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[2630]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[2638]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[2640]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[2648]*/ 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[2650]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[2658]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[2660]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[2668]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[2670]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[2678]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2680]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x80, 0x01, + /*[2688]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[2690]*/ 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[2698]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[26a0]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[26a8]*/ 0xfe, 0x00, 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, + /*[26b0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[26b8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[26c0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[26c8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[26d0]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x00, + /*[26d8]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[26e0]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[26e8]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[26f0]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x00, + /*[26f8]*/ 0x11, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[2700]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[2708]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[2710]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[2718]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[2720]*/ 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[2728]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[2730]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[2738]*/ 0xfe, 0x00, 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, + /*[2740]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x03, + /*[2748]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2750]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2758]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2760]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[2768]*/ 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[2770]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[2778]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[2780]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[2788]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[2790]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x00, 0x11, 0x35, + /*[2798]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[27a0]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[27a8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[27b0]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[27b8]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[27c0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[27c8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[27d0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[27d8]*/ 0x33, 0x15, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[27e0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[27e8]*/ 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, + /*[27f0]*/ 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, + /*[27f8]*/ 0xfe, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, + /*[2800]*/ 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[2808]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2810]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2818]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2820]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2828]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x0d, 0x00, 0x00, + /*[2830]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[2838]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[2840]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[2848]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[2850]*/ 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[2858]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[2860]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[2868]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[2870]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[2878]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[2880]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x80, 0x01, + /*[2888]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[2890]*/ 0xfe, 0x00, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[2898]*/ 0xff, 0x00, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, + /*[28a0]*/ 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, + /*[28a8]*/ 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[28b0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[28b8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[28c0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[28c8]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x00, + /*[28d0]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[28d8]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[28e0]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[28e8]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x00, + /*[28f0]*/ 0x11, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[28f8]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[2900]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[2908]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[2910]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[2918]*/ 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[2920]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[2928]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[2930]*/ 0xfe, 0x00, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[2938]*/ 0x80, 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, + /*[2940]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2948]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2950]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2958]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x0f, 0x00, 0x00, + /*[2960]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[2968]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[2970]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[2978]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[2980]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x00, 0x11, 0x35, + /*[2988]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2990]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2998]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[29a0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[29a8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[29b0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[29b8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[29c0]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[29c8]*/ 0x80, 0xff, 0x00, 0x80, 0xff, 0x00, 0x80, 0xff, + /*[29d0]*/ 0x00, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[29d8]*/ 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, + /*[29e0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[29e8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[29f0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[29f8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2a00]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x80, + /*[2a08]*/ 0xff, 0x80, 0x02, 0x00, 0x04, 0x00, 0x00, 0x03, + /*[2a10]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[2a18]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[2a20]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[2a28]*/ 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2a30]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[2a38]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[2a40]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[2a48]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[2a50]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2a58]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[2a60]*/ 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2a68]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2a70]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x03, 0x80, 0x80, + /*[2a78]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2a80]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2a88]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2a90]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2a98]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, + /*[2aa0]*/ 0xff, 0x80, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[2aa8]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[2ab0]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[2ab8]*/ 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[2ac0]*/ 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[2ac8]*/ 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[2ad0]*/ 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[2ad8]*/ 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[2ae0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2ae8]*/ 0x80, 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, + /*[2af0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2af8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2b00]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[2b08]*/ 0x00, 0x0d, 0x00, 0x80, 0xff, 0x80, 0x02, 0x00, + /*[2b10]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[2b18]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[2b20]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[2b28]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x00, 0x13, 0x35, + /*[2b30]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2b38]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[2b40]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[2b48]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[2b50]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[2b58]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2b60]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2b68]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2b70]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, + /*[2b78]*/ 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2b80]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2b88]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2b90]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2b98]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[2ba0]*/ 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x02, 0x80, + /*[2ba8]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[2bb0]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x00, 0x01, 0x35, + /*[2bb8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[2bc0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[2bc8]*/ 0x33, 0x15, 0x01, 0x00, 0x80, 0xff, 0x00, 0x80, + /*[2bd0]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, 0x80, 0x80, + /*[2bd8]*/ 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2be0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[2be8]*/ 0x00, 0x06, 0x00, 0x00, 0xff, 0x80, 0x03, 0x00, + /*[2bf0]*/ 0x00, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[2bf8]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x00, + /*[2c00]*/ 0x15, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[2c08]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[2c10]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[2c18]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2c20]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2c28]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x02, 0x01, 0x00, + /*[2c30]*/ 0x02, 0x80, 0x02, 0x00, 0x03, 0x80, 0x00, 0x03, + /*[2c38]*/ 0x00, 0x07, 0x00, 0x00, 0x01, 0x35, 0x33, 0x1d, + /*[2c40]*/ 0x01, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0x80, + /*[2c48]*/ 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[2c50]*/ 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[2c58]*/ 0x02, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[2c60]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[2c68]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[2c70]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, + /*[2c78]*/ 0x13, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[2c80]*/ 0x31, 0x35, 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, + /*[2c88]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[2c90]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[2c98]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[2ca0]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[2ca8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[2cb0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, + /*[2cb8]*/ 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, + /*[2cc0]*/ 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, 0x02, + /*[2cc8]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2cd0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2cd8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2ce0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2ce8]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, + /*[2cf0]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[2cf8]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[2d00]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[2d08]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[2d10]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[2d18]*/ 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[2d20]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[2d28]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2d30]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[2d38]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[2d40]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[2d48]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[2d50]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2d58]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[2d60]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2d68]*/ 0xfe, 0x00, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[2d70]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[2d78]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2d80]*/ 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2d88]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2d90]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2d98]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2da0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2da8]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x0b, 0x00, 0x00, + /*[2db0]*/ 0x00, 0x00, 0x02, 0x80, 0x02, 0x80, 0x00, 0x03, + /*[2db8]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[2dc0]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[2dc8]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x00, 0x13, 0x35, + /*[2dd0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2dd8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[2de0]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[2de8]*/ 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2df0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2df8]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, + /*[2e00]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2e08]*/ 0x80, 0x80, 0x02, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[2e10]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2e18]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2e20]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[2e28]*/ 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[2e30]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[2e38]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[2e40]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[2e48]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[2e50]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x00, 0x01, 0x35, + /*[2e58]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[2e60]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2e68]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2e70]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[2e78]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[2e80]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[2e88]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2e90]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2e98]*/ 0x33, 0x15, 0x02, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[2ea0]*/ 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, 0xfd, + /*[2ea8]*/ 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[2eb0]*/ 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, + /*[2eb8]*/ 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, 0x03, + /*[2ec0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2ec8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2ed0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2ed8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2ee0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2ee8]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, + /*[2ef0]*/ 0x00, 0x00, 0x02, 0x80, 0x02, 0x80, 0x00, 0x03, + /*[2ef8]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[2f00]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[2f08]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[2f10]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x00, 0x13, 0x35, + /*[2f18]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2f20]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[2f28]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2f30]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2f38]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[2f40]*/ 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2f48]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2f50]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[2f58]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, + /*[2f60]*/ 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, + /*[2f68]*/ 0x80, 0x80, 0x02, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[2f70]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2f78]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2f80]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[2f88]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[2f90]*/ 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[2f98]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[2fa0]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[2fa8]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[2fb0]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x00, 0x01, 0x35, + /*[2fb8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2fc0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[2fc8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2fd0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[2fd8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[2fe0]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[2fe8]*/ 0x33, 0x15, 0x01, 0x00, 0x80, 0x80, 0x80, 0xfe, + /*[2ff0]*/ 0x00, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, + /*[2ff8]*/ 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3000]*/ 0x80, 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, + /*[3008]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3010]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3018]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3020]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[3028]*/ 0x00, 0x11, 0x00, 0x00, 0xff, 0x00, 0x02, 0x80, + /*[3030]*/ 0x02, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[3038]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[3040]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[3048]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[3050]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x00, 0x13, 0x35, + /*[3058]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3060]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[3068]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[3070]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[3078]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3080]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3088]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[3090]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3098]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[30a0]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[30a8]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[30b0]*/ 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[30b8]*/ 0xfe, 0x00, 0x80, 0x80, 0x80, 0x02, 0x00, 0x80, + /*[30c0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[30c8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[30d0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[30d8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[30e0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[30e8]*/ 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[30f0]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[30f8]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[3100]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[3108]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[3110]*/ 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3118]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3120]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3128]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[3130]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[3138]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[3140]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[3148]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[3150]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3158]*/ 0xfe, 0x00, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[3160]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[3168]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[3170]*/ 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3178]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3180]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3188]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3190]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[3198]*/ 0x00, 0x07, 0x00, 0x80, 0x00, 0x00, 0x01, 0x80, + /*[31a0]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[31a8]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[31b0]*/ 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x01, 0x35, + /*[31b8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[31c0]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[31c8]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x01, 0x00, + /*[31d0]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[31d8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, + /*[31e0]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[31e8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[31f0]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x0b, 0x00, 0x00, + /*[31f8]*/ 0xff, 0x00, 0x02, 0x00, 0x03, 0x80, 0x00, 0x03, + /*[3200]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[3208]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[3210]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x00, 0x01, 0x35, + /*[3218]*/ 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3220]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3228]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3230]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[3238]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3240]*/ 0x33, 0x15, 0x01, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[3248]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3250]*/ 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, + /*[3258]*/ 0x03, 0x00, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, + /*[3260]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3268]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3270]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3278]*/ 0x00, 0x0d, 0x00, 0x80, 0x00, 0x00, 0x02, 0x80, + /*[3280]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[3288]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[3290]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[3298]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x00, 0x13, 0x35, + /*[32a0]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[32a8]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[32b0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[32b8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[32c0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[32c8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[32d0]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[32d8]*/ 0x80, 0x80, 0x01, 0x00, 0x80, 0xfe, 0x00, 0x80, + /*[32e0]*/ 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0xff, 0x00, + /*[32e8]*/ 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x01, 0x00, + /*[32f0]*/ 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[32f8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3300]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3308]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3310]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x09, 0x00, 0x80, + /*[3318]*/ 0x00, 0x00, 0x01, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[3320]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[3328]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[3330]*/ 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3338]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3340]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3348]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3350]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[3358]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3360]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x03, + /*[3368]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3370]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3378]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3380]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, + /*[3388]*/ 0x00, 0x00, 0x02, 0x80, 0x02, 0x80, 0x00, 0x03, + /*[3390]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[3398]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[33a0]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[33a8]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x00, 0x11, 0x35, + /*[33b0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[33b8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[33c0]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[33c8]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[33d0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[33d8]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[33e0]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[33e8]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[33f0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[33f8]*/ 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, + /*[3400]*/ 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, + /*[3408]*/ 0x80, 0x80, 0x02, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[3410]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3418]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3420]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3428]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[3430]*/ 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[3438]*/ 0x02, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[3440]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[3448]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[3450]*/ 0x00, 0x2f, 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, + /*[3458]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[3460]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3468]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3470]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3478]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3480]*/ 0x21, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[3488]*/ 0xfe, 0x00, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[3490]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[3498]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[34a0]*/ 0x02, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[34a8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[34b0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[34b8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[34c0]*/ 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[34c8]*/ 0x02, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[34d0]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[34d8]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[34e0]*/ 0x00, 0x2f, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, + /*[34e8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[34f0]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[34f8]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[3500]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[3508]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[3510]*/ 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[3518]*/ 0xfe, 0x00, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[3520]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[3528]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x02, + /*[3530]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3538]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3540]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3548]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[3550]*/ 0x00, 0x10, 0x00, 0x00, 0xff, 0x00, 0x02, 0x80, + /*[3558]*/ 0x02, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[3560]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[3568]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[3570]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[3578]*/ 0x00, 0x3f, 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, + /*[3580]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[3588]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3590]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3598]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[35a0]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[35a8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[35b0]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[35b8]*/ 0x07, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[35c0]*/ 0xfe, 0x00, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[35c8]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[35d0]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[35d8]*/ 0xfe, 0x00, 0x80, 0x80, 0x80, 0x02, 0x00, 0x80, + /*[35e0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[35e8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[35f0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[35f8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3600]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[3608]*/ 0x00, 0x10, 0x00, 0x00, 0xff, 0x00, 0x02, 0x80, + /*[3610]*/ 0x02, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[3618]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[3620]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[3628]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[3630]*/ 0x00, 0x3f, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, + /*[3638]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[3640]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3648]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3650]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3658]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3660]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[3668]*/ 0x31, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[3670]*/ 0x07, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[3678]*/ 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, + /*[3680]*/ 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[3688]*/ 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, + /*[3690]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x02, 0x00, 0x80, + /*[3698]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[36a0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[36a8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[36b0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[36b8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[36c0]*/ 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[36c8]*/ 0x02, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[36d0]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[36d8]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x00, 0x11, 0x35, + /*[36e0]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[36e8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[36f0]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[36f8]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3700]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[3708]*/ 0x80, 0x80, 0x01, 0x00, 0x80, 0xfd, 0x80, 0x80, + /*[3710]*/ 0x80, 0x80, 0x80, 0x80, 0x02, 0x00, 0x80, 0x80, + /*[3718]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3720]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3728]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x0d, 0x00, 0x00, + /*[3730]*/ 0x00, 0x00, 0x02, 0x80, 0x02, 0x80, 0x00, 0x03, + /*[3738]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[3740]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[3748]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[3750]*/ 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3758]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3760]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x1d, 0x01, 0x35, + /*[3768]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3770]*/ 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[3778]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3780]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[3788]*/ 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, + /*[3790]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3798]*/ 0x02, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[37a0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[37a8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[37b0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[37b8]*/ 0x00, 0x0b, 0x00, 0x80, 0x00, 0x00, 0x02, 0x80, + /*[37c0]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[37c8]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[37d0]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[37d8]*/ 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[37e0]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[37e8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[37f0]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[37f8]*/ 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3800]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[3808]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x80, + /*[3810]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3818]*/ 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3820]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3828]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3830]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[3838]*/ 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[3840]*/ 0x02, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[3848]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[3850]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[3858]*/ 0x00, 0x2f, 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, + /*[3860]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3868]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3870]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3878]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3880]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[3888]*/ 0x31, 0x35, 0x33, 0x15, 0x80, 0x01, 0x80, 0x80, + /*[3890]*/ 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[3898]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[38a0]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[38a8]*/ 0x02, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[38b0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[38b8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[38c0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[38c8]*/ 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[38d0]*/ 0x02, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[38d8]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[38e0]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x00, 0x11, 0x35, + /*[38e8]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[38f0]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[38f8]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[3900]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[3908]*/ 0x33, 0x15, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[3910]*/ 0x80, 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[3918]*/ 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, + /*[3920]*/ 0x80, 0x02, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3928]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3930]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3938]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, + /*[3940]*/ 0x00, 0x00, 0x02, 0x80, 0x02, 0x80, 0x00, 0x03, + /*[3948]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[3950]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[3958]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x00, + /*[3960]*/ 0x11, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[3968]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[3970]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3978]*/ 0x33, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[3980]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[3988]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[3990]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, + /*[3998]*/ 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, + /*[39a0]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0xfe, + /*[39a8]*/ 0x80, 0x80, 0x80, 0x80, 0x02, 0x00, 0x80, 0x80, + /*[39b0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[39b8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[39c0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[39c8]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, + /*[39d0]*/ 0x00, 0x00, 0x02, 0x80, 0x02, 0x80, 0x00, 0x03, + /*[39d8]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[39e0]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[39e8]*/ 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[39f0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[39f8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[3a00]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[3a08]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x80, 0x01, + /*[3a10]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0xff, + /*[3a18]*/ 0x00, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0xfe, + /*[3a20]*/ 0x00, 0x80, 0x01, 0x80, 0x80, 0x02, 0x00, 0x80, + /*[3a28]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3a30]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3a38]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[3a40]*/ 0x00, 0x10, 0x00, 0x00, 0xff, 0x00, 0x02, 0x80, + /*[3a48]*/ 0x02, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[3a50]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[3a58]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[3a60]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[3a68]*/ 0x00, 0x3f, 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, + /*[3a70]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3a78]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3a80]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3a88]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3a90]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[3a98]*/ 0x31, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[3aa0]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[3aa8]*/ 0x31, 0x35, 0x33, 0x15, 0x80, 0x01, 0x80, 0x80, + /*[3ab0]*/ 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[3ab8]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[3ac0]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[3ac8]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x02, + /*[3ad0]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3ad8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3ae0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3ae8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3af0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[3af8]*/ 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[3b00]*/ 0x02, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[3b08]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[3b10]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[3b18]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x00, 0x11, 0x35, + /*[3b20]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3b28]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3b30]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[3b38]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[3b40]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3b48]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3b50]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, + /*[3b58]*/ 0x00, 0x80, 0xff, 0x00, 0x80, 0xff, 0x00, 0x80, + /*[3b60]*/ 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x02, + /*[3b68]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3b70]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3b78]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3b80]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[3b88]*/ 0x00, 0x09, 0x00, 0x80, 0xff, 0x80, 0x02, 0x00, + /*[3b90]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[3b98]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[3ba0]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x00, 0x01, 0x35, + /*[3ba8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3bb0]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[3bb8]*/ 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3bc0]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x1d, 0x01, 0x35, + /*[3bc8]*/ 0x33, 0x15, 0x01, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[3bd0]*/ 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, + /*[3bd8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x03, 0x80, 0x80, + /*[3be0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3be8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3bf0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3bf8]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, + /*[3c00]*/ 0xff, 0x80, 0x01, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[3c08]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[3c10]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[3c18]*/ 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3c20]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3c28]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3c30]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3c38]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x01, 0x00, + /*[3c40]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3c48]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3c50]*/ 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3c58]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3c60]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3c68]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[3c70]*/ 0x00, 0x09, 0x00, 0x80, 0xff, 0x80, 0x02, 0x00, + /*[3c78]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[3c80]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[3c88]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x00, 0x13, 0x35, + /*[3c90]*/ 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3c98]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x1d, 0x01, 0x35, + /*[3ca0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3ca8]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[3cb0]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3cb8]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[3cc0]*/ 0x80, 0xff, 0x00, 0x80, 0x03, 0x80, 0x80, 0x80, + /*[3cc8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3cd0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3cd8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3ce0]*/ 0x00, 0x06, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, + /*[3ce8]*/ 0x02, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[3cf0]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x00, + /*[3cf8]*/ 0x13, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[3d00]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3d08]*/ 0x21, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[3d10]*/ 0x80, 0x80, 0x80, 0x01, 0x00, 0x80, 0xfd, 0x00, + /*[3d18]*/ 0x80, 0x01, 0x00, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[3d20]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3d28]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x13, 0x00, 0x00, + /*[3d30]*/ 0x00, 0x00, 0x03, 0x00, 0x03, 0x80, 0x00, 0x03, + /*[3d38]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[3d40]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[3d48]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[3d50]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[3d58]*/ 0x00, 0x47, 0x00, 0x4b, 0x00, 0x00, 0x01, 0x35, + /*[3d60]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3d68]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[3d70]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3d78]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3d80]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[3d88]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3d90]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[3d98]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[3da0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3da8]*/ 0x33, 0x15, 0x01, 0x00, 0x80, 0x80, 0x80, 0xfe, + /*[3db0]*/ 0x00, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x00, 0x80, + /*[3db8]*/ 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xff, 0x00, + /*[3dc0]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x01, + /*[3dc8]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x03, + /*[3dd0]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3dd8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3de0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3de8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3df0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3df8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[3e00]*/ 0x00, 0x02, 0x01, 0x00, 0xff, 0x80, 0x02, 0x00, + /*[3e08]*/ 0x00, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, + /*[3e10]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3e18]*/ 0x01, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[3e20]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x0d, 0x00, 0x80, + /*[3e28]*/ 0xff, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[3e30]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[3e38]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[3e40]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[3e48]*/ 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3e50]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3e58]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3e60]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[3e68]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3e70]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3e78]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x01, 0x80, + /*[3e80]*/ 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0xff, + /*[3e88]*/ 0x00, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, + /*[3e90]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, + /*[3e98]*/ 0x00, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3ea0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3ea8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3eb0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3eb8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[3ec0]*/ 0x00, 0x04, 0x00, 0x80, 0xff, 0x80, 0x02, 0x00, + /*[3ec8]*/ 0x00, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[3ed0]*/ 0x00, 0x0f, 0x00, 0x00, 0x33, 0x35, 0x33, 0x15, + /*[3ed8]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[3ee0]*/ 0x33, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[3ee8]*/ 0xfe, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3ef0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[3ef8]*/ 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[3f00]*/ 0x00, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[3f08]*/ 0x00, 0x00, 0x31, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[3f10]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[3f18]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3f20]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, + /*[3f28]*/ 0xff, 0x80, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[3f30]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[3f38]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[3f40]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[3f48]*/ 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3f50]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3f58]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3f60]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[3f68]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3f70]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3f78]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x01, 0x00, + /*[3f80]*/ 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, + /*[3f88]*/ 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3f90]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x03, + /*[3f98]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3fa0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3fa8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3fb0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[3fb8]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x11, 0x00, 0x00, + /*[3fc0]*/ 0xff, 0x80, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[3fc8]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[3fd0]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[3fd8]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[3fe0]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[3fe8]*/ 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[3ff0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[3ff8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[4000]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[4008]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[4010]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[4018]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[4020]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[4028]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x01, 0x00, + /*[4030]*/ 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, + /*[4038]*/ 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, + /*[4040]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, + /*[4048]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x03, 0x80, 0x80, + /*[4050]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4058]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4060]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4068]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4070]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4078]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x80, + /*[4080]*/ 0x03, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x03, + /*[4088]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x01, 0x35, + /*[4090]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[4098]*/ 0x33, 0x15, 0x01, 0x00, 0x80, 0xff, 0x00, 0x80, + /*[40a0]*/ 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[40a8]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x0b, 0x00, 0x00, + /*[40b0]*/ 0x00, 0x80, 0x03, 0x00, 0x03, 0x00, 0x00, 0x03, + /*[40b8]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[40c0]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[40c8]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x00, 0x13, 0x35, + /*[40d0]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[40d8]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[40e0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[40e8]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[40f0]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[40f8]*/ 0x33, 0x15, 0x80, 0x80, 0x01, 0x00, 0x80, 0xfe, + /*[4100]*/ 0x00, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0xff, + /*[4108]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x00, + /*[4110]*/ 0x80, 0x01, 0x00, 0x80, 0x80, 0x80, 0x02, 0x80, + /*[4118]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4120]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4128]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4130]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, + /*[4138]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[4140]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[4148]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[4150]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[4158]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[4160]*/ 0x00, 0x47, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, + /*[4168]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4170]*/ 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4178]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4180]*/ 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[4188]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4190]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4198]*/ 0x07, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[41a0]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[41a8]*/ 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[41b0]*/ 0xff, 0x00, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[41b8]*/ 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[41c0]*/ 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, + /*[41c8]*/ 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[41d0]*/ 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[41d8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[41e0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[41e8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[41f0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[41f8]*/ 0x00, 0x05, 0x00, 0x80, 0x00, 0x00, 0x02, 0x00, + /*[4200]*/ 0x02, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[4208]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x00, 0x01, 0x35, + /*[4210]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[4218]*/ 0x33, 0x1d, 0x01, 0x35, 0x33, 0x1d, 0x01, 0x35, + /*[4220]*/ 0x33, 0x15, 0x01, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[4228]*/ 0xff, 0x00, 0x80, 0x80, 0x80, 0x02, 0x00, 0x80, + /*[4230]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4238]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[4240]*/ 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + /*[4248]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[4250]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[4258]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[4260]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[4268]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x47, 0x00, 0x4b, + /*[4270]*/ 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[4278]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[4280]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[4288]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[4290]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[4298]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[42a0]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[42a8]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[42b0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[42b8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[42c0]*/ 0x80, 0x80, 0x80, 0x80, 0xfd, 0x00, 0x80, 0x01, + /*[42c8]*/ 0x00, 0x80, 0xfe, 0x00, 0x80, 0x01, 0x00, 0x80, + /*[42d0]*/ 0xfe, 0x00, 0x80, 0x01, 0x00, 0x80, 0x80, 0xfd, + /*[42d8]*/ 0x80, 0x80, 0x01, 0x00, 0x80, 0xfe, 0x00, 0x80, + /*[42e0]*/ 0x01, 0x00, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, + /*[42e8]*/ 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[42f0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[42f8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4300]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4308]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4310]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4318]*/ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[4320]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[4328]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[4330]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[4338]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[4340]*/ 0x00, 0x3f, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, + /*[4348]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4350]*/ 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4358]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4360]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4368]*/ 0x05, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4370]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4378]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4380]*/ 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[4388]*/ 0xff, 0x00, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, + /*[4390]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0xff, 0x00, 0x80, + /*[4398]*/ 0xff, 0x00, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[43a0]*/ 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[43a8]*/ 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[43b0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[43b8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[43c0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[43c8]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x03, 0x01, 0x00, + /*[43d0]*/ 0x02, 0x80, 0x02, 0x00, 0x04, 0x00, 0x00, 0x03, + /*[43d8]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x01, 0x35, + /*[43e0]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x1d, 0x01, 0x35, + /*[43e8]*/ 0x33, 0x15, 0x01, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[43f0]*/ 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[43f8]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, + /*[4400]*/ 0x02, 0x80, 0x02, 0x00, 0x04, 0x00, 0x00, 0x03, + /*[4408]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x01, 0x35, + /*[4410]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[4418]*/ 0x33, 0x15, 0x01, 0x80, 0x80, 0x80, 0x80, 0xff, + /*[4420]*/ 0x00, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4428]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x06, 0x00, 0x80, + /*[4430]*/ 0x02, 0x80, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[4438]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[4440]*/ 0x00, 0x17, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, + /*[4448]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4450]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4458]*/ 0x33, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[4460]*/ 0xfe, 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[4468]*/ 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4470]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4478]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x06, 0x00, 0x80, + /*[4480]*/ 0x02, 0x80, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[4488]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[4490]*/ 0x00, 0x17, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, + /*[4498]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[44a0]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[44a8]*/ 0x33, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0x80, + /*[44b0]*/ 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[44b8]*/ 0x80, 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, + /*[44c0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[44c8]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x04, 0x01, 0x00, + /*[44d0]*/ 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x03, + /*[44d8]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x00, + /*[44e0]*/ 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[44e8]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[44f0]*/ 0x01, 0x00, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, + /*[44f8]*/ 0x01, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4500]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x05, 0x00, 0x00, + /*[4508]*/ 0x01, 0x00, 0x02, 0x80, 0x01, 0x80, 0x00, 0x03, + /*[4510]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[4518]*/ 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[4520]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[4528]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[4530]*/ 0x80, 0x80, 0x80, 0x01, 0x00, 0x80, 0x80, 0x80, + /*[4538]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[4540]*/ 0x00, 0x06, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, + /*[4548]*/ 0x01, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[4550]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x00, + /*[4558]*/ 0x11, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4560]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4568]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4570]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01, 0x00, + /*[4578]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4580]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x04, 0x00, 0x80, + /*[4588]*/ 0x01, 0x00, 0x02, 0x80, 0x02, 0x00, 0x00, 0x03, + /*[4590]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x00, + /*[4598]*/ 0x01, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[45a0]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[45a8]*/ 0x01, 0x00, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, + /*[45b0]*/ 0x80, 0x80, 0x01, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[45b8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[45c0]*/ 0x00, 0x10, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, + /*[45c8]*/ 0x02, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[45d0]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[45d8]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[45e0]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[45e8]*/ 0x00, 0x3f, 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, + /*[45f0]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[45f8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4600]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4608]*/ 0x33, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4610]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4618]*/ 0x33, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[4620]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[4628]*/ 0x33, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[4630]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4638]*/ 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4640]*/ 0xfd, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x02, + /*[4648]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4650]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4658]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4660]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4668]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, + /*[4670]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[4678]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[4680]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[4688]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[4690]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x00, + /*[4698]*/ 0x13, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[46a0]*/ 0x05, 0x35, 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, + /*[46a8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[46b0]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x1d, + /*[46b8]*/ 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[46c0]*/ 0x31, 0x35, 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, + /*[46c8]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[46d0]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[46d8]*/ 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0xff, + /*[46e0]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[46e8]*/ 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, + /*[46f0]*/ 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[46f8]*/ 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[4700]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4708]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4710]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4718]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x05, 0x01, 0x00, + /*[4720]*/ 0x00, 0x00, 0x02, 0x80, 0x02, 0x80, 0x00, 0x03, + /*[4728]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[4730]*/ 0x00, 0x00, 0x01, 0x35, 0x33, 0x1d, 0x01, 0x35, + /*[4738]*/ 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[4740]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x01, 0x00, + /*[4748]*/ 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0xff, 0x00, + /*[4750]*/ 0x80, 0x02, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4758]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4760]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, + /*[4768]*/ 0x00, 0x00, 0x03, 0x00, 0x02, 0x80, 0x00, 0x03, + /*[4770]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[4778]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[4780]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[4788]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x00, 0x13, 0x35, + /*[4790]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[4798]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[47a0]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[47a8]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[47b0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[47b8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[47c0]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[47c8]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[47d0]*/ 0xfd, 0x00, 0x80, 0x01, 0x00, 0x80, 0xfe, 0x00, + /*[47d8]*/ 0x80, 0x01, 0x00, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[47e0]*/ 0x01, 0x00, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, + /*[47e8]*/ 0x80, 0x80, 0x02, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[47f0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[47f8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4800]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4808]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[4810]*/ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[4818]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[4820]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[4828]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[4830]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[4838]*/ 0x00, 0x3f, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, + /*[4840]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4848]*/ 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4850]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4858]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4860]*/ 0x05, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4868]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4870]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4878]*/ 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[4880]*/ 0xff, 0x00, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, + /*[4888]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0xff, 0x00, 0x80, + /*[4890]*/ 0xff, 0x00, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[4898]*/ 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[48a0]*/ 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[48a8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[48b0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[48b8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[48c0]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x0d, 0x00, 0x00, + /*[48c8]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[48d0]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[48d8]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[48e0]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[48e8]*/ 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[48f0]*/ 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[48f8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[4900]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[4908]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[4910]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[4918]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[4920]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, 0x80, 0x80, + /*[4928]*/ 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[4930]*/ 0x80, 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[4938]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x03, 0x80, + /*[4940]*/ 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, + /*[4948]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4950]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4958]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4960]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x00, + /*[4968]*/ 0x00, 0x00, 0x01, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[4970]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[4978]*/ 0x00, 0x17, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, + /*[4980]*/ 0x03, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[4988]*/ 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[4990]*/ 0x07, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0x80, + /*[4998]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[49a0]*/ 0x80, 0x03, 0x00, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[49a8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[49b0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[49b8]*/ 0x00, 0x0c, 0x00, 0x80, 0x00, 0x00, 0x02, 0x80, + /*[49c0]*/ 0x03, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[49c8]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[49d0]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[49d8]*/ 0x00, 0x2f, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, + /*[49e0]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[49e8]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[49f0]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[49f8]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4a00]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4a08]*/ 0x05, 0x35, 0x33, 0x15, 0x01, 0x80, 0x80, 0xff, + /*[4a10]*/ 0x00, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[4a18]*/ 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, + /*[4a20]*/ 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x02, 0x80, + /*[4a28]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4a30]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4a38]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4a40]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[4a48]*/ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[4a50]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[4a58]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[4a60]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[4a68]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[4a70]*/ 0x00, 0x3f, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, + /*[4a78]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4a80]*/ 0x07, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4a88]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4a90]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4a98]*/ 0x05, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4aa0]*/ 0x07, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4aa8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4ab0]*/ 0x31, 0x35, 0x33, 0x15, 0x01, 0x80, 0x80, 0x80, + /*[4ab8]*/ 0xfe, 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[4ac0]*/ 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xff, 0x00, + /*[4ac8]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4ad0]*/ 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4ad8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4ae0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4ae8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4af0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4af8]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x10, 0x00, 0x00, + /*[4b00]*/ 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x03, + /*[4b08]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[4b10]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[4b18]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[4b20]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x00, + /*[4b28]*/ 0x11, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[4b30]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4b38]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4b40]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[4b48]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[4b50]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4b58]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4b60]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[4b68]*/ 0x80, 0x02, 0x00, 0x80, 0xfd, 0x80, 0x80, 0x80, + /*[4b70]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, 0x00, 0x80, + /*[4b78]*/ 0xfe, 0x00, 0x80, 0x01, 0x00, 0x80, 0xfe, 0x00, + /*[4b80]*/ 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x02, + /*[4b88]*/ 0x00, 0x80, 0x02, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4b90]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4b98]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4ba0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4ba8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4bb0]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, + /*[4bb8]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[4bc0]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[4bc8]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[4bd0]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[4bd8]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x00, 0x11, 0x35, + /*[4be0]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[4be8]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[4bf0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[4bf8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[4c00]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[4c08]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[4c10]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[4c18]*/ 0x33, 0x15, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[4c20]*/ 0x80, 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[4c28]*/ 0x80, 0xff, 0x00, 0x80, 0xfe, 0x80, 0x80, 0x80, + /*[4c30]*/ 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, + /*[4c38]*/ 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4c40]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4c48]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4c50]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4c58]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[4c60]*/ 0x00, 0x08, 0x01, 0x00, 0xff, 0x80, 0x01, 0x80, + /*[4c68]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[4c70]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[4c78]*/ 0x00, 0x1f, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, + /*[4c80]*/ 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[4c88]*/ 0x07, 0x35, 0x33, 0x15, 0x03, 0x35, 0x33, 0x15, + /*[4c90]*/ 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[4c98]*/ 0x07, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0x80, + /*[4ca0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4ca8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x03, 0x80, 0x80, + /*[4cb0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4cb8]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[4cc0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[4cc8]*/ 0x00, 0x12, 0x00, 0x00, 0xff, 0x00, 0x02, 0x80, + /*[4cd0]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[4cd8]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[4ce0]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[4ce8]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[4cf0]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x47, 0x00, 0x00, + /*[4cf8]*/ 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4d00]*/ 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[4d08]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4d10]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4d18]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4d20]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4d28]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4d30]*/ 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[4d38]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[4d40]*/ 0x01, 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, + /*[4d48]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[4d50]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[4d58]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0xff, + /*[4d60]*/ 0x00, 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, + /*[4d68]*/ 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4d70]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4d78]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4d80]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4d88]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4d90]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[4d98]*/ 0x00, 0x02, 0x00, 0x80, 0x03, 0x00, 0x02, 0x00, + /*[4da0]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, + /*[4da8]*/ 0x13, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[4db0]*/ 0x80, 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, + /*[4db8]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, + /*[4dc0]*/ 0x00, 0x00, 0x03, 0x00, 0x03, 0x80, 0x00, 0x03, + /*[4dc8]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[4dd0]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[4dd8]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[4de0]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[4de8]*/ 0x00, 0x47, 0x00, 0x4b, 0x00, 0x4f, 0x00, 0x53, + /*[4df0]*/ 0x00, 0x57, 0x00, 0x5b, 0x00, 0x00, 0x13, 0x35, + /*[4df8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[4e00]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[4e08]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[4e10]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[4e18]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[4e20]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[4e28]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[4e30]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[4e38]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[4e40]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[4e48]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[4e50]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, + /*[4e58]*/ 0x80, 0x80, 0x02, 0x00, 0x80, 0xfd, 0x00, 0x80, + /*[4e60]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x00, 0x80, + /*[4e68]*/ 0x80, 0x80, 0x01, 0x00, 0x80, 0xfd, 0x00, 0x80, + /*[4e70]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x00, 0x80, + /*[4e78]*/ 0x02, 0x00, 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, + /*[4e80]*/ 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4e88]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4e90]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4e98]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4ea0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4ea8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4eb0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[4eb8]*/ 0x00, 0x08, 0x00, 0x80, 0x01, 0x80, 0x02, 0x00, + /*[4ec0]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[4ec8]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[4ed0]*/ 0x00, 0x1f, 0x00, 0x00, 0x01, 0x35, 0x33, 0x1d, + /*[4ed8]*/ 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4ee0]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4ee8]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4ef0]*/ 0x31, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0x80, + /*[4ef8]*/ 0xff, 0x00, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, + /*[4f00]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x03, 0x80, 0x80, + /*[4f08]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4f10]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4f18]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x0a, 0x00, 0x00, + /*[4f20]*/ 0x00, 0x00, 0x03, 0x00, 0x02, 0x80, 0x00, 0x03, + /*[4f28]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[4f30]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[4f38]*/ 0x00, 0x27, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, + /*[4f40]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4f48]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4f50]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4f58]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[4f60]*/ 0x21, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0x01, + /*[4f68]*/ 0x00, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x00, 0x80, + /*[4f70]*/ 0xfd, 0x80, 0x80, 0x01, 0x00, 0x80, 0xfe, 0x80, + /*[4f78]*/ 0x80, 0x01, 0x00, 0x80, 0xfe, 0x80, 0x80, 0x01, + /*[4f80]*/ 0x00, 0x80, 0x02, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[4f88]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4f90]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4f98]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x07, 0x00, 0x80, + /*[4fa0]*/ 0x00, 0x00, 0x02, 0x80, 0x02, 0x00, 0x00, 0x03, + /*[4fa8]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[4fb0]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x00, 0x13, 0x35, + /*[4fb8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[4fc0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[4fc8]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[4fd0]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4fd8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[4fe0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4fe8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[4ff0]*/ 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + /*[4ff8]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[5000]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[5008]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[5010]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[5018]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x47, 0x00, 0x4b, + /*[5020]*/ 0x00, 0x4f, 0x00, 0x53, 0x00, 0x00, 0x13, 0x35, + /*[5028]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[5030]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[5038]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[5040]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[5048]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[5050]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[5058]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[5060]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[5068]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[5070]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[5078]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, + /*[5080]*/ 0x80, 0x80, 0x02, 0x00, 0x80, 0xfd, 0x00, 0x80, + /*[5088]*/ 0x01, 0x00, 0x80, 0x80, 0x80, 0xfd, 0x00, 0x80, + /*[5090]*/ 0x80, 0x80, 0x01, 0x00, 0x80, 0xfd, 0x00, 0x80, + /*[5098]*/ 0x80, 0x80, 0x01, 0x00, 0x80, 0xfd, 0x00, 0x80, + /*[50a0]*/ 0x02, 0x00, 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, + /*[50a8]*/ 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[50b0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[50b8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[50c0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[50c8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[50d0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[50d8]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x05, 0x00, 0x00, + /*[50e0]*/ 0x03, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[50e8]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[50f0]*/ 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[50f8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[5100]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[5108]*/ 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, + /*[5110]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[5118]*/ 0x00, 0x04, 0x00, 0x80, 0x02, 0x00, 0x02, 0x00, + /*[5120]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[5128]*/ 0x00, 0x0f, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, + /*[5130]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[5138]*/ 0x05, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0xff, + /*[5140]*/ 0x00, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x03, + /*[5148]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5150]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x0e, 0x00, 0x00, + /*[5158]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x00, 0x00, 0x03, + /*[5160]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[5168]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[5170]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[5178]*/ 0x00, 0x37, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, + /*[5180]*/ 0x07, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5188]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5190]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5198]*/ 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[51a0]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[51a8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[51b0]*/ 0x31, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0x80, + /*[51b8]*/ 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[51c0]*/ 0xfe, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, + /*[51c8]*/ 0x80, 0x80, 0x80, 0x80, 0x02, 0x80, 0x80, 0x80, + /*[51d0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[51d8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[51e0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[51e8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[51f0]*/ 0x00, 0x08, 0x00, 0x80, 0x01, 0x80, 0x02, 0x00, + /*[51f8]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[5200]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[5208]*/ 0x00, 0x1f, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, + /*[5210]*/ 0x31, 0x35, 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, + /*[5218]*/ 0x05, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5220]*/ 0x07, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5228]*/ 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[5230]*/ 0xff, 0x00, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[5238]*/ 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5240]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5248]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5250]*/ 0x00, 0x08, 0x00, 0x80, 0x01, 0x80, 0x02, 0x00, + /*[5258]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[5260]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[5268]*/ 0x00, 0x1f, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, + /*[5270]*/ 0x31, 0x35, 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, + /*[5278]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x1d, + /*[5280]*/ 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5288]*/ 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[5290]*/ 0xfe, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, + /*[5298]*/ 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[52a0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[52a8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[52b0]*/ 0x00, 0x02, 0x01, 0x00, 0x03, 0x00, 0x02, 0x00, + /*[52b8]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, + /*[52c0]*/ 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[52c8]*/ 0x01, 0x80, 0x80, 0xff, 0x00, 0x80, 0x03, 0x80, + /*[52d0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[52d8]*/ 0x00, 0x0e, 0x00, 0x00, 0xff, 0x00, 0x03, 0x00, + /*[52e0]*/ 0x02, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[52e8]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[52f0]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[52f8]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, + /*[5300]*/ 0x13, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[5308]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[5310]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[5318]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[5320]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5328]*/ 0x31, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[5330]*/ 0x05, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5338]*/ 0x80, 0x80, 0x01, 0x00, 0x80, 0xfe, 0x00, 0x80, + /*[5340]*/ 0x01, 0x00, 0x80, 0xfe, 0x00, 0x80, 0x01, 0x00, + /*[5348]*/ 0x80, 0xfe, 0x00, 0x80, 0x01, 0x00, 0x80, 0xfe, + /*[5350]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, + /*[5358]*/ 0x80, 0xff, 0x00, 0x80, 0x02, 0x00, 0x80, 0x80, + /*[5360]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5368]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5370]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5378]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5380]*/ 0x00, 0x1a, 0x00, 0x00, 0xff, 0x00, 0x03, 0x00, + /*[5388]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[5390]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[5398]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[53a0]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[53a8]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x47, 0x00, 0x4b, + /*[53b0]*/ 0x00, 0x4f, 0x00, 0x53, 0x00, 0x57, 0x00, 0x5b, + /*[53b8]*/ 0x00, 0x5f, 0x00, 0x63, 0x00, 0x67, 0x00, 0x00, + /*[53c0]*/ 0x13, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[53c8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[53d0]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[53d8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[53e0]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[53e8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[53f0]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[53f8]*/ 0x31, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[5400]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[5408]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[5410]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[5418]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[5420]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[5428]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x00, + /*[5430]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[5438]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[5440]*/ 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0xfe, + /*[5448]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, + /*[5450]*/ 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x80, + /*[5458]*/ 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, + /*[5460]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5468]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5470]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5478]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5480]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5488]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5490]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5498]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + /*[54a0]*/ 0x01, 0x80, 0x01, 0x80, 0x02, 0x00, 0x00, 0x03, + /*[54a8]*/ 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x01, 0x00, + /*[54b0]*/ 0x80, 0x01, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[54b8]*/ 0x00, 0x04, 0x01, 0x00, 0xff, 0x00, 0x02, 0x00, + /*[54c0]*/ 0x00, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[54c8]*/ 0x00, 0x0f, 0x00, 0x00, 0x21, 0x35, 0x33, 0x15, + /*[54d0]*/ 0x31, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[54d8]*/ 0x05, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0x80, + /*[54e0]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[54e8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[54f0]*/ 0x00, 0x08, 0x00, 0x80, 0x01, 0x80, 0x02, 0x00, + /*[54f8]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[5500]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[5508]*/ 0x00, 0x1f, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, + /*[5510]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5518]*/ 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[5520]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5528]*/ 0x31, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0xff, + /*[5530]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, + /*[5538]*/ 0x00, 0x80, 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, + /*[5540]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5548]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5550]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x06, 0x00, 0x80, + /*[5558]*/ 0x02, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x03, + /*[5560]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[5568]*/ 0x00, 0x17, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, + /*[5570]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[5578]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[5580]*/ 0x05, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0xff, + /*[5588]*/ 0x00, 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, + /*[5590]*/ 0x80, 0xff, 0x00, 0x80, 0x03, 0x80, 0x80, 0x80, + /*[5598]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[55a0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[55a8]*/ 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + /*[55b0]*/ 0x02, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[55b8]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[55c0]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x00, + /*[55c8]*/ 0x11, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[55d0]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[55d8]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[55e0]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[55e8]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[55f0]*/ 0x80, 0x01, 0x00, 0x80, 0xfe, 0x80, 0x80, 0x01, + /*[55f8]*/ 0x00, 0x80, 0xfe, 0x80, 0x80, 0x01, 0x00, 0x80, + /*[5600]*/ 0xfd, 0x80, 0x80, 0x01, 0x00, 0x80, 0xfd, 0x80, + /*[5608]*/ 0x80, 0x01, 0x00, 0x80, 0x02, 0x00, 0x80, 0x80, + /*[5610]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5618]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5620]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[5628]*/ 0x00, 0x13, 0x00, 0x00, 0xff, 0x80, 0x03, 0x00, + /*[5630]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[5638]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[5640]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[5648]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[5650]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x47, 0x00, 0x4b, + /*[5658]*/ 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[5660]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[5668]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[5670]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[5678]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[5680]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[5688]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[5690]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[5698]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[56a0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x02, 0x00, + /*[56a8]*/ 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, + /*[56b0]*/ 0x80, 0x80, 0x01, 0x00, 0x80, 0xfe, 0x00, 0x80, + /*[56b8]*/ 0x01, 0x00, 0x80, 0xff, 0x00, 0x80, 0x01, 0x00, + /*[56c0]*/ 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, 0xfd, + /*[56c8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, + /*[56d0]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x00, 0x80, 0x03, + /*[56d8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[56e0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[56e8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[56f0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[56f8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5700]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[5708]*/ 0x00, 0x12, 0x00, 0x00, 0xff, 0x80, 0x03, 0x00, + /*[5710]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[5718]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[5720]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[5728]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[5730]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x47, 0x00, 0x00, + /*[5738]*/ 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5740]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5748]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5750]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5758]*/ 0x33, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5760]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[5768]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[5770]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[5778]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5780]*/ 0x02, 0x00, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, + /*[5788]*/ 0x80, 0xfd, 0x80, 0x80, 0x01, 0x00, 0x80, 0xfe, + /*[5790]*/ 0x00, 0x80, 0x01, 0x00, 0x80, 0xff, 0x00, 0x80, + /*[5798]*/ 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, 0x00, + /*[57a0]*/ 0x80, 0xfd, 0x80, 0x80, 0x01, 0x00, 0x80, 0xfe, + /*[57a8]*/ 0x00, 0x80, 0x01, 0x00, 0x80, 0x80, 0xfd, 0x00, + /*[57b0]*/ 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[57b8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[57c0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[57c8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[57d0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[57d8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[57e0]*/ 0x00, 0x16, 0x00, 0x00, 0xff, 0x80, 0x03, 0x00, + /*[57e8]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[57f0]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[57f8]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[5800]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[5808]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x47, 0x00, 0x4b, + /*[5810]*/ 0x00, 0x4f, 0x00, 0x53, 0x00, 0x57, 0x00, 0x00, + /*[5818]*/ 0x11, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[5820]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[5828]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5830]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5838]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5840]*/ 0x33, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[5848]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[5850]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5858]*/ 0x33, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5860]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5868]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5870]*/ 0x80, 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, + /*[5878]*/ 0x00, 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5880]*/ 0xfe, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, + /*[5888]*/ 0x80, 0x80, 0x01, 0x00, 0x80, 0xfe, 0x00, 0x80, + /*[5890]*/ 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, + /*[5898]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[58a0]*/ 0xfd, 0x00, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, + /*[58a8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[58b0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[58b8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[58c0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[58c8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[58d0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[58d8]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, + /*[58e0]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[58e8]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[58f0]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[58f8]*/ 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x03, 0x35, + /*[5900]*/ 0x33, 0x1d, 0x01, 0x35, 0x33, 0x1d, 0x01, 0x35, + /*[5908]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[5910]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[5918]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x01, 0x00, + /*[5920]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[5928]*/ 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, + /*[5930]*/ 0x03, 0x00, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, + /*[5938]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5940]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5948]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, + /*[5950]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[5958]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[5960]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[5968]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x00, + /*[5970]*/ 0x13, 0x35, 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, + /*[5978]*/ 0x03, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5980]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5988]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5990]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[5998]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[59a0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[59a8]*/ 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0xfe, + /*[59b0]*/ 0x00, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[59b8]*/ 0x01, 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, + /*[59c0]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[59c8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[59d0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[59d8]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x0c, 0x00, 0x00, + /*[59e0]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[59e8]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[59f0]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[59f8]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x00, + /*[5a00]*/ 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5a08]*/ 0x11, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5a10]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5a18]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5a20]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[5a28]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[5a30]*/ 0x01, 0x00, 0x80, 0xff, 0x00, 0x80, 0x80, 0xff, + /*[5a38]*/ 0x00, 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, + /*[5a40]*/ 0x80, 0xfe, 0x00, 0x80, 0x01, 0x80, 0x80, 0xfd, + /*[5a48]*/ 0x80, 0x80, 0x01, 0x80, 0x80, 0x03, 0x80, 0x80, + /*[5a50]*/ 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, + /*[5a58]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5a60]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5a68]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[5a70]*/ 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[5a78]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[5a80]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[5a88]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[5a90]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x00, 0x01, 0x35, + /*[5a98]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[5aa0]*/ 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[5aa8]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[5ab0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[5ab8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[5ac0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[5ac8]*/ 0x33, 0x15, 0x01, 0x00, 0x80, 0xff, 0x00, 0x80, + /*[5ad0]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0xff, 0x00, 0x80, + /*[5ad8]*/ 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0xfe, + /*[5ae0]*/ 0x00, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[5ae8]*/ 0x01, 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, + /*[5af0]*/ 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, + /*[5af8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5b00]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5b08]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[5b10]*/ 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[5b18]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[5b20]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[5b28]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[5b30]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, + /*[5b38]*/ 0x01, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[5b40]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[5b48]*/ 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5b50]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5b58]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5b60]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[5b68]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[5b70]*/ 0x01, 0x00, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, + /*[5b78]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0xff, 0x00, 0x80, + /*[5b80]*/ 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0xfe, + /*[5b88]*/ 0x00, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[5b90]*/ 0x01, 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, + /*[5b98]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, + /*[5ba0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5ba8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5bb0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5bb8]*/ 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[5bc0]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[5bc8]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[5bd0]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[5bd8]*/ 0x00, 0x2f, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, + /*[5be0]*/ 0x33, 0x35, 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, + /*[5be8]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[5bf0]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5bf8]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5c00]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5c08]*/ 0x21, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[5c10]*/ 0xff, 0x00, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[5c18]*/ 0xfe, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, + /*[5c20]*/ 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, + /*[5c28]*/ 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0xff, + /*[5c30]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5c38]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5c40]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5c48]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, + /*[5c50]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[5c58]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[5c60]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[5c68]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[5c70]*/ 0x00, 0x37, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, + /*[5c78]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[5c80]*/ 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[5c88]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[5c90]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5c98]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5ca0]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5ca8]*/ 0x21, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0xff, + /*[5cb0]*/ 0x00, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, + /*[5cb8]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0xfe, 0x80, + /*[5cc0]*/ 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, 0x80, + /*[5cc8]*/ 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0x03, + /*[5cd0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5cd8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5ce0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5ce8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5cf0]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x14, 0x00, 0x00, + /*[5cf8]*/ 0x00, 0x00, 0x03, 0x00, 0x03, 0x80, 0x00, 0x03, + /*[5d00]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[5d08]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[5d10]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[5d18]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[5d20]*/ 0x00, 0x47, 0x00, 0x4b, 0x00, 0x4f, 0x00, 0x00, + /*[5d28]*/ 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5d30]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5d38]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[5d40]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[5d48]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5d50]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5d58]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[5d60]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[5d68]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[5d70]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5d78]*/ 0x01, 0x00, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, + /*[5d80]*/ 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, + /*[5d88]*/ 0xfe, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, + /*[5d90]*/ 0x80, 0x01, 0x00, 0x80, 0xfe, 0x00, 0x80, 0x01, + /*[5d98]*/ 0x00, 0x80, 0xfe, 0x00, 0x80, 0x01, 0x00, 0x80, + /*[5da0]*/ 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[5da8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5db0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5db8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5dc0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5dc8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5dd0]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, + /*[5dd8]*/ 0xff, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[5de0]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[5de8]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[5df0]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[5df8]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x00, 0x13, 0x35, + /*[5e00]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[5e08]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[5e10]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[5e18]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[5e20]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[5e28]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[5e30]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[5e38]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[5e40]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, + /*[5e48]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[5e50]*/ 0xfe, 0x00, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[5e58]*/ 0xff, 0x00, 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, + /*[5e60]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5e68]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5e70]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5e78]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5e80]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x12, 0x00, 0x00, + /*[5e88]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[5e90]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[5e98]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[5ea0]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[5ea8]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[5eb0]*/ 0x00, 0x47, 0x00, 0x00, 0x13, 0x35, 0x33, 0x1d, + /*[5eb8]*/ 0x01, 0x35, 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, + /*[5ec0]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5ec8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5ed0]*/ 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[5ed8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5ee0]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5ee8]*/ 0x07, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5ef0]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5ef8]*/ 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0xfe, + /*[5f00]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, + /*[5f08]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[5f10]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x03, + /*[5f18]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, + /*[5f20]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5f28]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5f30]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5f38]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5f40]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x12, 0x00, 0x00, + /*[5f48]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[5f50]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[5f58]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[5f60]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[5f68]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[5f70]*/ 0x00, 0x47, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, + /*[5f78]*/ 0x05, 0x35, 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, + /*[5f80]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5f88]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5f90]*/ 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[5f98]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5fa0]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[5fa8]*/ 0x07, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5fb0]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[5fb8]*/ 0x31, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0xff, + /*[5fc0]*/ 0x00, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[5fc8]*/ 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5fd0]*/ 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5fd8]*/ 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5fe0]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5fe8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5ff0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[5ff8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6000]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[6008]*/ 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[6010]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[6018]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[6020]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[6028]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[6030]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x47, 0x00, 0x4b, + /*[6038]*/ 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[6040]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x01, 0x35, + /*[6048]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[6050]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[6058]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[6060]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[6068]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[6070]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[6078]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[6080]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x01, 0x00, + /*[6088]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[6090]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[6098]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, + /*[60a0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x03, 0x80, + /*[60a8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, + /*[60b0]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[60b8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[60c0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[60c8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[60d0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[60d8]*/ 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[60e0]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[60e8]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[60f0]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[60f8]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[6100]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x47, 0x00, 0x00, + /*[6108]*/ 0x13, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[6110]*/ 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6118]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6120]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[6128]*/ 0x07, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6130]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6138]*/ 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[6140]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6148]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6150]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[6158]*/ 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, + /*[6160]*/ 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, + /*[6168]*/ 0x80, 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, + /*[6170]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[6178]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6180]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6188]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6190]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6198]*/ 0x00, 0x0b, 0x00, 0x80, 0x00, 0x00, 0x02, 0x00, + /*[61a0]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[61a8]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[61b0]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[61b8]*/ 0x00, 0x00, 0x13, 0x35, 0x33, 0x1d, 0x01, 0x35, + /*[61c0]*/ 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[61c8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[61d0]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[61d8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[61e0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[61e8]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0xff, 0x00, + /*[61f0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[61f8]*/ 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6200]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6208]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6210]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6218]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x80, + /*[6220]*/ 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x03, + /*[6228]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[6230]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[6238]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x00, 0x01, 0x35, + /*[6240]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x03, 0x35, + /*[6248]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[6250]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[6258]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[6260]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[6268]*/ 0x33, 0x15, 0x01, 0x00, 0x80, 0xff, 0x00, 0x80, + /*[6270]*/ 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, + /*[6278]*/ 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[6280]*/ 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, + /*[6288]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6290]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6298]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[62a0]*/ 0x00, 0x0c, 0x00, 0x80, 0x00, 0x00, 0x02, 0x00, + /*[62a8]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[62b0]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[62b8]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[62c0]*/ 0x00, 0x2f, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, + /*[62c8]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[62d0]*/ 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[62d8]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[62e0]*/ 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[62e8]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[62f0]*/ 0x31, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0xff, + /*[62f8]*/ 0x00, 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, + /*[6300]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6308]*/ 0xff, 0x00, 0x80, 0x80, 0x80, 0x03, 0x80, 0x80, + /*[6310]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, + /*[6318]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6320]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6328]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[6330]*/ 0x00, 0x0b, 0x00, 0x80, 0x00, 0x00, 0x02, 0x00, + /*[6338]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[6340]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[6348]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[6350]*/ 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[6358]*/ 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[6360]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[6368]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[6370]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[6378]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[6380]*/ 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0xff, + /*[6388]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, + /*[6390]*/ 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, + /*[6398]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[63a0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[63a8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[63b0]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, + /*[63b8]*/ 0x00, 0x00, 0x03, 0x00, 0x03, 0x80, 0x00, 0x03, + /*[63c0]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[63c8]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[63d0]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[63d8]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[63e0]*/ 0x00, 0x47, 0x00, 0x4b, 0x00, 0x00, 0x13, 0x35, + /*[63e8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[63f0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[63f8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[6400]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[6408]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[6410]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[6418]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[6420]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[6428]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[6430]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x80, + /*[6438]*/ 0x80, 0x01, 0x00, 0x80, 0xfe, 0x00, 0x80, 0x01, + /*[6440]*/ 0x80, 0x80, 0xfd, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[6448]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[6450]*/ 0xfd, 0x80, 0x80, 0x01, 0x00, 0x80, 0xfe, 0x00, + /*[6458]*/ 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, + /*[6460]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6468]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6470]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6478]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6480]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6488]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, + /*[6490]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[6498]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[64a0]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[64a8]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[64b0]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[64b8]*/ 0x00, 0x47, 0x00, 0x4b, 0x00, 0x00, 0x01, 0x35, + /*[64c0]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[64c8]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x01, 0x35, + /*[64d0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[64d8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[64e0]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[64e8]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[64f0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[64f8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[6500]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[6508]*/ 0x33, 0x15, 0x01, 0x00, 0x80, 0x80, 0x80, 0xfe, + /*[6510]*/ 0x00, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[6518]*/ 0x01, 0x00, 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, + /*[6520]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6528]*/ 0x80, 0xfd, 0x80, 0x80, 0x01, 0x00, 0x80, 0x80, + /*[6530]*/ 0xfd, 0x80, 0x80, 0x01, 0x00, 0x80, 0x80, 0x03, + /*[6538]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6540]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[6548]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6550]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6558]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6560]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[6568]*/ 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[6570]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[6578]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[6580]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[6588]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, + /*[6590]*/ 0x13, 0x35, 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, + /*[6598]*/ 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[65a0]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[65a8]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[65b0]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[65b8]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[65c0]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[65c8]*/ 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[65d0]*/ 0xfe, 0x00, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[65d8]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[65e0]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x03, + /*[65e8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, + /*[65f0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[65f8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6600]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6608]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x0e, 0x00, 0x00, + /*[6610]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[6618]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[6620]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[6628]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[6630]*/ 0x00, 0x37, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, + /*[6638]*/ 0x05, 0x35, 0x33, 0x15, 0x03, 0x35, 0x33, 0x15, + /*[6640]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6648]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[6650]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[6658]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[6660]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6668]*/ 0x31, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0xff, + /*[6670]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[6678]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[6680]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[6688]*/ 0xfe, 0x00, 0x80, 0x80, 0x80, 0x03, 0x80, 0x80, + /*[6690]*/ 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, + /*[6698]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[66a0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[66a8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[66b0]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, + /*[66b8]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[66c0]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[66c8]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[66d0]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[66d8]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x00, 0x01, 0x35, + /*[66e0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[66e8]*/ 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[66f0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[66f8]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[6700]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[6708]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[6710]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[6718]*/ 0x33, 0x15, 0x01, 0x00, 0x80, 0xff, 0x00, 0x80, + /*[6720]*/ 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0xfe, + /*[6728]*/ 0x00, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[6730]*/ 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, + /*[6738]*/ 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x03, 0x80, + /*[6740]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, + /*[6748]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6750]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6758]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6760]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[6768]*/ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[6770]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[6778]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[6780]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[6788]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[6790]*/ 0x00, 0x3f, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, + /*[6798]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[67a0]*/ 0x33, 0x35, 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, + /*[67a8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[67b0]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[67b8]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[67c0]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[67c8]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[67d0]*/ 0x31, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0x80, + /*[67d8]*/ 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0xfe, 0x80, + /*[67e0]*/ 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, 0x80, + /*[67e8]*/ 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, + /*[67f0]*/ 0x80, 0x80, 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, + /*[67f8]*/ 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6800]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[6808]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6810]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6818]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6820]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x0e, 0x00, 0x00, + /*[6828]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[6830]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[6838]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[6840]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[6848]*/ 0x00, 0x37, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, + /*[6850]*/ 0x33, 0x35, 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, + /*[6858]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6860]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[6868]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[6870]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[6878]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6880]*/ 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[6888]*/ 0xfe, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, + /*[6890]*/ 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, + /*[6898]*/ 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfe, + /*[68a0]*/ 0x00, 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, + /*[68a8]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[68b0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[68b8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[68c0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[68c8]*/ 0x00, 0x09, 0x00, 0x00, 0x00, 0x80, 0x02, 0x80, + /*[68d0]*/ 0x03, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[68d8]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[68e0]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x00, 0x11, 0x35, + /*[68e8]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[68f0]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[68f8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[6900]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[6908]*/ 0x33, 0x15, 0x80, 0x01, 0x80, 0x80, 0xfe, 0x00, + /*[6910]*/ 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0xff, 0x00, + /*[6918]*/ 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, 0x80, + /*[6920]*/ 0x80, 0x02, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6928]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6930]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6938]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + /*[6940]*/ 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x03, + /*[6948]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[6950]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[6958]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[6960]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x00, + /*[6968]*/ 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6970]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[6978]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[6980]*/ 0x33, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6988]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6990]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[6998]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[69a0]*/ 0x33, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[69a8]*/ 0x01, 0x00, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, + /*[69b0]*/ 0x80, 0x01, 0x00, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[69b8]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[69c0]*/ 0xfe, 0x00, 0x80, 0x01, 0x00, 0x80, 0xfd, 0x80, + /*[69c8]*/ 0x80, 0x80, 0x80, 0x80, 0x02, 0x80, 0x80, 0x80, + /*[69d0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[69d8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[69e0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[69e8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[69f0]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x0d, 0x00, 0x00, + /*[69f8]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[6a00]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[6a08]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[6a10]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[6a18]*/ 0x00, 0x00, 0x13, 0x35, 0x33, 0x1d, 0x01, 0x35, + /*[6a20]*/ 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[6a28]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[6a30]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[6a38]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[6a40]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[6a48]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[6a50]*/ 0x80, 0xfe, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, + /*[6a58]*/ 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[6a60]*/ 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, + /*[6a68]*/ 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x03, 0x80, + /*[6a70]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[6a78]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6a80]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6a88]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6a90]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, + /*[6a98]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[6aa0]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[6aa8]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[6ab0]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[6ab8]*/ 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[6ac0]*/ 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[6ac8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[6ad0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[6ad8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[6ae0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[6ae8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x01, 0x00, + /*[6af0]*/ 0x80, 0xff, 0x00, 0x80, 0xff, 0x00, 0x80, 0x01, + /*[6af8]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[6b00]*/ 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[6b08]*/ 0x80, 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[6b10]*/ 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6b18]*/ 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6b20]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6b28]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6b30]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x0e, 0x00, 0x00, + /*[6b38]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[6b40]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[6b48]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[6b50]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[6b58]*/ 0x00, 0x37, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, + /*[6b60]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[6b68]*/ 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[6b70]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[6b78]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[6b80]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[6b88]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6b90]*/ 0x31, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0xff, + /*[6b98]*/ 0x00, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, + /*[6ba0]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[6ba8]*/ 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[6bb0]*/ 0x80, 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[6bb8]*/ 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6bc0]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[6bc8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6bd0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6bd8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[6be0]*/ 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[6be8]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[6bf0]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[6bf8]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[6c00]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x00, 0x13, 0x35, + /*[6c08]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x01, 0x35, + /*[6c10]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[6c18]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[6c20]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[6c28]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[6c30]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[6c38]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[6c40]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[6c48]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[6c50]*/ 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfe, 0x00, + /*[6c58]*/ 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, + /*[6c60]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6c68]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6c70]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6c78]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[6c80]*/ 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[6c88]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[6c90]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[6c98]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[6ca0]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x00, 0x01, 0x35, + /*[6ca8]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[6cb0]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[6cb8]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[6cc0]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[6cc8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[6cd0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[6cd8]*/ 0x33, 0x15, 0x01, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[6ce0]*/ 0xfe, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[6ce8]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[6cf0]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0xff, + /*[6cf8]*/ 0x00, 0x80, 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, + /*[6d00]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6d08]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6d10]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6d18]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[6d20]*/ 0x00, 0x0e, 0x00, 0x80, 0xff, 0x80, 0x02, 0x80, + /*[6d28]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[6d30]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[6d38]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[6d40]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, + /*[6d48]*/ 0x13, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[6d50]*/ 0x07, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6d58]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[6d60]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[6d68]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[6d70]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6d78]*/ 0x05, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[6d80]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6d88]*/ 0xfe, 0x80, 0x80, 0x01, 0x00, 0x80, 0xfe, 0x00, + /*[6d90]*/ 0x80, 0x01, 0x00, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[6d98]*/ 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0x03, 0x00, + /*[6da0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6da8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6db0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6db8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6dc0]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x12, 0x00, 0x00, + /*[6dc8]*/ 0xff, 0x80, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[6dd0]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[6dd8]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[6de0]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[6de8]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[6df0]*/ 0x00, 0x47, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, + /*[6df8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6e00]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[6e08]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[6e10]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6e18]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[6e20]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[6e28]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[6e30]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6e38]*/ 0x05, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[6e40]*/ 0xfe, 0x80, 0x80, 0x01, 0x00, 0x80, 0xfe, 0x00, + /*[6e48]*/ 0x80, 0x01, 0x00, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[6e50]*/ 0x80, 0xfe, 0x80, 0x80, 0x01, 0x00, 0x80, 0xfe, + /*[6e58]*/ 0x00, 0x80, 0x01, 0x00, 0x80, 0xfe, 0x00, 0x80, + /*[6e60]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x03, 0x00, 0x80, + /*[6e68]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6e70]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6e78]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6e80]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6e88]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6e90]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + /*[6e98]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[6ea0]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[6ea8]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[6eb0]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[6eb8]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x00, + /*[6ec0]*/ 0x13, 0x35, 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, + /*[6ec8]*/ 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6ed0]*/ 0x31, 0x35, 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, + /*[6ed8]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6ee0]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6ee8]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[6ef0]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6ef8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6f00]*/ 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[6f08]*/ 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, 0xfd, + /*[6f10]*/ 0x80, 0x80, 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, + /*[6f18]*/ 0x80, 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, + /*[6f20]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[6f28]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6f30]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6f38]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6f40]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, + /*[6f48]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[6f50]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[6f58]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[6f60]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[6f68]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x00, + /*[6f70]*/ 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[6f78]*/ 0x03, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6f80]*/ 0x31, 0x35, 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, + /*[6f88]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6f90]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6f98]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[6fa0]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6fa8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[6fb0]*/ 0x01, 0x00, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[6fb8]*/ 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, + /*[6fc0]*/ 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfe, + /*[6fc8]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x03, 0x80, 0x80, + /*[6fd0]*/ 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, + /*[6fd8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6fe0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6fe8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[6ff0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[6ff8]*/ 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[7000]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[7008]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[7010]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[7018]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[7020]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x00, 0x01, 0x35, + /*[7028]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[7030]*/ 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[7038]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x1d, 0x01, 0x35, + /*[7040]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[7048]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[7050]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[7058]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[7060]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[7068]*/ 0x33, 0x15, 0x01, 0x00, 0x80, 0xff, 0x00, 0x80, + /*[7070]*/ 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7078]*/ 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, + /*[7080]*/ 0x80, 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[7088]*/ 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7090]*/ 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[7098]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[70a0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[70a8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[70b0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[70b8]*/ 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[70c0]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[70c8]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[70d0]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[70d8]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[70e0]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x47, 0x00, 0x00, + /*[70e8]*/ 0x01, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[70f0]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[70f8]*/ 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7100]*/ 0x31, 0x35, 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, + /*[7108]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7110]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7118]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[7120]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7128]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7130]*/ 0x01, 0x00, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, + /*[7138]*/ 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7140]*/ 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, + /*[7148]*/ 0x80, 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[7150]*/ 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7158]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[7160]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7168]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7170]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7178]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[7180]*/ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[7188]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[7190]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[7198]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[71a0]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[71a8]*/ 0x00, 0x3f, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, + /*[71b0]*/ 0x33, 0x35, 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, + /*[71b8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x1d, + /*[71c0]*/ 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[71c8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[71d0]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[71d8]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[71e0]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[71e8]*/ 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[71f0]*/ 0xfe, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[71f8]*/ 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[7200]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[7208]*/ 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, + /*[7210]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7218]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7220]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7228]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7230]*/ 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[7238]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[7240]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[7248]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[7250]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[7258]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x47, 0x00, 0x00, + /*[7260]*/ 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[7268]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[7270]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7278]*/ 0x31, 0x35, 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, + /*[7280]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7288]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7290]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[7298]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[72a0]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[72a8]*/ 0x01, 0x00, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[72b0]*/ 0xff, 0x00, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[72b8]*/ 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, 0xfd, + /*[72c0]*/ 0x80, 0x80, 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, + /*[72c8]*/ 0x80, 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, + /*[72d0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[72d8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[72e0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[72e8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[72f0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[72f8]*/ 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + /*[7300]*/ 0x02, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[7308]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[7310]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[7318]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[7320]*/ 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[7328]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[7330]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[7338]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[7340]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[7348]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[7350]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[7358]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[7360]*/ 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[7368]*/ 0xfd, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, + /*[7370]*/ 0x80, 0x01, 0x00, 0x80, 0xfe, 0x80, 0x80, 0x80, + /*[7378]*/ 0x80, 0x80, 0x80, 0x02, 0x00, 0x80, 0x80, 0x80, + /*[7380]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7388]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7390]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7398]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[73a0]*/ 0x00, 0x0d, 0x00, 0x00, 0xff, 0x00, 0x02, 0x80, + /*[73a8]*/ 0x02, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[73b0]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[73b8]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[73c0]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x00, 0x13, 0x35, + /*[73c8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[73d0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[73d8]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[73e0]*/ 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[73e8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[73f0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[73f8]*/ 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, + /*[7400]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7408]*/ 0x80, 0x80, 0xfe, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[7410]*/ 0x02, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7418]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7420]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7428]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7430]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, + /*[7438]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[7440]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[7448]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[7450]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[7458]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[7460]*/ 0x00, 0x00, 0x13, 0x35, 0x33, 0x1d, 0x01, 0x35, + /*[7468]*/ 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[7470]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[7478]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[7480]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[7488]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[7490]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x1d, 0x01, 0x35, + /*[7498]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[74a0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[74a8]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[74b0]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, + /*[74b8]*/ 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, + /*[74c0]*/ 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[74c8]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[74d0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[74d8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[74e0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[74e8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[74f0]*/ 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[74f8]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[7500]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[7508]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[7510]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[7518]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x00, 0x01, 0x35, + /*[7520]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x03, 0x35, + /*[7528]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[7530]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[7538]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[7540]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[7548]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[7550]*/ 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[7558]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[7560]*/ 0x33, 0x15, 0x01, 0x00, 0x80, 0xff, 0x00, 0x80, + /*[7568]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, + /*[7570]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7578]*/ 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7580]*/ 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, + /*[7588]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7590]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7598]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[75a0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[75a8]*/ 0x80, 0x80, 0x80, 0x00, 0x00, 0x12, 0x00, 0x00, + /*[75b0]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[75b8]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[75c0]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[75c8]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[75d0]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[75d8]*/ 0x00, 0x47, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, + /*[75e0]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[75e8]*/ 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[75f0]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[75f8]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[7600]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7608]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7610]*/ 0x05, 0x35, 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, + /*[7618]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7620]*/ 0x31, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0xff, + /*[7628]*/ 0x00, 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, + /*[7630]*/ 0x80, 0xfe, 0x00, 0x80, 0x01, 0x80, 0x80, 0xfd, + /*[7638]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, + /*[7640]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x03, 0x80, 0x80, + /*[7648]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, + /*[7650]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7658]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7660]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7668]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7670]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, + /*[7678]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[7680]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[7688]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[7690]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[7698]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[76a0]*/ 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[76a8]*/ 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[76b0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[76b8]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[76c0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[76c8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[76d0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x1d, 0x01, 0x35, + /*[76d8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[76e0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, + /*[76e8]*/ 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0xfe, + /*[76f0]*/ 0x00, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[76f8]*/ 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, + /*[7700]*/ 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, + /*[7708]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7710]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7718]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7720]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7728]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[7730]*/ 0x00, 0x08, 0x00, 0x80, 0x00, 0x00, 0x01, 0x80, + /*[7738]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[7740]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[7748]*/ 0x00, 0x1f, 0x00, 0x00, 0x13, 0x35, 0x33, 0x1d, + /*[7750]*/ 0x01, 0x35, 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, + /*[7758]*/ 0x31, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[7760]*/ 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[7768]*/ 0x07, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0xff, + /*[7770]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7778]*/ 0x80, 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, + /*[7780]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[7788]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7790]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x08, 0x00, 0x80, + /*[7798]*/ 0x00, 0x00, 0x01, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[77a0]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[77a8]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x00, + /*[77b0]*/ 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[77b8]*/ 0x03, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[77c0]*/ 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[77c8]*/ 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[77d0]*/ 0x01, 0x00, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[77d8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[77e0]*/ 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[77e8]*/ 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[77f0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[77f8]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x09, 0x00, 0x80, + /*[7800]*/ 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x03, + /*[7808]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[7810]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[7818]*/ 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[7820]*/ 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, 0x01, 0x35, + /*[7828]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[7830]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, + /*[7838]*/ 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, 0x01, 0x00, + /*[7840]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0xfe, 0x80, + /*[7848]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7850]*/ 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7858]*/ 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[7860]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7868]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[7870]*/ 0x00, 0x08, 0x00, 0x80, 0x00, 0x00, 0x02, 0x00, + /*[7878]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[7880]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[7888]*/ 0x00, 0x1f, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, + /*[7890]*/ 0x33, 0x35, 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, + /*[7898]*/ 0x31, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[78a0]*/ 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[78a8]*/ 0x07, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[78b0]*/ 0xfe, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[78b8]*/ 0x80, 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, + /*[78c0]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[78c8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[78d0]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, + /*[78d8]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[78e0]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[78e8]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[78f0]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[78f8]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x00, + /*[7900]*/ 0x13, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7908]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x1d, + /*[7910]*/ 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[7918]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7920]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[7928]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[7930]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[7938]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7940]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[7948]*/ 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, 0xfd, + /*[7950]*/ 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[7958]*/ 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, + /*[7960]*/ 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7968]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7970]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7978]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7980]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7988]*/ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[7990]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[7998]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[79a0]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[79a8]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[79b0]*/ 0x00, 0x3f, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, + /*[79b8]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[79c0]*/ 0x33, 0x35, 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, + /*[79c8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[79d0]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[79d8]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[79e0]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[79e8]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[79f0]*/ 0x21, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0x80, + /*[79f8]*/ 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[7a00]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, + /*[7a08]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[7a10]*/ 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[7a18]*/ 0x80, 0x01, 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, + /*[7a20]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, + /*[7a28]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7a30]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7a38]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7a40]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[7a48]*/ 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[7a50]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[7a58]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[7a60]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[7a68]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, + /*[7a70]*/ 0x13, 0x35, 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, + /*[7a78]*/ 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7a80]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[7a88]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[7a90]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[7a98]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[7aa0]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7aa8]*/ 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[7ab0]*/ 0xfe, 0x00, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[7ab8]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[7ac0]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x03, + /*[7ac8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, + /*[7ad0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7ad8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7ae0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7ae8]*/ 0x80, 0x80, 0x80, 0x80, 0x00, 0x0e, 0x00, 0x00, + /*[7af0]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[7af8]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[7b00]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[7b08]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[7b10]*/ 0x00, 0x37, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, + /*[7b18]*/ 0x05, 0x35, 0x33, 0x15, 0x03, 0x35, 0x33, 0x15, + /*[7b20]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7b28]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[7b30]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[7b38]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[7b40]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7b48]*/ 0x31, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0xff, + /*[7b50]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[7b58]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[7b60]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[7b68]*/ 0xfe, 0x00, 0x80, 0x80, 0x80, 0x03, 0x80, 0x80, + /*[7b70]*/ 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, + /*[7b78]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7b80]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7b88]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7b90]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, + /*[7b98]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[7ba0]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[7ba8]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[7bb0]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[7bb8]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x00, 0x01, 0x35, + /*[7bc0]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[7bc8]*/ 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[7bd0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[7bd8]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[7be0]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[7be8]*/ 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, + /*[7bf0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[7bf8]*/ 0x33, 0x15, 0x01, 0x00, 0x80, 0xff, 0x00, 0x80, + /*[7c00]*/ 0x80, 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0xfe, + /*[7c08]*/ 0x00, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[7c10]*/ 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, + /*[7c18]*/ 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x03, 0x80, + /*[7c20]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff, + /*[7c28]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7c30]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7c38]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7c40]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, + /*[7c48]*/ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[7c50]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[7c58]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[7c60]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[7c68]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[7c70]*/ 0x00, 0x3f, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, + /*[7c78]*/ 0x33, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[7c80]*/ 0x33, 0x35, 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, + /*[7c88]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7c90]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[7c98]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[7ca0]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[7ca8]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7cb0]*/ 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[7cb8]*/ 0xfe, 0x00, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, + /*[7cc0]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, 0x80, 0x80, + /*[7cc8]*/ 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[7cd0]*/ 0x80, 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[7cd8]*/ 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7ce0]*/ 0x80, 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, + /*[7ce8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7cf0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7cf8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7d00]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, + /*[7d08]*/ 0x00, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[7d10]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[7d18]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[7d20]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[7d28]*/ 0x00, 0x37, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, + /*[7d30]*/ 0x33, 0x35, 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, + /*[7d38]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7d40]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[7d48]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[7d50]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[7d58]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7d60]*/ 0x31, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[7d68]*/ 0xfe, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, + /*[7d70]*/ 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, + /*[7d78]*/ 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfe, + /*[7d80]*/ 0x00, 0x80, 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, + /*[7d88]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[7d90]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7d98]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7da0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7da8]*/ 0x00, 0x07, 0x00, 0x00, 0x00, 0x80, 0x02, 0x80, + /*[7db0]*/ 0x03, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[7db8]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[7dc0]*/ 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x01, 0x35, + /*[7dc8]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[7dd0]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[7dd8]*/ 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, 0x01, 0x00, + /*[7de0]*/ 0x80, 0xfe, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7de8]*/ 0xfe, 0x80, 0x80, 0x02, 0x80, 0x80, 0x80, 0xff, + /*[7df0]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7df8]*/ 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x00, + /*[7e00]*/ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + /*[7e08]*/ 0x03, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[7e10]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[7e18]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[7e20]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[7e28]*/ 0x00, 0x3f, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, + /*[7e30]*/ 0x31, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[7e38]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[7e40]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[7e48]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[7e50]*/ 0x31, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[7e58]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[7e60]*/ 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[7e68]*/ 0x31, 0x35, 0x33, 0x15, 0x01, 0x00, 0x80, 0x80, + /*[7e70]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x00, 0x80, + /*[7e78]*/ 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[7e80]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, + /*[7e88]*/ 0x00, 0x80, 0xfd, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7e90]*/ 0x02, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7e98]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7ea0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7ea8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7eb0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[7eb8]*/ 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[7ec0]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[7ec8]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[7ed0]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[7ed8]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, + /*[7ee0]*/ 0x13, 0x35, 0x33, 0x1d, 0x01, 0x35, 0x33, 0x15, + /*[7ee8]*/ 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[7ef0]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[7ef8]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[7f00]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[7f08]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7f10]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7f18]*/ 0x80, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x01, 0x80, + /*[7f20]*/ 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, + /*[7f28]*/ 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, + /*[7f30]*/ 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, + /*[7f38]*/ 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7f40]*/ 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7f48]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7f50]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7f58]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[7f60]*/ 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[7f68]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[7f70]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[7f78]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[7f80]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, + /*[7f88]*/ 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[7f90]*/ 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[7f98]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[7fa0]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[7fa8]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[7fb0]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7fb8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[7fc0]*/ 0x01, 0x00, 0x80, 0xff, 0x00, 0x80, 0xff, 0x00, + /*[7fc8]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[7fd0]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[7fd8]*/ 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfe, 0x00, + /*[7fe0]*/ 0x80, 0x80, 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, + /*[7fe8]*/ 0x80, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, + /*[7ff0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[7ff8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[8000]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[8008]*/ 0x80, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, + /*[8010]*/ 0x00, 0x00, 0x02, 0x80, 0x04, 0x00, 0x00, 0x03, + /*[8018]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[8020]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[8028]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[8030]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x00, 0x01, 0x35, + /*[8038]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x33, 0x35, + /*[8040]*/ 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[8048]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[8050]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[8058]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, + /*[8060]*/ 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[8068]*/ 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, + /*[8070]*/ 0x33, 0x15, 0x01, 0x00, 0x80, 0xff, 0x00, 0x80, + /*[8078]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, 0x80, 0x80, + /*[8080]*/ 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[8088]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[8090]*/ 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[8098]*/ 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[80a0]*/ 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[80a8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[80b0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[80b8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[80c0]*/ 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, + /*[80c8]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[80d0]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[80d8]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[80e0]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, + /*[80e8]*/ 0x13, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[80f0]*/ 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[80f8]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[8100]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[8108]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[8110]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[8118]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[8120]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, + /*[8128]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[8130]*/ 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[8138]*/ 0x80, 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[8140]*/ 0x80, 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[8148]*/ 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[8150]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[8158]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[8160]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + /*[8168]*/ 0x00, 0x12, 0x00, 0x00, 0xff, 0x00, 0x02, 0x80, + /*[8170]*/ 0x04, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[8178]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[8180]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[8188]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[8190]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x47, 0x00, 0x00, + /*[8198]*/ 0x01, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[81a0]*/ 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[81a8]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[81b0]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[81b8]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[81c0]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[81c8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[81d0]*/ 0x07, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[81d8]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[81e0]*/ 0x01, 0x00, 0x80, 0xff, 0x00, 0x80, 0xff, 0x00, + /*[81e8]*/ 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, + /*[81f0]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[81f8]*/ 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfe, 0x00, + /*[8200]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, + /*[8208]*/ 0x80, 0x80, 0x80, 0x03, 0x80, 0x80, 0x80, 0x80, + /*[8210]*/ 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0x80, 0x80, + /*[8218]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[8220]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[8228]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[8230]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[8238]*/ 0x80, 0x80, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, + /*[8240]*/ 0xff, 0x00, 0x02, 0x80, 0x03, 0x80, 0x00, 0x03, + /*[8248]*/ 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, + /*[8250]*/ 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, + /*[8258]*/ 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, + /*[8260]*/ 0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, + /*[8268]*/ 0x00, 0x47, 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, + /*[8270]*/ 0x07, 0x35, 0x33, 0x15, 0x07, 0x35, 0x33, 0x15, + /*[8278]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[8280]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[8288]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[8290]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[8298]*/ 0x21, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[82a0]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[82a8]*/ 0x31, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[82b0]*/ 0x07, 0x35, 0x33, 0x15, 0x80, 0x80, 0x80, 0x80, + /*[82b8]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, + /*[82c0]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[82c8]*/ 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[82d0]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[82d8]*/ 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[82e0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[82e8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[82f0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[82f8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[8300]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[8308]*/ 0x00, 0x12, 0x00, 0x00, 0xff, 0x00, 0x02, 0x80, + /*[8310]*/ 0x03, 0x80, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, + /*[8318]*/ 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, + /*[8320]*/ 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, + /*[8328]*/ 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, + /*[8330]*/ 0x00, 0x3f, 0x00, 0x43, 0x00, 0x47, 0x00, 0x00, + /*[8338]*/ 0x13, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15, + /*[8340]*/ 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[8348]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[8350]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[8358]*/ 0x05, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, + /*[8360]*/ 0x05, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[8368]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[8370]*/ 0x07, 0x35, 0x33, 0x15, 0x05, 0x35, 0x33, 0x15, + /*[8378]*/ 0x31, 0x35, 0x33, 0x15, 0x31, 0x35, 0x33, 0x15, + /*[8380]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x01, + /*[8388]*/ 0x80, 0x80, 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, + /*[8390]*/ 0xfd, 0x80, 0x80, 0x01, 0x80, 0x80, 0xfd, 0x80, + /*[8398]*/ 0x80, 0x01, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[83a0]*/ 0x80, 0x80, 0x80, 0x80, 0xfe, 0x00, 0x80, 0x80, + /*[83a8]*/ 0x80, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0xff, + /*[83b0]*/ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[83b8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[83c0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[83c8]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + /*[83d0]*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + /*[83d8]*/ 0x00, 0x00, 0x00, 0x15, 0x01, 0x02, 0x00, 0x00, + /*[83e0]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, + /*[83e8]*/ 0x00, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[83f0]*/ 0x00, 0x01, 0x00, 0x18, 0x00, 0x81, 0x00, 0x00, + /*[83f8]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0e, + /*[8400]*/ 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[8408]*/ 0x00, 0x03, 0x00, 0x18, 0x00, 0x81, 0x00, 0x00, + /*[8410]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x18, + /*[8418]*/ 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[8420]*/ 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, + /*[8428]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x18, + /*[8430]*/ 0x00, 0x81, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + /*[8438]*/ 0x00, 0x00, 0x00, 0x12, 0x00, 0x14, 0x00, 0x01, + /*[8440]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0c, + /*[8448]*/ 0x00, 0x31, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + /*[8450]*/ 0x00, 0x02, 0x00, 0x07, 0x00, 0x26, 0x00, 0x01, + /*[8458]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x10, + /*[8460]*/ 0x00, 0x2d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + /*[8468]*/ 0x00, 0x04, 0x00, 0x0c, 0x00, 0x31, 0x00, 0x01, + /*[8470]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, + /*[8478]*/ 0x00, 0x3d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + /*[8480]*/ 0x00, 0x06, 0x00, 0x0c, 0x00, 0x31, 0x00, 0x03, + /*[8488]*/ 0x00, 0x01, 0x04, 0x09, 0x00, 0x00, 0x00, 0x24, + /*[8490]*/ 0x00, 0x47, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09, + /*[8498]*/ 0x00, 0x01, 0x00, 0x18, 0x00, 0x81, 0x00, 0x03, + /*[84a0]*/ 0x00, 0x01, 0x04, 0x09, 0x00, 0x02, 0x00, 0x0e, + /*[84a8]*/ 0x00, 0x6b, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09, + /*[84b0]*/ 0x00, 0x03, 0x00, 0x20, 0x00, 0x79, 0x00, 0x03, + /*[84b8]*/ 0x00, 0x01, 0x04, 0x09, 0x00, 0x04, 0x00, 0x18, + /*[84c0]*/ 0x00, 0x81, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09, + /*[84c8]*/ 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, + /*[84d0]*/ 0x00, 0x01, 0x04, 0x09, 0x00, 0x06, 0x00, 0x18, + /*[84d8]*/ 0x00, 0x81, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, + /*[84e0]*/ 0x00, 0x34, 0x00, 0x2f, 0x00, 0x30, 0x00, 0x34, + /*[84e8]*/ 0x00, 0x2f, 0x00, 0x31, 0x00, 0x35, 0x62, 0x79, + /*[84f0]*/ 0x20, 0x54, 0x72, 0x69, 0x73, 0x74, 0x61, 0x6e, + /*[84f8]*/ 0x20, 0x47, 0x72, 0x69, 0x6d, 0x6d, 0x65, 0x72, + /*[8500]*/ 0x52, 0x65, 0x67, 0x75, 0x6c, 0x61, 0x72, 0x54, + /*[8508]*/ 0x54, 0x58, 0x20, 0x50, 0x72, 0x6f, 0x67, 0x67, + /*[8510]*/ 0x79, 0x54, 0x69, 0x6e, 0x79, 0x54, 0x54, 0x32, + /*[8518]*/ 0x30, 0x30, 0x34, 0x2f, 0x30, 0x34, 0x2f, 0x31, + /*[8520]*/ 0x35, 0x00, 0x62, 0x00, 0x79, 0x00, 0x20, 0x00, + /*[8528]*/ 0x54, 0x00, 0x72, 0x00, 0x69, 0x00, 0x73, 0x00, + /*[8530]*/ 0x74, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x20, 0x00, + /*[8538]*/ 0x47, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6d, 0x00, + /*[8540]*/ 0x6d, 0x00, 0x65, 0x00, 0x72, 0x00, 0x52, 0x00, + /*[8548]*/ 0x65, 0x00, 0x67, 0x00, 0x75, 0x00, 0x6c, 0x00, + /*[8550]*/ 0x61, 0x00, 0x72, 0x00, 0x54, 0x00, 0x54, 0x00, + /*[8558]*/ 0x58, 0x00, 0x20, 0x00, 0x50, 0x00, 0x72, 0x00, + /*[8560]*/ 0x6f, 0x00, 0x67, 0x00, 0x67, 0x00, 0x79, 0x00, + /*[8568]*/ 0x54, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x79, 0x00, + /*[8570]*/ 0x54, 0x00, 0x54, 0x00, 0x00, 0x02, 0x00, 0x00, + /*[8578]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, + /*[8580]*/ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + /*[8588]*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /*[8590]*/ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + /*[8598]*/ 0x00, 0x01, 0x01, 0x02, 0x01, 0x03, 0x01, 0x04, + /*[85a0]*/ 0x01, 0x05, 0x01, 0x06, 0x01, 0x07, 0x01, 0x08, + /*[85a8]*/ 0x01, 0x09, 0x01, 0x0a, 0x01, 0x0b, 0x01, 0x0c, + /*[85b0]*/ 0x01, 0x0d, 0x01, 0x0e, 0x01, 0x0f, 0x01, 0x10, + /*[85b8]*/ 0x01, 0x11, 0x01, 0x12, 0x01, 0x13, 0x01, 0x14, + /*[85c0]*/ 0x01, 0x15, 0x01, 0x16, 0x01, 0x17, 0x01, 0x18, + /*[85c8]*/ 0x01, 0x19, 0x01, 0x1a, 0x01, 0x1b, 0x01, 0x1c, + /*[85d0]*/ 0x01, 0x1d, 0x01, 0x1e, 0x01, 0x1f, 0x01, 0x20, + /*[85d8]*/ 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, + /*[85e0]*/ 0x00, 0x07, 0x00, 0x08, 0x00, 0x09, 0x00, 0x0a, + /*[85e8]*/ 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, + /*[85f0]*/ 0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x12, + /*[85f8]*/ 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, + /*[8600]*/ 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, + /*[8608]*/ 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, + /*[8610]*/ 0x00, 0x1f, 0x00, 0x20, 0x00, 0x21, 0x00, 0x22, + /*[8618]*/ 0x00, 0x23, 0x00, 0x24, 0x00, 0x25, 0x00, 0x26, + /*[8620]*/ 0x00, 0x27, 0x00, 0x28, 0x00, 0x29, 0x00, 0x2a, + /*[8628]*/ 0x00, 0x2b, 0x00, 0x2c, 0x00, 0x2d, 0x00, 0x2e, + /*[8630]*/ 0x00, 0x2f, 0x00, 0x30, 0x00, 0x31, 0x00, 0x32, + /*[8638]*/ 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, + /*[8640]*/ 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, + /*[8648]*/ 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3e, + /*[8650]*/ 0x00, 0x3f, 0x00, 0x40, 0x00, 0x41, 0x00, 0x42, + /*[8658]*/ 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, + /*[8660]*/ 0x00, 0x47, 0x00, 0x48, 0x00, 0x49, 0x00, 0x4a, + /*[8668]*/ 0x00, 0x4b, 0x00, 0x4c, 0x00, 0x4d, 0x00, 0x4e, + /*[8670]*/ 0x00, 0x4f, 0x00, 0x50, 0x00, 0x51, 0x00, 0x52, + /*[8678]*/ 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, + /*[8680]*/ 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, + /*[8688]*/ 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5e, + /*[8690]*/ 0x00, 0x5f, 0x00, 0x60, 0x00, 0x61, 0x01, 0x21, + /*[8698]*/ 0x01, 0x22, 0x01, 0x23, 0x01, 0x24, 0x01, 0x25, + /*[86a0]*/ 0x01, 0x26, 0x01, 0x27, 0x01, 0x28, 0x01, 0x29, + /*[86a8]*/ 0x01, 0x2a, 0x01, 0x2b, 0x01, 0x2c, 0x01, 0x2d, + /*[86b0]*/ 0x01, 0x2e, 0x01, 0x2f, 0x01, 0x30, 0x01, 0x31, + /*[86b8]*/ 0x01, 0x32, 0x01, 0x33, 0x01, 0x34, 0x01, 0x35, + /*[86c0]*/ 0x01, 0x36, 0x01, 0x37, 0x01, 0x38, 0x01, 0x39, + /*[86c8]*/ 0x01, 0x3a, 0x01, 0x3b, 0x01, 0x3c, 0x01, 0x3d, + /*[86d0]*/ 0x01, 0x3e, 0x01, 0x3f, 0x01, 0x40, 0x01, 0x41, + /*[86d8]*/ 0x00, 0xac, 0x00, 0xa3, 0x00, 0x84, 0x00, 0x85, + /*[86e0]*/ 0x00, 0xbd, 0x00, 0x96, 0x00, 0xe8, 0x00, 0x86, + /*[86e8]*/ 0x00, 0x8e, 0x00, 0x8b, 0x00, 0x9d, 0x00, 0xa9, + /*[86f0]*/ 0x00, 0xa4, 0x00, 0xef, 0x00, 0x8a, 0x00, 0xda, + /*[86f8]*/ 0x00, 0x83, 0x00, 0x93, 0x00, 0xf2, 0x00, 0xf3, + /*[8700]*/ 0x00, 0x8d, 0x00, 0x97, 0x00, 0x88, 0x00, 0xc3, + /*[8708]*/ 0x00, 0xde, 0x00, 0xf1, 0x00, 0x9e, 0x00, 0xaa, + /*[8710]*/ 0x00, 0xf5, 0x00, 0xf4, 0x00, 0xf6, 0x00, 0xa2, + /*[8718]*/ 0x00, 0xad, 0x00, 0xc9, 0x00, 0xc7, 0x00, 0xae, + /*[8720]*/ 0x00, 0x62, 0x00, 0x63, 0x00, 0x90, 0x00, 0x64, + /*[8728]*/ 0x00, 0xcb, 0x00, 0x65, 0x00, 0xc8, 0x00, 0xca, + /*[8730]*/ 0x00, 0xcf, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, + /*[8738]*/ 0x00, 0xe9, 0x00, 0x66, 0x00, 0xd3, 0x00, 0xd0, + /*[8740]*/ 0x00, 0xd1, 0x00, 0xaf, 0x00, 0x67, 0x00, 0xf0, + /*[8748]*/ 0x00, 0x91, 0x00, 0xd6, 0x00, 0xd4, 0x00, 0xd5, + /*[8750]*/ 0x00, 0x68, 0x00, 0xeb, 0x00, 0xed, 0x00, 0x89, + /*[8758]*/ 0x00, 0x6a, 0x00, 0x69, 0x00, 0x6b, 0x00, 0x6d, + /*[8760]*/ 0x00, 0x6c, 0x00, 0x6e, 0x00, 0xa0, 0x00, 0x6f, + /*[8768]*/ 0x00, 0x71, 0x00, 0x70, 0x00, 0x72, 0x00, 0x73, + /*[8770]*/ 0x00, 0x75, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, + /*[8778]*/ 0x00, 0xea, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x79, + /*[8780]*/ 0x00, 0x7b, 0x00, 0x7d, 0x00, 0x7c, 0x00, 0xb8, + /*[8788]*/ 0x00, 0xa1, 0x00, 0x7f, 0x00, 0x7e, 0x00, 0x80, + /*[8790]*/ 0x00, 0x81, 0x00, 0xec, 0x00, 0xee, 0x00, 0xba, + /*[8798]*/ 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, + /*[87a0]*/ 0x23, 0x30, 0x78, 0x30, 0x30, 0x30, 0x31, 0x0e, + /*[87a8]*/ 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, + /*[87b0]*/ 0x30, 0x78, 0x30, 0x30, 0x30, 0x32, 0x0e, 0x75, + /*[87b8]*/ 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, + /*[87c0]*/ 0x78, 0x30, 0x30, 0x30, 0x33, 0x0e, 0x75, 0x6e, + /*[87c8]*/ 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, + /*[87d0]*/ 0x30, 0x30, 0x30, 0x34, 0x0e, 0x75, 0x6e, 0x69, + /*[87d8]*/ 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, + /*[87e0]*/ 0x30, 0x30, 0x35, 0x0e, 0x75, 0x6e, 0x69, 0x63, + /*[87e8]*/ 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, + /*[87f0]*/ 0x30, 0x36, 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, + /*[87f8]*/ 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, 0x30, + /*[8800]*/ 0x37, 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, + /*[8808]*/ 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, 0x30, 0x38, + /*[8810]*/ 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, + /*[8818]*/ 0x23, 0x30, 0x78, 0x30, 0x30, 0x30, 0x39, 0x0e, + /*[8820]*/ 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, + /*[8828]*/ 0x30, 0x78, 0x30, 0x30, 0x30, 0x61, 0x0e, 0x75, + /*[8830]*/ 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, + /*[8838]*/ 0x78, 0x30, 0x30, 0x30, 0x62, 0x0e, 0x75, 0x6e, + /*[8840]*/ 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, + /*[8848]*/ 0x30, 0x30, 0x30, 0x63, 0x0e, 0x75, 0x6e, 0x69, + /*[8850]*/ 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, + /*[8858]*/ 0x30, 0x30, 0x64, 0x0e, 0x75, 0x6e, 0x69, 0x63, + /*[8860]*/ 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, + /*[8868]*/ 0x30, 0x65, 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, + /*[8870]*/ 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, 0x30, + /*[8878]*/ 0x66, 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, + /*[8880]*/ 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, 0x31, 0x30, + /*[8888]*/ 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, + /*[8890]*/ 0x23, 0x30, 0x78, 0x30, 0x30, 0x31, 0x31, 0x0e, + /*[8898]*/ 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, + /*[88a0]*/ 0x30, 0x78, 0x30, 0x30, 0x31, 0x32, 0x0e, 0x75, + /*[88a8]*/ 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, + /*[88b0]*/ 0x78, 0x30, 0x30, 0x31, 0x33, 0x0e, 0x75, 0x6e, + /*[88b8]*/ 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, + /*[88c0]*/ 0x30, 0x30, 0x31, 0x34, 0x0e, 0x75, 0x6e, 0x69, + /*[88c8]*/ 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, + /*[88d0]*/ 0x30, 0x31, 0x35, 0x0e, 0x75, 0x6e, 0x69, 0x63, + /*[88d8]*/ 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, + /*[88e0]*/ 0x31, 0x36, 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, + /*[88e8]*/ 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, 0x31, + /*[88f0]*/ 0x37, 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, + /*[88f8]*/ 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, 0x31, 0x38, + /*[8900]*/ 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, + /*[8908]*/ 0x23, 0x30, 0x78, 0x30, 0x30, 0x31, 0x39, 0x0e, + /*[8910]*/ 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, + /*[8918]*/ 0x30, 0x78, 0x30, 0x30, 0x31, 0x61, 0x0e, 0x75, + /*[8920]*/ 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, + /*[8928]*/ 0x78, 0x30, 0x30, 0x31, 0x62, 0x0e, 0x75, 0x6e, + /*[8930]*/ 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, + /*[8938]*/ 0x30, 0x30, 0x31, 0x63, 0x0e, 0x75, 0x6e, 0x69, + /*[8940]*/ 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, + /*[8948]*/ 0x30, 0x31, 0x64, 0x0e, 0x75, 0x6e, 0x69, 0x63, + /*[8950]*/ 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, + /*[8958]*/ 0x31, 0x65, 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, + /*[8960]*/ 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, 0x31, + /*[8968]*/ 0x66, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, + /*[8970]*/ 0x04, 0x45, 0x75, 0x72, 0x6f, 0x0e, 0x75, 0x6e, + /*[8978]*/ 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, + /*[8980]*/ 0x30, 0x30, 0x38, 0x31, 0x0e, 0x75, 0x6e, 0x69, + /*[8988]*/ 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, + /*[8990]*/ 0x30, 0x38, 0x32, 0x0e, 0x75, 0x6e, 0x69, 0x63, + /*[8998]*/ 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, + /*[89a0]*/ 0x38, 0x33, 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, + /*[89a8]*/ 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, 0x38, + /*[89b0]*/ 0x34, 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, + /*[89b8]*/ 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, 0x38, 0x35, + /*[89c0]*/ 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, + /*[89c8]*/ 0x23, 0x30, 0x78, 0x30, 0x30, 0x38, 0x36, 0x0e, + /*[89d0]*/ 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, + /*[89d8]*/ 0x30, 0x78, 0x30, 0x30, 0x38, 0x37, 0x0e, 0x75, + /*[89e0]*/ 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, + /*[89e8]*/ 0x78, 0x30, 0x30, 0x38, 0x38, 0x0e, 0x75, 0x6e, + /*[89f0]*/ 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, + /*[89f8]*/ 0x30, 0x30, 0x38, 0x39, 0x0e, 0x75, 0x6e, 0x69, + /*[8a00]*/ 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, + /*[8a08]*/ 0x30, 0x38, 0x61, 0x0e, 0x75, 0x6e, 0x69, 0x63, + /*[8a10]*/ 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, + /*[8a18]*/ 0x38, 0x62, 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, + /*[8a20]*/ 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, 0x38, + /*[8a28]*/ 0x63, 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, + /*[8a30]*/ 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, 0x38, 0x64, + /*[8a38]*/ 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, + /*[8a40]*/ 0x23, 0x30, 0x78, 0x30, 0x30, 0x38, 0x65, 0x0e, + /*[8a48]*/ 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, + /*[8a50]*/ 0x30, 0x78, 0x30, 0x30, 0x38, 0x66, 0x0e, 0x75, + /*[8a58]*/ 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, + /*[8a60]*/ 0x78, 0x30, 0x30, 0x39, 0x30, 0x0e, 0x75, 0x6e, + /*[8a68]*/ 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, + /*[8a70]*/ 0x30, 0x30, 0x39, 0x31, 0x0e, 0x75, 0x6e, 0x69, + /*[8a78]*/ 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, + /*[8a80]*/ 0x30, 0x39, 0x32, 0x0e, 0x75, 0x6e, 0x69, 0x63, + /*[8a88]*/ 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, + /*[8a90]*/ 0x39, 0x33, 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, + /*[8a98]*/ 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, 0x39, + /*[8aa0]*/ 0x34, 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, + /*[8aa8]*/ 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, 0x39, 0x35, + /*[8ab0]*/ 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, + /*[8ab8]*/ 0x23, 0x30, 0x78, 0x30, 0x30, 0x39, 0x36, 0x0e, + /*[8ac0]*/ 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, + /*[8ac8]*/ 0x30, 0x78, 0x30, 0x30, 0x39, 0x37, 0x0e, 0x75, + /*[8ad0]*/ 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, + /*[8ad8]*/ 0x78, 0x30, 0x30, 0x39, 0x38, 0x0e, 0x75, 0x6e, + /*[8ae0]*/ 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, + /*[8ae8]*/ 0x30, 0x30, 0x39, 0x39, 0x0e, 0x75, 0x6e, 0x69, + /*[8af0]*/ 0x63, 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, + /*[8af8]*/ 0x30, 0x39, 0x61, 0x0e, 0x75, 0x6e, 0x69, 0x63, + /*[8b00]*/ 0x6f, 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, + /*[8b08]*/ 0x39, 0x62, 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, + /*[8b10]*/ 0x64, 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, 0x39, + /*[8b18]*/ 0x63, 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, + /*[8b20]*/ 0x65, 0x23, 0x30, 0x78, 0x30, 0x30, 0x39, 0x64, + /*[8b28]*/ 0x0e, 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, + /*[8b30]*/ 0x23, 0x30, 0x78, 0x30, 0x30, 0x39, 0x65, 0x0e, + /*[8b38]*/ 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x23, + /*[8b40]*/ 0x30, 0x78, 0x30, 0x30, 0x39, 0x66, 0x00, 0x00, + }; diff --git a/engine/Makefile b/engine/Makefile new file mode 100644 index 0000000..22ad915 --- /dev/null +++ b/engine/Makefile @@ -0,0 +1,66 @@ +include ../Makefile.common + +CXXFLAGS += -I../glfw/include +LDFLAGS += -L../glfw -lglfw + +CXXFLAGS += -I../trimeshloader/include +LDFLAGS += -L../trimeshloader -ltrimeshloader + +CXXFLAGS += -I../physfs +LDFLAGS += -L../physfs -lphysfs + +CXXFLAGS += -I../ftgl +LDFLAGS += -L../ftgl -lftgl + +CXXFLAGS += -I../freetype/include +LDFLAGS += -L../freetype -lfreetype + +CXXFLAGS += -I../corona/src +LDFLAGS += -L../corona -lcorona + +CXXFLAGS += -I../glew/include +LDFLAGS += -L../glew -lglew + +CXXFLAGS += -I../squirrel/include +LDFLAGS += -L../squirrel -lsquirrel + +CXXFLAGS += -I../ode/include +LDFLAGS += -L../ode -lode + +CXXFLAGS += -I../tinyxml +LDFLAGS += -L../tinyxml -ltinyxml + +CXXFLAGS += -I../libpng +LDFLAGS += -L../libpng -lpng + +LDFLAGS += -L../zlib -lz + +ifeq ($(OS),WIN32) + LDFLAGS += -lglu32 -lwinmm -lole32 -lws2_32 -lopengl32 +else + LDFLAGS += -lm -lGL -lGLU -lX11 -lXxf86vm -lXext #-lopenal -Wl,-rpath=. -L/usr/X11R6/lib +endif + +SRC = $(wildcard */*.cpp) $(wildcard *.cpp) +OBJ = $(patsubst %.cpp,%.o,$(SRC) ) +DEP = $(patsubst %.cpp,%.d,$(SRC) ) + +NAME = redcore$(EXT) + +all: $(NAME) + +$(NAME): $(OBJ) + @echo Linking $@... + @g++ -o $(NAME) $(OBJ) $(LDFLAGS) + @echo done + +clean: + -@$(RMSUB) *.o + -@$(RMSUB) *.d + -@$(RMSUB) Utilities$(SLASH)*.o + -@$(RMSUB) Utilities$(SLASH)*.d + -@$(RMSUB) Math$(SLASH)*.o + -@$(RMSUB) Math$(SLASH)*.d + -@$(RM) $(NAME) + +-include $(SRC:%.cpp=%.d) diff --git a/engine/Math/Matrix.h b/engine/Math/Matrix.h new file mode 100644 index 0000000..3d7c6db --- /dev/null +++ b/engine/Math/Matrix.h @@ -0,0 +1,866 @@ +#ifndef BLUECORE_MATRIX_H +#define BLUECORE_MATRIX_H + +// system includes +#include +#include + +// local includes +#include "Scalar.h" +#include "Vector.h" +#include "Quaternion.h" + +namespace BlueCore +{ + template + class Matrix3x3Template + { + public: + T m[9]; + + /** + * default constructor + */ + inline Matrix3x3Template() + { + identity(); + } + + + /** + * constructor from array + */ + template + inline Matrix3x3Template( const S a[9] ) + { + m[0] = static_cast(a[0]); + m[1] = static_cast(a[1]); + m[2] = static_cast(a[2]); + + m[3] = static_cast(a[3]); + m[4] = static_cast(a[4]); + m[5] = static_cast(a[5]); + + m[6] = static_cast(a[6]); + m[7] = static_cast(a[7]); + m[8] = static_cast(a[8]); + } + + + /** + * copy constructor + */ + template + inline Matrix3x3Template( const Matrix3x3Template &a ) + { + m[0] = static_cast(a.m[0]); + m[1] = static_cast(a.m[1]); + m[2] = static_cast(a.m[2]); + + m[3] = static_cast(a.m[3]); + m[4] = static_cast(a.m[4]); + m[5] = static_cast(a.m[5]); + + m[6] = static_cast(a.m[6]); + m[7] = static_cast(a.m[7]); + m[8] = static_cast(a.m[8]); + } + + + /** + * constructor from heading and up vectors + */ + inline Matrix3x3Template( + const Vector3Template &h, + const Vector3Template &u ) + { + Vector3Template a, b, c; + c = h.unit(); + a = h.crossProduct( u ).unit(); + b = c.crossProduct( a ).unit(); + + m[0] = a.x; + m[1] = b.x; + m[2] = c.x; + + m[3] = a.y; + m[4] = b.y; + m[5] = c.y; + + m[6] = a.z; + m[7] = b.z; + m[8] = c.z; + } + + + /** + * contructor from euler angles + */ + inline Matrix3x3Template( T a, T e, T t ) + { + T ch = cos( a ); + T sh = sin( a ); + T ca = cos( e ); + T sa = sin( e ); + T cb = cos( t ); + T sb = sin( t ); + + m[0] = ch * ca; + m[1] = sh*sb - ch*sa*cb; + m[2] = ch*sa*sb + sh*cb; + + m[3] = sa; + m[4] = ca*cb; + m[5] = -ca*sb; + + m[6] = -sh*ca; + m[7] = sh*sa*cb + ch*sb; + m[8] = -sh*sa*sb + ch*cb; + } + + + /** + * contructor from quaternion + */ + template + inline Matrix3x3Template( const QuaternionTemplate &q ) + { + T xx = static_cast(q.x*q.x), + xy = static_cast(q.x*q.y), + xz = static_cast(q.x*q.z), + xw = static_cast(q.x*q.w), + + yy = static_cast(q.y*q.y), + yz = static_cast(q.y*q.z), + yw = static_cast(q.y*q.w), + + zz = static_cast(q.z*q.z), + zw = static_cast(q.z*q.w); + + m[0] = 1 - yy - yy - zz - zz; + m[1] = xy + xy + zw + zw; + m[2] = xz + xz - yw - yw; + + m[3] = xy + xy - zw - zw; + m[4] = 1 - xx - xx - zz - zz; + m[5] = yz + yz + xw + xw; + + m[6] = xz + xz + yw + yw; + m[7] = yz + yz - xw - xw; + m[8] = 1 - xx - xx - yy - yy; + } + + + /** + * set matrix to identity matrix + */ + inline void identity() + { + m[0] = static_cast(1); + m[1] = static_cast(0); + m[2] = static_cast(0); + + m[3] = static_cast(0); + m[4] = static_cast(1); + m[5] = static_cast(0); + + m[6] = static_cast(0); + m[7] = static_cast(0); + m[8] = static_cast(1); + } + + + /** + * get transposed matrix + */ + inline Matrix3x3Template transposed() const + { + Matrix3x3Template a; + + a.m[0] = m[0]; a.m[3] = m[1]; a.m[6] = m[2]; + a.m[1] = m[3]; a.m[4] = m[4]; a.m[7] = m[5]; + a.m[2] = m[6]; a.m[5] = m[7]; a.m[8] = m[8]; + + return a; + } + + + /** + * transpose + */ + inline void transpose() + { + swap( m[3], m[1] ); + swap( m[6], m[2] ); + swap( m[7], m[5] ); + } + + + /** + * determinate + */ + inline T determinant() const + { + return ( m[0]*m[4]*m[8] + m[1]*m[5]*m[6] + m[2]*m[3]*m[7] ) + - ( m[6]*m[4]*m[2] + m[7]*m[5]*m[0] + m[8]*m[3]*m[1] ); + } + + inline Matrix3x3Template inverted() + { + T det = determinant(); + T one_over_det = 1.0f / det; + + Matrix3x3Template result; + result.m[0] = +(m[4] * m[8] - m[5] * m[7]) * one_over_det; + result.m[1] = -(m[1] * m[8] - m[2] * m[7]) * one_over_det; + result.m[2] = +(m[1] * m[5] - m[2] * m[4]) * one_over_det; + result.m[3] = -(m[3] * m[8] - m[5] * m[6]) * one_over_det; + result.m[4] = +(m[0] * m[8] - m[2] * m[6]) * one_over_det; + result.m[5] = -(m[0] * m[5] - m[2] * m[3]) * one_over_det; + result.m[6] = +(m[3] * m[7] - m[4] * m[6]) * one_over_det; + result.m[7] = -(m[0] * m[7] - m[1] * m[6]) * one_over_det; + result.m[8] = +(m[0] * m[4] - m[1] * m[3]) * one_over_det; + + return result; + } + + + /** + * add/assign operator + */ + template + inline Matrix3x3Template &operator += ( + const Matrix3x3Template &a ) + { + m[0] += static_cast(a.m[0]); + m[1] += static_cast(a.m[1]); + m[2] += static_cast(a.m[2]); + + m[3] += static_cast(a.m[3]); + m[4] += static_cast(a.m[4]); + m[5] += static_cast(a.m[5]); + + m[6] += static_cast(a.m[6]); + m[7] += static_cast(a.m[7]); + m[8] += static_cast(a.m[8]); + + return *this; + } + + + /** + * matrix multiplication + */ + template + inline Matrix3x3Template operator * ( const Matrix3x3Template &a ) + { + Matrix3x3Template result; + + result.m[0] = m[0] * static_cast(a.m[0]) + + m[3] * static_cast(a.m[1]) + + m[6] * static_cast(a.m[2]); + + result.m[1] = m[1] * static_cast(a.m[0]) + + m[4] * static_cast(a.m[1]) + + m[7] * static_cast(a.m[2]); + + result.m[2] = m[2] * static_cast(a.m[0]) + + m[5] * static_cast(a.m[1]) + + m[8] * static_cast(a.m[2]); + + + result.m[3] = m[0] * static_cast(a.m[3]) + + m[3] * static_cast(a.m[4]) + + m[6] * static_cast(a.m[5]); + + result.m[4] = m[1] * static_cast(a.m[3]) + + m[4] * static_cast(a.m[4]) + + m[7] * static_cast(a.m[5]); + + result.m[5] = m[2] * static_cast(a.m[3]) + + m[5] * static_cast(a.m[4]) + + m[8] * static_cast(a.m[5]); + + + result.m[6] = m[0] * static_cast(a.m[6]) + + m[3] * static_cast(a.m[7]) + + m[6] * static_cast(a.m[8]); + + result.m[7] = m[1] * static_cast(a.m[6]) + + m[4] * static_cast(a.m[7]) + + m[7] * static_cast(a.m[8]); + + result.m[8] = m[2] * static_cast(a.m[6]) + + m[5] * static_cast(a.m[7]) + + m[8] * static_cast(a.m[8]); + + return result; + } + + + /** + * matrix vector multiplication + */ + template + Vector3Template operator * ( const Vector3Template& a ) + { + return Vector3Template( + m[0] * static_cast(a.x) + + m[3] * static_cast(a.y) + + m[6] * static_cast(a.z), + m[1] * static_cast(a.x) + + m[4] * static_cast(a.y) + + m[7] * static_cast(a.z), + m[2] * static_cast(a.x) + + m[5] * static_cast(a.y) + + m[8] * static_cast(a.z) ); + } + + /** + * matrix scalar multiplication + */ + template + Matrix3x3Template operator * ( const S a ) + { + Matrix3x3Template result; + + result.m[0] = m[0] * static_cast(a); + result.m[1] = m[1] * static_cast(a); + result.m[2] = m[2] * static_cast(a); + result.m[3] = m[3] * static_cast(a); + result.m[4] = m[4] * static_cast(a); + result.m[5] = m[5] * static_cast(a); + result.m[6] = m[6] * static_cast(a); + result.m[7] = m[7] * static_cast(a); + result.m[8] = m[8] * static_cast(a); + + return result; + } + + friend std::ostream &operator << ( std::ostream& os, Matrix3x3Template m ) + { + os << "( " << m.m[0] << ", " << m.m[1] << ", " << m.m[2] << " )" << std::endl; + os << "( " << m.m[3] << ", " << m.m[4] << ", " << m.m[5] << " )" << std::endl; + os << "( " << m.m[6] << ", " << m.m[7] << ", " << m.m[8] << " )" << std::endl; + return os; + } + + }; + + typedef Matrix3x3Template Matrix3x3Float; + typedef Matrix3x3Template Matrix3x3Double; + typedef Matrix3x3Template Matrix3x3; + + + template + class Matrix4x4Template + { + + public: + + T m[16]; + + /** + * default constructor + */ + inline Matrix4x4Template() + { + identity(); + } + + inline void identity() + { + m[0] = static_cast(1); + m[1] = static_cast(0); + m[2] = static_cast(0); + m[3] = static_cast(0); + + m[4] = static_cast(0); + m[5] = static_cast(1); + m[6] = static_cast(0); + m[7] = static_cast(0); + + m[8] = static_cast(0); + m[9] = static_cast(0); + m[10] = static_cast(1); + m[11] = static_cast(0); + + m[12] = static_cast(0); + m[13] = static_cast(0); + m[14] = static_cast(0); + m[15] = static_cast(1); + } + + + /** + * constructor from array + */ + inline Matrix4x4Template( const T a[16] ) + { + m[0] = a[0]; m[1] = a[1]; m[2] = a[2]; m[3] = a[3]; + m[4] = a[4]; m[5] = a[5]; m[6] = a[6]; m[7] = a[7]; + m[8] = a[8]; m[9] = a[9]; m[10] = a[10]; m[11] = a[11]; + m[12] = a[12]; m[13] = a[13]; m[14] = a[14]; m[15] = a[15]; + } + + /** + * constructor from 3x3 matrix, add. element are set to zero + */ + inline Matrix4x4Template( const Matrix3x3Template &a ) + { + m[0] = a[0]; + m[1] = a[1]; + m[2] = a[2]; + m[3] = static_cast(0); + + m[4] = a[3]; + m[5] = a[4]; + m[6] = a[5]; + m[7] = static_cast(0); + + m[8] = a[6]; + m[9] = a[7]; + m[10] = a[8]; + m[11] = static_cast(0); + + m[12] = static_cast(0); + m[13] = static_cast(0); + m[14] = static_cast(0); + m[15] = static_cast(1); + } + + /** + * constructor from 3x3 rotation matrix and translation vector + */ + inline Matrix4x4Template( + const Matrix3x3Template &a, + const Vector3Template &b ) + { + m[0] = a[0]; + m[1] = a[1]; + m[2] = a[2]; + m[3] = static_cast(0); + + m[4] = a[3]; + m[5] = a[4]; + m[6] = a[5]; + m[7] = static_cast(0); + + m[8] = a[6]; + m[9] = a[7]; + m[10] = a[8]; + m[11] = static_cast(0); + + m[12] = b.x; + m[13] = b.y; + m[14] = b.z; + m[15] = static_cast(1); + } + + + /** + * copy constructor + */ + inline Matrix4x4Template( const Matrix4x4Template &a ) + { + m[0] = a.m[0]; + m[1] = a.m[1]; + m[2] = a.m[2]; + m[3] = a.m[3]; + + m[4] = a.m[4]; + m[5] = a.m[5]; + m[6] = a.m[6]; + m[7] = a.m[7]; + + m[8] = a.m[8]; + m[9] = a.m[9]; + m[10] = a.m[10]; + m[11] = a.m[11]; + + m[12] = a.m[12]; + m[13] = a.m[13]; + m[14] = a.m[14]; + m[15] = a.m[15]; + } + + + /** + * constructor from quaternion + */ + inline Matrix4x4Template( const QuaternionTemplate &q ) + { + m[0] = 1 - 2*q.y*q.y - 2*q.z*q.z; + m[1] = 2*q.x*q.y - 2*q.z*q.w; + m[2] = 2*q.x*q.z + 2*q.y*q.w; + m[3] = static_cast(0); + + m[4] = 2*q.x*q.y + 2*q.z*q.w; + m[5] = 1 - 2*q.x*q.x - 2*q.z*q.z; + m[6] = 2*q.y*q.z - 2*q.x*q.w; + m[7] = static_cast(0); + + m[8] = 2*q.x*q.z - 2*q.y*q.w; + m[9] = 2*q.y*q.z + 2*q.x*q.w; + m[10] = 1 - 2*q.x*q.x - 2*q.y*q.y; + m[11] = static_cast(0); + + m[12] = static_cast(0); + m[13] = static_cast(0); + m[14] = static_cast(0); + m[15] = static_cast(1); + } + + + /** + * constructor from quaternion and translation vector + */ + inline Matrix4x4Template( + const QuaternionTemplate &q, + const Vector3Template &a ) + { + m[0] = 1 - 2*q.y*q.y - 2*q.z*q.z; + m[1] = 2*q.x*q.y - 2*q.z*q.w; + m[2] = 2*q.x*q.z + 2*q.y*q.w; + m[3] = static_cast(0); + + m[4] = 2*q.x*q.y + 2*q.z*q.w; + m[5] = 1 - 2*q.x*q.x - 2*q.z*q.z; + m[6] = 2*q.y*q.z - 2*q.x*q.w; + m[7] = static_cast(0); + + m[8] = 2*q.x*q.z - 2*q.y*q.w; + m[9] = 2*q.y*q.z + 2*q.x*q.w; + m[10] = 1 - 2*q.x*q.x - 2*q.y*q.y; + m[11] = static_cast(0); + + m[12] = a.x; + m[13] = a.y; + m[14] = a.z; + m[15] = static_cast(1); + } + + + /** + * constructor from heading, up and translation vectors + */ + inline Matrix4x4Template( + const Vector3Template &h, + const Vector3Template &u, + const Vector3Template &t ) + { + Vector3Template a, b, c; + c = h.unit(); + a = h.crossProduct( u ).unit(); + b = c.crossProduct( a ).unit(); + + m[0] = a.x; + m[1] = b.x; + m[2] = c.x; + m[3] = static_cast(0); + + m[4] = a.y; + m[5] = b.y; + m[6] = c.y; + m[7] = static_cast(0); + + m[8] = a.z; + m[9] = b.z; + m[10] = c.z; + m[11] = static_cast(0); + + m[12] = t.x; + m[13] = t.y; + m[14] = t.z; + m[15] = static_cast(1); + } + + + /** + * contructor from euler angles + */ + inline Matrix4x4Template( + T a, + T e, + T t, + const Vector3Template &tr ) + { + T ch = cos( a ); + T sh = sin( a ); + T ca = cos( e ); + T sa = sin( e ); + T cb = cos( t ); + T sb = sin( t ); + + m[0] = ch * ca; + m[1] = sh*sb - ch*sa*cb; + m[2] = ch*sa*sb + sh*cb; + m[3] = static_cast(0); + + m[4] = sa; + m[5] = ca*cb; + m[6] = -ca*sb; + m[7] = static_cast(0); + + + m[8] = -sh*ca; + m[9] = sh*sa*cb + ch*sb; + m[10] = -sh*sa*sb + ch*cb; + m[11] = static_cast(0); + + m[12] = tr.x; + m[13] = tr.y; + m[14] = tr.z; + m[15] = static_cast(1); + } + + + /** + * transpose this matrix + */ + inline void transpose() + { + std::swap( m[4], m[1] ); + std::swap( m[8], m[2] ); + std::swap( m[12], m[3] ); + + std::swap( m[9], m[6] ); + std::swap( m[13], m[7] ); + std::swap( m[14], m[11] ); + } + + + /** + * get transposed matrix + */ + inline Matrix4x4Template transposed() + { + Matrix4x4Template a; + + a.m[0] = m[0]; a.m[4] = m[1]; a.m[8] = m[2]; a.m[12] = m[3]; + a.m[1] = m[4]; a.m[5] = m[5]; a.m[9] = m[6]; a.m[13] = m[7]; + a.m[2] = m[8]; a.m[6] = m[9]; a.m[10] = m[10]; a.m[14] = m[11]; + a.m[3] = m[12]; a.m[7] = m[13]; a.m[11] = m[14]; a.m[15] = m[15]; + + return a; + } + + + /** + * matrix multiplication + */ + template + inline Matrix4x4Template operator * ( const Matrix4x4Template &a ) + { + Matrix4x4Template result; + + result.m[0] = m[0] * static_cast(a.m[0]) + + m[4] * static_cast(a.m[1]) + + m[8] * static_cast(a.m[2]) + + m[12] * static_cast(a.m[3]); + + result.m[1] = m[1] * static_cast(a.m[0]) + + m[5] * static_cast(a.m[1]) + + m[9] * static_cast(a.m[2]) + + m[13] * static_cast(a.m[3]); + + result.m[2] = m[2] * static_cast(a.m[0]) + + m[6] * static_cast(a.m[1]) + + m[10] * static_cast(a.m[2]) + + m[14] * static_cast(a.m[3]); + + result.m[3] = m[3] * static_cast(a.m[0]) + + m[7] * static_cast(a.m[1]) + + m[11] * static_cast(a.m[2]) + + m[15] * static_cast(a.m[3]); + + + result.m[4] = m[0] * static_cast(a.m[4]) + + m[4] * static_cast(a.m[5]) + + m[8] * static_cast(a.m[6]) + + m[12] * static_cast(a.m[7]); + + result.m[5] = m[1] * static_cast(a.m[4]) + + m[5] * static_cast(a.m[5]) + + m[9] * static_cast(a.m[6]) + + m[13] * static_cast(a.m[7]); + + result.m[6] = m[2] * static_cast(a.m[4]) + + m[6] * static_cast(a.m[5]) + + m[10] * static_cast(a.m[6]) + + m[14] * static_cast(a.m[7]); + + result.m[7] = m[3] * static_cast(a.m[4]) + + m[7] * static_cast(a.m[5]) + + m[11] * static_cast(a.m[6]) + + m[15] * static_cast(a.m[7]); + + + result.m[8] = m[0] * static_cast(a.m[8]) + + m[4] * static_cast(a.m[9]) + + m[8] * static_cast(a.m[10]) + + m[12] * static_cast(a.m[11]); + + result.m[9] = m[1] * static_cast(a.m[8]) + + m[5] * static_cast(a.m[9]) + + m[9] * static_cast(a.m[10]) + + m[13] * static_cast(a.m[11]); + + result.m[10] = m[2] * static_cast(a.m[8]) + + m[6] * static_cast(a.m[9]) + + m[10] * static_cast(a.m[10]) + + m[14] * static_cast(a.m[11]); + + result.m[11] = m[3] * static_cast(a.m[8]) + + m[7] * static_cast(a.m[9]) + + m[11] * static_cast(a.m[10]) + + m[15] * static_cast(a.m[11]); + + + result.m[12] = m[0] * static_cast(a.m[12]) + + m[4] * static_cast(a.m[13]) + + m[8] * static_cast(a.m[14]) + + m[12] * static_cast(a.m[15]); + + result.m[13] = m[1] * static_cast(a.m[12]) + + m[5] * static_cast(a.m[13]) + + m[9] * static_cast(a.m[14]) + + m[13] * static_cast(a.m[15]); + + result.m[14] = m[2] * static_cast(a.m[12]) + + m[6] * static_cast(a.m[13]) + + m[10] * static_cast(a.m[14]) + + m[14] * static_cast(a.m[15]); + + result.m[15] = m[3] * static_cast(a.m[12]) + + m[7] * static_cast(a.m[13]) + + m[11] * static_cast(a.m[14]) + + m[15] * static_cast(a.m[15]); + + return result; + } + + /** + * matrix multiplication + */ +/* + template + inline Matrix4x4Template operator *= ( const Matrix4x4Template &a ) + { + Matrix4x4Template result; + + result.m[0] = m[0] * static_cast(a.m[0]) + + m[4] * static_cast(a.m[1]) + + m[8] * static_cast(a.m[2]); + + m[12] * static_cast(a.m[3]); + + result.m[1] = m[1] * static_cast(a.m[0]) + + m[5] * static_cast(a.m[1]) + + m[9] * static_cast(a.m[2]); + + m[13] * static_cast(a.m[3]); + + result.m[2] = m[2] * static_cast(a.m[0]) + + m[6] * static_cast(a.m[1]) + + m[10] * static_cast(a.m[2]); + + m[14] * static_cast(a.m[3]); + + result.m[3] = m[3] * static_cast(a.m[0]) + + m[7] * static_cast(a.m[1]) + + m[11] * static_cast(a.m[2]); + + m[15] * static_cast(a.m[3]); + + + result.m[4] = m[0] * static_cast(a.m[4]) + + m[4] * static_cast(a.m[5]) + + m[8] * static_cast(a.m[6]); + + m[12] * static_cast(a.m[7]); + + result.m[5] = m[1] * static_cast(a.m[4]) + + m[5] * static_cast(a.m[5]) + + m[9] * static_cast(a.m[6]); + + m[13] * static_cast(a.m[7]); + + result.m[6] = m[2] * static_cast(a.m[4]) + + m[6] * static_cast(a.m[5]) + + m[10] * static_cast(a.m[6]); + + m[14] * static_cast(a.m[7]); + + result.m[7] = m[3] * static_cast(a.m[4]) + + m[7] * static_cast(a.m[5]) + + m[11] * static_cast(a.m[6]); + + m[15] * static_cast(a.m[7]); + + + result.m[8] = m[0] * static_cast(a.m[8]) + + m[4] * static_cast(a.m[9]) + + m[8] * static_cast(a.m[10]); + + m[12] * static_cast(a.m[11]); + + result.m[9] = m[1] * static_cast(a.m[8]) + + m[5] * static_cast(a.m[9]) + + m[9] * static_cast(a.m[10]); + + m[13] * static_cast(a.m[11]); + + result.m[10] = m[2] * static_cast(a.m[8]) + + m[6] * static_cast(a.m[9]) + + m[10] * static_cast(a.m[10]); + + m[14] * static_cast(a.m[11]); + + result.m[11] = m[3] * static_cast(a.m[8]) + + m[7] * static_cast(a.m[9]) + + m[11] * static_cast(a.m[10]); + + m[15] * static_cast(a.m[11]); + + + result.m[12] = m[0] * static_cast(a.m[12]) + + m[4] * static_cast(a.m[13]) + + m[8] * static_cast(a.m[14]); + + m[12] * static_cast(a.m[15]); + + result.m[13] = m[1] * static_cast(a.m[12]) + + m[5] * static_cast(a.m[13]) + + m[9] * static_cast(a.m[14]); + + m[13] * static_cast(a.m[15]); + + result.m[14] = m[2] * static_cast(a.m[12]) + + m[6] * static_cast(a.m[13]) + + m[10] * static_cast(a.m[14]); + + m[14] * static_cast(a.m[15]); + + result.m[15] = m[3] * static_cast(a.m[12]) + + m[7] * static_cast(a.m[13]) + + m[11] * static_cast(a.m[14]); + + m[15] * static_cast(a.m[15]); + + m = result.m; + + return *this; + } + */ + /** + * matrix vector multiplication + */ + Vector3Template operator * ( const Vector3Template &a ) + { + Vector3Template result; + + result.x = m[0] * a.x + m[4] * a.y + m[8] * a.z + m[12]; + result.y = m[1] * a.x + m[5] * a.y + m[9] * a.z + m[13]; + result.z = m[2] * a.x + m[6] * a.y + m[11] * a.z + m[14]; + + return result; + } + + T *data() + { + return &m[0]; + } + }; + + typedef Matrix4x4Template Matrix4x4Float; + typedef Matrix4x4Template Matrix4x4Double; + typedef Matrix4x4Template Matrix4x4; + +} // namespace bc + +#endif // BLUECORE_MATRIX_H diff --git a/engine/Math/Plane.h b/engine/Math/Plane.h new file mode 100644 index 0000000..714fc03 --- /dev/null +++ b/engine/Math/Plane.h @@ -0,0 +1,51 @@ +#ifndef BLUECORE_PLANE_H +#define BLUECORE_PLANE_H + +#include "Scalar.h" +#include "Vector.h" + +namespace BlueCore +{ + template + class PlaneTemplate + { + public: + Vector3Template _n; + T _d; + + /** + * contructor + */ + inline PlaneTemplate() : _d( 0. ) + { + } + + + /** + * contructor + */ + inline PlaneTemplate( + Vector3Template a, + Vector3Template b, + Vector3Template c ) + { + _n = (a - b).cross(a - c).normalized(); + _d = _n.dot( a ); + } + + + /** + * distance + */ + inline T distance( const Vector3Template a ) + { + return _n.dot( a ) - _d; + } + }; + + typedef PlaneTemplate PlaneFloat; + typedef PlaneTemplate PlaneDouble; + typedef PlaneTemplate Plane; +} + +#endif diff --git a/engine/Math/Point.h b/engine/Math/Point.h new file mode 100644 index 0000000..7a54762 --- /dev/null +++ b/engine/Math/Point.h @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +// Author: Gero Mueller +// Copyright: (c) 2006 Gero Mueller +// License: MIT License +//------------------------------------------------------------------------------ + +#ifndef BLUECORE_POINT_H +#define BLUECORE_POINT_H + +#include "rectangle.h" + +namespace BlueCore +{ + template + struct Rectangle2DTemplate; + + template + struct Point2DTemplate + { + type _x, _y; + + Point2DTemplate() : _x(0), _y(0) + { + } + + Point2DTemplate( type x, type y ) : _x(x), _y(y) + { + } + + bool in( const Rectangle2DTemplate &rect ); + }; + + #include "rectangle.h" + + template + bool Point2DTemplate::in( const Rectangle2DTemplate &rect ) + { + if( _x < rect._x ) + return false; + + if( _x > (rect._x + rect._width) ) + return false; + + if( _y < rect._y ) + return false; + + if( _y > (rect._y + rect._height) ) + return false; + + return true; + } + + typedef Point2DTemplate Point2D; + +}; + +#endif diff --git a/engine/Math/Quaternion.h b/engine/Math/Quaternion.h new file mode 100644 index 0000000..b02e3f5 --- /dev/null +++ b/engine/Math/Quaternion.h @@ -0,0 +1,366 @@ +#ifndef BLUECORE_QUATERNION_H +#define BLUECORE_QUATERNION_H + +#include "Vector.h" + +namespace BlueCore +{ + template + class QuaternionTemplate + { + public: + T w, x, y, z; + + + /** + * constructor + */ + inline QuaternionTemplate() + { + w = static_cast(1.0); + x = static_cast(0.0); + y = static_cast(0.0); + z = static_cast(0.0); + } + + + /** + * contructor + */ + template + inline QuaternionTemplate( S w, S x, S y, S z ) + { + this->w = static_cast(w); + this->x = static_cast(x); + this->y = static_cast(y); + this->z = static_cast(z); + } + + + /** + * contructor + */ + template + inline QuaternionTemplate( Vector3Template axis, S angle ) + { + T half_angle = static_cast(angle)/static_cast(2.0); + T sin_half_angle = static_cast( sin( half_angle ) ); + + w = static_cast( cos( half_angle ) ); + x = sin_half_angle * static_cast(axis.x); + y = sin_half_angle * static_cast(axis.y); + z = sin_half_angle * static_cast(axis.z); + } + + + /** + * contructor + */ + template + inline QuaternionTemplate( S h, S a, S b ) + { + T c1 = static_cast( cos(h / 2.0) ); + T c2 = static_cast( cos(a / 2.0) ); + T c3 = static_cast( cos(b / 2.0) ); + T s1 = static_cast( sin(h / 2.0) ); + T s2 = static_cast( sin(a / 2.0) ); + T s3 = static_cast( sin(b / 2.0) ); + + w = c1 * c2 * c3 - s1 * s2 * s3; + x = s1 * s2 * c3 + c1 * c2 * s3; + y = s1 * c2 * c3 + c1 * s2 * s3; + z = c1 * s2 * c3 - s1 * c2 * s3; + } + + + /** + * identity + */ + inline void identity() + { + w = static_cast(1.0); + x = static_cast(0.0); + y = static_cast(0.0); + z = static_cast(0.0); + } + + + /** + * operator + + */ + template + inline QuaternionTemplate operator + ( + const QuaternionTemplate &a ) const + { + return QuaternionTemplate( + w + static_cast(a.w), + x + static_cast(a.x), + y + static_cast(a.y), + z + static_cast(a.z) ); + } + + + /** + * operator += + */ + template + inline QuaternionTemplate &operator += ( + const QuaternionTemplate &a ) + { + w += static_cast(a.w); + x += static_cast(a.x); + y += static_cast(a.y); + z += static_cast(a.z); + + return *this; + } + + + /** + * operator - + */ + template + inline QuaternionTemplate operator - ( + const QuaternionTemplate &a ) const + { + return QuaternionTemplate( + w - static_cast(a.w), + x - static_cast(a.x), + y - static_cast(a.y), + z - static_cast(a.z) ); + } + + + /** + * operator * + */ + template + inline QuaternionTemplate operator * ( + const QuaternionTemplate &a ) const + { + return QuaternionTemplate( + w * static_cast(a.w) - + x * static_cast(a.x) - + y * static_cast(a.y) - + z * static_cast(a.z), + w * static_cast(a.x) + + x * static_cast(a.w) + + y * static_cast(a.z) - + z * static_cast(a.y), + w * static_cast(a.y) - + x * static_cast(a.z) + + y * static_cast(a.w) + + z * static_cast(a.x), + w * static_cast(a.z) + + x * static_cast(a.y) - + y * static_cast(a.x) + + z * static_cast(a.w) ); + } + + + /** + * operator *= + */ + template + inline QuaternionTemplate &operator *=( + const QuaternionTemplate &a ) + { + w = w * static_cast(a.w) - x * static_cast(a.x) - + y * static_cast(a.y) - z * static_cast(a.z); + x = w * static_cast(a.x) + x * static_cast(a.w) + + y * static_cast(a.z) - z * static_cast(a.y); + y = w * static_cast(a.y) - x * static_cast(a.z) + + y * static_cast(a.w) + z * static_cast(a.x); + z = w * static_cast(a.z) + x * static_cast(a.y) - + y * static_cast(a.x) + z * static_cast(a.w); + + return *this; + } + + + /** + * operator -= + */ + template + inline QuaternionTemplate &operator -= ( + const QuaternionTemplate &a ) + { + w -= static_cast(a.w); + x -= static_cast(a.x); + y -= static_cast(a.y); + z -= static_cast(a.z); + + return *this; + } + + + /** + * operator = + */ + template + inline QuaternionTemplate &operator = ( + const QuaternionTemplate &a ) + { + w = static_cast(a.w); + x = static_cast(a.x); + y = static_cast(a.y); + z = static_cast(a.z); + + return *this; + } + + + /** + * unit + */ + inline QuaternionTemplate unit() const + { + T d = 1/sqrt( w*w + x*x + y*y + z*z ); + return QuaternionTemplate( w * d, x * d, y * d, z * d ); + } + + + /** + * inversed + */ + inline QuaternionTemplate inversed() const + { + return QuaternionTemplate( w, -x, -y, -z ); + } + + /** + * inversed + */ + inline void inverse() + { + x = -x; + y = -y; + z = -z; + } + + + /** + * normalize + */ + inline QuaternionTemplate &normalize() + { + T d = 1/sqrt( w*w + x*x + y*y + z*z ); + w *= d; + x *= d; + y *= d; + z *= d; + + return *this; + } + + + /** + * apply + */ + template + inline const Vector3Template apply( + const Vector3Template &a ) const + { + T xx = x*x, xy = x*y, xz = x*z, xw = x*w, + yy = y*y, yz = y*z, yw = y*w, + zz = z*z, zw = z*w; + + return Vector3Template( + 2.0 * ( static_cast(a.x) * ( 0.5 - yy - zz ) + + static_cast(a.y) * ( xy - zw ) + + static_cast(a.z) * ( xz + yw ) ), + + 2.0 * ( static_cast(a.x) * ( xy + zw ) + + static_cast(a.y) * ( 0.5 - xx - zz ) + + static_cast(a.z) * ( yz - xw ) ), + + 2.0 * ( static_cast(a.x) * ( xz - yw ) + + static_cast(a.y) * ( yz + xw ) + + static_cast(a.z) * ( 0.5 - xx - yy ) ) ); + } + + + /** + * apply + */ + template + inline const Vector3Template operator * ( + const Vector3Template &a ) const + { + T xx = x*x, xy = x*y, xz = x*z, xw = x*w, + yy = y*y, yz = y*z, yw = y*w, + zz = z*z, zw = z*w; + + return Vector3Template( + 2.0 * ( static_cast(a.x) * ( 0.5 - yy - zz ) + + static_cast(a.y) * ( xy - zw ) + + static_cast(a.z) * ( xz + yw ) ), + + 2.0 * ( static_cast(a.x) * ( xy + zw ) + + static_cast(a.y) * ( 0.5 - xx - zz ) + + static_cast(a.z) * ( yz - xw ) ), + + 2.0 * ( static_cast(a.x) * ( xz - yw ) + + static_cast(a.y) * ( yz + xw ) + + static_cast(a.z) * ( 0.5 - xx - yy ) ) ); + } + + + /** + * applyInversed + */ + template + inline Vector3Template applyInversed( + const Vector3Template &a ) const + { + T xx = x*x, xy = x*y, xz = x*z, xw = -x*w, + yy = y*y, yz = y*z, yw = -y*w, + zz = z*z, zw = -z*w; + + return Vector3Template( + 2.0 * ( static_cast(a.x) * ( 0.5 - yy - zz ) + + static_cast(a.y) * ( xy - zw ) + + static_cast(a.z) * ( xz + yw ) ), + + 2.0 * ( static_cast(a.x) * ( xy + zw ) + + static_cast(a.y) * ( 0.5 - xx - zz ) + + static_cast(a.z) * ( yz - xw ) ), + + 2.0 * ( static_cast(a.x) * ( xz - yw ) + + static_cast(a.y) * ( yz + xw ) + + static_cast(a.z) * ( 0.5 - xx - yy ) ) ); + } + + /** + * transform from ode to gl coodinates + */ + inline QuaternionTemplate toGl() const + { + return QuaternionTemplate( w, x, z, -y ); + } + + /** + * transform from gl to ode coodinates + */ + inline QuaternionTemplate toOde() const + { + return QuaternionTemplate( w, x, -z, y ); + } + + inline QuaternionTemplate slerp( const QuaternionTemplate &q, const Scalar &t ) + { + Scalar phi = acos(w*q.w + x*q.x + y*q.y + z*q.z); + Scalar s = 1 / sin(phi); + Scalar a = sin(phi*(1-t)) * s; + Scalar b = sin(phi*t) * s; + + return QuaternionTemplate( a*w+b*q.w, a*x+b*q.x, a*y+b*q.y, a*z+b*q.z ); + } + }; + + typedef QuaternionTemplate QuaternionFloat; + typedef QuaternionTemplate QuaternionDouble; + typedef QuaternionTemplate Quaternion; +} + +#endif diff --git a/engine/Math/Ray.h b/engine/Math/Ray.h new file mode 100644 index 0000000..c0011ef --- /dev/null +++ b/engine/Math/Ray.h @@ -0,0 +1,119 @@ +//------------------------------------------------------------------------------ +// Author: Gero Mueller +// Copyright: (c) 2006 Gero Mueller +// License: MIT License +//------------------------------------------------------------------------------ + +#ifndef BLUECORE_RAY_H +#define BLUECORE_RAY_H + +#include "scalar.h" +#include "vector.h" + +namespace BlueCore +{ + template + class RayTemplate + { + Vector3Template point, direction; + + public: + + /** + * normalize the vector to length 1 + */ + inline RayTemplate() + { + } + + + /** + * constructor from point and direction + */ + template + inline RayTemplate( + const Vector3Template &point, + const Vector3Template &direction ) + { + this->point = point; + this->direction = direction.normalized(); + } + + + /** + * set the point + */ + template + inline setPoint( const Vector3Template &point ) + { + this->point = point; + } + + + /** + * set the direction + */ + template + inline setDirection( const Vector3Template &direction ) + { + this->direction = direction.normalized(); + } + + + /** + * get point + */ + inline const Vector3Template &getPoint() const + { + return point; + } + + + /** + * get direction + */ + inline const Vector3Template &getDirection() const + { + return direction; + } + + + /** + * checks if a point lies on the ray + */ + template + inline bool contains( const Vector3Template &a ) + { + return (a - point).parallel( direction ); + } + + /** + * calculate the distance between a point and the ray + */ + template + inline T distance( const Vector3Template &a ) const + { + T t = direction.dot( a - point ); + Vector3Template b = at(t) - a; + return b.length(); + } + + + /** + * gets the position at distance t + */ + template + inline Vector3Template at( const T a ) const + { + return Vector3Template( point + direction * a ); + } + + + }; + + typedef RayTemplate RayFloat; + typedef RayTemplate RayDouble; + typedef RayTemplate Ray; +} + +#endif diff --git a/engine/Math/Rectangle.h b/engine/Math/Rectangle.h new file mode 100644 index 0000000..beb66e3 --- /dev/null +++ b/engine/Math/Rectangle.h @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// Author: Gero Mueller +// Copyright: (c) 2006 Gero Mueller +// License: MIT License +//------------------------------------------------------------------------------ + +#ifndef BLUECORE_RECTANGLE_H +#define BLUECORE_RECTANGLE_H + +#include "point.h" + +namespace BlueCore +{ + template + struct Rectangle2DTemplate + { + type _x, _y, _width, _height; + + Rectangle2DTemplate() : + _x(0), + _y(0), + _width(0), + _height(0) + { + } + + Rectangle2DTemplate( int x, int y, int width, int height ) : + _x(x), + _y(y), + _width(width), + _height(height) + { + } + + Rectangle2DTemplate( const Point2DTemplate &topLeft, const Point2DTemplate &bottomRight ) : + _x(topLeft._x), + _y(topLeft._y), + _width(bottomRight._x - topLeft._x), + _height(bottomRight._y - topLeft._y) + { + } + + Rectangle2DTemplate &operator=(const Rectangle2DTemplate &rect) + { + _x = rect._x; + _y = rect._y; + _width = rect._width; + _height = rect._height; + + return *this; + } + + bool overlaps( const Rectangle2DTemplate &rect ) + { + if( (rect._x + rect._width) < _x ) + return false; + + if( rect._x > (_x + _width) ) + return false; + + if( rect._y < (_y - _height) ) + return false; + + if( (rect._y - rect._height) > _y ) + return false; + + return true; + } + + bool contains( const Point2DTemplate &point ) const + { + if( point._x < _x ) + return false; + + if( point._y < _y ) + return false; + + if( point._x > (_x + _width) ) + return false; + + if( point._y > (_y + _height) ) + return false; + + return true; + } + }; + + typedef Rectangle2DTemplate Rectangle2D; +} + +#endif diff --git a/engine/Math/Scalar.h b/engine/Math/Scalar.h new file mode 100644 index 0000000..39a66df --- /dev/null +++ b/engine/Math/Scalar.h @@ -0,0 +1,19 @@ +//------------------------------------------------------------------------------ +// Author: Gero Mueller +// Copyright: (c) 2006 Gero Mueller +// License: MIT License +//------------------------------------------------------------------------------ + +#ifndef BLUECORE_COMMON_H +#define BLUECORE_COMMON_H + +namespace BlueCore +{ + /* Scalar defines the default floating point type */ + typedef double Scalar; + + const Scalar Pi = 3.141592653589793; + +} // namespace bc + +#endif // BLUECORE_COMMON_H diff --git a/engine/Math/Transformation.h b/engine/Math/Transformation.h new file mode 100644 index 0000000..6274fe0 --- /dev/null +++ b/engine/Math/Transformation.h @@ -0,0 +1,51 @@ +#ifndef BLUECORE_TRANSFORMATION_H +#define BLUECORE_TRANSFORMATION_H + +#include "Quaternion.h" +#include "Vector.h" + +namespace BlueCore +{ +template class TransformationTemplate +{ + public: + + QuaternionTemplate rotation; + Vector3Template translation; + + TransformationTemplate() + { + } + + template TransformationTemplate( + const QuaternionTemplate &rot) : + rotation(rot) + { + } + + template TransformationTemplate( + const Vector3Template &trans) : + translation(trans) + { + } + + template TransformationTemplate( + const QuaternionTemplate &rot, + const Vector3Template &trans) : + rotation(rot), translation(trans) + { + } + + template Vector3Template transform(const Vector3Template &v) + { + return rotation.apply(v) + translation; + } +}; + +typedef TransformationTemplate TransformationFloat; +typedef TransformationTemplate TransformationDouble; +typedef TransformationTemplate Transformation; + +} // namespace BlueCore + +#endif /*TRANSFORMATION_H_*/ diff --git a/engine/Math/Vector.h b/engine/Math/Vector.h new file mode 100644 index 0000000..ff1a14f --- /dev/null +++ b/engine/Math/Vector.h @@ -0,0 +1,432 @@ +//------------------------------------------------------------------------------ +// Author: Gero Mueller +// Copyright: (c) 2006 Gero Mueller +// License: MIT License +//------------------------------------------------------------------------------ + +#ifndef BLUECORE_VECTOR_H +#define BLUECORE_VECTOR_H + +// system includes +#include +#include + +// project includes +#include "Scalar.h" + +namespace BlueCore +{ + template + class Vector3Template + { + public: + + T x, y, z; + + /** + * Default Contructor + */ + inline Vector3Template() + { + x = static_cast(0); + y = static_cast(0); + z = static_cast(0); + } + + /** + * Constructor from three values + */ + template + inline Vector3Template( S x, S y, S z ) + { + this->x = static_cast(x); + this->y = static_cast(y); + this->z = static_cast(z); + } + + /** + * copy contructor + */ + template + inline Vector3Template( Vector3Template &a ) + { + x = static_cast(a.x); + y = static_cast(a.y); + z = static_cast(a.z); + } + + + /** + * add operataor + */ + template + inline Vector3Template operator + ( + const Vector3Template &a ) const + { + return Vector3Template( + x + static_cast(a.x), + y + static_cast(a.y), + z + static_cast(a.z) ); + } + + + /** + * zero the vector + */ + inline void zero() + { + x = static_cast(0); + y = static_cast(0); + z = static_cast(0); + } + + /** + * zero the vector + */ + inline bool isZero() const + { + if( x != 0.0 ) + return false; + + if( y != 0.0 ) + return false; + + if( z != 0.0 ) + return false; + + return true; + } + + + /** + * add-assign operator + */ + template + inline Vector3Template &operator += ( + const Vector3Template &a ) + { + x += static_cast(a.x); + y += static_cast(a.y); + z += static_cast(a.z); + + return *this; + } + + + /** + * sub operator + */ + template + inline Vector3Template operator - ( + const Vector3Template &a ) const + { + return Vector3Template( + x - static_cast(a.x), + y - static_cast(a.y), + z - static_cast(a.z) + ); + } + + + /** + * sub-assign operator + */ + template + inline Vector3Template &operator -= ( + const Vector3Template &a ) + { + x -= static_cast(a.x); + y -= static_cast(a.y); + z -= static_cast(a.z); + + return *this; + } + + + /** + * multiply by scalar operator + */ + template + inline Vector3Template operator * ( const S a ) const + { + return Vector3Template( + x * static_cast(a), + y * static_cast(a), + z * static_cast(a) + ); + } + + + /** + * multiply-assign by scalar operator + */ + template + inline Vector3Template &operator *= ( const S a ) + { + x *= static_cast(a); + y *= static_cast(a); + z *= static_cast(a); + + return *this; + } + + + /** + * divide by scalar operator + */ + template + inline Vector3Template operator / ( const S a ) const + { + return Vector3Template( + x / static_cast(a), + y / static_cast(a), + z / static_cast(a) + ); + } + + + /** + * divide-assign by scalar operator + */ + template + inline Vector3Template &operator /= ( const S a ) + { + x /= static_cast(a); + y /= static_cast(a); + z /= static_cast(a); + + return *this; + } + + + /** + * assign operator + */ + template + inline Vector3Template operator = ( + const Vector3Template &a ) + { + x = static_cast(a.x); + y = static_cast(a.y); + z = static_cast(a.z); + + return *this; + } + + inline Vector3Template operator - (void) const + { + return Vector3Template( -x, -y, -z ); + } + + /** + * compare operator + */ + template + inline bool operator == ( const Vector3Template &a ) + { + if( x != static_cast(a.x) ) + return false; + + if( y != static_cast(a.y) ) + return false; + + if( z != static_cast(a.z) ) + return false; + + return true; + } + + + /** + * anti compare operator + */ + template + inline bool operator != ( const Vector3Template &a ) + { + if( x != static_cast(a.x) ) + return true; + + if( y != static_cast(a.y) ) + return true; + + if( z != static_cast(a.z) ) + return true; + + return false; + } + + + /** + * return the length of the vector + */ + inline T length() const + { + return static_cast( sqrt(x*x + y*y + z*z) ); + } + + + /** + * length squared + */ + inline T length2() const + { + return x*x + y*y + z*z; + } + + + /** + * calculate the cross product + */ + template + inline Vector3Template cross( + const Vector3Template &a ) const + { + return Vector3Template( + y * static_cast(a.z) - z * static_cast(a.y), + z * static_cast(a.x) - x * static_cast(a.z), + x * static_cast(a.y) - y * static_cast(a.x) + ); + } + + + /** + * calculate dot product + */ + template + inline T dot( const Vector3Template &a ) const + { + return ( + x * static_cast(a.x) + + y * static_cast(a.y) + + z * static_cast(a.z) ); + } + + + /** + * check if vectors are nearly equal + */ + template + inline bool nearlyEquals( + const Vector3Template &a, + T epsilon = 0.00001 ) const + { + if( fabs(x-static_cast(a.x)) > epsilon ) + return false; + + if( fabs(y-static_cast(a.y)) > epsilon ) + return false; + + if( fabs(z-static_cast(a.z)) > epsilon ) + return false; + + return true; + } + + + /** + * normalize the vector to length 1 + */ + inline void normalize() + { + T a = length(); + if( a == 0 ) + return; + + x /= a; + y /= a; + z /= a; + } + + + /** + * get normalized vector + */ + inline Vector3Template normalized() const + { + T a = length(); + if( a == 0 ) + return Vector3Template( 0.0, 0.0, 1.0 ); + return Vector3Template( x / a, y / a, z / a ); + } + + /** + * check if vectors are parallel + */ + inline bool parallel( const Vector3Template &a ) const + { + return ( (static_cast(a.x) / x) == + (static_cast(a.y) / y) == + (static_cast(a.z) / z) ); + } + + + /** + * check if vectors are parallel + */ + template + inline bool nearlyParallel( + const Vector3Template &a, + T epsilon = 0.00001 ) const + { + T ax = static_cast(a.x) / x; + T ay = static_cast(a.y) / y; + T az = static_cast(a.z) / z; + + if( fabs(ax - ay) > epsilon ) + return false; + + if( fabs(ax - az) > epsilon ) + return false; + + if( fabs(ay - az) > epsilon ) + return false; + + return true; + } + + /** + * check if vectors are orthogonal + */ + inline bool orthogonal( const Vector3Template &a ) const + { + return ( dot(a) == static_cast(0) ); + } + + + /** + * check if vectors are orthogonal + */ + template + inline bool nearlyOrthogonal( + const Vector3Template &a, + T epsilon = 0.00001 ) const + { + return ( dot(a) < epsilon ); + } + + T& operator[] (unsigned int i) + { + if( i == 0 ) + return x; + else if( i == 1 ) + return y; + else if( i == 2 ) + return z; + else + throw "index out of bound"; + } + + friend std::ostream &operator << ( std::ostream& os, Vector3Template v ) + { + os << "( " << v.x << ", " << v.y << ", " << v.z << " )"; + return os; + } + + }; + + typedef Vector3Template Vector3Float; + typedef Vector3Template Vector3Double; + typedef Vector3Template Vector3; +} + +#endif diff --git a/engine/MeshManager.cpp b/engine/MeshManager.cpp new file mode 100644 index 0000000..f2566de --- /dev/null +++ b/engine/MeshManager.cpp @@ -0,0 +1,419 @@ +#include "MeshManager.h" + +#include "Utilities/format.h" +#include "Utilities/Log.h" + +#include "physfs.h" +#include "trimeshloader.h" + +#include +#include + +using namespace std; + +namespace BlueCore +{ + +//------------------------------------------------------------------------------ +Mesh::Mesh(RenderDevice* device) : + _Device(device) +{ +} + +//------------------------------------------------------------------------------ +Mesh::~Mesh() +{ + clog << ">>> Mesh destructed ..."<< endline; +} + +//------------------------------------------------------------------------------ +void Mesh::render() +{ + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + + glVertexPointer( 3, GL_FLOAT, sizeof(Vertex), &VertexBuffer[0].point.x); + + glTexCoordPointer( 2, GL_FLOAT, sizeof(Vertex), &VertexBuffer[0].u); + + glNormalPointer(GL_FLOAT, sizeof(Vertex), &VertexBuffer[0].normal.x); + + glDrawElements(GL_TRIANGLES, IndexBuffer.count() * 3, GL_UNSIGNED_SHORT, + IndexBuffer.data() ); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); +} + +#if 0 +//-------------------------------------------------------------------------- +void Mesh::createVBO() +{ + if ( GLEW_ARB_vertex_buffer_object && !vbo ) + { + glGenBuffersARB ( 1, &vbo ); + + glBindBufferARB ( GL_ARRAY_BUFFER_ARB, vbo ); + + glBufferDataARB ( + GL_ARRAY_BUFFER_ARB, + size(), + data(), + GL_STATIC_DRAW_ARB ); + } +} + +//-------------------------------------------------------------------------- +void Mesh::releaseVBO() +{ + if ( GLEW_ARB_vertex_buffer_object && vbo ) + { + glDeleteBuffersARB ( 1, &vbo ); + vbo = 0; + } +} + +//-------------------------------------------------------------------------- +void Mesh::bind() +{ + if ( GLEW_ARB_vertex_buffer_object && vbo ) + { + glBindBufferARB ( GL_ARRAY_BUFFER_ARB, vbo ); + + // hardcoded offset and stride + glVertexPointer ( 3, GL_FLOAT, sizeof ( Vertex ), 0 ); + + glTexCoordPointer ( 2, GL_FLOAT, sizeof ( Vertex ), ( const GLvoid* ) ( sizeof ( float ) * 3 ) ); + + glNormalPointer ( GL_FLOAT, sizeof ( Vertex ), ( const GLvoid* ) ( sizeof ( float ) * 5 ) ); + } + else + { + // hardcoded offset and stride + glVertexPointer ( 3, GL_FLOAT, sizeof ( Vertex ), data() ); + glTexCoordPointer ( 2, GL_FLOAT, sizeof ( Vertex ), data() + sizeof ( float ) * 3 ); + glNormalPointer ( GL_FLOAT, sizeof ( Vertex ), data() + sizeof ( float ) * 5 ); + } +} +#endif + +//------------------------------------------------------------------------------ +MeshManager::MeshManager(RenderDevice* device) : + _Device(device) +{ + BlueCore::clog << ">>> MeshManager constructed..." << endlog; +} + +//------------------------------------------------------------------------------ +MeshManager::~MeshManager() +{ + clog << ">>> MeshManager destructed ..." << endlog; +} + +//------------------------------------------------------------------------------ +bool loadTrimesh(const char* filename, Mesh *mesh) +{ + PHYSFS_file* file = PHYSFS_openRead(filename); + if (file != 0) + { + char buffer[1024]; + unsigned int size = 0; + + if (tlObjCheckFileExtension(filename) == 0) + { + tlObjState *state = tlObjCreateState(); + while (PHYSFS_eof(file) == 0) + { + size = (unsigned int) PHYSFS_read(file, buffer, 1, + sizeof(buffer)); + tlObjParse(state, buffer, size, size < sizeof(buffer) ? 1 : 0); + } + + mesh->VertexBuffer.create(tlObjVertexCount(state)); + for (unsigned int i = 0; i < mesh->VertexBuffer.count(); i++) + tlObjGetVertex(state, i, &mesh->VertexBuffer[i].point.x, + &mesh->VertexBuffer[i].point.y, + &mesh->VertexBuffer[i].point.z, + &mesh->VertexBuffer[i].u, &mesh->VertexBuffer[i].v, + &mesh->VertexBuffer[i].normal.x, + &mesh->VertexBuffer[i].normal.y, + &mesh->VertexBuffer[i].normal.z); + + mesh->IndexBuffer.create(tlObjFaceCount(state)); + for (unsigned int i = 0; i < mesh->IndexBuffer.count(); i++) + tlObjGetFace(state, i, &mesh->IndexBuffer[i].a, + &mesh->IndexBuffer[i].b, &mesh->IndexBuffer[i].c); + + mesh->SubsetBuffer.create(tlObjObjectCount(state)); + for (unsigned int i = 0; i < mesh->SubsetBuffer.count(); i++) + { + mesh->SubsetBuffer[i].first = tlObjObjectFaceIndex(state, i); + mesh->SubsetBuffer[i].count = tlObjObjectFaceCount(state, i); + } + + tlObjDestroyState(state); + } + else if (tl3dsCheckFileExtension(filename) == 0) + { + tl3dsState *state = tl3dsCreateState(); + while (PHYSFS_eof(file) == 0) + { + size = (unsigned int) PHYSFS_read(file, buffer, 1, + sizeof(buffer)); + tl3dsParse(state, buffer, size, size < sizeof(buffer) ? 1 : 0); + } + + mesh->VertexBuffer.create(tl3dsVertexCount(state)); + for (unsigned int i = 0; i < mesh->VertexBuffer.count(); i++) + tl3dsGetVertex(state, i, &mesh->VertexBuffer[i].point.x, + &mesh->VertexBuffer[i].point.y, + &mesh->VertexBuffer[i].point.z, + &mesh->VertexBuffer[i].u, &mesh->VertexBuffer[i].v, + &mesh->VertexBuffer[i].normal.x, + &mesh->VertexBuffer[i].normal.y, + &mesh->VertexBuffer[i].normal.z); + + mesh->IndexBuffer.create(tl3dsFaceCount(state)); + for (unsigned int i = 0; i < mesh->IndexBuffer.count(); i++) + tl3dsGetFace(state, i, &mesh->IndexBuffer[i].a, + &mesh->IndexBuffer[i].b, &mesh->IndexBuffer[i].c); + + mesh->SubsetBuffer.create(tl3dsObjectCount(state)); + for (unsigned int i = 0; i < mesh->SubsetBuffer.count(); i++) + { + mesh->SubsetBuffer[i].first = tl3dsObjectFaceIndex(state, i); + mesh->SubsetBuffer[i].count = tl3dsObjectFaceCount(state, i); + } + + tl3dsDestroyState(state); + } + + PHYSFS_close(file); + + return 0; + } + + return 1; +} + +//------------------------------------------------------------------------------ +Mesh *MeshManager::loadMesh(const string &filename) +{ + // check if this mesh is already loaded + std::map >::const_iterator result; + result = _Meshes.find(filename); + + if (result != _Meshes.end() && result->second.valid()) + { + return result->second.get(); + } + + Mesh *mesh = new Mesh (_Device.get()); + + // check cache + PHYSFS_sint64 mod_file = PHYSFS_getLastModTime(filename.c_str()); + + std::string cachename = filename + ".msc"; + PHYSFS_sint64 mod_cache = PHYSFS_getLastModTime(cachename.c_str()); + + if ( (mod_cache > mod_file) && loadFromCache(mesh, filename) ) + { + clog << ">>> Mesh '" << filename << "' loaded from cache." << endline; + + _Meshes[filename] = mesh; + + return mesh; + } + + clog << ">>> Mesh '" << filename << "': loading from file..." << endlog; + + if (loadTrimesh(filename.c_str(), mesh) != 0) + { + mesh->removeReference(); + clog << "!!! Mesh not found!" << endline; + return 0; + } + +#if 0 + mesh->buildShadowFaceBuffer(); + mesh->buildTangentBuffer(); + + unsigned int counter; + + // calculate bounding sphere + clog << " calculate bounding sphere"; + + mesh->bounding_sphere.center().zero(); + + for ( counter = 0; counter < mesh->vertex_buffer.count(); counter++ ) + { + mesh->bounding_sphere.center() += mesh->vertex_buffer[counter].point; + } + + mesh->bounding_sphere.center() /= mesh->vertex_buffer.size(); + + mesh->bounding_sphere.radius() = 0.; + + for ( counter = 0; counter < mesh->vertex_buffer.count(); counter++ ) + { + Scalar distance = ( mesh->vertex_buffer[counter].point - mesh->bounding_sphere.center() ).length(); + + if ( distance > mesh->bounding_sphere.radius() ) + mesh->bounding_sphere.radius() = distance; + } + + clog << ": " << mesh->bounding_sphere.radius() << endline; + + // create normlas, tangents, and binomials + clog << " create normlas, tangents, and binomials" << endline; + + for ( counter = 0; counter < mesh->vertex_buffer.count(); counter += 1 ) + { + mesh->vertex_buffer[counter].normal.zero(); + } + + for ( counter = 0; counter < mesh->index_buffer.count(); counter += 3 ) + { + Index a = mesh->index_buffer[counter]; + Index b = mesh->index_buffer[counter+1]; + Index c = mesh->index_buffer[counter+2]; + + Vector3Template ab = mesh->vertex_buffer[ a ].point - mesh->vertex_buffer[ b ].point; + Vector3Template ac = mesh->vertex_buffer[ a ].point - mesh->vertex_buffer[ c ].point; + Vector3Template n = ab.cross ( ac ); + + mesh->vertex_buffer[ a ].normal += n; + mesh->vertex_buffer[ b ].normal += n; + mesh->vertex_buffer[ c ].normal += n; + } + + for ( counter = 0; counter < mesh->vertex_buffer.count(); counter += 1 ) + { + mesh->vertex_buffer[counter].normal.normalize(); + } + + clog << " create vbos" << endline; + + mesh->vertex_buffer.createVBO(); + mesh->index_buffer.createVBO(); + mesh->tangent_buffer.createVBO(); + mesh->bitangent_buffer.createVBO(); +#endif + + _Meshes[filename] = mesh; + + saveToCache(mesh, filename); + + return mesh; +} + +//------------------------------------------------------------------------------ +bool MeshManager::loadFromCache(Mesh *mesh, const std::string &name) +{ + std::string filename = name + ".msc"; + + PHYSFS_file *file = PHYSFS_openRead(filename.c_str()); + + if (file == 0) + return false; + + unsigned int size; + + // read vertex_buffer + PHYSFS_read(file, &size, sizeof (size ), 1); + mesh->VertexBuffer.create(size); + PHYSFS_read(file, mesh->VertexBuffer.data(), mesh->VertexBuffer.size(), 1); + + // read index buffer + PHYSFS_read(file, &size, sizeof (size ), 1); + mesh->IndexBuffer.create(size); + PHYSFS_read(file, mesh->IndexBuffer.data(), mesh->IndexBuffer.size(), 1); +#if 0 + // read tangent buffer + PHYSFS_read ( file, &size, sizeof ( size ), 1 ); + mesh->tangent_buffer.create ( size ); + PHYSFS_read ( file, mesh->tangent_buffer.data(), mesh->tangent_buffer.size(), 1 ); + + // read bitangent buffer + PHYSFS_read ( file, &size, sizeof ( size ), 1 ); + mesh->bitangent_buffer.create ( size ); + PHYSFS_read ( file, mesh->bitangent_buffer.data(), mesh->bitangent_buffer.size(), 1 ); + + // read bounding sphere + PHYSFS_read ( file, &mesh->bounding_sphere, sizeof ( mesh->bounding_sphere ), 1 ); +#endif + + // read subsets + PHYSFS_read(file, &size, sizeof (size ), 1); + mesh->SubsetBuffer.create(size); + PHYSFS_read(file, mesh->SubsetBuffer.data(), mesh->SubsetBuffer.size(), 1); +#if 0 + // read bitangent buffer + PHYSFS_read ( file, &size, sizeof ( size ), 1 ); + mesh->shadowface_buffer.create ( size ); + PHYSFS_read ( file, mesh->shadowface_buffer.data(), mesh->shadowface_buffer.size(), 1 ); +#endif + + PHYSFS_close(file); + + return true; +} + +//------------------------------------------------------------------------------ +bool MeshManager::saveToCache(Mesh *mesh, const std::string &name) + +{ + std::string filename = name + ".msc"; + + PHYSFS_file *file = PHYSFS_openWrite(filename.c_str() ); + + if (file == 0) + { + clog << ">>> cannot open cache file for mesh: " << name << endline; + return false; + } + + // write vertex_buffer + unsigned int size = mesh->VertexBuffer.count(); + PHYSFS_write(file, &size, sizeof (size ), 1); + PHYSFS_write(file, mesh->VertexBuffer.data(), mesh->VertexBuffer.size(), 1); + + // write index_buffer + size = mesh->IndexBuffer.count(); + PHYSFS_write(file, &size, sizeof (size ), 1); + PHYSFS_write(file, mesh->IndexBuffer.data(), mesh->IndexBuffer.size(), 1); + + // write subsets + size = mesh->SubsetBuffer.count(); + PHYSFS_write(file, &size, sizeof (size ), 1); + PHYSFS_write(file, mesh->SubsetBuffer.data(), mesh->SubsetBuffer.size(), 1); + +#if 0 + // write tangent_buffer + size = mesh->tangent_buffer.count(); + PHYSFS_write ( file, &size, sizeof ( size ), 1 ); + PHYSFS_write ( file, mesh->tangent_buffer.data(), mesh->tangent_buffer.size(), 1 ); + + // write bitangent_buffer + size = mesh->bitangent_buffer.count(); + PHYSFS_write ( file, &size, sizeof ( size ), 1 ); + PHYSFS_write ( file, mesh->bitangent_buffer.data(), mesh->bitangent_buffer.size(), 1 ); + + // write bounding sphere + PHYSFS_write ( file, &mesh->bounding_sphere, sizeof ( mesh->bounding_sphere ), 1 ); + + // write shadowfaces + size = mesh->shadowface_buffer.count(); + PHYSFS_write ( file, &size, sizeof ( size ), 1 ); + PHYSFS_write ( file, mesh->shadowface_buffer.data(), mesh->shadowface_buffer.size(), 1 ); +#endif + + PHYSFS_close(file); + + return true; +} + +} // namespace BlueCore diff --git a/engine/MeshManager.h b/engine/MeshManager.h new file mode 100644 index 0000000..b77491e --- /dev/null +++ b/engine/MeshManager.h @@ -0,0 +1,94 @@ +#ifndef BLUECORE_MESH_MANAGER_H +#define BLUECORE_MESH_MANAGER_H + +#include +#include + +#include "RenderDevice.h" +#include "Math/Vector.h" +#include "Math/Plane.h" +#include "Utilities/Buffer.h" +#include "Utilities/Referenced.h" + +namespace BlueCore { + +typedef struct +{ + Vector3Float point; + float u, v; + Vector3Float normal; +} Vertex; + +typedef struct +{ + unsigned short a, b, c; +} TriangleIndices; + +typedef struct +{ +public: + unsigned int first; + unsigned int count; +} Subset; + +typedef struct +{ + unsigned short neighbours[3]; + PlaneFloat plane; + bool backFace; +} ShadowFace; + +class Mesh : public Referenced +{ + + ref_ptr _Device; + +public: + + Buffer VertexBuffer; + Buffer IndexBuffer; + Buffer SubsetBuffer; + + Buffer tangents; + Buffer bitangents; + Buffer shadowfaces; + +public: + + Mesh (RenderDevice* device); + + void render (); + void upload (); + +protected: + + ~Mesh(); + +}; + + + +class MeshManager : public Referenced +{ +private: + + std::map > _Meshes; + + bool saveToCache ( Mesh *mesh, const std::string &name ); + + bool loadFromCache ( Mesh *mesh, const std::string &name ); + + ref_ptr _Device; + +public: + + MeshManager (RenderDevice *device); + + ~MeshManager(); + + Mesh *loadMesh (const std::string &name); +}; + +} // namespace BlueCore + +#endif diff --git a/engine/ModelManager.cpp b/engine/ModelManager.cpp new file mode 100644 index 0000000..80eb2c7 --- /dev/null +++ b/engine/ModelManager.cpp @@ -0,0 +1,322 @@ +#include "ModelManager.h" + +#include "MeshManager.h" +#include "ShaderManager.h" + +#include "Utilities/StringUtilities.h" +#include "Utilities/Log.h" + +#include "tinyxml.h" +#include "physfs.h" + +#include + +using namespace std; + +namespace BlueCore +{ + +//------------------------------------------------------------------------------ +void Model::render() +{ + ModelMesh->render(); + //glEnable(GL_LIGHTING); + //glDepthFunc ( GL_LEQUAL ); + //glEnable ( GL_DEPTH_TEST ); + //glDepthMask ( GL_TRUE ); + + /* + glEnableClientState (GL_VERTEX_ARRAY ); + glEnableClientState (GL_TEXTURE_COORD_ARRAY ); + glEnableClientState (GL_NORMAL_ARRAY ); + + glMatrixMode (GL_MODELVIEW ); + glPushMatrix(); + Matrix4x4 m(_AbsoluteRotation, _AbsoluteTranslation); + glMultMatrixd ( ( GLdouble * ) &m.m ); + + mesh->vertex_buffer.bind(); + mesh->index_buffer.bind(); + */ + /* + glMaterialfv (GL_FRONT, GL_SHININESS, &pass.Shininess); + glMaterialfv (GL_FRONT, GL_SPECULAR, ( GLfloat * ) &pass.Specular ); + + // setup shader + if (pass.Program && ShaderManager::getSingleton()->usingShaders() ) + { + ShaderManager::getSingleton()->useShaderProgram(pass.Program); + + int textureId = 0; + std::vector::iterator titer; + + for (titer = pass.Textures.begin(); titer != pass.Textures.end(); titer++) + { + Renderer::getSingleton()->bindTexture(( *titer ).mTexture, textureId ); + ShaderManager::getSingleton()->useTexture(pass.Program, textureId, ( *titer ).mName); + textureId++; + } + + if (pass.Tangents) + { + ShaderManager::getSingleton()->useTangentBuffer(pass.Program, &mesh->tangent_buffer, "tangent"); + ShaderManager::getSingleton()->useTangentBuffer(pass.Program, &mesh->bitangent_buffer, + "bitangent"); + } + } + else + { + glActiveTextureARB (GL_TEXTURE2_ARB ); + glDisable (GL_TEXTURE_2D ); + glActiveTextureARB (GL_TEXTURE1_ARB ); + glDisable (GL_TEXTURE_2D ); + int textureId = 0; + + std::vector::iterator titer; + for (titer = pass.Textures.begin(); titer != pass.Textures.end(); titer++) + { + Renderer::getSingleton()->bindTexture(( *titer ).mTexture, textureId ); + ShaderManager::getSingleton()->useTexture(pass.Program, textureId, ( *titer ).mName); + textureId++; + break; + } + } + + // render subsets + SubsetVector::iterator iter; + for (iter = mesh->surface_subsets.begin(); iter + != mesh->surface_subsets.end(); iter++) + { + Subset subset = *iter; + mesh->index_buffer.draw(subset.first, subset.count); + } + + // cleanup + if (pass.Program && ShaderManager::getSingleton()->usingShaders() ) + { + ShaderManager::getSingleton()->useShaderProgram( 0); + if (pass.Tangents) + { + ShaderManager::getSingleton()->disableTangentBuffer(pass.Program, "tangent"); + ShaderManager::getSingleton()->disableTangentBuffer(pass.Program, "bitangent"); + } + } + + glPopMatrix(); + + glActiveTextureARB (GL_TEXTURE2_ARB ); + glDisable (GL_TEXTURE_2D ); + glActiveTextureARB (GL_TEXTURE1_ARB ); + glDisable (GL_TEXTURE_2D ); + glActiveTextureARB (GL_TEXTURE0_ARB ); + glDisable (GL_TEXTURE_2D ); + */ + /* + glDisableClientState (GL_VERTEX_ARRAY ); + glDisableClientState (GL_TEXTURE_COORD_ARRAY ); + glDisableClientState (GL_NORMAL_ARRAY ); + */ + +} +//------------------------------------------------------------------------------ +ModelManager::ModelManager(TextureManager *texturemanager, + ShaderManager* shadermanager, MeshManager* meshmanager) : + _TextureManager(texturemanager), _ShaderManager(shadermanager), + _MeshManager(meshmanager) +{ +} + +//------------------------------------------------------------------------------ +ModelManager::~ModelManager() +{ +} + +//------------------------------------------------------------------------------ +void ModelManager::parseRenderPassDefinition( + Model::RenderPassDefinition &Definition, + const TiXmlElement* DefinitionElement) +{ + + const TiXmlElement * TextureElement = + DefinitionElement->FirstChildElement("Texture"); + + while (TextureElement) + { + Model::TextureUnit unit; + unit.mName = TextureElement->Attribute("name"); + + const char *file = TextureElement->Attribute("file"); + + if (file) + unit.mTexture = _TextureManager->loadTexture(file); + else + unit.mTexture = 0; + + Definition.Textures.push_back(unit); + + TextureElement = TextureElement->NextSiblingElement("Texture"); + } + + const TiXmlElement * ShaderElement = + DefinitionElement->FirstChildElement("Shader"); + + if (ShaderElement) + { + Definition.Program + = _ShaderManager->loadShaderProgram(ShaderElement->Attribute("name") ); + + const char *t = ShaderElement->Attribute("tangents"); + if (t && string("true") == t) + Definition.Tangents = true; + else + Definition.Tangents = false; + } + else + Definition.Program = 0; + + const TiXmlElement * MaterialElement = + DefinitionElement->FirstChildElement("Material"); + + if (MaterialElement) + { + double s; + MaterialElement->QueryDoubleAttribute("shininess", &s); + Definition.Shininess = s; + + std::vector components; + explode(MaterialElement->Attribute("specular"), components); + + for (unsigned int i = 0; i < components.size(); i++) + { + Definition.Specular[i] = atof(components[i].c_str() ); + + if (i == 3) + break; + } + + } + + Definition.Enabled = true; +} + +//------------------------------------------------------------------------------ +Model *ModelManager::loadModel(const string &name) +{ + // check if this model is already loaded + ModelContainer::const_iterator result; + result = _Models.find(name); + + if (result != _Models.end() ) + { + return result->second.get(); + } + + string filename = name + ".model.xml"; + // load the document + PHYSFS_file *file = PHYSFS_openRead(filename.c_str()); + + if ( !file) + { + clog << "!!! XML-File '"<< name << "' not found"<< endline; + return 0; + } + + unsigned int fileSize = PHYSFS_fileLength(file); + + Buffer buffer(fileSize); + + PHYSFS_read(file, buffer.data(), 1, buffer.size() ); + buffer[buffer.count() - 1] = 0; + + PHYSFS_close(file); + + TiXmlDocument *document = new TiXmlDocument(); + document->Parse(buffer.data() ); + + if (document->Error() ) + { + clog << "!!! Error loading XML-File'"<< name << "': "; + clog << document->ErrorRow() << ","<< document->ErrorCol(); + clog << " "<< document->ErrorDesc() << endline; + return 0; + } + + Model *model = new Model(); + + if (document == 0) + { + clog << "!!! Model '"<< name << "' not found!"<< endline; + return 0; + } + + const TiXmlElement *model_element = document->FirstChildElement("Model"); + + if (model_element) + { + const TiXmlElement* MeshElement = + model_element->FirstChildElement("Mesh"); + + if (MeshElement) + { + model->ModelMesh + = _MeshManager->loadMesh(MeshElement->Attribute("file") ); + } + + const TiXmlElement * ShadowMeshElement = + model_element->FirstChildElement("ShadowMesh"); + + if (ShadowMeshElement) + { + model->ShadowMesh + = _MeshManager->loadMesh(ShadowMeshElement->Attribute("file") ); + } + else + { + model->ShadowMesh = 0; + } + + const TiXmlElement* definition_element; + + definition_element + = model_element->FirstChildElement("AmbientRenderPass"); + if (definition_element) + parseRenderPassDefinition(model->AmbientPass, definition_element); + else + model->AmbientPass.Enabled = false; + + definition_element = model_element->FirstChildElement("LitRenderPass"); + if (definition_element) + parseRenderPassDefinition(model->LitPass, definition_element); + else + model->LitPass.Enabled = false; + + definition_element + = model_element->FirstChildElement("DefaultRenderPass"); + if (definition_element) + parseRenderPassDefinition(model->DefaultPass, definition_element); + else + model->DefaultPass.Enabled = false; + + } + + _Models[name] = model; + + return model; +} +#if 0 +//------------------------------------------------------------------------------ +void ModelManager::unload() +{ + ModelContainer::iterator iter; + + for (iter = _Models.begin(); iter != _Models.end(); iter++) + { + clog << ">>> ModelManager: unload " << iter->first << endlog; + + delete iter->second; + } + + _Models.clear(); +} +#endif +} // namespace BlueCore diff --git a/engine/ModelManager.h b/engine/ModelManager.h new file mode 100644 index 0000000..a1f0c3f --- /dev/null +++ b/engine/ModelManager.h @@ -0,0 +1,80 @@ +#ifndef BLUECORE_MODEL_MANAGER_H +#define BLUECORE_MODEL_MANAGER_H + +// system includes +#include +#include + +// project includes +#include "Utilities/Referenced.h" +#include "Utilities/Named.h" + +#include "TextureManager.h" +#include "ShaderManager.h" +#include "MeshManager.h" + +// forward declaration +class TiXmlElement; + +namespace BlueCore +{ + +class Model : public Referenced, public Named +{ + +public: + + struct TextureUnit + { + std::string mName; + ref_ptr mTexture; + }; + + struct RenderPassDefinition + { + bool Enabled; + std::vector Textures; + ShaderProgram Program; + bool Tangents; + float Shininess; + float Specular[4]; + }; + + ref_ptr ModelMesh; + ref_ptr ShadowMesh; + RenderPassDefinition AmbientPass; + RenderPassDefinition LitPass; + RenderPassDefinition DefaultPass; + + void render(); +}; + +class ModelManager : public Referenced +{ + +private: + + typedef std::map< std::string, weak_ptr > ModelContainer; + + ModelContainer _Models; + weak_ptr _TextureManager; + weak_ptr _ShaderManager; + weak_ptr _MeshManager; + + void parseRenderPassDefinition(Model::RenderPassDefinition &Definition, + const TiXmlElement* DefinitionElement); + + void unload(); + +public: + + ModelManager(TextureManager *texturemanager, ShaderManager* shadermanager, + MeshManager* meshmanager); + ~ModelManager(); + + Model *loadModel(const std::string &name); +}; + +} // namespace BlueCore + +#endif diff --git a/engine/Node.h b/engine/Node.h new file mode 100644 index 0000000..52512f4 --- /dev/null +++ b/engine/Node.h @@ -0,0 +1,26 @@ +#ifndef NODE_H_ +#define NODE_H_ + +class Node +{ + class UpdateListener + { + virtual void update() = 0; + virtual ~UpdateListener() = 0; + }; + typedef std::list NodeContainer; + typedef std::list NodeContainer; + + Node *parent; + NodeContainer children; + + Vector3 getRelativePosition; + Vector3 getAbsolutePosition; + /* ... */ + + bool _Updating; + void update (double time); + void render (Camera *camera, RenderDevice *device); +}; + +#endif /*NODE_H_*/ diff --git a/engine/RenderDevice.cpp b/engine/RenderDevice.cpp new file mode 100644 index 0000000..7a88699 --- /dev/null +++ b/engine/RenderDevice.cpp @@ -0,0 +1,329 @@ +#include "RenderDevice.h" + +#include "Utilities/Log.h" + +#include "GL/gl.h" +#include "GL/glu.h" + +namespace BlueCore +{ + +//------------------------------------------------------------------------------ +void RenderDevice::WindowResizeSlot(int width, int height) +{ + glViewport( 0, 0, width, height); + _ViewportWidth = width; + _ViewportHeight = height; +} + +//------------------------------------------------------------------------------ +RenderDevice::RenderDevice(RenderWindow* renderWindow) : + _RenderWindow(renderWindow) +{ + if (_RenderWindow.valid()) + { + _RenderWindow->WindowResizeSignal.connect(this, + &RenderDevice::WindowResizeSlot); + + _RenderWindow->WindowCloseSignal.connect(this, + &RenderDevice::WindowCloseSlot); + + _ViewportWidth = _RenderWindow->getWidth(); + _ViewportHeight = _RenderWindow->getHeight(); + glViewport( 0, 0, _ViewportWidth, _ViewportHeight); + } + + clog << ">>> RenderDevice constructed..."<< endlog; +} + +//------------------------------------------------------------------------------ +RenderDevice::~RenderDevice() +{ + clog << ">>> RenderDevice destructed..."<< endlog; +} + +//------------------------------------------------------------------------------ +void RenderDevice::WindowCloseSlot() +{ + DeviceShutdownSignal(); +} + +//------------------------------------------------------------------------------ +int RenderDevice::getViewportWidth() +{ + return _ViewportWidth; +} + +//------------------------------------------------------------------------------ +int RenderDevice::getViewportHeight() +{ + return _ViewportHeight; +} + +//------------------------------------------------------------------------------ +void RenderDevice::begin2D() +{ + // prepare state + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glNormal3f( 0.0, 0.0, 1.0); + + // set projection matrix + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + //int newheight = (_width / 16.0) * 9.0; + gluOrtho2D( 0, _ViewportWidth-1, 0, _ViewportHeight-1); + + // prepare model matrix + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); +} + +//------------------------------------------------------------------------------ +void RenderDevice::end2D() +{ + // restore projection matrix + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + // restore model matrix + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + // restore old state + glEnable(GL_DEPTH_TEST); + glEnable(GL_LIGHTING); +} + +//------------------------------------------------------------------------------ +void RenderDevice::clear() +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); +} + +//------------------------------------------------------------------------------ +void RenderDevice::setAmbientLight(float r, float g, float b) +{ + GLfloat lightAmbient[] = + { r, g, b, 1.0f }; + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lightAmbient); +} + +//------------------------------------------------------------------------------ +/* + void RenderDevice::renderShadowVolume(Mesh *mesh, + const Vector3Float &direction, const Vector3Float &extrude) + { + // Calculate visibility + unsigned int i, frontface_count = 0; + for (i=0; i < mesh->shadowfaces.count(); i++) + { + if (mesh->shadowfaces[i].plane.distance(extrude) < 0) + { + shadow_faces[i].backFace = false; + frontface_count++; + } + else + { + shadow_faces[i].backFace = true; + } + } + + unsigned int max_edges = (frontface_count) * 3; + if (_extrudeVertexBuffer.count() < (max_edges * 4)) + { + clog << ">>> increase shadow buffers to "<< max_edges << " Edges"<< endline; + _extrudeVertexBuffer.create( (max_edges + 100) * 4); + } + + if (_extrudeIndexBuffer.count() < (max_edges * 6)) + _extrudeIndexBuffer.create( (max_edges + 100) * 6); + + // fill the buffer + unsigned int j, k; + extrude_quad_count = 0; + Vertex *face_vertices[3]; + for (i=0; ivertex_buffer[ mesh->index_buffer[ src_index_offset ] ]; + face_vertices[1] + = &mesh->vertex_buffer[ mesh->index_buffer[ src_index_offset + 1] ]; + face_vertices[2] + = &mesh->vertex_buffer[ mesh->index_buffer[ src_index_offset + 2] ]; + + #ifdef DEBUG_SILHOUETTE + glBegin( GL_LINES ); + glVertex3f( face_vertices[j]->point.x, face_vertices[j]->point.y, face_vertices[j]->point.z ); + glVertex3f( face_vertices[(j+1)%3]->point.x, face_vertices[(j+1)%3]->point.y, face_vertices[(j+1)%3]->point.z ); + glEnd(); + #endif + + unsigned int vertex_offset = extrude_quad_count*4; + _extrudeVertexBuffer[vertex_offset] = face_vertices[j]->point; + _extrudeVertexBuffer[vertex_offset+1] = face_vertices[(j+1)%3]->point; + _extrudeVertexBuffer[vertex_offset+2] = face_vertices[(j+1)%3]->point + extrude_vector; + _extrudeVertexBuffer[vertex_offset+3] = face_vertices[j]->point + + extrude_vector; + + unsigned int index_offset = extrude_quad_count*6; + _extrudeIndexBuffer[index_offset] = vertex_offset; + _extrudeIndexBuffer[index_offset+1] = vertex_offset + 3; + _extrudeIndexBuffer[index_offset+2] = vertex_offset + 1; + _extrudeIndexBuffer[index_offset+3] = vertex_offset + 1; + _extrudeIndexBuffer[index_offset+4] = vertex_offset + 3; + _extrudeIndexBuffer[index_offset+5] = vertex_offset + 2; + + extrude_quad_count++; + } + } + } + + } + + if (_capIndexBuffer.count() < mesh->index_buffer.count() ) + _capIndexBuffer.create(mesh->index_buffer.count() ); + + bf = 0; + ff = 0; + + for (i=0; iindex_buffer[src_offset]; + _capIndexBuffer[dst_offset+1] = mesh->index_buffer[src_offset+1]; + _capIndexBuffer[dst_offset+2] = mesh->index_buffer[src_offset+2]; + } + + _lastOrientation = _AbsoluteRotation; + } + + if (_extrudeVertexBuffer.count() > 0) + { + // draw the volume + glPushAttrib( GL_ALL_ATTRIB_BITS); + + glDisable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); + + glEnable( GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + + #if defined(DEBUG_SILHOUETTE) || defined(DEBUG_CAPS) || defined(DEBUG_VOLUME) + glColorMask( 1, 1, 1, 1 ); + glDepthFunc(GL_LEQUAL); + #else + glColorMask(0, 0, 0, 0); + glDepthFunc(GL_LESS); + #endif + + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 0, ~0); + glStencilMask( ~0); + + glEnable(GL_CULL_FACE); + + glMatrixMode ( GL_MODELVIEW); + glPushMatrix(); + Matrix4x4 m(_AbsoluteRotation, _AbsoluteTranslation); + glMultMatrixd ( ( GLdouble * ) &m.m ); + + glActiveTextureARB ( GL_TEXTURE2_ARB); + glDisable ( GL_TEXTURE_2D); + glActiveTextureARB ( GL_TEXTURE1_ARB); + glDisable ( GL_TEXTURE_2D); + glActiveTextureARB ( GL_TEXTURE0_ARB); + glDisable ( GL_TEXTURE_2D); + + ShaderManager::getSingleton()->useShaderProgram( 0); + + #ifndef DEBUG_SILHOUETTE + + glEnableClientState( GL_VERTEX_ARRAY); + glDisableClientState( GL_TEXTURE_COORD_ARRAY); + glDisableClientState( GL_NORMAL_ARRAY); + glDisableClientState( GL_ARRAY_BUFFER_ARB); + glDisableClientState( GL_ELEMENT_ARRAY_BUFFER_ARB); + + for (int p=0; p<2; p++) + { + if (p==0) + { + glStencilOp(GL_KEEP, GL_INCR, GL_KEEP); + glCullFace(GL_FRONT); + //glColorMask(0, 1, 0, 0); + } + else + { + glStencilOp(GL_KEEP, GL_DECR, GL_KEEP); + glCullFace(GL_BACK); + //glColorMask(1, 0, 0, 0); + } + + if (GLEW_ARB_vertex_buffer_object ) + { + glBindBufferARB ( GL_ARRAY_BUFFER_ARB, 0); + glBindBufferARB ( GL_ELEMENT_ARRAY_BUFFER_ARB, 0); + } + // #if !defined(DEBUG_CAPS) || defined(DEBUG_VOLUME) + glVertexPointer ( 3, GL_FLOAT, 0, _extrudeVertexBuffer.data() ); + glDrawElements ( + GL_TRIANGLES, + extrude_quad_count * 6, + GL_UNSIGNED_SHORT, + _extrudeIndexBuffer.data()); + // #endif + + // #if !defined(DEBUG_VOLUME) || defined(DEBUG_CAPS) + // draw caps + mesh->vertex_buffer.bind(); + //glVertexPointer ( 3, GL_FLOAT, sizeof(Vertex), mesh->vertex_buffer.data() ); + glDrawElements ( + GL_TRIANGLES, + ff*3, + GL_UNSIGNED_SHORT, + _capIndexBuffer.data()); + //&_capIndexBuffer[_capIndexBuffer.count() - bf*3]); + + glPushMatrix(); + glTranslatef(extrude_vector.x, extrude_vector.y, extrude_vector.z); + glDrawElements ( + GL_TRIANGLES, + bf*3, + GL_UNSIGNED_SHORT, + &_capIndexBuffer[_capIndexBuffer.count() - bf*3]); + //_capIndexBuffer.data() ); + glPopMatrix(); + // #endif + } + + #endif + + glPopAttrib(); + + glPopMatrix(); + } + */ +} // namespace BlueCore diff --git a/engine/RenderDevice.h b/engine/RenderDevice.h new file mode 100644 index 0000000..2b7bc2d --- /dev/null +++ b/engine/RenderDevice.h @@ -0,0 +1,58 @@ +#ifndef BLUECORE_RENDERDEVICE_H +#define BLUECORE_RENDERDEVICE_H + +#include "RenderWindow.h" + +#include "Utilities/Buffer.h" +#include "Utilities/Referenced.h" +#include "Utilities/sigslot.h" + +namespace BlueCore +{ + +class RenderDevice : public Referenced, public sigslot::has_slots<> +{ + public: + + class RenderItem + { + + public: + //virtual void render ( RenderPass pass ) = 0; + virtual ~RenderItem() + {}; + }; + + private: + int _ViewportWidth, _ViewportHeight; + + void WindowResizeSlot(int width, int height); + void WindowCloseSlot(); + + ref_ptr _RenderWindow; + + public: + + RenderDevice(RenderWindow* renderWindow); + ~RenderDevice(); + + int getViewportWidth(); + int getViewportHeight(); + + void begin2D(); + void end2D(); + + void clear(); + + void setAmbientLight(float r, float g, float b); + + void setTexture(unsigned int unit, unsigned int texture); + + sigslot::signal0<> DeviceShutdownSignal; + + //Buffer &_ShadowFaces; +}; + +} // namespaced BlueCore + +#endif // BLUECORE_RENDERDEVICE_H diff --git a/engine/RenderWindow.cpp b/engine/RenderWindow.cpp new file mode 100644 index 0000000..71fa94c --- /dev/null +++ b/engine/RenderWindow.cpp @@ -0,0 +1,176 @@ +#include "RenderWindow.h" + +#include "Utilities/Log.h" + +using namespace std; + +namespace BlueCore +{ + +static RenderWindow* gRenderWindow = 0; + +//------------------------------------------------------------------------------ +RenderWindow::RenderWindow() : + _Initialized(false) +{ + glfwInit(); + gRenderWindow = this; + clog << ">>> RenderWindow constructed..."<< endlog; +} + +//------------------------------------------------------------------------------ +RenderWindow::~RenderWindow() +{ + destroy(); + glfwTerminate(); + gRenderWindow = 0; + clog << ">>> RenderWindow destructed..."<< endlog; +} + +//------------------------------------------------------------------------------ +void RenderWindow::WindowRefreshCallback() +{ + gRenderWindow->WindowRefreshSignal(); +} + +//------------------------------------------------------------------------------ +int RenderWindow::WindowCloseCallback() +{ + clog << ">>> RenderWindow closed..."<< endlog; + gRenderWindow->closeWindow(); + return GL_FALSE; +} + +//------------------------------------------------------------------------------ +void RenderWindow::closeWindow() +{ + _Closed = true; +} + +//------------------------------------------------------------------------------ +void RenderWindow::WindowSizeCallback(int width, int height) +{ + gRenderWindow->resizeWindow(width, height); +} + +//------------------------------------------------------------------------------ +void RenderWindow::resizeWindow(int width, int height) +{ + _Resized = true; + + if ( !_Initialized) + return; + + clog << ">>> RenderWindow resized: "<< width << "x"<< height << endlog; + + _Width = width; + _Height = height; + + WindowResizeSignal(width, height); +} + +//------------------------------------------------------------------------------ +int RenderWindow::getWidth() +{ + return _Width; +} + +//------------------------------------------------------------------------------ +int RenderWindow::getHeight() +{ + return _Height; +} + +//------------------------------------------------------------------------------ +bool RenderWindow::create(int width, int height, int pixelbits, int depthbits, + int stencilbits, bool fullscreen) +{ + int channelbits = 8; + if (pixelbits == 16) + channelbits = 4; + + if (glfwOpenWindow(width, height, channelbits, channelbits, channelbits, + channelbits, depthbits, stencilbits, fullscreen ? GLFW_FULLSCREEN + : GLFW_WINDOW) == GL_FALSE) + return false; + + glfwSetWindowSizeCallback(WindowSizeCallback); + glfwSetWindowCloseCallback(WindowCloseCallback); + glfwSetWindowRefreshCallback(WindowRefreshCallback); + + glfwSetMousePosCallback(MousePosCallback); + glfwSetMouseButtonCallback(MouseButtonCallback); + glfwSetMouseWheelCallback(MouseWheelCallback); + glfwSetKeyCallback(KeyCallback); + glfwSetCharCallback(CharCallback); + + clog << ">>> RenderWindow created"<< endlog; + + _Initialized = true; + + glfwGetWindowSize( &_Width, &_Height); + resizeWindow(_Width, _Height); + + return true; +} + +//------------------------------------------------------------------------------ +void RenderWindow::swap() +{ + glfwSwapBuffers(); +} + +//------------------------------------------------------------------------------ +void RenderWindow::destroy() +{ + if (_Initialized) + { + glfwCloseWindow(); + clog << ">>> RenderWindow destroyed"<< endlog; + _Initialized = false; + } +} + +//------------------------------------------------------------------------------ +bool RenderWindow::isOpen() +{ + if (_Initialized == false) + return false; + + if (_Closed) + return false; + + return (glfwGetWindowParam(GLFW_OPENED) == GL_TRUE); +} + +//------------------------------------------------------------------------------ +void RenderWindow::MousePosCallback(int x, int y) +{ + gRenderWindow->MouseMoveSignal(x, y); +} + +//------------------------------------------------------------------------------ +void RenderWindow::MouseButtonCallback(int button, int action) +{ + gRenderWindow->MouseButtonSignal(button, action); +} + +//------------------------------------------------------------------------------ +void RenderWindow::MouseWheelCallback(int pos) +{ + gRenderWindow->MouseWheelSignal(pos); +} + +//------------------------------------------------------------------------------ +void RenderWindow::KeyCallback(int key, int action) +{ + gRenderWindow->KeySignal(key, action); +} + +//------------------------------------------------------------------------------ +void RenderWindow::CharCallback(int character, int action) +{ + gRenderWindow->CharSignal(character); +} + +} // namespace BlueCore diff --git a/engine/RenderWindow.h b/engine/RenderWindow.h new file mode 100644 index 0000000..a4bc808 --- /dev/null +++ b/engine/RenderWindow.h @@ -0,0 +1,71 @@ +#ifndef BLUECORE_RENDER_WINDOW_H +#define BLUECORE_RENDER_WINDOW_H + +// system includes +#include + +// library includes +#include "GL/glfw.h" + +// project includes +#include "Math/Vector.h" +#include "Utilities/sigslot.h" +#include "Utilities/Referenced.h" + +namespace BlueCore +{ + +class RenderWindow : public Referenced +{ + + private: + + static void WindowSizeCallback(int width, int height); + static int WindowCloseCallback(); + static void WindowRefreshCallback(); + + static void MousePosCallback(int x, int y); + static void MouseButtonCallback(int button, int action); + static void MouseWheelCallback(int pos); + static void KeyCallback(int key, int action); + static void CharCallback(int character, int action); + + bool _Resized; + bool _Initialized; + bool _Closed; + int _Width, _Height; + + void resizeWindow(int width, int height); + + public: + + sigslot::signal0<> WindowCloseSignal; + sigslot::signal2 WindowResizeSignal; + sigslot::signal0<> WindowRefreshSignal; + + sigslot::signal2 MouseMoveSignal; + sigslot::signal2 MouseButtonSignal; + sigslot::signal1 MouseWheelSignal; + sigslot::signal2 KeySignal; + sigslot::signal1 CharSignal; + + public: + + RenderWindow(); + ~RenderWindow(); + + bool create(int width, int height, int pixelbits, int depthbits, + int stencilbits, bool fullscreen); + + void destroy(); + void closeWindow(); + int getWidth(); + int getHeight(); + + bool isOpen(); + + void swap(); +}; +} + +#endif diff --git a/engine/RigidBodySimulation.cpp b/engine/RigidBodySimulation.cpp new file mode 100644 index 0000000..cc71105 --- /dev/null +++ b/engine/RigidBodySimulation.cpp @@ -0,0 +1,618 @@ +#include "RigidBodySimulation.h" + +// local includes +//#include "scriptsystem.h" + +// library includes +#include "Utilities/StringUtilities.h" +#include "Utilities/Log.h" +//#include "Utilities/xmlconfig.h" + +// system includes +#include + +using namespace std; + +namespace BlueCore +{ +static RigidBodySimulation* gRigidBodySimulation; + +//------------------------------------------------------------------------------ +RigidBody::RigidBody(RigidBodySimulation* simulation, dWorldID world, + dSpaceID space) : + _Body( 0), _Space( 0), _Simulation(simulation) +{ + _Body = dBodyCreate(world); + _Space = dHashSpaceCreate(space); + dHashSpaceSetLevels(_Space, -10, 10); + dBodySetData(_Body, this); + clog << ">>> RigidBody constructed..."<< endlog; +} + +//------------------------------------------------------------------------------ +RigidBody::~RigidBody() +{ + dSpaceDestroy(_Space); + dBodyDestroy(_Body); + + std::vector::iterator iter; + for (iter = _CollisionMeshes.begin(); iter != _CollisionMeshes.end(); iter++) + { + dGeomTriMeshDataDestroy((*iter).trimeshdata); + } + + clog << ">>> RigidBody destructed..."<< endlog; +} + +//------------------------------------------------------------------------------ + +const Vector3 &RigidBody::getPosition() const +{ + return _Position; +} + +//------------------------------------------------------------------------------ +void RigidBody::setPosition(const Vector3 &v) +{ + dBodySetPosition(_Body, v.x, v.y, v.z); + _Position = v; +} + +//------------------------------------------------------------------------------ +const Vector3 &RigidBody::getLinearVelocity() const +{ + return _LinearVelocity; +} + +//------------------------------------------------------------------------------ +void RigidBody::setLinearVelocity(const Vector3 &v) +{ + dBodySetLinearVel(_Body, v.x, v.y, v.z); + _LinearVelocity = v; +} + +//------------------------------------------------------------------------------ +const Quaternion &RigidBody::getOrientation() const +{ + return _Orientation; +} + +//------------------------------------------------------------------------------ +void RigidBody::setOrientation(const Quaternion &q) +{ + + dBodySetQuaternion(_Body, ( const dQuaternion & ) q ); + + _Orientation = q; +} + +//------------------------------------------------------------------------------ +const Vector3 &RigidBody::getAngularVelocity() const +{ + return _AngularVelocity; +} + +//------------------------------------------------------------------------------ +void RigidBody::applyGlobalForce(const Vector3 &force, const Vector3 &point) +{ + dBodyAddForceAtPos(_Body, force.x, force.y, force.z, point.x, point.y, + point.z); +} + +//------------------------------------------------------------------------------ +void RigidBody::applyGlobalForce(const Vector3 &force) +{ + dBodyAddForce(_Body, force.x, force.y, force.z); +} + +//------------------------------------------------------------------------------ +void RigidBody::applyLocalForce(const Vector3 &force, const Vector3 &point) +{ + if (point.isZero() ) + { + dBodyAddRelForce(_Body, force.x, force.y, force.z); + } + else + { + dBodyAddRelForceAtRelPos(_Body, force.x, force.y, force.z, point.x, + point.y, point.z); + } +} + +//------------------------------------------------------------------------------ +void RigidBody::applyLocalForce(const Vector3 &force) +{ + dBodyAddRelForce(_Body, force.x, force.y, force.z); +} + +//------------------------------------------------------------------------------ +Scalar RigidBody::getMass() const +{ + dMass m; + dBodyGetMass(_Body, &m); + return m.mass; +} + +//------------------------------------------------------------------------------ +void RigidBody::saveState() +{ + // save position + + const Scalar *p = dBodyGetPosition(_Body); + _Position.x = p[0]; + _Position.y = p[1]; + _Position.z = p[2]; + + // save linear velocity + const Scalar *v = dBodyGetLinearVel(_Body); + _LinearVelocity.x = v[0]; + _LinearVelocity.y = v[1]; + _LinearVelocity.z = v[2]; + + // save orientation + const Scalar *q = dBodyGetQuaternion(_Body); + _Orientation.w = q[0]; + _Orientation.x = q[1]; + _Orientation.y = q[2]; + _Orientation.z = q[3]; + + // save angular velocity + const Scalar *a = dBodyGetAngularVel(_Body); + _AngularVelocity.x = a[0]; + _AngularVelocity.y = a[1]; + _AngularVelocity.z = a[2]; +} + +//------------------------------------------------------------------------------ +unsigned int RigidBody::addCollisionMesh(const std::string &meshname, + Scalar density) +{ + if (_Simulation.valid() == false) + return 0; + + const RigidBodySimulation::Trimesh *trimesh = + _Simulation->getTrimesh(meshname); + + if (trimesh) + { + CollisionMesh collisionmesh; + + collisionmesh.trimeshdata = dGeomTriMeshDataCreate(); + dGeomTriMeshDataBuildSingle(collisionmesh.trimeshdata, + trimesh->vertices.const_data(), 3 * sizeof(float), + trimesh->vertices.count() / 3, trimesh->indices.const_data(), + trimesh->indices.count(), 3 * sizeof(int)); + + collisionmesh.geom = dCreateTriMesh(_Space, collisionmesh.trimeshdata, + 0, 0, 0); + dGeomSetBody(collisionmesh.geom, _Body); + + dMass mass; + dMassSetTrimesh( &mass, density, collisionmesh.geom); + //dMassSetBox( &mass, density, 5.0, 5.0, 5.0 ); + dBodySetMass(_Body, &mass); + + _CollisionMeshes.push_back(collisionmesh); + return _CollisionMeshes.size(); + } + + return 0; +} + +//------------------------------------------------------------------------------ +void RigidBody::setCollisionMeshPosition(unsigned int geom, + const Vector3 &position) +{ + if ( (geom > 0) && (geom <= _CollisionMeshes.size() )) + { + dGeomSetOffsetPosition(_CollisionMeshes[geom-1].geom, position.x, + position.y, position.z); + } +} + +//------------------------------------------------------------------------------ +void RigidBody::setCollisionMeshRotation(unsigned int geom, + const Quaternion &rotation) +{ + if ( (geom > 0) && (geom <= _CollisionMeshes.size() )) + { + dGeomSetOffsetQuaternion(_CollisionMeshes[geom-1].geom, + ( dReal * ) &rotation ); + } +} + +//------------------------------------------------------------------------------ +void RigidBody::getCollisionMeshPosition(unsigned int geom, Vector3 &position) +{ + if ( (geom > 0) && (geom <= _CollisionMeshes.size() )) + { + const Scalar*p = dGeomGetOffsetPosition(_CollisionMeshes[geom-1].geom); + position.x = p[0]; + position.y = p[1]; + position.z = p[2]; + } +} + +//------------------------------------------------------------------------------ +void RigidBody::getCollisionMeshRotation(unsigned int geom, + Quaternion &rotation) +{ + if ( (geom > 0) && (geom <= _CollisionMeshes.size() )) + { + dGeomGetOffsetQuaternion(_CollisionMeshes[geom-1].geom, + ( dReal * ) &rotation ); + } +} + +//------------------------------------------------------------------------------ +void RigidBody::enableCollisionMesh(unsigned int geom) +{ + if ( (geom > 0) && (geom <= _CollisionMeshes.size() )) + { + dGeomEnable(_CollisionMeshes[geom-1].geom); + } +} + +//------------------------------------------------------------------------------ +void RigidBody::disableCollisionMesh(unsigned int geom) +{ + if ( (geom > 0) && (geom <= _CollisionMeshes.size() )) + { + dGeomDisable(_CollisionMeshes[geom-1].geom); + } +} + +//------------------------------------------------------------------------------ +unsigned int RigidBody::getCollisionMeshId(dGeomID geom) +{ + for (unsigned int i = 0; i < _CollisionMeshes.size(); i++) + { + if (_CollisionMeshes[i].geom == geom) + return i + 1; + } + + return 0; +} + +//------------------------------------------------------------------------------ +HSQOBJECT RigidBody::getCollisionHandler() +{ + return _CollisionHandler; +} + +//------------------------------------------------------------------------------ +void RigidBody::setCollisionHandler(HSQOBJECT handler) +{ + _CollisionHandler = handler; +} + +//------------------------------------------------------------------------------ +RigidBodySimulation::RigidBodySimulation(ScriptSystem *scriptsystem) : + _world( 0), _space( 0), _contactgroup( 0), _delta( 0.0), _steps( 0), + _ScriptSystem(scriptsystem) +{ + dInitODE(); + + gRigidBodySimulation = this; + //XmlConfig config("config.xml"); + + clog << ">>> initialize RigidBodySimulation..."<< endline; + + Scalar erp = 1.0, cfm = 0.0; + _stepSize = 0.02; + _maxContacts = 5; + /* + config.getDouble("RigidBodySimulation", "ERP", erp, 1.0); + config.getDouble("RigidBodySimulation", "CFM", cfm, 0.0); + config.getDouble("RigidBodySimulation", "StepSize", _stepSize, 0.02); + config.getInt("RigidBodySimulation", "MaxContacts", _maxContacts, 5); + */ + // sanity check + + if (erp > 1.0) + erp = 1.0; + else if (erp < 0.0001) + erp = 0.0001; + + clog << " ERP: "<< erp << endline; + + // sanity check + if (cfm > 1.0) + cfm = 1.0; + else if (cfm < 0.0) + cfm = 0.0; + + clog << " CFM: "<< cfm << endline; + + // sanity check + if (_stepSize < 0.0001) + _stepSize = 0.0001; + else if (_stepSize > 1.0) + _stepSize = 1.0; + + clog << " step size: "<< _stepSize << endline; + + // sanity check + if (_maxContacts < 1) + _maxContacts = 1; + else if (_maxContacts > 500) + _maxContacts = 500; + + clog << " max counts: "<< _maxContacts << endline; + + _world = dWorldCreate(); + + dWorldSetERP(_world, erp); + + dWorldSetCFM(_world, cfm); + + _space = dHashSpaceCreate( 0); + + dHashSpaceSetLevels(_space, -10, 10); + + _contactgroup = dJointGroupCreate( 0); + /* + // Bullet + btVector3 worldAabbMin(-100000,-100000,-100000); + btVector3 worldAabbMax(100000,100000,100000); + btOverlappingPairCache* broadphase = new btAxisSweep3(worldAabbMin,worldAabbMax,32766); + btCollisionDispatcher* dispatcher = new btCollisionDispatcher(); + btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver(); + _World = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver); + */ +} + +//------------------------------------------------------------------------------ +RigidBodySimulation::~RigidBodySimulation() +{ + + if (_contactgroup) + dJointGroupDestroy(_contactgroup); + + if (_space) + dSpaceDestroy(_space); + + if (_world) + dWorldDestroy(_world); + + std::map::iterator iter; + for (iter = _Trimeshes.begin(); iter != _Trimeshes.end(); iter++) + { + Trimesh *trimesh = iter->second; + if (trimesh) + delete trimesh; + } + + _Trimeshes.clear(); + + dCloseODE(); + + gRigidBodySimulation = 0; + + clog << ">>> RigidBodySimulation destructed ..."<< endline; +} + +//------------------------------------------------------------------------------ +RigidBody *RigidBodySimulation::createRigidBody() +{ + RigidBody *rigidBody = new RigidBody ( this, _world, _space ); + + _bodies.push_back(rigidBody); + + return rigidBody; +} + +//------------------------------------------------------------------------------ +void RigidBodySimulation::deleteRigidBody(RigidBody *rigidBody) +{ +#ifdef DEBUG_PHYSICS + clog << ">>> delete rigid body" << endline; +#endif + _bodies.remove(rigidBody); + delete rigidBody; +} + +//------------------------------------------------------------------------------ +void RigidBodySimulation::removeAll() +{ + std::list::iterator i; + + for (i = _bodies.begin(); i != _bodies.end(); i++) + delete *i; + + _bodies.clear(); +} + +//------------------------------------------------------------------------------ +void RigidBodySimulation::saveStates() +{ + std::list::iterator i; + + for (i = _bodies.begin(); i != _bodies.end(); i++) + ( *i )->saveState(); +} + +//------------------------------------------------------------------------------ +void RigidBodySimulation::nearCallback(void *data, dGeomID o1, dGeomID o2) +{ + if (gRigidBodySimulation == 0) + return; + + if (dGeomIsSpace(o1) && dGeomIsSpace(o2) ) + { + + // colliding a space with something + dSpaceCollide2(o1, o2, data, &nearCallback); + /* + // collide all geoms internal to the space(s) + if( dGeomIsSpace(o1) ) + dSpaceCollide( (dxSpace *)o1, data, &nearCallback ); + + if( dGeomIsSpace(o2) ) + dSpaceCollide( (dxSpace *)o2, data,&nearCallback ); + */ + } + else + { + static Buffer contacts; + + if (contacts.count() == 0) + contacts.create(gRigidBodySimulation->getMaxContacts()); + + dBodyID b1, b2; + b1 = dGeomGetBody(o1); + b2 = dGeomGetBody(o2); + + // exit without doing anything if the two bodies are connected by a joint + if (b1 && b2 && dAreConnected(b1, b2) ) + return; + + for (unsigned int i = 0; i < contacts.count(); i++) + { + contacts[i].surface.mode = 0; //dContactBounce; + contacts[i].surface.mu = 1.0; + contacts[i].surface.mu2 = 0; + contacts[i].surface.bounce = 0.1; + contacts[i].surface.bounce_vel = 0.1; + contacts[i].surface.soft_cfm = 0.01; + } + + unsigned int collisions = dCollide(o1, o2, contacts.count(), + &contacts[0].geom, sizeof(dContact)); + + //clog << "collide" << endline; + + unsigned int actual_collisions = 0; + + for (unsigned int i = 0; i < collisions; i++) + { + clog << "depth: "<< contacts[i].geom.depth<< endline; + dJointID joint = dJointCreateContact(gRigidBodySimulation->_world, + gRigidBodySimulation->_contactgroup, &contacts[i]); + dJointAttach(joint, b1, b2); + } + +#ifdef DEBUG_PHYSICS + if ( collisions == contacts.count() ) + clog << "!!! Max collisions reached!" << endline; + +#endif + + if (collisions > 0) + { + //clog << "rbsim handle collision" << endline; + //ScriptManager::getSingleton()->handleCollision ( o1, o2 ); + } + } +} + +//------------------------------------------------------------------------------ +int RigidBodySimulation::getSteps() +{ + return _steps; +} + +//------------------------------------------------------------------------------ +Scalar RigidBodySimulation::getStepSize() +{ + return _stepSize; +} + +//------------------------------------------------------------------------------ +int RigidBodySimulation::getMaxContacts() +{ + return _maxContacts; +} + +//------------------------------------------------------------------------------ +void RigidBodySimulation::updateSteps(Scalar delta) +{ + _delta += delta; + + if (_delta < 0.0) + return; + + _steps = ( int ) floor(_delta / _stepSize); + + if (_steps > 10) + _steps = 10; + + _delta -= _steps * _stepSize; +} + +//------------------------------------------------------------------------------ +bool RigidBodySimulation::step() +{ + if (_steps > 0) + { + _ScriptSystem->callFunction("OnStep", _stepSize); + dSpaceCollide(_space, 0, &nearCallback); + dWorldQuickStep(_world, _stepSize); + StepSignal(_stepSize); + dJointGroupEmpty(_contactgroup); + _steps--; + } + + if (_steps > 0) + return true; + + return false; +} + +//------------------------------------------------------------------------------ +const RigidBodySimulation::Trimesh *RigidBodySimulation::getTrimesh( + const std::string &name) +{ + Trimesh *trimesh = 0; + + std::map::const_iterator result; + result = _Trimeshes.find(name); + + if (result != _Trimeshes.end() ) + { + trimesh = result->second; + } + /* + if ( trimesh == 0 ) + { + MeshLoader loader; + + clog << ">>> loading collision trimesh: " << name << endline; + loader.load ( name + ".3ds" ); + + if ( loader.objects().count() > 0 ) + { + trimesh = new Trimesh(); + + MeshLoader::Object &object = loader.objects().item ( 0 ); + + clog << " " << object.vertices.count() << " Vertices" << endline; + trimesh->vertices.create ( object.vertices.count() * 3 ); + + clog << " " << object.faces.count() * 3 << " Indices" << endline; + trimesh->indices.create ( object.faces.count() * 3 ); + + unsigned int i; + + for ( i = 0; i < object.vertices.count(); i++ ) + { + trimesh->vertices[i*3] = -object.vertices[i].x; + trimesh->vertices[i*3+1] = object.vertices[i].y; + trimesh->vertices[i*3+2] = object.vertices[i].z; + } + + for ( i = 0; i < object.faces.count(); i++ ) + { + trimesh->indices[i*3] = object.faces[i].a; + trimesh->indices[i*3+2] = object.faces[i].b; + trimesh->indices[i*3+1] = object.faces[i].c; + } + + _Trimeshes[name] = trimesh; + } + } + */ + return trimesh; +} + +} // namespace BlueCore diff --git a/engine/RigidBodySimulation.h b/engine/RigidBodySimulation.h new file mode 100644 index 0000000..bc2e223 --- /dev/null +++ b/engine/RigidBodySimulation.h @@ -0,0 +1,180 @@ +#ifndef BLUECORE_RIGID_BODY_SIMULATION_H +#define BLUECORE_RIGID_BODY_SIMULATION_H + +// system includes +#include +#include +#include + +#include "ode/ode.h" +#include "squirrel.h" +#include "trimeshloader.h" + +// project includes +#include "Utilities/Referenced.h" +#include "Utilities/Buffer.h" +#include "Utilities/sigslot.h" + +#include "Math/Vector.h" +#include "Math/Matrix.h" +#include "Math/Quaternion.h" + +#include "ScriptSystem.h" + +namespace BlueCore +{ +class RigidBodySimulation; + +class RigidBody +{ + Vector3 _Position; + Quaternion _Orientation; + Vector3 _LinearVelocity; + Vector3 _AngularVelocity; + + dBodyID _Body; + dSpaceID _Space; + + struct CollisionMesh + { + dTriMeshDataID trimeshdata; + dGeomID geom; + }; + + std::vector _CollisionMeshes; + HSQOBJECT _CollisionHandler; + weak_ptr _Simulation; + +public: + + RigidBody(RigidBodySimulation* simulation, dWorldID world, dSpaceID space); + ~RigidBody(); + + const Vector3 &getPosition() const; + void setPosition(const Vector3 &v); + + const Vector3 &getLinearVelocity() const; + void setLinearVelocity(const Vector3 &v); + + const Quaternion &getOrientation() const; + void setOrientation(const Quaternion &q); + + const Vector3 &getAngularVelocity() const; + + void applyGlobalForce(const Vector3 &force, const Vector3 &point); + void applyGlobalForce(const Vector3 &force); + + void applyLocalForce(const Vector3 &force, const Vector3 &point); + void applyLocalForce(const Vector3 &force); + + void applyGlobalMomentum(const Vector3 &momentum, const Vector3 &point); + void applyLocalMomentum(const Vector3 &momentum, const Vector3 &point); + + Vector3 getLocalLinearMomentum() const; + Vector3 getGlobalLinearMomentum() const; + + Vector3 getLocalAngularMomentum() const; + Vector3 getGlobalAngularMomentum() const; + + Vector3 getLocalVelocity() const; + Vector3 getGlobalVelocity() const; + + Scalar getMass() const; + + sigslot::signal1 CollisionSignal; + + void saveState(); + + unsigned int addCollisionMesh(const std::string &meshname, Scalar density); + + void setCollisionMeshPosition(unsigned int geom, const Vector3 &position); + + void + setCollisionMeshRotation(unsigned int geom, + const Quaternion &rotation); + + void getCollisionMeshPosition(unsigned int geom, Vector3 &position); + + void getCollisionMeshRotation(unsigned int geom, Quaternion &rotation); + + void enableCollisionMesh(unsigned int geom); + + void disableCollisionMesh(unsigned int geom); + + unsigned int getCollisionMeshId(dGeomID geom); + + HSQOBJECT getCollisionHandler(); + + void setCollisionHandler(HSQOBJECT handler); +}; + +class RigidBodySimulation : public Referenced +{ + +public: + + typedef struct + { + Buffer vertices; + Buffer indices; + } Trimesh; + +private: + + static void nearCallback(void *data, dGeomID o1, dGeomID o2); + + dWorldID _world; + + dSpaceID _space; + + dJointGroupID _contactgroup; + + int _maxContacts; + + Scalar _delta; + + Scalar _stepSize; + + int _steps; + + std::list _bodies; + + std::map _Trimeshes; + + ref_ptr _ScriptSystem; + +public: + + RigidBodySimulation(ScriptSystem *scriptsystem); + + ~RigidBodySimulation(); + + void initializeSingleton(); + + void shutdownSingleton(); + + RigidBody *createRigidBody(); + + void deleteRigidBody(RigidBody *rigid_body); + + void removeAll(); + + int getSteps(); + + Scalar getStepSize(); + + int getMaxContacts(); + + void saveStates(); + + void updateSteps(Scalar time); + + bool step(); + + sigslot::signal1 StepSignal; + + const Trimesh *getTrimesh(const std::string &name); +}; +} + +#endif diff --git a/engine/SceneNode.cpp b/engine/SceneNode.cpp new file mode 100644 index 0000000..7b1cb51 --- /dev/null +++ b/engine/SceneNode.cpp @@ -0,0 +1,162 @@ +#include "SceneNode.h" + +namespace BlueCore { + +#define DEBUG_SCENEGRAPH + +//------------------------------------------------------------------------------ +SceneNode::SceneNode() : + Named("unnamed SceneNode"), _Parent(0) +{ +#ifdef DEBUG_SCENEGRAPH + clog << "SceneNode 'Unnamed SceneNode' created." << endline; +#endif +} + +//------------------------------------------------------------------------------ +SceneNode::SceneNode(const std::string &name) : + Named(name), _Parent(0) +{ +#ifdef DEBUG_SCENEGRAPH + clog << "SceneNode '" << name << "' created." << endline; +#endif +} + +//------------------------------------------------------------------------------ +SceneNode::~SceneNode() +{ + detachAll(); + + #ifdef DEBUG_SCENEGRAPH + clog << "SceneNode '" << getName() << "' deleted." << endline; + #endif +} + +//------------------------------------------------------------------------------ +void SceneNode::attach(SceneNode *node) +{ + if (node == 0) + return; + + _Children.push_back(node ); + + node->_Parent = this; + + node->addReference(); + +#ifdef DEBUG_SCENEGRAPH + clog << "SceneNode '" << node->getName() << "' attached to '" << this->getName() << "'" << endline; + +#endif +} + +//------------------------------------------------------------------------------ +void SceneNode::detach(SceneNode *node) +{ + node->_Parent = 0; + node->removeReference(); + + _Children.remove(node ); + +#ifdef DEBUG_SCENEGRAPH + clog << "SceneNode '" << node->getName() << "' detach from '" << this->getName() << "'" << endline; +#endif +} + +//------------------------------------------------------------------------------ +void SceneNode::detachAll() +{ + SceneNodeList::iterator i; + + for (i = _Children.begin(); i != _Children.end(); i++) + { + ( *i )->_Parent = 0; + ( *i )->removeReference(); + } + + _Children.clear(); +} + +//------------------------------------------------------------------------------ +SceneNode *SceneNode::getParent() const +{ + return _Parent; +} + +//------------------------------------------------------------------------------ +void SceneNode::detachFromParent() +{ + if (_Parent ) + _Parent->detach( this); +} + +//------------------------------------------------------------------------------ +const SceneNode::SceneNodeList& SceneNode::getChildren () const +{ + return _Children; +} + +//------------------------------------------------------------------------------ +void SceneNode::update(Scalar time) +{ + updateAbsoluteTransformation(); + +// if (isActive() ) + { + SceneNodeList::iterator i; + + for (i = _Children.begin(); i != _Children.end(); i++) + ( *i )->update(time ); + } +} + +//------------------------------------------------------------------------------ +void SceneNode::render(RenderDevice *device, Camera *camera) +{ + if (isActive() ) + { + SceneNodeList::iterator i; + + for (i = _Children.begin(); i != _Children.end(); i++) + { + ( *i )->render(device, camera); + } + } +} + +//------------------------------------------------------------------------------ +const Transformation& SceneNode::getRelativeTransformation() +{ + return _RelativeTransformation; +} + +const Transformation& SceneNode::getAbsoluteTransformation() +{ + return _AbsoluteTransformation; +} + +//------------------------------------------------------------------------------ +void SceneNode::setRelativeTranslation(const Vector3 &translation) +{ + _RelativeTransformation.translation = translation; +} + +//------------------------------------------------------------------------------ +void SceneNode::setRelativeRotation (const Quaternion &rotation) +{ + _RelativeTransformation.rotation = rotation; +} + +//------------------------------------------------------------------------------ +void SceneNode::updateAbsoluteTransformation() +{ +/* + if (_Parent ) + _AbsoluteTranslation = _Parent->getAbsoluteTranslation() + +_Parent->getAbsoluteRotation().inversed() * _RelativeTranslation; + else + _AbsoluteTranslation = _RelativeTranslation; + */ +} + +} // namespace BlueCore diff --git a/engine/SceneNode.h b/engine/SceneNode.h new file mode 100644 index 0000000..eae7653 --- /dev/null +++ b/engine/SceneNode.h @@ -0,0 +1,61 @@ +#ifndef BLUECORE_SCENE_NODE_H +#define BLUECORE_SCENE_NODE_H + +#include "Camera.h" +#include "RenderDevice.h" + +#include "Utilities/Referenced.h" +#include "Utilities/Named.h" +#include "Utilities/Log.h" +#include "Utilities/Activated.h" +#include "Math/Transformation.h" + +#include +#include + +namespace BlueCore +{ + +class SceneNode : public Referenced, public Named, public Activated +{ + + public: + + typedef std::list< ref_ptr > SceneNodeList; + + protected: + + SceneNode *_Parent; + SceneNodeList _Children; + Transformation _RelativeTransformation; + Transformation _AbsoluteTransformation; + + public: + + SceneNode(); + SceneNode(const std::string &name); + virtual ~SceneNode(); + + SceneNode *getParent() const; + const SceneNodeList &getChildren() const; + + void attach(SceneNode *node); + void detach(SceneNode *node); + void detachAll(); + void detachFromParent(); + + virtual void update(Scalar time); + virtual void render (RenderDevice *device, Camera *camera); + + const Transformation& getRelativeTransformation(); + const Transformation& getAbsoluteTransformation(); + + void setRelativeTranslation (const Vector3 &translation); + void setRelativeRotation (const Quaternion &rotation); + + virtual void updateAbsoluteTransformation(); +}; + +} // namespace BlueCore + +#endif diff --git a/engine/ScriptSystem.cpp b/engine/ScriptSystem.cpp new file mode 100644 index 0000000..cd97745 --- /dev/null +++ b/engine/ScriptSystem.cpp @@ -0,0 +1,203 @@ +#include "ScriptSystem.h" + +#include "Utilities/Log.h" + +// library includes +#include "physfs.h" + +// system includes +#include +#include +#include +#include + +using namespace std; + +namespace BlueCore +{ +//-------------------------------------------------------------------------- +static ScriptSystem* gScriptSystem = 0; + +//-------------------------------------------------------------------------- +void _sq_compiler_error_handler(HSQUIRRELVM vm, const SQChar *error, + const SQChar *source, SQInteger line, SQInteger column) +{ + clog << "!!! Error compiling script '"<< source << ":"<< line << ": " + << error << endlog; +} + +//-------------------------------------------------------------------------- +static SQInteger _sq_runtime_error_handler(HSQUIRRELVM vm) +{ + const SQChar *error = 0; + + if (sq_gettop(vm) >= 1) + { + if (SQ_SUCCEEDED(sq_getstring(vm, 2, &error) ) ) + { + clog << "!!! Script Error: "<< error << endlog; + } + else + { + clog << "!!! Script Error: unknown"<< endlog; + } + } + + return 0; +} + +//-------------------------------------------------------------------------- +void _sq_print(HSQUIRRELVM vm, const SQChar* s, ...) +{ + va_list vl; + va_start ( vl, s ); + vprintf(s, vl); + va_end ( vl ); +} + +//-------------------------------------------------------------------------- +static SQInteger _sq_require(HSQUIRRELVM v) +{ + const SQChar *scriptname = 0; + + if (SQ_SUCCEEDED(sq_getstring(v, 2, &scriptname) )) + { + if (gScriptSystem) + gScriptSystem->loadScript(scriptname); + } + + return 0; +} + +//-------------------------------------------------------------------------- +SQInteger _sq_file_reader(SQUserPointer file) +{ + char c; + unsigned int count; + + count = PHYSFS_read( (PHYSFS_file *)file, &c, sizeof (c), 1); + if (count > 0) + return c; + + return 0; +} + +//-------------------------------------------------------------------------- +ScriptSystem::ScriptSystem() +{ + gScriptSystem = this; + + _VM = sq_open( 1024); + sq_setcompilererrorhandler(_VM, _sq_compiler_error_handler); + sq_newclosure(_VM, _sq_runtime_error_handler, 0); + sq_seterrorhandler(_VM); + sq_setprintfunc(_VM, _sq_print); + + sq_pushroottable(_VM); + sq_pushstring(_VM, "require", -1); + sq_newclosure(_VM, _sq_require, 0); + sq_setparamscheck(_VM, 2, ".s"); + sq_newslot(_VM, -3, false); + sq_pop(_VM, 1); + + clog << ">>> ScriptSystem constructed ..."<< endlog; +} + +//-------------------------------------------------------------------------- +ScriptSystem::~ScriptSystem() +{ + sq_close(_VM); + clog << ">>> ScriptSystem destructed ..."<< endlog; +} + +//-------------------------------------------------------------------------- +HSQUIRRELVM ScriptSystem::getVM() +{ + return _VM; +} + +//-------------------------------------------------------------------------- +bool ScriptSystem::loadScript(const std::string &name) +{ + for (unsigned int i = 0; i < _LoadedScripts.size(); i++) + { + if (_LoadedScripts[i] == name) + return true; + } + + if (executeScript(name) ) + { + _LoadedScripts.push_back(name); + return true; + } + else + clog << "!!! Script '"<< name << "' not found!"<< endlog; + + return false; +} + +//-------------------------------------------------------------------------- +bool ScriptSystem::executeScript(const std::string &name) +{ + string filename = name + ".nut"; + PHYSFS_file *file = PHYSFS_openRead(filename.c_str() ); + + if ( !file) + { + clog << "!!! Script '"<< name << "' not found!"<< endlog; + return false; + } + + SQInteger ret; + + ret = sq_compile(_VM, _sq_file_reader, file, name.c_str(), 1); + PHYSFS_close(file); + + if (SQ_FAILED(ret) ) + { + return false; + } + + sq_pushroottable(_VM); + + ret = sq_call(_VM, 1, SQFalse, SQTrue); + sq_pop(_VM, 1); + + if (SQ_FAILED(ret) ) + { + clog << "!!! Script execution failed: "<< name << endlog; + return false; + } + else + return true; +} + +//-------------------------------------------------------------------------- +void ScriptSystem::callFunction(const std::string &name) +{ + sq_pushroottable(_VM); + sq_pushstring(_VM, name.c_str(), -1); + if (SQ_SUCCEEDED(sq_get(_VM, -2)) ) + { + sq_pushroottable(_VM); + if (sq_call(_VM, 1, SQTrue, SQTrue) ) + sq_pop(_VM, 1); + } + sq_pop(_VM, 1); +} + +//-------------------------------------------------------------------------- +void ScriptSystem::callFunction(const std::string &name, double value) +{ + sq_pushroottable(_VM); + sq_pushstring(_VM, name.c_str(), -1); + if (SQ_SUCCEEDED(sq_get(_VM, -2)) ) + { + sq_pushroottable(_VM); + sq_pushfloat(_VM, value); + if (sq_call(_VM, 2, SQTrue, SQTrue) ) + sq_pop(_VM, 1); + } + sq_pop(_VM, 1); +} +} diff --git a/engine/ScriptSystem.h b/engine/ScriptSystem.h new file mode 100644 index 0000000..aa97a69 --- /dev/null +++ b/engine/ScriptSystem.h @@ -0,0 +1,38 @@ +#ifndef BLUECORE_SCRIPT_SYSTEM_H +#define BLUECORE_SCRIPT_SYSTEM_H + +// system includes +#include +#include + +// library includes +#include "squirrel.h" + +// project includes +#include "Utilities/Referenced.h" + +namespace BlueCore{ + +class ScriptSystem : public Referenced +{ + HSQUIRRELVM _VM; + + std::vector _LoadedScripts; + + public: + + ScriptSystem(); + ~ScriptSystem(); + + HSQUIRRELVM getVM(); + + bool executeScript(const std::string &filename); + bool loadScript(const std::string &filename); + + void callFunction(const std::string &name); + void callFunction(const std::string &name, double value); +}; + +} // namespace BlueCore + +#endif diff --git a/engine/ScriptSystem_Font.cpp b/engine/ScriptSystem_Font.cpp new file mode 100644 index 0000000..2a80c8d --- /dev/null +++ b/engine/ScriptSystem_Font.cpp @@ -0,0 +1,126 @@ +#include "ScriptSystem_Font.h" + +namespace BlueCore +{ + +//------------------------------------------------------------------------------ +static weak_ptr gFontManager; + +//------------------------------------------------------------------------------ +static SQInteger _font_releasehook(SQUserPointer p, SQInteger size) +{ + Font *font = (Font *)p; + + if (font) + font->removeReference(); + + return 1; +} + +//------------------------------------------------------------------------------ +static SQInteger _font_constructor(HSQUIRRELVM vm) +{ + SQInteger argc = sq_gettop (vm ); + + Font *font = 0; + + if (argc < 3) + { + if (gFontManager.valid()) + font = gFontManager->getDefaultFont(); + } + else + { + const SQChar *name = 0; + SQInteger size = 1; + SQBool hinting = SQFalse; + + sq_getstring (vm, 2, &name ); + sq_getinteger (vm, 3, &size ); + if (argc > 3) + sq_getbool(vm, 4, &hinting ); + + if (gFontManager.valid()) + { + font = gFontManager->loadFont(name, size, hinting == SQTrue ); + if (font) + font->addReference(); + } + } + + sq_setinstanceup(vm, 1, (void *)font ); + sq_setreleasehook (vm, 1, _font_releasehook ); + + return 0; +} + +//------------------------------------------------------------------------------ +static SQInteger _font_print(HSQUIRRELVM vm) +{ + int argc = sq_gettop (vm ); + + if (argc < 4) + return 0; + + const Font *font = 0; + sq_getinstanceup (vm, 1, ( void ** ) &font, 0); + + if (font) + { + SQFloat x, y; + const char *text; + SQInteger halign = 0, valign = 0; + + sq_getfloat (vm, 2, &x ); + sq_getfloat (vm, 3, &y ); + sq_getstring(vm, 4, &text); + + if (argc > 4) + { + sq_getinteger(vm, 5, &halign ); + } + + if (argc > 5) + { + sq_getinteger(vm, 6, &valign ); + } + + font->print( (float)x, (float)y, text, halign, valign ); + } + + return 0; +} + +//------------------------------------------------------------------------------ +void setupScriptSystem_Font (ScriptSystem* scriptsystem, FontManager* fontmanager) +{ + if (scriptsystem && fontmanager) + { + HSQUIRRELVM vm = scriptsystem->getVM(); + + gFontManager = fontmanager; + + sq_pushroottable (vm ); + + // push class + sq_pushstring (vm, "Font", -1); + + if (SQ_SUCCEEDED (sq_newclass (vm, SQFalse ) ) ) + { + // register constructor + sq_pushstring (vm, "constructor", -1); + sq_newclosure (vm, _font_constructor, 0); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "print", -1); + sq_newclosure (vm, _font_print, 0); + sq_newslot (vm, -3, false); + + // create class + sq_newslot (vm, -3, false); + } + + sq_poptop (vm ); + } +} +} diff --git a/engine/ScriptSystem_Font.h b/engine/ScriptSystem_Font.h new file mode 100644 index 0000000..9bd3b91 --- /dev/null +++ b/engine/ScriptSystem_Font.h @@ -0,0 +1,14 @@ +#ifndef BLUECORE_SCRIPTING_FONT_H +#define BLUECORE_SCRIPTING_FONT_H + +#include "FontManager.h" +#include "ScriptSystem.h" + +#include "squirrel.h" + +namespace BlueCore +{ + void setupScriptSystem_Font(ScriptSystem* scriptsystem, FontManager* fontmanager); +} + +#endif diff --git a/engine/ScriptSystem_Image.cpp b/engine/ScriptSystem_Image.cpp new file mode 100644 index 0000000..04dcd22 --- /dev/null +++ b/engine/ScriptSystem_Image.cpp @@ -0,0 +1,126 @@ +#include "ScriptSystem_Image.h" + +#include "TextureImage.h" + +namespace BlueCore +{ + +//------------------------------------------------------------------------------ +static weak_ptr gTextureManager; +static weak_ptr gRenderDevice; + +//------------------------------------------------------------------------------ +static SQInteger _image_releasehook(SQUserPointer p, SQInteger size) +{ + TextureImage *image = (TextureImage *)p; + + if (image) + image->removeReference(); + + return 1; +} + +//------------------------------------------------------------------------------ +static SQInteger _image_constructor(HSQUIRRELVM vm) +{ + SQInteger argc = sq_gettop (vm ); + + if (argc < 6) + return 0; + + TextureImage *image = 0; + + const SQChar *texturename = 0; + SQFloat ax, ay, bx, by; + + sq_getstring(vm, 2, &texturename ); + sq_getfloat(vm, 3, &ax ); + sq_getfloat(vm, 4, &ay ); + sq_getfloat(vm, 5, &bx ); + sq_getfloat(vm, 6, &by ); + + Texture *texture = gTextureManager->loadTexture(texturename, 0, 0); + image = new TextureImage( gRenderDevice.get(), texture, ax, ay, bx, by ); + image->addReference(); + + sq_setinstanceup(vm, 1, (void *)image ); + sq_setreleasehook (vm, 1, _image_releasehook ); + + return 0; +} + +//------------------------------------------------------------------------------ +static SQInteger _image_draw(HSQUIRRELVM vm) +{ + int argc = sq_gettop (vm ); + + if (argc < 3) + return 0; + + TextureImage *image = 0; + sq_getinstanceup (vm, 1, ( void ** ) &image, 0); + + if (image ) + { + SQFloat x, y, r = 0.0; + SQInteger halign = 0, valign = 0; + + sq_getfloat (vm, 2, &x ); + sq_getfloat (vm, 3, &y ); + + if (argc > 3) + { + sq_getinteger(vm, 4, &halign ); + } + + if (argc > 4) + { + sq_getinteger(vm, 5, &valign ); + } + + if (argc > 5) + { + sq_getfloat(vm, 6, &r ); + } + + image->draw(x, y, halign, valign, r ); + } + + return 0; +} + +//------------------------------------------------------------------------------ +void setupScriptSystem_Image(ScriptSystem* scriptsystem, + TextureManager* texturemanager, RenderDevice* renderdevice) +{ + if (scriptsystem == 0|| texturemanager == 0|| renderdevice == 0) + return; + + gTextureManager = texturemanager; + gRenderDevice = renderdevice; + + HSQUIRRELVM vm = scriptsystem->getVM(); + + sq_pushroottable (vm ); + + // push class + sq_pushstring (vm, "Image", -1); + + if (SQ_SUCCEEDED (sq_newclass (vm, SQFalse ) ) ) + { + // register constructor + sq_pushstring (vm, "constructor", -1); + sq_newclosure (vm, _image_constructor, 0); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "draw", -1); + sq_newclosure (vm, _image_draw, 0); + sq_newslot (vm, -3, false); + + // create class + sq_newslot (vm, -3, false); + } + + sq_poptop (vm ); +} +} diff --git a/engine/ScriptSystem_Image.h b/engine/ScriptSystem_Image.h new file mode 100644 index 0000000..6d62fc1 --- /dev/null +++ b/engine/ScriptSystem_Image.h @@ -0,0 +1,13 @@ +#ifndef BLUECORE_SCRIPTING_IMAGE_H +#define BLUECORE_SCRIPTING_IMAGE_H + +#include "ScriptSystem.h" +#include "TextureManager.h" +#include "RenderDevice.h" + +namespace BlueCore +{ + void setupScriptSystem_Image (ScriptSystem* scriptsystem, TextureManager* texturemanager, RenderDevice* renderdevice); +} + +#endif diff --git a/engine/ScriptSystem_Math.cpp b/engine/ScriptSystem_Math.cpp new file mode 100644 index 0000000..37a8821 --- /dev/null +++ b/engine/ScriptSystem_Math.cpp @@ -0,0 +1,440 @@ +#include "ScriptSystem_Math.h" + +#include "Utilities/MersenneTwister.h" + +#include + +namespace BlueCore +{ + +static MTRand mtrand; + +//------------------------------------------------------------------------------ +static SQInteger _sin(HSQUIRRELVM v) +{ + SQFloat f; + sq_getfloat (v, 2, &f ); + sq_pushfloat (v, ( SQFloat ) sin (f ) ); + return 1; +} + +//------------------------------------------------------------------------------ +static SQInteger _cos(HSQUIRRELVM v) +{ + SQFloat f; + sq_getfloat (v, 2, &f ); + sq_pushfloat (v, ( SQFloat ) cos (f ) ); + return 1; +} + +//------------------------------------------------------------------------------ +static SQInteger _tan(HSQUIRRELVM v) +{ + SQFloat f; + sq_getfloat (v, 2, &f ); + sq_pushfloat (v, ( SQFloat ) tan (f ) ); + return 1; +} + +//------------------------------------------------------------------------------ +static SQInteger _sqrt(HSQUIRRELVM v) +{ + SQFloat f; + sq_getfloat (v, 2, &f ); + sq_pushfloat (v, ( SQFloat ) sqrt (f ) ); + return 1; +} + +//------------------------------------------------------------------------------ +static SQInteger _trunc(HSQUIRRELVM v) +{ + SQFloat f; + sq_getfloat (v, 2, &f ); + sq_pushfloat (v, ( SQFloat ) f - fmod(f, 1.0) ); + return 1; +} + +//------------------------------------------------------------------------------ +static SQInteger _floor(HSQUIRRELVM v) +{ + SQFloat f; + sq_getfloat (v, 2, &f ); + sq_pushfloat (v, ( SQFloat ) floor (f ) ); + return 1; +} + +//------------------------------------------------------------------------------ +static SQInteger _ceil(HSQUIRRELVM v) +{ + SQFloat f; + sq_getfloat (v, 2, &f ); + sq_pushfloat (v, ( SQFloat ) ceil (f ) ); + return 1; +} + +//------------------------------------------------------------------------------ +static SQInteger _rand(HSQUIRRELVM v) +{ + sq_pushfloat (v, mtrand.rand() ); + return 1; +} + +//------------------------------------------------------------------------------ +static SQInteger _seed_rand(HSQUIRRELVM v) +{ + SQInteger seed; + sq_getinteger(v, 2, &seed ); + mtrand.seed(seed ); + return 0; +} + +//------------------------------------------------------------------------------ +static void _setup_basic_math (HSQUIRRELVM vm) +{ + sq_pushroottable (vm); + + sq_pushstring (vm, "sin", -1); + sq_newclosure (vm, _sin, 0); + sq_setparamscheck (vm, 2, ".n"); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "cos", -1); + sq_newclosure (vm, _cos, 0); + sq_setparamscheck (vm, 2, ".n"); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "tan", -1); + sq_newclosure (vm, _tan, 0); + sq_setparamscheck (vm, 2, ".n"); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "sqrt", -1); + sq_newclosure (vm, _sqrt, 0); + sq_setparamscheck (vm, 2, ".n"); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "trunc", -1); + sq_newclosure (vm, _trunc, 0); + sq_setparamscheck (vm, 2, ".n"); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "floor", -1); + sq_newclosure (vm, _floor, 0); + sq_setparamscheck (vm, 2, ".n"); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "ceil", -1); + sq_newclosure (vm, _ceil, 0); + sq_setparamscheck (vm, 2, ".n"); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "rand", -1); + sq_newclosure (vm, _rand, 0); + sq_setparamscheck (vm, 1, "."); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "seed_rand", -1); + sq_newclosure (vm, _seed_rand, 0); + sq_setparamscheck (vm, 2, ".n"); + sq_newslot (vm, -3, false); + + // pop the root table + sq_pop (vm, 1); +} + +//------------------------------------------------------------------------------ +static SQInteger _vector__add ( HSQUIRRELVM v ) +{ + SQFloat ax, ay, az; + SQFloat bx = 0.0, by = 0.0, bz = 0.0; + + _getvectorvalues ( v, 1, ax, ay, az ); + _getvectorvalues ( v, 2, bx, by, bz ); + _pushvector ( v, ax + bx, ay + by, az + bz ); + + return 1; +} + +//------------------------------------------------------------------------------ +static SQInteger _vector__sub ( HSQUIRRELVM v ) +{ + SQFloat ax, ay, az; + SQFloat bx = 0.0, by = 0.0, bz = 0.0; + + _getvectorvalues ( v, 1, ax, ay, az ); + _getvectorvalues ( v, 2, bx, by, bz ); + _pushvector ( v, ax - bx, ay - by, az - bz ); + + return 1; +} + +//------------------------------------------------------------------------------ +static SQInteger _vector__mul ( HSQUIRRELVM v ) +{ + SQFloat x, y, z, f; + + _getvectorvalues ( v, 1, x, y, z ); + sq_getfloat ( v, 2, &f ); + _pushvector ( v, x*f, y*f, z*f ); + + return 1; +} + +//------------------------------------------------------------------------------ +static SQInteger _vector__div ( HSQUIRRELVM v ) +{ + SQFloat x, y, z, f; + + _getvectorvalues ( v, 1, x, y, z ); + sq_getfloat ( v, 2, &f ); + _pushvector ( v, x / f, y / f, y / f ); + + return 1; +} + +//------------------------------------------------------------------------------ +static SQInteger _vector_length ( HSQUIRRELVM v ) +{ + SQFloat x, y, z; + + _getvectorvalues ( v, 1, x, y, z ); + + sq_pushfloat ( v, std::sqrt ( x*x + y*y + z*z ) ); + + return 1; +} + +//------------------------------------------------------------------------------ +static SQInteger _vector_normalize ( HSQUIRRELVM v ) +{ + SQFloat x, y, z; + + _getvectorvalues ( v, 1, x, y, z ); + + SQFloat l = 1 / std::sqrt ( x * x + y * y + z * z ); + _setvectorvalues ( v, 1, x*l, y*l, z*l ); + + return 0; +} + +//------------------------------------------------------------------------------ +static SQInteger _vector_constructor ( HSQUIRRELVM v ) +{ + SQFloat x = 0.0; + SQFloat y = 0.0; + SQFloat z = 0.0; + + SQInteger argc = sq_gettop ( v ); + + if ( argc > 1 ) + sq_getfloat ( v, 2, &x ); + + if ( argc > 2 ) + sq_getfloat ( v, 3, &y ); + + if ( argc > 3 ) + sq_getfloat ( v, 4, &z ); + + _setvectorvalues ( v, 1, x, y, z ); + + return 0; +} + +//------------------------------------------------------------------------------ +static void _register_vector_class( HSQUIRRELVM _vm ) +{ + sq_pushroottable ( _vm ); + + // push class + sq_pushstring ( _vm, "Vector", -1 ); + if ( SQ_SUCCEEDED ( sq_newclass ( _vm, SQFalse ) ) ) + { + // register variables + sq_pushstring ( _vm, "x", -1 ); + sq_pushfloat ( _vm, 0.0 ); + sq_newslot ( _vm, -3, false ); + + sq_pushstring ( _vm, "y", -1 ); + sq_pushfloat ( _vm, 0.0 ); + sq_newslot ( _vm, -3, false ); + + sq_pushstring ( _vm, "z", -1 ); + sq_pushfloat ( _vm, 0.0 ); + sq_newslot ( _vm, -3, false ); + + // register constructor + sq_pushstring ( _vm, "constructor", -1 ); + sq_newclosure ( _vm, _vector_constructor, 0 ); + sq_newslot ( _vm, -3, false ); + + // register _add + sq_pushstring ( _vm, "_add", -1 ); + sq_newclosure ( _vm, _vector__add, 0 ); + sq_newslot ( _vm, -3, false ); + + // register _sub + sq_pushstring ( _vm, "_sub", -1 ); + sq_newclosure ( _vm, _vector__sub, 0 ); + sq_newslot ( _vm, -3, false ); + + // register _mul + sq_pushstring ( _vm, "_mul", -1 ); + sq_newclosure ( _vm, _vector__mul, 0 ); + sq_newslot ( _vm, -3, false ); + + // register _div + sq_pushstring ( _vm, "_div", -1 ); + sq_newclosure ( _vm, _vector__div, 0 ); + sq_newslot ( _vm, -3, false ); + + // register length + sq_pushstring ( _vm, "length", -1 ); + sq_newclosure ( _vm, _vector_length, 0 ); + sq_newslot ( _vm, -3, false ); + + // register normalize + sq_pushstring ( _vm, "normalize", -1 ); + sq_newclosure ( _vm, _vector_normalize, 0 ); + sq_newslot ( _vm, -3, false ); + + // create_vector class + sq_newslot ( _vm, -3, false ); + } + + sq_poptop ( _vm ); +} + +//------------------------------------------------------------------------------ +static SQInteger _quaternion_constructor ( HSQUIRRELVM v ) +{ + SQFloat w = 0.0; + SQFloat x = 0.0; + SQFloat y = 0.0; + SQFloat z = 0.0; + + SQInteger argc = sq_gettop ( v ); + + if ( argc > 1 ) + sq_getfloat ( v, 2, &w ); + + if ( argc > 2 ) + sq_getfloat ( v, 3, &x ); + + if ( argc > 3 ) + sq_getfloat ( v, 4, &y ); + + if ( argc > 4 ) + sq_getfloat ( v, 5, &z ); + + _setquaternionvalues ( v, 1, w, x, y, z ); + + return 0; +} + +//------------------------------------------------------------------------------ +static SQInteger _quaternion_apply ( HSQUIRRELVM vm ) +{ + SQFloat qw, qx, qy, qz; + SQFloat vx, vy, vz; + + _getquaternionvalues ( vm, 1, qw, qx, qy, qz ); + _getvectorvalues ( vm, 2, vx, vy, vz ); + + SQFloat xx = qx*qx, xy = qx*qy, xz = qx*qz, xw = qx*qw, + yy = qy*qy, yz = qy*qz, yw = qy*qw, + zz = qz*qz, zw = qz*qw; + + SQFloat rx = 2.0 * (vx * (0.5 - yy - zz) + vy * (xy - zw) + vz * (xz + yw)); + SQFloat ry = 2.0 * (vx * (xy + zw) + vy * (0.5 - xx - zz) + vz * (yz - xw)); + SQFloat rz = 2.0 * (vx * (xz - yw) + vy * (yz + xw) + vz * (0.5 - xx - yy)); + + _pushvector ( vm, rx, ry, rz ); + + return 1; +} + +//------------------------------------------------------------------------------ +static SQInteger _quaternion_applyinversed ( HSQUIRRELVM vm ) +{ + SQFloat qw, qx, qy, qz; + SQFloat vx, vy, vz; + + _getquaternionvalues ( vm, 1, qw, qx, qy, qz ); + _getvectorvalues ( vm, 2, vx, vy, vz ); + + SQFloat xx = qx*qx, xy = qx*qy, xz = qx*qz, xw = -qx*qw, + yy = qy*qy, yz = qy*qz, yw = -qy*qw, + zz = qz*qz, zw = -qz*qw; + + SQFloat rx = 2.0 * (vx * (0.5 - yy - zz) + vy * (xy - zw) + vz * (xz + yw)); + SQFloat ry = 2.0 * (vx * (xy + zw) + vy * (0.5 - xx - zz) + vz * (yz - xw)); + SQFloat rz = 2.0 * (vx * (xz - yw) + vy * (yz + xw) + vz * (0.5 - xx - yy)); + + _pushvector ( vm, rx, ry, rz ); + + return 1; +} + +//------------------------------------------------------------------------------ +static void _register_quaternion_class (HSQUIRRELVM vm) +{ + sq_pushroottable ( vm ); + + // push class + sq_pushstring ( vm, "Quaternion", -1 ); + + if ( SQ_SUCCEEDED ( sq_newclass ( vm, SQFalse ) ) ) + { + // register variables + sq_pushstring ( vm, "w", -1 ); + sq_pushfloat ( vm, 0.0 ); + sq_newslot ( vm, -3, false ); + + sq_pushstring ( vm, "x", -1 ); + sq_pushfloat ( vm, 0.0 ); + sq_newslot ( vm, -3, false ); + + sq_pushstring ( vm, "y", -1 ); + sq_pushfloat ( vm, 0.0 ); + sq_newslot ( vm, -3, false ); + + sq_pushstring ( vm, "z", -1 ); + sq_pushfloat ( vm, 0.0 ); + sq_newslot ( vm, -3, false ); + + // register constructor + sq_pushstring ( vm, "constructor", -1 ); + sq_newclosure ( vm, _quaternion_constructor, 0 ); + sq_newslot ( vm, -3, false ); + + sq_pushstring ( vm, "apply", -1 ); + sq_newclosure ( vm, _quaternion_apply, 0 ); + sq_newslot ( vm, -3, false ); + + sq_pushstring ( vm, "applyInversed", -1 ); + sq_newclosure ( vm, _quaternion_applyinversed, 0 ); + sq_newslot ( vm, -3, false ); + + // create_vector class + sq_newslot ( vm, -3, false ); + } + + sq_poptop ( vm ); +} + +//------------------------------------------------------------------------------ +void setupScriptSystem_Math (ScriptSystem* scriptsystem) +{ + if (scriptsystem == 0) + return; + + HSQUIRRELVM vm = scriptsystem->getVM(); + if (vm) + { + _setup_basic_math (vm); + _register_vector_class(vm); + _register_quaternion_class(vm); + } +} + +} // namespace BlueCore diff --git a/engine/ScriptSystem_Math.h b/engine/ScriptSystem_Math.h new file mode 100644 index 0000000..4fc25c7 --- /dev/null +++ b/engine/ScriptSystem_Math.h @@ -0,0 +1,138 @@ +#ifndef BLUECORE_SCRIPTING_MATH_H +#define BLUECORE_SCRIPTING_MATH_H + +#include "ScriptSystem.h" + +namespace BlueCore +{ + +//------------------------------------------------------------------------------ +inline void _setvectorvalues(HSQUIRRELVM &v, const SQInteger &idx, + const SQFloat &x, const SQFloat &y, const SQFloat &z) +{ + sq_pushstring (v, "x", 1); + sq_pushfloat (v, x ); + sq_rawset (v, idx ); + + sq_pushstring (v, "y", 1); + sq_pushfloat (v, y ); + sq_rawset (v, idx ); + + sq_pushstring (v, "z", 1); + sq_pushfloat (v, z ); + sq_rawset (v, idx ); +} + +//------------------------------------------------------------------------------ +inline void _getvectorvalues(HSQUIRRELVM &v, const SQInteger &idx, SQFloat &x, + SQFloat &y, SQFloat &z) +{ + sq_pushstring (v, "x", 1); + sq_rawget (v, idx ); + sq_getfloat (v, -1, &x ); + + sq_pushstring (v, "y", 1); + sq_rawget (v, idx ); + sq_getfloat (v, -1, &y ); + + sq_pushstring (v, "z", 1); + sq_rawget (v, idx ); + sq_getfloat (v, -1, &z ); + + sq_pop (v, 3); +} + +//------------------------------------------------------------------------------ +inline void _pushvector(HSQUIRRELVM &v, const SQFloat &x, const SQFloat &y, + const SQFloat &z) +{ + // get closure/class + // TODO: use SQObject rfom creation + sq_pushroottable (v ); + sq_pushstring (v, "Vector", -1); + sq_get (v, -2); + sq_remove (v, -2); + + // call constructor + sq_pushroottable (v ); + sq_pushfloat (v, x ); + sq_pushfloat (v, y ); + sq_pushfloat (v, z ); + sq_call (v, 4, SQTrue, SQTrue ); + + // remove class, leave instance + sq_remove (v, -2); +} + +//------------------------------------------------------------------------------ +inline void _pushquaternion(HSQUIRRELVM &v, const SQFloat &w, const SQFloat &x, + const SQFloat &y, const SQFloat &z) +{ + // get closure/class + // TODO: use SQObject rfom creation!!!!!! + sq_pushroottable (v ); + sq_pushstring (v, "Quaternion", -1); + sq_get (v, -2); + sq_remove (v, -2); + + // call constructor + sq_pushroottable (v ); + sq_pushfloat (v, w ); + sq_pushfloat (v, x ); + sq_pushfloat (v, y ); + sq_pushfloat (v, z ); + sq_call (v, 5, SQTrue, SQTrue ); + + // remove class, leave instance + sq_remove (v, -2); +} + +//------------------------------------------------------------------------------ +inline void _setquaternionvalues(HSQUIRRELVM &v, const SQInteger &idx, + const SQFloat &w, const SQFloat &x, const SQFloat &y, const SQFloat &z) +{ + sq_pushstring (v, "w", -1); + sq_pushfloat (v, w ); + sq_rawset (v, idx ); + + sq_pushstring (v, "x", -1); + sq_pushfloat (v, x ); + sq_rawset (v, idx ); + + sq_pushstring (v, "y", -1); + sq_pushfloat (v, y ); + sq_rawset (v, idx ); + + sq_pushstring (v, "z", -1); + sq_pushfloat (v, z ); + sq_rawset (v, idx ); +} + +//------------------------------------------------------------------------------ +inline void _getquaternionvalues(HSQUIRRELVM &v, const SQInteger &idx, + SQFloat &w, SQFloat &x, SQFloat &y, SQFloat &z) +{ + sq_pushstring (v, "w", -1); + sq_get (v, idx ); + sq_getfloat (v, -1, &w ); + + sq_pushstring (v, "x", -1); + sq_get (v, idx ); + sq_getfloat (v, -1, &x ); + + sq_pushstring (v, "y", -1); + sq_get (v, idx ); + sq_getfloat (v, -1, &y ); + + sq_pushstring (v, "z", -1); + sq_get (v, idx ); + sq_getfloat (v, -1, &z ); + + sq_pop (v, 3); +} + +void setupScriptSystem_Math(ScriptSystem* scriptsystem); + +} // namespace BlueCore + +#endif diff --git a/engine/ScriptSystem_RigidBody.cpp b/engine/ScriptSystem_RigidBody.cpp new file mode 100644 index 0000000..3bc5a76 --- /dev/null +++ b/engine/ScriptSystem_RigidBody.cpp @@ -0,0 +1,542 @@ +#include "RigidBodySimulation.h" + +#include "ScriptSystem_Math.h" + +namespace BlueCore +{ + +//------------------------------------------------------------------------------ +static weak_ptr gRigidBodySimulation; + +//------------------------------------------------------------------------------ +static SQInteger _rigidbody_getposition(HSQUIRRELVM v) +{ + RigidBody *body = 0; + Vector3 position; + + if (sq_gettop (v ) == 1) + { + sq_getinstanceup (v, 1, ( void ** ) &body, 0); + } + + if (body ) + { + position = body->getPosition(); + _pushvector (v, position.x, position.y, position.z); + return 1; + } + + return 0; +} + +//------------------------------------------------------------------------------ +static SQInteger _rigidbody_getrotation(HSQUIRRELVM v) +{ + RigidBody *body = 0; + Quaternion q; + + if (sq_gettop (v ) == 1) + { + sq_getinstanceup (v, 1, ( void ** ) &body, 0); + } + + if (body ) + { + q = body->getOrientation(); + _pushquaternion (v, q.w, q.x, q.y, q.z); + return 1; + } + + return 0; +} + +//------------------------------------------------------------------------------ +static SQInteger _rigidbody_getangularvelocity(HSQUIRRELVM v) +{ + RigidBody *body = 0; + Vector3 velocity; + + if (sq_gettop (v ) == 1) + { + sq_getinstanceup (v, 1, ( void ** ) &body, 0); + } + + if (body ) + { + velocity = body->getAngularVelocity(); + + _pushvector (v, velocity.x, velocity.y, velocity.z); + + return 1; + } + + return 0; +} + +//------------------------------------------------------------------------------ +static SQInteger _rigidbody_getlinearvelocity(HSQUIRRELVM v) +{ + RigidBody *body = 0; + Vector3 velocity; + + if (sq_gettop (v ) == 1) + { + sq_getinstanceup (v, 1, ( void ** ) &body, 0); + } + + if (body ) + { + velocity = body->getLinearVelocity(); + + _pushvector (v, velocity.x, velocity.y, velocity.z); + + return 1; + } + + return 0; +} + +//------------------------------------------------------------------------------ +static SQInteger _rigidbody_setposition(HSQUIRRELVM v) +{ + RigidBody *body = 0; + + sq_getinstanceup (v, 1, ( void ** ) &body, 0); + + if (body ) + { + Vector3 position; + int argc = sq_gettop (v ); + + if (argc == 2) + { + _getvectorvalues (v, 2, position.x, position.y, position.z); + } + else if (argc == 4) + { + sq_getfloat (v, 2, &position.x); + sq_getfloat (v, 3, &position.y); + sq_getfloat (v, 4, &position.z); + } + + body->setPosition(position ); + } + + return 0; +} + +//------------------------------------------------------------------------------ +static SQInteger _rigidbody_setlinearvelocity(HSQUIRRELVM v) +{ + RigidBody *body = 0; + + sq_getinstanceup (v, 1, ( void ** ) &body, 0); + + if (body ) + { + Vector3 velocity; + int argc = sq_gettop (v ); + + if (argc == 2) + { + _getvectorvalues (v, 2, velocity.x, velocity.y, velocity.z); + } + else if (argc == 4) + { + sq_getfloat (v, 2, &velocity.x); + sq_getfloat (v, 3, &velocity.y); + sq_getfloat (v, 4, &velocity.z); + } + + body->setLinearVelocity(velocity ); + } + + return 0; +} + +//------------------------------------------------------------------------------ +static SQInteger _rigidbody_setrotation(HSQUIRRELVM v) +{ + RigidBody *body = 0; + + sq_getinstanceup (v, 1, ( void ** ) &body, 0); + + if (body ) + { + Quaternion q; + int argc = sq_gettop (v ); + + if (argc == 2) + { + _getquaternionvalues (v, 2, q.w, q.x, q.y, q.z); + } + else if (argc == 5) + { + sq_getfloat (v, 2, &q.w); + sq_getfloat (v, 3, &q.x); + sq_getfloat (v, 4, &q.y); + sq_getfloat (v, 5, &q.z); + } + + body->setOrientation(q ); + } + + return 0; +} + +//------------------------------------------------------------------------------ +static SQInteger _rigidbody_applylocalforce(HSQUIRRELVM v) +{ + RigidBody *body = 0; + + sq_getinstanceup (v, 1, ( void ** ) &body, 0); + + if (body ) + { + int argc = sq_gettop (v ); + + if (argc == 2) + { + Vector3 force; + _getvectorvalues (v, 2, force.x, force.y, force.z); + body->applyLocalForce(force ); + } + else if (argc == 3) + { + Vector3 force, point; + _getvectorvalues (v, 2, force.x, force.y, force.z); + _getvectorvalues (v, 3, point.x, point.y, point.z); + body->applyLocalForce(force, point ); + } + } + + return 0; +} + +//------------------------------------------------------------------------------ +static SQInteger _rigidbody_applyglobalforce(HSQUIRRELVM v) +{ + RigidBody *body = 0; + + sq_getinstanceup (v, 1, ( void ** ) &body, 0); + + if (body ) + { + int argc = sq_gettop (v ); + + if (argc == 2) + { + Vector3 force; + _getvectorvalues (v, 2, force.x, force.y, force.z); + body->applyGlobalForce(force ); + } + else if (argc == 3) + { + Vector3 force, point; + _getvectorvalues (v, 2, force.x, force.y, force.z); + _getvectorvalues (v, 3, point.x, point.y, point.z); + body->applyGlobalForce(force, point ); + } + } + + return 0; +} + +//------------------------------------------------------------------------------ +static SQInteger _rigidbody_addcollisionmesh(HSQUIRRELVM v) +{ + SQInteger argc = sq_gettop (v ); + + if (argc < 3) + { + sq_pushinteger (v, 0); + return 1; + } + + RigidBody *body = 0; + + const SQChar *meshname = 0; + + SQFloat density = 1.0; + + sq_getinstanceup (v, 1, ( void ** ) &body, 0); + + sq_getstring (v, 2, &meshname ); + + sq_getfloat (v, 3, &density ); + + if (body ) + { + sq_pushinteger (v, body->addCollisionMesh(meshname, density ) ); + } + else + sq_pushinteger (v, 0); + + return 1; +} + +//------------------------------------------------------------------------------ +static SQInteger _rigidbody_setcollisionmeshposition(HSQUIRRELVM v) +{ + int argc = sq_gettop (v ); + + // need at least geom + vector + + if (argc < 3) + return 0; + + RigidBody *body = 0; + + sq_getinstanceup (v, 1, ( void ** ) &body, 0); + + if (body ) + { + SQInteger geom = 0; + sq_getinteger (v, 2, &geom ); + + Vector3 position; + + if (argc == 3) + { + _getvectorvalues (v, 3, position.x, position.y, position.z); + } + else if (argc == 5) + { + sq_getfloat (v, 3, &position.x); + sq_getfloat (v, 4, &position.y); + sq_getfloat (v, 5, &position.z); + } + + body->setCollisionMeshPosition(geom, position ); + } + + return 0; +} + +//------------------------------------------------------------------------------ +static SQInteger _rigidbody_disablecollisionmesh(HSQUIRRELVM v) +{ + int argc = sq_gettop (v ); + + // need at least geom + + if (argc < 2) + return 0; + + RigidBody *body = 0; + + sq_getinstanceup (v, 1, ( void ** ) &body, 0); + + if (body ) + { + SQInteger geom = 0; + sq_getinteger (v, 2, &geom ); + + body->disableCollisionMesh(geom ); + } + + return 0; +} + +//------------------------------------------------------------------------------ +static SQInteger _rigidbody_enablecollisionmesh(HSQUIRRELVM v) +{ + int argc = sq_gettop (v ); + + // need at least geom + + if (argc < 2) + return 0; + + RigidBody *body = 0; + + sq_getinstanceup (v, 1, ( void ** ) &body, 0); + + if (body ) + { + SQInteger geom = 0; + sq_getinteger (v, 2, &geom ); + + body->enableCollisionMesh(geom ); + } + + return 0; +} + +//------------------------------------------------------------------------------ +static SQInteger _rigidbody_setcollisionmeshrotation(HSQUIRRELVM v) +{ + int argc = sq_gettop (v ); + + // need at least geom + quaternion + + if (argc < 3) + return 0; + + RigidBody *body = 0; + + sq_getinstanceup (v, 1, ( void ** ) &body, 0); + + if (body ) + { + SQInteger geom = 0; + sq_getinteger (v, 2, &geom ); + + Quaternion rotation; + + if (argc == 3) + { + _getquaternionvalues (v, 3, rotation.w, rotation.x, rotation.y, + rotation.z); + } + else if (argc == 4) + { + Vector3 axis; + SQFloat angle; + _getvectorvalues (v, 3, axis.x, axis.y, axis.z); + sq_getfloat (v, 5, &angle ); + rotation = Quaternion (axis, angle ); + } + else if (argc == 5) + { + SQFloat h, a, b; + sq_getfloat (v, 3, &h ); + sq_getfloat (v, 4, &a ); + sq_getfloat (v, 5, &b ); + rotation = Quaternion (h, a, b ); + } + + body->setCollisionMeshRotation(geom, rotation ); + } + + return 0; +} + +//------------------------------------------------------------------------------ +static SQInteger _rigidbody_releasehook(SQUserPointer p, SQInteger size) +{ + if (gRigidBodySimulation.valid()) + { + RigidBody *self = ( RigidBody* ) p; + gRigidBodySimulation->deleteRigidBody(self ); + } + + return 1; +} + +//------------------------------------------------------------------------------ +static SQInteger _rigidbody_constructor(HSQUIRRELVM v) +{ + RigidBody *body = gRigidBodySimulation->createRigidBody(); + + if (SQ_FAILED (sq_setinstanceup (v, 1, body ) ) ) + { + gRigidBodySimulation->deleteRigidBody(body ); + return 0; + } + + HSQOBJECT obj; + + sq_getstackobj (v, 2, &obj ); + body->setCollisionHandler(obj ); + sq_setreleasehook (v, 1, _rigidbody_releasehook ); + return 0; +} + +//------------------------------------------------------------------------------ +void setupScriptSystem_RigidBody(ScriptSystem *scriptsystem, + RigidBodySimulation *simulation) +{ + gRigidBodySimulation = simulation; + + HSQUIRRELVM vm = scriptsystem->getVM(); + sq_pushroottable(vm ); + + sq_pushstring (vm, "RigidBody", -1); + if (SQ_SUCCEEDED (sq_newclass (vm, SQFalse ) ) ) + { + // register rigidbody functions + sq_pushstring (vm, "constructor", -1); + sq_newclosure (vm, _rigidbody_constructor, 0); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "setPosition", -1); + sq_newclosure (vm, _rigidbody_setposition, 0); + //sq_setparamscheck( vm, 2, "xx" ); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "getPosition", -1); + sq_newclosure (vm, _rigidbody_getposition, 0); + sq_setparamscheck (vm, 1, "x"); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "setRotation", -1); + sq_newclosure (vm, _rigidbody_setrotation, 0); + //sq_setparamscheck( vm, 2, "xx" ); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "getRotation", -1); + sq_newclosure (vm, _rigidbody_getrotation, 0); + sq_setparamscheck (vm, 1, "x"); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "getLinearVelocity", -1); + sq_newclosure (vm, _rigidbody_getlinearvelocity, 0); + sq_setparamscheck (vm, 1, "x"); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "setLinearVelocity", -1); + sq_newclosure (vm, _rigidbody_setlinearvelocity, 0); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "getAngularVelocity", -1); + sq_newclosure (vm, _rigidbody_getangularvelocity, 0); + sq_setparamscheck (vm, 1, "x"); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "applyLocalForce", -1); + sq_newclosure (vm, _rigidbody_applylocalforce, 0); + //sq_setparamscheck (vm, 3, "xxx"); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "applyGlobalForce", -1); + sq_newclosure (vm, _rigidbody_applyglobalforce, 0); + sq_setparamscheck (vm, 3, "xxx"); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "addCollisionMesh", -1); + sq_newclosure (vm, _rigidbody_addcollisionmesh, 0); + sq_setparamscheck (vm, 3, "xsn"); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "setCollisionMeshPosition", -1); + sq_newclosure (vm, _rigidbody_setcollisionmeshposition, 0); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "setCollisionMeshRotation", -1); + sq_newclosure (vm, _rigidbody_setcollisionmeshrotation, 0); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "getCollisionMeshPosition", -1); + sq_newclosure (vm, _rigidbody_setcollisionmeshposition, 0); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "getCollisionMeshRotation", -1); + sq_newclosure (vm, _rigidbody_setcollisionmeshrotation, 0); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "disableCollisionMesh", -1); + sq_newclosure (vm, _rigidbody_disablecollisionmesh, 0); + sq_newslot (vm, -3, false); + + sq_pushstring (vm, "enableCollisionMesh", -1); + sq_newclosure (vm, _rigidbody_enablecollisionmesh, 0); + sq_newslot (vm, -3, false); + + sq_newslot (vm, -3, false); + } + + sq_poptop (vm ); +} + +} // namespace BlueCore diff --git a/engine/ScriptSystem_RigidBody.h b/engine/ScriptSystem_RigidBody.h new file mode 100644 index 0000000..6d531a3 --- /dev/null +++ b/engine/ScriptSystem_RigidBody.h @@ -0,0 +1,12 @@ +#ifndef BLUECORE_SCRIPTING_RIGID_BODY_H +#define BLUECORE_SCRIPTING_RIGID_BODY_H + +#include "squirrel.h" +#include "RigidBodySimulation.h" + +namespace BlueCore +{ + void setupScriptSystem_RigidBody( ScriptSystem *scriptsystem, RigidBodySimulation *simulation); +} + +#endif diff --git a/engine/ShaderManager.cpp b/engine/ShaderManager.cpp new file mode 100644 index 0000000..53d80eb --- /dev/null +++ b/engine/ShaderManager.cpp @@ -0,0 +1,332 @@ +#include "ShaderManager.h" + +#include "Utilities/Log.h" +#include "Utilities/CfgParser.h" + +// library includes +#include "physfs.h" + +// system includes +#include +#include + +using namespace std; + +namespace BlueCore +{ +//------------------------------------------------------------------------------ +ShaderManager::ShaderManager(RenderWindow* renderwindow) : + _usingShaders(true), _RenderWindow(renderwindow) +{ + glewInit(); + + if (GLEW_ARB_fragment_shader == false) + _usingShaders = false; + + _RenderWindow->WindowCloseSignal.connect(this, + &ShaderManager::WindowCloseSlot); + + clog << ">>> ShaderManager constructed ..."<< endline; + if (_usingShaders) + clog << " using shaders"<< endline; + else + clog << " shaders not supported!"<< endlog; +} + +//------------------------------------------------------------------------------ +ShaderManager::~ShaderManager() +{ + clog << ">>> ShaderManager destructed ..."<< endlog; +} + +//------------------------------------------------------------------------------ +void ShaderManager::shutdown() +{ + clog << ">>> ShaderManager shutting down ..."<< endlog; + + ShaderProgramMap::iterator prog_iter; + for (prog_iter = shader_programs.begin(); prog_iter!= shader_programs.end(); prog_iter++) + { + glDeleteObjectARB(prog_iter->second); + } + shader_programs.clear(); + + VertexShaderMap::iterator vert_iter; + for (vert_iter = vertex_shader.begin(); vert_iter != vertex_shader.end(); vert_iter++) + { + glDeleteObjectARB(vert_iter->second); + } + vertex_shader.clear(); + + FragmentShaderMap::iterator frag_iter; + for (frag_iter = fragment_shader.begin(); frag_iter!= fragment_shader.end(); frag_iter++) + { + glDeleteObjectARB(frag_iter->second); + } + fragment_shader.clear(); +} + +//------------------------------------------------------------------------------ +void ShaderManager::WindowCloseSlot() +{ + shutdown(); +} + +//------------------------------------------------------------------------------ +bool ShaderManager::usingShaders() +{ + return _usingShaders; +} + +//------------------------------------------------------------------------------ +void printInfoLog(GLhandleARB obj) +{ + int infologLength = 0; + int charsWritten = 0; + char *infoLog; + + glGetObjectParameterivARB(obj, GL_OBJECT_INFO_LOG_LENGTH_ARB, + &infologLength); + + if (infologLength > 1) + { + infoLog = new char[infologLength+1]; + + glGetInfoLogARB(obj, infologLength, &charsWritten, infoLog); + + clog << " "<< infoLog << endline; + + delete [] infoLog; + } +} + +//------------------------------------------------------------------------------ +VertexShader ShaderManager::loadVertexShader(const string &name) +{ + if ( !_usingShaders) + return 0; + + // check if this mesh is already loaded + VertexShaderMap::const_iterator result; + + result = vertex_shader.find(name); + + if (result != vertex_shader.end() ) + { + return result->second; + } + + string filename = name + ".vert"; + + PHYSFS_file *file = PHYSFS_openRead(filename.c_str() ); + + if ( !file) + { + clog << "!!! VertexShader '"<< name << "' not found"<< endline; + return 0; + } + + char *buffer = new char[ PHYSFS_fileLength ( file ) ]; + + PHYSFS_read(file, buffer, 1, PHYSFS_fileLength(file) ); + buffer[ PHYSFS_fileLength ( file ) - 1] = 0; + + PHYSFS_close(file); + + VertexShader shader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); + + glShaderSourceARB(shader, 1, ( const GLcharARB** ) &buffer, NULL); + + delete [] buffer; + + glCompileShaderARB(shader); + + vertex_shader[name] = shader; + + clog << ">>> VertexShader '"<< name << "' loaded"<< endline; + + return shader; +} + +//------------------------------------------------------------------------------ +FragmentShader ShaderManager::loadFragmentShader(const string &name) +{ + if ( !_usingShaders) + return 0; + + // check if this mesh is already loaded + FragmentShaderMap::const_iterator result; + + result = fragment_shader.find(name); + + if (result != fragment_shader.end() ) + { + return result->second; + } + + string filename = name + ".frag"; + + PHYSFS_file *file = PHYSFS_openRead(filename.c_str() ); + + if ( !file) + { + clog << "!!! FragmentShader '"<< name << "' not found"<< endline; + return 0; + } + + char *buffer = new char[ PHYSFS_fileLength ( file ) ]; + + PHYSFS_read(file, buffer, 1, PHYSFS_fileLength(file) ); + buffer[ PHYSFS_fileLength ( file ) - 1] = 0; + + PHYSFS_close(file); + + FragmentShader shader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); + + glShaderSourceARB(shader, 1, ( const GLcharARB** ) &buffer, NULL); + + delete [] buffer; + + glCompileShaderARB(shader); + + printInfoLog(shader); + + fragment_shader[name] = shader; + + clog << ">>> FragmentShader '"<< name << "' loaded"<< endline; + + return shader; +} + +//------------------------------------------------------------------------------ +ShaderProgram ShaderManager::loadShaderProgram(const std::string &name) +{ + if ( !_usingShaders) + return 0; + + if (name.empty() ) + return 0; + + // check if this mesh is already loaded + ShaderProgramMap::const_iterator result; + + result = shader_programs.find(name); + + if (result != shader_programs.end() ) + { + return result->second; + } + + string filename = name + ".prog"; + + PHYSFS_file *file = PHYSFS_openRead(filename.c_str() ); + + if ( !file) + { + clog << "!!! ShaderProgramm '"<< name << "' not found"<< endline; + return 0; + } + + unsigned int length = PHYSFS_fileLength(file); + char *buffer = new char[length]; + + PHYSFS_read(file, buffer, 1, length); + PHYSFS_close(file); + + CfgParser cfg; + cfg.parse(buffer, length); + + delete [] buffer; + + ShaderProgram program = glCreateProgramObjectARB(); + + std::vector vertex_shaders; + if (cfg.getStrings("vertex_shaders", vertex_shaders) ) + { + for (unsigned int i = 0; i < vertex_shaders.size(); i++) + { + VertexShader shader = loadVertexShader(vertex_shaders[i]); + + if (shader != 0) + glAttachObjectARB(program, shader); + } + } + + std::vector fragment_shaders; + if (cfg.getStrings("fragment_shaders", fragment_shaders) ) + { + for (unsigned int i = 0; i < fragment_shaders.size(); i++) + { + FragmentShader shader = loadFragmentShader(fragment_shaders[i]); + + if (shader != 0) + glAttachObjectARB(program, shader); + } + } + + glLinkProgramARB(program); + + printInfoLog(program); + + shader_programs[name] = program; + + clog << ">>> ShaderProgramm '"<< name << "' loaded"<< endline; + + return program; + +} + +//------------------------------------------------------------------------------ +void ShaderManager::useShaderProgram(ShaderProgram shader_program) +{ + if ( !_usingShaders) + return; + + glUseProgramObjectARB(shader_program); +} + +//------------------------------------------------------------------------------ +void ShaderManager::useTexture(ShaderProgram shader_program, + unsigned int texture, const std::string &name) +{ + if ( !_usingShaders) + return; + + GLint location = glGetUniformLocationARB(shader_program, name.c_str() ); + + //if( location > 0 ) + glUniform1iARB(location, texture); +} +/* + //------------------------------------------------------------------------------ + void ShaderManager::useTangentBuffer ( + ShaderProgram shader_program, + TangentBuffer *tangent_buffer, + const std::string &name ) + { + if ( !_usingShaders || !tangent_buffer ) + return; + + GLuint location = glGetAttribLocationARB ( shader_program, name.c_str() ); + + glEnableVertexAttribArrayARB ( location ); + + tangent_buffer->bind(); + + glVertexAttribPointerARB ( location, 3, GL_FLOAT, 0, 0, 0 ); + } + + //------------------------------------------------------------------------------ + void ShaderManager::disableTangentBuffer ( + ShaderProgram shader_program, + const std::string &name ) + { + if ( !_usingShaders ) + return; + + GLuint location = glGetAttribLocationARB ( shader_program, name.c_str() ); + + glDisableVertexAttribArrayARB ( location ); + } + */ +} diff --git a/engine/ShaderManager.h b/engine/ShaderManager.h new file mode 100644 index 0000000..0d421aa --- /dev/null +++ b/engine/ShaderManager.h @@ -0,0 +1,66 @@ +#ifndef BLUECORE_SHADER_MANAGER_H +#define BLUECORE_SHADER_MANAGER_H + +#include +#include + +#include "GL/glew.h" + +#include "Utilities/Referenced.h" + +#include "TextureManager.h" +#include "RenderWindow.h" + +namespace BlueCore { + +typedef GLhandleARB ShaderProgram; +typedef GLhandleARB VertexShader; +typedef GLhandleARB FragmentShader; + +class ShaderManager : public Referenced, public sigslot::has_slots<> +{ + typedef std::map VertexShaderMap; + typedef std::map FragmentShaderMap; + typedef std::map ShaderProgramMap; + + VertexShaderMap vertex_shader; + FragmentShaderMap fragment_shader; + ShaderProgramMap shader_programs; + + VertexShader loadVertexShader(const std::string &name); + + FragmentShader loadFragmentShader(const std::string &name); + + bool _usingShaders; + + ref_ptr _RenderWindow; + + void shutdown(); + + void WindowCloseSlot(); + + public: + + ShaderManager (RenderWindow* renderwindow); + + virtual ~ShaderManager(); + + bool usingShaders(); + + ShaderProgram loadShaderProgram(const std::string &name); + + void useShaderProgram(ShaderProgram shader_program); + + void useTexture(ShaderProgram shader_program, unsigned int texture, + const std::string &name); +/* + void useTangentBuffer(ShaderProgram shader_program, + TangentBuffer *tangent_buffer, const std::string &name); + + void disableTangentBuffer(ShaderProgram shader_program, + const std::string &name);*/ +}; + +} // namespace BlueCore + +#endif diff --git a/engine/TextureImage.cpp b/engine/TextureImage.cpp new file mode 100644 index 0000000..f8c16a3 --- /dev/null +++ b/engine/TextureImage.cpp @@ -0,0 +1,146 @@ +#include "TextureImage.h" +#include "RenderDevice.h" +#include "Utilities/Log.h" + +#include + +#if defined(max) +#define std::max max +#else +#define max(a,b) ( a > b ? a : b ) +#endif + +namespace BlueCore +{ + +//------------------------------------------------------------------------------ +TextureImage::TextureImage(RenderDevice* device, Texture *texture, float ax, + float ay, float bx, float by) : + _Texture(texture), _Device(device) +{ + unsigned int texture_width = _Texture->getWidth(); + unsigned int texture_height = _Texture->getHeight(); + + // greater 0, and values < 1 are fractions of texture dimemsions + float e_ax= max( ax <= 1.0 ? texture_width * ax : ax, 0.0f ); + float e_ay= max( ay <= 1.0 ? texture_height * ay : ay, 0.0f ); + float e_bx= max( bx <= 1.0 ? texture_width * bx : bx, 0.0f ); + float e_by= max( by <= 1.0 ? texture_height * by : by, 0.0f ); + + float width = e_bx - e_ax; + float height = e_by - e_ay; + + // fractional values + float au= max( ax > 1.0 ? ax / texture_width : ax, 0.0f ); + float av= max( ay > 1.0 ? ay / texture_height : ay, 0.0f ); + float bu= max( bx > 1.0 ? bx / texture_width : bx, 0.0f ); + float bv= max( by > 1.0 ? by / texture_height : by, 0.0f ); + + // bottom left + _Vertices[0] = 0.0; + _Vertices[1] = 0.0; + _TexCoords[0] = au; + _TexCoords[1] = 1.0-bv; + + // top left + _Vertices[2] = 0.0; + _Vertices[3] = height-1; + _TexCoords[2] = au; + _TexCoords[3] = 1.0-av; + + // top right + _Vertices[4] = width-1; + _Vertices[5] = height-1; + _TexCoords[4] = bu; + _TexCoords[5] = 1.0-av; + + // bottom right + _Vertices[6] = width-1; + _Vertices[7] = 0.0; + _TexCoords[6] = bu; + _TexCoords[7] = 1.0-bv; + + _Width = (unsigned int)width; + _Height = (unsigned int)height; + + clog << ">>> TextureImage constructed..."<< endlog; +} + +//------------------------------------------------------------------------------ +TextureImage::~TextureImage() +{ + clog << ">>> TextureImage destructed..."<< endlog; +} + +//------------------------------------------------------------------------------ +void TextureImage::draw(float x, float y, int halign, int valign, float r) +{ + if (!_Device.valid()) + return; + + _Device->begin2D(); + + if (x < 0.0) + x += _Device->getViewportWidth(); + else if (x < 1.0) + x *= _Device->getViewportWidth(); + + if (halign == -1) + x -= _Width; + else if (halign == 0) + x -= _Width / 2; + + if (y < 0.0) + y += _Device->getViewportHeight(); + else if (y < 1.0) + y *= _Device->getViewportHeight(); + + if (valign == -1) + y -= _Height; + else if (valign == 0) + y -= _Height / 2; + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, _Texture->getId() ); + + glPushMatrix(); + glLoadIdentity(); + glTranslatef(x, y, 0); + if (r != 0.) + glRotatef(r, 0.0, 0.0, 1.0); + + glBegin(GL_QUADS); + + glTexCoord2f(_TexCoords[2], _TexCoords[3]); + glVertex2f(_Vertices[2], _Vertices[3]); + + glTexCoord2f(_TexCoords[0], _TexCoords[1]); + glVertex2f(_Vertices[0], _Vertices[1]); + + glTexCoord2f(_TexCoords[6], _TexCoords[7]); + glVertex2f(_Vertices[6], _Vertices[7]); + + glTexCoord2f(_TexCoords[4], _TexCoords[5]); + glVertex2f(_Vertices[4], _Vertices[5]); + + glEnd(); + + glPopMatrix(); + + _Device->end2D(); + +} + +//------------------------------------------------------------------------------ +unsigned int TextureImage::getWidth() +{ + return _Width; +} + +//------------------------------------------------------------------------------ +unsigned int TextureImage::getHeight() +{ + return _Height; +} + +} // namespace BlueCore diff --git a/engine/TextureImage.h b/engine/TextureImage.h new file mode 100644 index 0000000..c230306 --- /dev/null +++ b/engine/TextureImage.h @@ -0,0 +1,26 @@ +#ifndef BLUECORE_TEXTURE_IMAGE_H +#define BLUECORE_TEXTURE_IMAGE_H + +#include "TextureManager.h" +#include "RenderDevice.h" + +namespace BlueCore +{ + class TextureImage : public Referenced + { + ref_ptr _Texture; + ref_ptr _Device; + float _Vertices[3 * 4]; + float _TexCoords[2 * 4]; + unsigned int _Width, _Height; + + public: + TextureImage (RenderDevice* device, Texture *texture, float ax, float ay, float bx, float by ); + ~TextureImage(); + void draw (float x, float y, int halign = 0, int valign = 0, float r = 0.0 ); + unsigned int getWidth(); + unsigned int getHeight(); + }; +} + +#endif /*IMAGE_H_*/ diff --git a/engine/TextureManager.cpp b/engine/TextureManager.cpp new file mode 100644 index 0000000..764cb38 --- /dev/null +++ b/engine/TextureManager.cpp @@ -0,0 +1,444 @@ +#include "TextureManager.h" + +#include "Utilities/Log.h" +#include "Utilities/format.h" + +// library includes +//#include "png.h" +#include "physfs.h" +#include "corona.h" +#include "GL/glew.h" + +// system includes +#include +#include + +using namespace std; + +namespace BlueCore +{ + +//---------------------------------------------------------------------------------- +TextureManager::TextureManager() +{ + _maxAnisotopy = 128.0; + _maxTextureSize = 1024 * 16; + _lodDrop = 0; + _textureCompression = true; + + glewInit(); + + if (GLEW_ARB_texture_compression == false) + _textureCompression = false; + + clog << ">>> TextureManager constructed ..."<< endline; + if (_textureCompression) + clog << " using texture compression"<< endline; + else + clog << " texture compression not supported!"<< endline; + +} + +//------------------------------------------------------------------------------ +TextureManager::~TextureManager() +{ + clog << ">>> TextureManager destructed ..."<< endline; +} + +Texture::~Texture() +{ + glDeleteTextures( 1, &_Id); + clog << ">>> Texture destructed ..."<< endline; +} +/* + //------------------------------------------------------------------------------ + void TextureManager::initializeSingleton() + { + + XmlConfig config("config.xml"); + + _lodDrop = 0; + + config.getBool("TextureManager", "TextureCompression", _textureCompression, + true); + + if (GLEW_ARB_texture_compression == false) + _textureCompression = false; + + if (_textureCompression ) + clog << " using texture compression"<< endline; + else + clog << " texture compression not supported!"<< endline; + + config.getUInt("TextureManager", "MaxTextureSize", _maxTextureSize, 8192); + + GLint maxTextureSize; + + glGetIntegerv (GL_MAX_TEXTURE_SIZE, &maxTextureSize ); + + if (_maxTextureSize > (unsigned int)maxTextureSize ) + _maxTextureSize = maxTextureSize; + + clog << " maximum texture size: "<< _maxTextureSize << endline; + + config.getFloat("TextureManager", "MaxAnisotopy", _maxAnisotopy, 128.0); + + float maxAnisotopy = 0.0; + + if (GLEW_EXT_texture_filter_anisotropic ) + glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotopy ); + + if (_maxAnisotopy > maxAnisotopy ) + _maxAnisotopy = maxAnisotopy; + + if (_maxAnisotopy > 1.0) + clog << " using anisotopic texture filtering, level: "<< _maxAnisotopy + << endlog; + else + clog << " not using anisotopic texture filtering."<< endlog; + } + //------------------------------------------------------------------------------ + void TextureManager::shutdownSingleton() + { + clog << ">>> shutdown TextureManager..."<< endline; + + clearTextures(); + } + */ + +//------------------------------------------------------------------------------ +bool TextureManager::saveToCache(const std::string &filename, int levels) +{ + unsigned char *data = 0; + GLint internalformat, compressed_size = 0, width = 0, height = 0, level = 0; + + PHYSFS_file *file = PHYSFS_openWrite(filename.c_str() ); + + if ( !file) + { + return false; + } + + level = 0; + + while ( !PHYSFS_eof(file) ) + { + glGetTexLevelParameteriv(GL_TEXTURE_2D, level, + GL_TEXTURE_INTERNAL_FORMAT, &internalformat); + + glGetTexLevelParameteriv(GL_TEXTURE_2D, level, + GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB, &compressed_size); + + if (compressed_size == 0) + break; + + glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_WIDTH, &width); + + if (width == 0) + break; + + glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_HEIGHT, + &height); + + if (height == 0) + break; + + glGetTexLevelParameteriv(GL_TEXTURE_2D, level, + GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB, &compressed_size); + + data = new unsigned char[ compressed_size ]; + + glGetCompressedTexImageARB(GL_TEXTURE_2D, level, data); + + PHYSFS_write(file, &internalformat, sizeof (internalformat ), 1); + PHYSFS_write(file, &level, sizeof (level ), 1); + PHYSFS_write(file, &width, sizeof (width ), 1); + PHYSFS_write(file, &height, sizeof (height ), 1); + PHYSFS_write(file, &compressed_size, sizeof (compressed_size ), 1); + PHYSFS_write(file, data, compressed_size, 1); + + delete [] data; + + level += 1; + + if ( (width == 1 ) && (height == 1 )) + break; + + if (level >= levels) + break; + } + + PHYSFS_close(file); + + return true; +} + +//------------------------------------------------------------------------------ +bool TextureManager::loadFromCache(const std::string &filename, + unsigned int &max_width, unsigned int &max_height) +{ + unsigned char *data = 0; + GLuint internalformat, compressed_size, width, height, level; + + max_width = 0; + max_height = 0; + + PHYSFS_file *file = PHYSFS_openRead(filename.c_str() ); + + if ( !file) + { + return false; + } + + while ( !PHYSFS_eof(file) ) + { + PHYSFS_read(file, &internalformat, sizeof (internalformat ), 1); + PHYSFS_read(file, &level, sizeof (level ), 1); + PHYSFS_read(file, &width, sizeof (width ), 1); + PHYSFS_read(file, &height, sizeof (height ), 1); + PHYSFS_read(file, &compressed_size, sizeof (compressed_size ), 1); + + if ( !data) + data = new unsigned char[ compressed_size ]; + + PHYSFS_read(file, data, compressed_size, 1); + + glCompressedTexImage2DARB(GL_TEXTURE_2D, level, internalformat, width, + height, 0, compressed_size, data); + + max_width = max(width, max_width); + max_height = max(height, max_height); + + if (width == 1 && height == 1) + break; + } + + delete [] data; + + PHYSFS_close(file); + + return true; +} + +//------------------------------------------------------------------------------ +Texture *TextureManager::loadTexture(const std::string &filename, + int mipmapLevel, int compressionLevel) +{ + if (filename.empty() ) + return 0; + + std::string cachename = filename; + cachename += "_"; + cachename += format("%d", mipmapLevel); + cachename += "_"; + cachename += format("%d", compressionLevel); + cachename += ".cache"; + + TextureMap::const_iterator result; + result = textures.find(filename); + + if (result != textures.end() ) + { + return result->second; + } + + GLint UnpackAlignment; + + // Set unpack alignment to one byte + glGetIntegerv(GL_UNPACK_ALIGNMENT, &UnpackAlignment); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + unsigned int id = 0; + glGenTextures( 1, &id); + glBindTexture(GL_TEXTURE_2D, id); + + if (mipmapLevel == 0) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + else + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + GL_LINEAR_MIPMAP_NEAREST); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if (_maxAnisotopy > 0.0) + { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, + ( float ) _maxAnisotopy ); + } + + Texture *t = 0; + unsigned int width = 0, height = 0; + + // check for cached texture + if (_textureCompression && (compressionLevel > 0)) + { + PHYSFS_sint64 mod_file = PHYSFS_getLastModTime(filename.c_str() ); + PHYSFS_sint64 mod_config = PHYSFS_getLastModTime("config.xml"); + + PHYSFS_sint64 mod_cache = PHYSFS_getLastModTime(cachename.c_str() ); + + if ( (mod_cache > mod_file) && (mod_cache > mod_config)) + { + if (loadFromCache(cachename, width, height) ) + { + glPixelStorei(GL_UNPACK_ALIGNMENT, UnpackAlignment); + clog << ">>> Texture '" << filename << "' loaded from cache (" + << width << ", " << height << ")." << endline; + t = new Texture( id, width, height ); + textures[filename] = t; + return t; + } + } + } + + //clog << ">>> Texture '" << filename << "': loading from file..." << endline; + corona::Image *image = corona::OpenImage(filename.c_str(), + corona::PF_R8G8B8A8); + if (image) + { + int level = 0; + unsigned int width_scaled = image->getWidth(); + unsigned int height_scaled = image->getHeight(); + + corona::FlipImage(image, corona::CA_X); + // create mipmaps + while ( 1) + { + if ( (width_scaled <= _maxTextureSize ) && (height_scaled + <= _maxTextureSize )) + { + width = max(width_scaled, width); + height = max(height_scaled, height); + + if (_textureCompression && (compressionLevel > 0)) + { + glHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST); + + GLuint compression = GL_COMPRESSED_RGBA_ARB; + if (GLEW_EXT_texture_compression_s3tc) + { + if (compressionLevel == 1) + compression = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + else if (compressionLevel == 2) + compression = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + else + compression = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + } + + glTexImage2D(GL_TEXTURE_2D, level, compression, + width_scaled, height_scaled, 0, GL_RGBA, + GL_UNSIGNED_BYTE, ( void* ) image->getPixels() ); + } + else + { + glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, width_scaled, + height_scaled, 0, GL_RGBA, GL_UNSIGNED_BYTE, + ( void* ) image->getPixels() ); + } + + level += 1; + } + + if ( (width_scaled == 1 ) && (height_scaled == 1 )) + break; + + if ( (mipmapLevel >= 0 ) && (level >= mipmapLevel )) + break; + + // rescale image + int halfwidth = width_scaled > 1 ? width_scaled / 2 : 1; + int halfheight = height_scaled > 1 ? height_scaled / 2 : 1; + int idx1 = width_scaled * 4; + int idx2 = (width_scaled + 1 ) * 4; + unsigned char *dst = (unsigned char *)image->getPixels(); + unsigned char *src = (unsigned char *)image->getPixels(); + + for (int y = 0; y < halfheight; y++) + { + for (int x = 0; x < halfwidth; x++) + { + for (int k = 0; k < 4; k ++) + { + *dst ++ = ( GLubyte ) ( ( ( int ) *src + ( int ) src[4] + + ( int ) src[idx1] + ( int ) src[idx2] + 2 ) + >> 2 ); + src ++; + } + + src += 4; + } + + src += 4 * width_scaled; + } + + width_scaled = halfheight; + height_scaled = halfwidth; + } + + delete image; + + if (_textureCompression && (compressionLevel > 0)) + saveToCache(cachename, level); + + t = new Texture( id, width, height ); + clog << ">>> Texture '" << filename << "': loaded from file" << endline; + } + else + { + glDeleteTextures( 1, &id); + t = new Texture( 0, 0, 0 ); + clog << "!!! Texture '" << filename << "' not found!" << endline; + } + + glPixelStorei(GL_UNPACK_ALIGNMENT, UnpackAlignment); + + textures[filename] = t; + + return t; +} +/* + //------------------------------------------------------------------------------ + void TextureManager::releaseTexture ( const Texture *texture ) + { + Texture *t = (Texture *)texture; + + if( t->_RefCount > 1 ) + t->_RefCount--; + else if( t->_RefCount == 1 ) + { + glDeleteTextures ( 1, &t->_Id ); + delete t; + + TextureMap::iterator i; + for( i = textures.begin(); i != textures.end(); i++ ) + { + if( (*i).second == t ) + { + textures.erase( i ); + break; + } + } + } + + } + */ +//------------------------------------------------------------------------------ +void TextureManager::TextureDestroySlot(Referenced *referenced) +{ + TextureMap::iterator i; + + for (i = textures.begin(); i != textures.end(); i++) + { + Texture *t = (*i).second; + if (t == (Texture*)referenced) + { + textures.erase(i); + break; + } + } +} +} diff --git a/engine/TextureManager.h b/engine/TextureManager.h new file mode 100644 index 0000000..bc3c316 --- /dev/null +++ b/engine/TextureManager.h @@ -0,0 +1,92 @@ +//------------------------------------------------------------------------------ +// Author: Gero Mueller +// Copyright: (c) 2006 Gero Mueller +// License: MIT License +//------------------------------------------------------------------------------ + +#ifndef BLUECORE_TEXTURE_MANAGER_H +#define BLUECORE_TEXTURE_MANAGER_H + +// system includes +#include +#include + +// project includes +#include "Utilities/Referenced.h" + +namespace BlueCore +{ + // forward declaration + class TextureManager; + + class Texture : public Referenced + { + unsigned int _Id; + unsigned int _Width, _Height; + + ~Texture(); + + public: + + Texture( unsigned int id, unsigned int width, unsigned int height ) : + _Id( id ), + _Width( width ), + _Height( height ) + { + } + + unsigned int getWidth() const + { + return _Width; + } + + unsigned int getHeight() const + { + return _Height; + } + + unsigned int getId() const + { + return _Id; + } + }; + + class TextureManager : public Referenced + { + + private: + + typedef std::map TextureMap; + + TextureMap textures; + + float _maxAnisotopy; + unsigned int _maxTextureSize; + unsigned int _lodDrop; + bool _textureCompression; + + void clearTextures(); + + bool saveToCache ( const std::string &name, int levels ); + + bool loadFromCache ( + const std::string &filename, + unsigned int &max_width, + unsigned int &max_height ); + + ~TextureManager(); + + void TextureDestroySlot(Referenced *referenced); + + public: + + TextureManager(); + + Texture *loadTexture ( + const std::string &filename, + int mipmapLevel = -1, + int compressionLevel = 1 ); + }; +} + +#endif diff --git a/engine/Utilities/Activated.h b/engine/Utilities/Activated.h new file mode 100644 index 0000000..705dce4 --- /dev/null +++ b/engine/Utilities/Activated.h @@ -0,0 +1,87 @@ +#ifndef BLUECORE_ACTIVATED_H +#define BLUECORE_ACTIVATED_H + +namespace BlueCore +{ +class Activated +{ + private: + + bool _Active; + + public: + + /** + * Default constructor. + * inital state is active + */ + inline Activated() + { + _Active = true; + } + + /** + * Constructor. + * @param set inital state + */ + inline Activated(bool state) : + _Active(state ) + { + } + + /** + * Destructor. + */ + virtual inline ~Activated() + { + } + + /** + * Activate the object. + */ + inline void activate() + { + _Active = true; + onActivation(); + } + + virtual void onActivation() + { + } + + /** + * Deactivate the object. + */ + inline void deactivate() + { + _Active = false; + onDeactivation(); + } + + virtual void onDeactivation() + { + } + + /** + * toggles the object's state + */ + inline void toggleActive() + { + if (_Active ) + deactivate(); + else + activate(); + } + + /** + * returns the object's state + */ + inline bool isActive() + { + return _Active; + } +}; + +} // namespace BlueCore + +#endif // BLUECORE_ACTIVATED_H diff --git a/engine/Utilities/Buffer.h b/engine/Utilities/Buffer.h new file mode 100644 index 0000000..82a606b --- /dev/null +++ b/engine/Utilities/Buffer.h @@ -0,0 +1,151 @@ +#ifndef BLUECORE_BUFFER_H +#define BLUECORE_BUFFER_H + +#include + +namespace BlueCore +{ + template + class Buffer + { + public: + + typedef T type; + + private: + + type *_buffer; + unsigned int _count; + + public: + + /** + * default constructor + */ + Buffer() : + _buffer( 0 ), + _count( 0 ) + { + } + + /** + * constructor with initial size + */ + Buffer( unsigned int count ) : + _buffer( 0 ), + _count( 0 ) + { + create( count ); + } + + /** + * destructor + */ + virtual ~Buffer() + { + destroy(); + } + + /** + * create the buffer + */ + bool create( unsigned int count ) + { + destroy(); + + _buffer = new type[count]; + + if( _buffer ) + { + _count = count; + return true; + } + else + { + return false; + } + } + + /** + * destroy the buffer + */ + void destroy() + { + delete [] _buffer; + _buffer = 0; + _count = 0; + } + + void resize( unsigned int new_count ) + { + type *new_buffer = new type[new_count]; + + for( unsigned int i = 0; i<_count; i++ ) + new_buffer[i] = _buffer[i]; + + destroy(); + + _buffer = new_buffer; + _count = new_count; + + } + + /** + * returns the item count + */ + unsigned int count() const + { + return _count; + } + + /** + * returns the buffer size in bytes + */ + unsigned int size() const + { + return _count * sizeof(type); + } + + /** + * return a pointer to the data + */ + type *data() + { + return _buffer; + } + + /** + * return a pointer to the(const) data + */ + const type *const_data() const + { + return _buffer; + } + + /** + * array operator + */ + type &operator[](const unsigned int index) + { + if( index >= _count ) + throw std::range_error("Buffer::[] index_out_of_bound!"); + return _buffer[index]; + } + + type &item(const unsigned int index) + { + if( index >= _count ) + throw std::range_error("Buffer::[] index_out_of_bound!"); + return _buffer[index]; + } + + const type &const_item(const unsigned int index) const + { + if( index >= _count ) + throw std::range_error("Buffer::[] index_out_of_bound!"); + return _buffer[index]; + } + }; +} + +#endif diff --git a/engine/Utilities/CfgParser.cpp b/engine/Utilities/CfgParser.cpp new file mode 100644 index 0000000..f9757aa --- /dev/null +++ b/engine/Utilities/CfgParser.cpp @@ -0,0 +1,198 @@ +#include "CfgParser.h" +#include "StringUtilities.h" + +#include + +using namespace std; + +namespace BlueCore { + +void CfgParser::parseFile (const std::string& filename) +{ + ifstream file (filename.c_str(), ios::in); + std::string line; + + while (file.good()) + { + getline (file, line); + parseLine (line); + } +} + + + +void CfgParser::parseLine (const std::string& line) +{ + std::string::size_type i (line.find_first_of ('=')); + + if (i == std::string::npos) + return; + + std::string key = trim (string (line, 0, i-1)); + std::string value = trim (string (line, i+1)); + + _Pairs[key] = value; +} + + + +void CfgParser::parse (const char* buffer, unsigned int length) +{ + const char* ptr = buffer; + const char* end = buffer + length; + unsigned int l = 0; + + while (ptr < end) + { + // find end of line + while (ptr[l] != '\n') + { + l++; + if (ptr + l >= end) + break; + } + + parseLine (string(ptr, l)); + ptr += l+1; + l = 0; + } +} + + + +double CfgParser::get (const std::string& key, double defaultValue) +{ + double value; + + if (getDouble (key, value) == false) + return defaultValue; + else + return value; +} + + + +bool CfgParser::getDouble (const std::string& key, double& value) +{ + std::map::const_iterator result; + result = _Pairs.find (key); + + if (result != _Pairs.end()) + { + value = atof (result->second.c_str()); + return true; + } + else + return false; +} + + + +int CfgParser::get (const std::string& key, int defaultValue) +{ + int value; + + if (getInteger (key, value) == false) + return defaultValue; + else + return value; +} + + + +bool CfgParser::getInteger (const std::string& key, int& value) +{ + std::map::const_iterator result; + result = _Pairs.find (key); + + if (result != _Pairs.end()) + { + value = atoi (result->second.c_str()); + return true; + } + else + return false; +} + + +bool CfgParser::get (const std::string& key, bool defaultValue) +{ + bool value; + + if (getBoolean (key, value) == false) + return defaultValue; + else + return value; +} + + + +bool CfgParser::getBoolean (const std::string& key, bool& value) +{ + std::map::const_iterator result; + result = _Pairs.find (key); + + if (result != _Pairs.end()) + { + value = false; + + if (result->second == "true") + value = true; + else if (result->second == "1") + value = true; + else if (result->second == "yes") + value = true; + + return true; + } + else + return false; +} + + + +std::string CfgParser::get (const std::string& key, std::string defaultValue) +{ + std::string value; + + if (getString (key, value) == false) + return defaultValue; + else + return value; +} + + + +bool CfgParser::getString (const std::string& key, std::string& value) +{ + std::map::const_iterator result; + result = _Pairs.find (key); + + if (result != _Pairs.end()) + { + value = result->second; + return true; + } + else + return false; +} + + + +bool CfgParser::getStrings (const std::string& key, std::vector& strings) +{ + std::map::const_iterator result; + result = _Pairs.find (key); + + if (result != _Pairs.end()) + { + explode (result->second, strings, true, ","); + return true; + } + else + return false; +} + + +} // namespace BlueCore + diff --git a/engine/Utilities/CfgParser.h b/engine/Utilities/CfgParser.h new file mode 100644 index 0000000..dcf35df --- /dev/null +++ b/engine/Utilities/CfgParser.h @@ -0,0 +1,39 @@ +#ifndef BLUECORE_UTILITIES_CFGPARSER +#define BLUECORE_UTILITIES_CFGPARSER + +#include +#include +#include + +namespace BlueCore { + +class CfgParser +{ +public: + void parseFile (const std::string& filename); + void parseLine (const std::string& line); + void parse (const char* buffer, unsigned int length); + + double get (const std::string& key, double value = 0.0); + bool getDouble (const std::string& key, double& defaultValue); + + int get (const std::string& key, int value = 0); + bool getInteger (const std::string& key, int& defaultValue); + + bool get (const std::string& key, bool value = false); + bool getBoolean (const std::string& key, bool& defaultValue); + + std::string get (const std::string& key, std::string value = ""); + bool getString (const std::string& key, std::string& defaultValue); + + bool getStrings (const std::string& key, std::vector& strings); + +private: + + std::map _Pairs; +}; + +} // namespace BlueCore + +#endif + diff --git a/engine/Utilities/IniParser.h b/engine/Utilities/IniParser.h new file mode 100644 index 0000000..5fa21cb --- /dev/null +++ b/engine/Utilities/IniParser.h @@ -0,0 +1,11 @@ +#include +#include + +class IniParser +{ + std::map _Pairs; + + void parse (const std::string &text) + { + } +} diff --git a/engine/Utilities/Kernel.cpp b/engine/Utilities/Kernel.cpp new file mode 100644 index 0000000..b9662e9 --- /dev/null +++ b/engine/Utilities/Kernel.cpp @@ -0,0 +1,82 @@ +#include "Kernel.h" + +namespace BlueCore +{ + +Kernel::Kernel() : + _Changed(true), _Clear(false) +{ +} + +void Kernel::addTask(KernelTask* task, int priority) +{ + _AddedTasks.push_back(KernelTaskContainer (task, priority)); + _Changed = true; +} + +void Kernel::removeTask(KernelTask* task) +{ + _RemovedTasks.push_back(task ); + _Changed = true; +} + +void Kernel::removeAllTasks() +{ + _Clear = true; +} + +void Kernel::setTaskPriority(KernelTask* task, int priority) +{ + std::list::iterator i; + for (i = _Tasks.begin(); i != _Tasks.end(); i++) + { + if ((*i)._Task.get() == task) + { + (*i)._Priority = priority; + _Changed = true; + return; + } + } + +} + +bool Kernel::tick() +{ + if (_Changed) + { + std::list::iterator i; + for (i = _AddedTasks.begin(); i != _AddedTasks.end(); i++) + { + _Tasks.push_back( *i ); + } + + _AddedTasks.clear(); + _RemovedTasks.clear(); + _Tasks.sort(); + _Changed = false; + } + + std::list::iterator i = _Tasks.begin(); + std::list::iterator end = _Tasks.end(); + + if (i == end) + return false; + + while (i != end) + { + if ((*i)._Task.valid()) + (*i)._Task->tick(); + i++; + } + + if (_Clear) + { + _Tasks.clear(); + _Clear = false; + } + + return true; +} + +} // namespace BlueCore + diff --git a/engine/Utilities/Kernel.h b/engine/Utilities/Kernel.h new file mode 100644 index 0000000..eb4a064 --- /dev/null +++ b/engine/Utilities/Kernel.h @@ -0,0 +1,83 @@ +#ifndef KERNEL_H_ +#define KERNEL_H_ + +#include "Referenced.h" + +#include +#include +#include +#include + +namespace BlueCore +{ + +class Kernel; + +//----------------------------------------------------------------------------- +class KernelTask : public Referenced +{ + protected: + + ~KernelTask() + { + } + + public: + + KernelTask() + { + } + + virtual void tick() + { + } +}; + +//----------------------------------------------------------------------------- +class Kernel : public Referenced +{ + private: + + struct KernelTaskContainer + { + ref_ptr _Task; + int _Priority; + + KernelTaskContainer(KernelTask* task, int priority) : + _Task(task), _Priority(priority) + { + } + + bool operator <(const KernelTaskContainer& other) const + { + return (_Priority > other._Priority); + } + }; + + std::list _Tasks; + std::list _AddedTasks; + std::list _RemovedTasks; + bool _Changed; + bool _Clear; + + protected: + + ~Kernel() + { + } + + public: + + Kernel(); + + void addTask(KernelTask* task, int priority); + void removeTask(KernelTask* task); + void removeAllTasks(); + void setTaskPriority(KernelTask* task, int priority); + bool tick(); +}; + +} // namespace BlueCore + +#endif /*KERNEL_H_*/ + diff --git a/engine/Utilities/Log.cpp b/engine/Utilities/Log.cpp new file mode 100644 index 0000000..fc0a5ad --- /dev/null +++ b/engine/Utilities/Log.cpp @@ -0,0 +1,75 @@ +#include "Log.h" +#include + +namespace BlueCore +{ + //-------------------------------------------------------------------------- + Log::Log() : + _LogFilename( "log.txt" ) + { + reset(); + } + + //-------------------------------------------------------------------------- + void Log::reset() + { + std::ifstream testfile( _LogFilename.c_str() ); + if( testfile.is_open() ) + { + testfile.close(); + _LogFile.open( _LogFilename.c_str(), std::ios::trunc ); + _LogFile.close(); + } + } + + //-------------------------------------------------------------------------- + void Log::setFilename( const std::string &name ) + { + _LogFilename = name; + reset(); + } + + //-------------------------------------------------------------------------- + Log &Log::operator << (const EndLine &e) + { + _LogFile << '\n'; + std::cout << '\n'; + return *this; + } + + //-------------------------------------------------------------------------- + Log &Log::operator << (const EndLog &e) + { + _LogFile << '\n'; + _LogFile.close(); + std::cout << '\n'; + std::cout.flush(); + return *this; + } + + //-------------------------------------------------------------------------- + Log &Log::operator << (const Time &t) + { + std::time_t rawtime; + std::time ( &rawtime ); + char *tc = std::ctime( &rawtime ); + _LogFile << tc; + std::cout << tc; + return *this; + } + + //-------------------------------------------------------------------------- + Log &Log::operator << (const Flush &f) + { + _LogFile.close(); + std::cout.flush(); + return *this; + } + + //-------------------------------------------------------------------------- + Log::EndLine endline; + Log::EndLog endlog; + Log::Time time; + Log::Flush flush; + Log clog; +} diff --git a/engine/Utilities/Log.h b/engine/Utilities/Log.h new file mode 100644 index 0000000..62d13c4 --- /dev/null +++ b/engine/Utilities/Log.h @@ -0,0 +1,60 @@ +#ifndef BLUECORE_LOG_H +#define BLUECORE_LOG_H + +#include +#include + +namespace BlueCore +{ + class Log + { + std::string _LogFilename; + std::ofstream _LogFile; + + public: + class EndLine + { + }; + + class EndLog + { + }; + + class Time + { + }; + + class Flush + { + }; + + Log(); + + void setFilename( const std::string &name ); + void reset(); + + template + Log &operator << (const T &v) + { + if( !_LogFile.is_open() ) + _LogFile.open(_LogFilename.c_str(), std::ios::app); + _LogFile << v; + std::cout << v; + return *this; + } + + Log &operator << (const EndLine &e); + Log &operator << (const EndLog &e); + Log &operator << (const Time &t); + Log &operator << (const Flush &f); + }; + + extern Log::EndLine endline; + extern Log::EndLog endlog; + extern Log::Time time; + extern Log::Flush flush; + extern Log clog; + +} // namespace bc + +#endif // BLUECORE_LOG_H diff --git a/engine/Utilities/MersenneTwister.h b/engine/Utilities/MersenneTwister.h new file mode 100644 index 0000000..16e6458 --- /dev/null +++ b/engine/Utilities/MersenneTwister.h @@ -0,0 +1,423 @@ +// MersenneTwister.h +// Mersenne Twister random number generator -- a C++ class MTRand +// Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus +// Richard J. Wagner v1.0 15 May 2003 rjwagner@writeme.com + +// The Mersenne Twister is an algorithm for generating random numbers. It +// was designed with consideration of the flaws in various other generators. +// The period, 2^19937-1, and the order of equidistribution, 623 dimensions, +// are far greater. The generator is also fast; it avoids multiplication and +// division, and it benefits from caches and pipelines. For more information +// see the inventors' web page at http://www.math.keio.ac.jp/~matumoto/emt.html + +// Reference +// M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally +// Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on +// Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30. + +// Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, +// Copyright (C) 2000 - 2003, Richard J. Wagner +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The names of its contributors may not be used to endorse or promote +// products derived from this software without specific prior written +// permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The original code included the following notice: +// +// When you use this, send an email to: matumoto@math.keio.ac.jp +// with an appropriate reference to your work. +// +// It would be nice to CC: rjwagner@writeme.com and Cokus@math.washington.edu +// when you write. + +#ifndef MERSENNETWISTER_H +#define MERSENNETWISTER_H + +// Not thread safe (unless auto-initialization is avoided and each thread has +// its own MTRand object) + +#include +#include +#include +#include +#include + +class MTRand { +// Data +public: + typedef unsigned long uint32; // unsigned integer type, at least 32 bits + + enum { N = 624 }; // length of state vector + enum { SAVE = N + 1 }; // length of array for save() + +protected: + enum { M = 397 }; // period parameter + + uint32 state[N]; // internal state + uint32 *pNext; // next value to get from state + int left; // number of values left before reload needed + + +//Methods +public: + MTRand( const uint32& oneSeed ); // initialize with a simple uint32 + MTRand( uint32 *const bigSeed, uint32 const seedLength = N ); // or an array + MTRand(); // auto-initialize with /dev/urandom or time() and clock() + + // Do NOT use for CRYPTOGRAPHY without securely hashing several returned + // values together, otherwise the generator state can be learned after + // reading 624 consecutive values. + + // Access to 32-bit random numbers + double rand(); // real number in [0,1] + double rand( const double& n ); // real number in [0,n] + double randExc(); // real number in [0,1) + double randExc( const double& n ); // real number in [0,n) + double randDblExc(); // real number in (0,1) + double randDblExc( const double& n ); // real number in (0,n) + uint32 randInt(); // integer in [0,2^32-1] + uint32 randInt( const uint32& n ); // integer in [0,n] for n < 2^32 + double operator()() { return rand(); } // same as rand() + + // Access to 53-bit random numbers (capacity of IEEE double precision) + double rand53(); // real number in [0,1) + + // Access to nonuniform random number distributions + double randNorm( const double& mean = 0.0, const double& variance = 0.0 ); + + // Re-seeding functions with same behavior as initializers + void seed( const uint32 oneSeed ); + void seed( uint32 *const bigSeed, const uint32 seedLength = N ); + void seed(); + + // Saving and loading generator state + void save( uint32* saveArray ) const; // to array of size SAVE + void load( uint32 *const loadArray ); // from such array + friend std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ); + friend std::istream& operator>>( std::istream& is, MTRand& mtrand ); + +protected: + void initialize( const uint32 oneSeed ); + void reload(); + uint32 hiBit( const uint32& u ) const { return u & 0x80000000UL; } + uint32 loBit( const uint32& u ) const { return u & 0x00000001UL; } + uint32 loBits( const uint32& u ) const { return u & 0x7fffffffUL; } + uint32 mixBits( const uint32& u, const uint32& v ) const + { return hiBit(u) | loBits(v); } + uint32 twist( const uint32& m, const uint32& s0, const uint32& s1 ) const + { return m ^ (mixBits(s0,s1)>>1) ^ (-loBit(s1) & 0x9908b0dfUL); } + static uint32 hash( time_t t, clock_t c ); +}; + + +inline MTRand::MTRand( const uint32& oneSeed ) + { seed(oneSeed); } + +inline MTRand::MTRand( uint32 *const bigSeed, const uint32 seedLength ) + { seed(bigSeed,seedLength); } + +inline MTRand::MTRand() + { seed(); } + +inline double MTRand::rand() + { return double(randInt()) * (1.0/4294967295.0); } + +inline double MTRand::rand( const double& n ) + { return rand() * n; } + +inline double MTRand::randExc() + { return double(randInt()) * (1.0/4294967296.0); } + +inline double MTRand::randExc( const double& n ) + { return randExc() * n; } + +inline double MTRand::randDblExc() + { return ( double(randInt()) + 0.5 ) * (1.0/4294967296.0); } + +inline double MTRand::randDblExc( const double& n ) + { return randDblExc() * n; } + +inline double MTRand::rand53() +{ + uint32 a = randInt() >> 5, b = randInt() >> 6; + return ( a * 67108864.0 + b ) * (1.0/9007199254740992.0); // by Isaku Wada +} + +inline double MTRand::randNorm( const double& mean, const double& variance ) +{ + // Return a real number from a normal (Gaussian) distribution with given + // mean and variance by Box-Muller method + double r = sqrt( -2.0 * log( 1.0-randDblExc()) ) * variance; + double phi = 2.0 * 3.14159265358979323846264338328 * randExc(); + return mean + r * cos(phi); +} + +inline MTRand::uint32 MTRand::randInt() +{ + // Pull a 32-bit integer from the generator state + // Every other access function simply transforms the numbers extracted here + + if( left == 0 ) reload(); + --left; + + register uint32 s1; + s1 = *pNext++; + s1 ^= (s1 >> 11); + s1 ^= (s1 << 7) & 0x9d2c5680UL; + s1 ^= (s1 << 15) & 0xefc60000UL; + return ( s1 ^ (s1 >> 18) ); +} + +inline MTRand::uint32 MTRand::randInt( const uint32& n ) +{ + // Find which bits are used in n + // Optimized by Magnus Jonsson (magnus@smartelectronix.com) + uint32 used = n; + used |= used >> 1; + used |= used >> 2; + used |= used >> 4; + used |= used >> 8; + used |= used >> 16; + + // Draw numbers until one is found in [0,n] + uint32 i; + do + i = randInt() & used; // toss unused bits to shorten search + while( i > n ); + return i; +} + + +inline void MTRand::seed( const uint32 oneSeed ) +{ + // Seed the generator with a simple uint32 + initialize(oneSeed); + reload(); +} + + +inline void MTRand::seed( uint32 *const bigSeed, const uint32 seedLength ) +{ + // Seed the generator with an array of uint32's + // There are 2^19937-1 possible initial states. This function allows + // all of those to be accessed by providing at least 19937 bits (with a + // default seed length of N = 624 uint32's). Any bits above the lower 32 + // in each element are discarded. + // Just call seed() if you want to get array from /dev/urandom + initialize(19650218UL); + register int i = 1; + register uint32 j = 0; + register int k = ( N > seedLength ? N : seedLength ); + for( ; k; --k ) + { + state[i] = + state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1664525UL ); + state[i] += ( bigSeed[j] & 0xffffffffUL ) + j; + state[i] &= 0xffffffffUL; + ++i; ++j; + if( i >= N ) { state[0] = state[N-1]; i = 1; } + if( j >= seedLength ) j = 0; + } + for( k = N - 1; k; --k ) + { + state[i] = + state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1566083941UL ); + state[i] -= i; + state[i] &= 0xffffffffUL; + ++i; + if( i >= N ) { state[0] = state[N-1]; i = 1; } + } + state[0] = 0x80000000UL; // MSB is 1, assuring non-zero initial array + reload(); +} + + +inline void MTRand::seed() +{ + // Seed the generator with an array from /dev/urandom if available + // Otherwise use a hash of time() and clock() values + + // First try getting an array from /dev/urandom + FILE* urandom = fopen( "/dev/urandom", "rb" ); + if( urandom ) + { + uint32 bigSeed[N]; + register uint32 *s = bigSeed; + register int i = N; + register bool success = true; + while( success && i-- ) + success = fread( s++, sizeof(uint32), 1, urandom ); + fclose(urandom); + if( success ) { seed( bigSeed, N ); return; } + } + + // Was not successful, so use time() and clock() instead + seed( hash( time(NULL), clock() ) ); +} + + +inline void MTRand::initialize( const uint32 seed ) +{ + // Initialize generator state with seed + // See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier. + // In previous versions, most significant bits (MSBs) of the seed affect + // only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. + register uint32 *s = state; + register uint32 *r = state; + register int i = 1; + *s++ = seed & 0xffffffffUL; + for( ; i < N; ++i ) + { + *s++ = ( 1812433253UL * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffUL; + r++; + } +} + + +inline void MTRand::reload() +{ + // Generate N new values in state + // Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) + register uint32 *p = state; + register int i; + for( i = N - M; i--; ++p ) + *p = twist( p[M], p[0], p[1] ); + for( i = M; --i; ++p ) + *p = twist( p[M-N], p[0], p[1] ); + *p = twist( p[M-N], p[0], state[0] ); + + left = N, pNext = state; +} + + +inline MTRand::uint32 MTRand::hash( time_t t, clock_t c ) +{ + // Get a uint32 from t and c + // Better than uint32(x) in case x is floating point in [0,1] + // Based on code by Lawrence Kirby (fred@genesis.demon.co.uk) + + static uint32 differ = 0; // guarantee time-based seeds will change + + uint32 h1 = 0; + unsigned char *p = (unsigned char *) &t; + for( size_t i = 0; i < sizeof(t); ++i ) + { + h1 *= UCHAR_MAX + 2U; + h1 += p[i]; + } + uint32 h2 = 0; + p = (unsigned char *) &c; + for( size_t j = 0; j < sizeof(c); ++j ) + { + h2 *= UCHAR_MAX + 2U; + h2 += p[j]; + } + return ( h1 + differ++ ) ^ h2; +} + + +inline void MTRand::save( uint32* saveArray ) const +{ + register uint32 *sa = saveArray; + register const uint32 *s = state; + register int i = N; + for( ; i--; *sa++ = *s++ ) {} + *sa = left; +} + + +inline void MTRand::load( uint32 *const loadArray ) +{ + register uint32 *s = state; + register uint32 *la = loadArray; + register int i = N; + for( ; i--; *s++ = *la++ ) {} + left = *la; + pNext = &state[N-left]; +} + + +inline std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ) +{ + register const MTRand::uint32 *s = mtrand.state; + register int i = mtrand.N; + for( ; i--; os << *s++ << "\t" ) {} + return os << mtrand.left; +} + + +inline std::istream& operator>>( std::istream& is, MTRand& mtrand ) +{ + register MTRand::uint32 *s = mtrand.state; + register int i = mtrand.N; + for( ; i--; is >> *s++ ) {} + is >> mtrand.left; + mtrand.pNext = &mtrand.state[mtrand.N-mtrand.left]; + return is; +} + +#endif // MERSENNETWISTER_H + +// Change log: +// +// v0.1 - First release on 15 May 2000 +// - Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus +// - Translated from C to C++ +// - Made completely ANSI compliant +// - Designed convenient interface for initialization, seeding, and +// obtaining numbers in default or user-defined ranges +// - Added automatic seeding from /dev/urandom or time() and clock() +// - Provided functions for saving and loading generator state +// +// v0.2 - Fixed bug which reloaded generator one step too late +// +// v0.3 - Switched to clearer, faster reload() code from Matthew Bellew +// +// v0.4 - Removed trailing newline in saved generator format to be consistent +// with output format of built-in types +// +// v0.5 - Improved portability by replacing static const int's with enum's and +// clarifying return values in seed(); suggested by Eric Heimburg +// - Removed MAXINT constant; use 0xffffffffUL instead +// +// v0.6 - Eliminated seed overflow when uint32 is larger than 32 bits +// - Changed integer [0,n] generator to give better uniformity +// +// v0.7 - Fixed operator precedence ambiguity in reload() +// - Added access for real numbers in (0,1) and (0,n) +// +// v0.8 - Included time.h header to properly support time_t and clock_t +// +// v1.0 - Revised seeding to match 26 Jan 2002 update of Nishimura and Matsumoto +// - Allowed for seeding with arrays of any length +// - Added access for real numbers in [0,1) with 53-bit resolution +// - Added access for real numbers from normal (Gaussian) distributions +// - Increased overall speed by optimizing twist() +// - Doubled speed of integer [0,n] generation +// - Fixed out-of-range number generation on 64-bit machines +// - Improved portability by substituting literal constants for long enum's +// - Changed license from GNU LGPL to BSD diff --git a/engine/Utilities/Named.h b/engine/Utilities/Named.h new file mode 100644 index 0000000..e971a21 --- /dev/null +++ b/engine/Utilities/Named.h @@ -0,0 +1,64 @@ +#ifndef BLUECORE_NAMED_H +#define BLUECORE_NAMED_H + +#include + +namespace BlueCore +{ + class Named + { + private: + + std::string _Name; + + public: + + /** + * Default constructor. + */ + inline Named() : + _Name( "" ) + { + + } + + + /** + * Constructor. + * @param strName set initial name + */ + inline Named( const std::string &name ) : + _Name( name ) + { + } + + + /** + * Destructor. + */ + virtual inline ~Named() + { + } + + + /** + * Get the name. + * @return returns the name + */ + inline const std::string &getName() const + { + return _Name; + } + + + /** + * Set the name. + */ + inline void setName( const std::string &name ) + { + _Name = name; + } + }; +} + +#endif diff --git a/engine/Utilities/Referenced.h b/engine/Utilities/Referenced.h new file mode 100644 index 0000000..191e809 --- /dev/null +++ b/engine/Utilities/Referenced.h @@ -0,0 +1,236 @@ +#ifndef BLUECORE_REFERENCED_COUNTER_H +#define BLUECORE_REFERENCED_COUNTER_H + +#include + +namespace BlueCore +{ + +class Referenced +{ +public: + + class DestructionListener + { + public: + virtual void onDestruction(Referenced *referenced) = 0; + }; + +private: + + std::list _DestructionListeners; + + unsigned int _ReferenceCount; + + virtual void destroyReferenced() + { + while (_DestructionListeners.begin() != _DestructionListeners.end()) + { + if (_DestructionListeners.front() != 0) + _DestructionListeners.front()->onDestruction(this); + _DestructionListeners.pop_front(); + } + + delete this; + } + +public: + + inline Referenced() : + _ReferenceCount(0) + { + } + + virtual inline ~Referenced() + { + } + + inline void addReference() + { + _ReferenceCount += 1; + } + + inline void removeReference() + { + if (_ReferenceCount > 0) + { + _ReferenceCount -= 1; + + if ( 0 == _ReferenceCount) + { + destroyReferenced(); + } + } + } + + inline unsigned int getReferenceCount() + { + return _ReferenceCount; + } + + inline void addDestructionListener(DestructionListener *listener) + { + if (listener != 0) + _DestructionListeners.push_back(listener); + } + + inline void removeDestructionListener(DestructionListener *listener) + { + _DestructionListeners.remove(listener); + } +}; + +template class ref_ptr +{ +public: + ref_ptr() : + _Referenced(0) + { + } + + ref_ptr(T* referenced) : + _Referenced(referenced) + { + if (_Referenced) + _Referenced->addReference(); + } + + ref_ptr(const ref_ptr& op) : + _Referenced(op._Referenced) + { + if (_Referenced) + _Referenced->addReference(); + } + + ~ref_ptr() + { + if (_Referenced) + _Referenced->removeReference(); + } + + T* operator ->() const + { + return _Referenced; + } + + ref_ptr& operator =(T* op) + { + if (op != _Referenced) + { + if (_Referenced != 0) + _Referenced->removeReference(); + + _Referenced = op; + + if (_Referenced) + _Referenced->addReference(); + } + + return *this; + } + + bool operator ==(const ref_ptr& ref) + { + if (_Referenced == ref._Referenced) + return true; + else + return false; + } + + bool valid() const + { + return (_Referenced != 0); + } + + operator T*() const + { + return _Referenced; + } + + T* get() const + { + return _Referenced; + } + +private: + + T* _Referenced; +}; + +template class weak_ptr : public Referenced::DestructionListener +{ +public: + weak_ptr() : + _Referenced(0) + { + } + + weak_ptr(T* referenced) : + _Referenced(referenced) + { + if (_Referenced) + _Referenced->addDestructionListener(this); + } + + weak_ptr(const weak_ptr& op) : + _Referenced(op._Referenced) + { + if (_Referenced) + _Referenced->addDestructionListener(this); + } + + virtual ~weak_ptr() + { + if (_Referenced) + _Referenced->removeDestructionListener(this); + } + + T* operator ->() const + { + return _Referenced; + } + + weak_ptr& operator =(T* op) + { + if (op != _Referenced && _Referenced != 0) + _Referenced->removeDestructionListener(this); + + _Referenced = op; + _Referenced->addDestructionListener(this); + + return *this; + } + + bool operator ==(const weak_ptr& ref) + { + if (_Referenced == ref._Referenced) + return true; + else + return false; + } + + bool valid() const + { + return (_Referenced != 0); + } + + + T* get() const + { + return _Referenced; + } + + + void onDestruction(Referenced *referenced) + { + _Referenced = 0; + } + +private: + + T* _Referenced; +}; + +} // namespace BlueCore + +#endif diff --git a/engine/Utilities/StringUtilities.h b/engine/Utilities/StringUtilities.h new file mode 100644 index 0000000..54a4799 --- /dev/null +++ b/engine/Utilities/StringUtilities.h @@ -0,0 +1,119 @@ +#ifndef BLUECORE_STRING_UTILITIES_H +#define BLUECORE_STRING_UTILITIES_H + +#include +#include +#include + +namespace BlueCore { + + +#define SPACES " \t\r\n" + + +/** + * Function to remove whitespaces from the end of the string. + * @param s String to be trimmed. + * @param t String containing whitespaces. Default: space, tab, carriage return and newline. + */ +inline std::string trim_right( + const std::string &s, + const std::string &t = SPACES ) +{ + std::string::size_type i (s.find_last_not_of (t)); + + if (i == std::string::npos) + return ""; + else + return std::string( s, 0, i ); +} + + + +/** + * Function to remove whitespaces from the beginning of the string. + * @param s String to be trimmed. + * @param t String containing whitespaces. Default: space, tab, carriage return and newline. + */ +inline std::string trim_left( + const std::string &s, + const std::string &t = SPACES ) +{ + return std::string( s, s.find_first_not_of(t) ); +} + + +/** + * Function to remove whitespaces from the beginning and the end of the string. + * @param s String to be trimmed. + * @param t String containing whitespaces. Default: space, tab, carriage return and newline. + */ +inline std::string trim( + const std::string &s, + const std::string &t = SPACES ) +{ + std::string::size_type a = s.find_first_not_of(t), b = s.find_last_not_of(t); + + if ( a == std::string::npos || b == std::string::npos ) + return ""; + + return std::string( s, a, b-a+1 ); +} + + + +/** + * Splits a string into pieces, and returns them in an array. + * @param s String to be exploded. + * @param v Vector which receives the pieces. + * @param t String containing whitespaces. Default: space, tab, carriage return and newline. + * @param trim_spaces Flag to decide if pieces should be trimmed. Default: false. + */ +inline void explode( + const std::string &s, + std::vector &v, + const bool trim_spaces = false, + const std::string &t = SPACES ) +{ + std::string::size_type a, b; + + a = s.find_first_not_of(t), b = s.find_first_of(t, a); + + while( a != std::string::npos ) + { + if( trim_spaces ) + v.push_back( trim( s.substr(a, b-a) ) ); + else + v.push_back( s.substr(a, b-a) ); + + a = s.find_first_not_of(t, b), b = s.find_first_of(t, a); + } +} + + + +/** + * Function to assemble strings from a vector into one strng. + * @param v Vector which conatins the string pieces. + * @param t String which is places between peaces. Default: one space " ". + * @return Assembled string. + */ +inline std::string implode( + const std::vector &v, + const std::string &t = " " ) +{ + unsigned int i; + std::string s; + + for( i = 0; i < (v.size() - 1); i++) + { + s.append( v[i] ); + s.append( t ); + } + + return s+v[i]; +} + +} // namespace BlueCore + +#endif diff --git a/engine/Utilities/format.cpp b/engine/Utilities/format.cpp new file mode 100644 index 0000000..d066846 --- /dev/null +++ b/engine/Utilities/format.cpp @@ -0,0 +1,142 @@ +#include "format.h" + +#ifndef NFORMAT + +void Format::CFormat::reset() +{ + valid = false; + adjust = RIGHT; + special = false; + precision = 6; + precision_explicit = false; + zero = false; + sign = false; + width = 0; + internal = false; + setupper = false; + grouping = false; + conversion = false; + base = DEC; + floating = FIXED; + showbase = false; + strlength = 0; +} + +int Format::skip_atoi( std::string s, ST start, ST& pos ) +{ + pos = start; + ST len = s.size(); + + while( (pos < len) && isdigit( s[pos] ) ) + pos++; + + return atoi( s.substr( start, start-pos ).c_str() ); +} + +void Format::CFormat::set( std::ostream& out ) +{ + if( !valid ) + { + return; + } + +/* + printf( "valid: %d\n", valid ); + printf( "adjust: %d\n", adjust ); + printf( "special: %d\n", special ); + printf( "precision: %d\n", precision ); + printf( "precision_explicit: %d\n", precision_explicit ); + printf( "zero: %d\n", zero ); + printf( "sign: %d\n", sign ); + printf( "width: %d\n", width ); + printf( "internal: %d\n", internal ); + printf( "setupper: %d\n", setupper ); + printf( "grouping: %d\n", grouping ); + printf( "conversion: %d\n", conversion ); + printf( "base: %d\n", base ); + printf( "floating: %d\n", floating ); + printf( "showbase: %d\n", showbase ); + printf( "strlength: %d\n", strlength ); +*/ + + if( base == HEX && special && showbase && zero ) + { + // without this correction: + // printf( "[%#08x]", 0x42 ) => [0x000042] + // fromat( "[%#08x]", 0x42 ) => [00000x42] + + showbase = false; + out << '0' << ( setupper ? 'X' : 'x' ); + width -= 2; + } + + if( base == HEX && special && showbase && strlength ) + { + /* + sprintf( buffer, "[%#8.3x]", 0x42 ) => [ 0x042] + */ + + showbase = false; + + if( width ) + { + for( int i = 0; i + strlength + 2 + 1 < width; ++i ) + out << ' '; + width = 0; + } + + out << '0' << ( setupper ? 'X' : 'x' ); + for( int i = 0; i + strlength < precision; ++i ) + out << '0'; + } + + if( adjust == LEFT && zero ) + { +/* + sprintf( buffer, "[%-#08x]", 0x42 ); => [0x42 ] + not => [0x420000] +*/ + zero = false; + } + + switch( adjust ) + { + case LEFT: out.setf( std::ios::left, std::ios::adjustfield ); break; + case RIGHT: out.setf( std::ios::right, std::ios::adjustfield ); break; + } + + if( zero ) out << std::setfill('0'); + else out << std::setfill( ' ' ); + + if( sign ) out.setf( std::ios::showpos ); + else out.unsetf( std::ios::showpos ); + + if( internal ) + out.setf( std::ios::internal, std::ios::adjustfield ); + + switch( base ) + { + case OCT: out.setf( std::ios::oct, std::ios::basefield ); break; + case DEC: out.setf( std::ios::dec, std::ios::basefield ); break; + case HEX: out.setf( std::ios::hex, std::ios::basefield ); break; + } + + if( setupper ) out.setf( std::ios::uppercase ); + else out.unsetf( std::ios::uppercase ); + + switch( floating ) + { + case FIXED: out.setf( std::ios::fixed, std::ios::floatfield ); break; + case SCIENTIFIC: out.setf( std::ios::scientific, std::ios::floatfield ); break; + } + + if( showbase ) + out.setf( std::ios::showbase ); + else + out.unsetf( std::ios::showbase ); + + out << std::setw( width ); + out << std::setprecision( precision ); +} + +#endif diff --git a/engine/Utilities/format.h b/engine/Utilities/format.h new file mode 100644 index 0000000..983a299 --- /dev/null +++ b/engine/Utilities/format.h @@ -0,0 +1,919 @@ +/* + Format a C++ library for typesafe string formating in printf style + (C) 2001 - 2003 by Martin Oberzalek + + Examples: + std::cout << format( "Hello %s, I have $05d$ in my pocket", "world", 5 ) << std::endl; + std::cout << format( "Do not try this with printf: %s", 10101 ) << std::endl; + + +*/ + +#ifndef format_h +#define format_h + +#include +#include +#include + +/** + Can we use stringstreams or do we have to use the deprecated strstreams instead? + If we have to use strstreams, simple comment the next #define +**/ +#define HAVE_STL_SSTREAM +#define NFORMAT + + +#if __GNUC__ == 2 +#undef HAVE_STL_SSTREAM +#endif + +#ifdef HAVE_STL_SSTREAM +# include +# include +# define IS_DIGIT( x ) std::isdigit( x ) +#else +extern "C" { +# include +} +# include +# define IS_DIGIT( x ) isdigit( x ) +#endif + +#ifndef NFORMAT + +namespace Format +{ + typedef std::string::size_type ST; + + class CFormat + { + public: + + typedef enum Adjust + { + LEFT, + RIGHT + }; + + typedef enum Base + { + OCT, + DEC, + HEX + }; + + typedef enum Floating + { + FIXED, + SCIENTIFIC + }; + + bool valid; + Adjust adjust; + bool special; + bool sign; + bool grouping; // SUSv2 extension + bool conversion; // glibc 2.2 extension + bool zero; + bool precision_explicit; + bool internal; + Base base; + bool setupper; + Floating floating; + bool showbase; + + int width; + int precision; + int strlength; + + std::string format; + + public: + CFormat() { reset(); } + + void set( std::ostream& out ); + + private: + void reset(); + }; + + /****************************************/ + // all the errors that are thrown + // are a cause of a mistake with %* or %*m$ + class Error + { + public: + std::string err; + Error( std::string s ) : err( s ) {} + }; + + /****************************************/ + + template + class Format + { + private: + struct Arg + { + bool is_int; + bool is_string; + }; + + Arg args[6]; + + std::string format; + + A a; + B b; + C c; + D d; + E e; + F f; + + unsigned int num_of_args; + + std::string s; + + public: + Format( const std::string &format, A a, B b, C c, D d, E e, F f, unsigned int num_of_args ); + + std::string get_string() const { return s; } + + private: + void parse(); + + template bool is_int( N &n ) { return false; } + bool is_int( int &n ) { return true; } + bool is_int( unsigned int &n ) { return true; } + bool is_int( short &n ) { return true; } + bool is_int( unsigned short ) { return true; } + + template bool is_string( S &s ) { return false; } + bool is_string( std::string& s ) { return true; } + bool is_string( const std::string& s ) { return true; } + bool is_string( char* ) { return true; } + bool is_string( const char* ) { return true; } + + int get_int_arg( unsigned int num ); + void gen_arg_list(); + std::string use_arg( unsigned int i, const CFormat &cf ); + + template std::string x2s( S ss, const CFormat &cf ) + { +#ifdef HAVE_STL_SSTREAM + std::stringstream str; + str << cf << ss; + std::string st = str.str(); + return st; +#else + std::strstream str; + str << cf << ss << std::ends; + std::string st = str.str(); + str.freeze(0); + return st; +#endif + } + + }; + + int skip_atoi( std::string s, ST start, ST& pos ); + +} // namespace Format + +inline std::ostream& operator<<( std::ostream& out, Format::CFormat cf ) +{ + cf.set( out ); + return out; +} + + +template +Format::Format::Format( std::string const &format, A a, B b, C c, D d, E e, F f, unsigned int num_of_args ) + : format( format ), a(a), b(b), c(c), d(d), e(e), f(f), num_of_args( num_of_args ) +{ + if( num_of_args > 6 ) + throw Error( "Number of args out of range" ); + + gen_arg_list(); + + parse(); +} + +template +int Format::Format::get_int_arg( unsigned int num ) +{ + if( static_cast(num) > num_of_args - 1 ) + throw Error( "The arg you wan't to use is out of range" ); + + if( num < 0 ) + throw Error( "negativ number for arg number not allowed" ); + + if( args[num].is_int ) + { + switch( num ) + { + case 0: return *((int*) &a); // I have to cast here cause the compiler + case 1: return *((int*) &b); // will make troubles if any of these + case 2: return *((int*) &c); // values is not an unsigned int. + case 3: return *((int*) &d); // Even if we are sure that + case 4: return *((int*) &e); // an unsigned int value will be returned + case 5: return *((int*) &f); + } + } + else + throw Error( "expecting int arg" ); + + return 0; // should never be reached +} + +template +void Format::Format::gen_arg_list() +{ + for( unsigned int i = 0; i < num_of_args; i++ ) + { + switch( i ) + { + case 0: + args[i].is_int = is_int( a ); + args[i].is_string = is_string( a ); + break; + case 1: + args[i].is_int = is_int( b ); + args[i].is_string = is_string( b ); + break; + + case 2: + args[i].is_int = is_int( c ); + args[i].is_string = is_string( c ); + break; + + case 3: + args[i].is_int = is_int( d ); + args[i].is_string = is_string( d ); + break; + + case 4: + args[i].is_int = is_int( e ); + args[i].is_string = is_string( e ); + break; + + case 5: + args[i].is_int = is_int( f ); + args[i].is_string = is_string( f ); + break; + } + } +} + +template +std::string Format::Format::use_arg( unsigned int i, const CFormat &cf ) +{ + if( i > num_of_args || i < 0 ) + throw Error( "out of arg range" ); + + switch( i ) + { + case 0: return x2s( a, cf ); + case 1: return x2s( b, cf ); + case 2: return x2s( c, cf ); + case 3: return x2s( d, cf ); + case 4: return x2s( e, cf ); + case 5: return x2s( f, cf ); + } + + return ""; +} + +template +void Format::Format::parse() +{ + if( format.empty() ) + return; + + unsigned int par = 0; + unsigned int use_par = 0; + ST pos = 0; + ST len = format.size(); + s = ""; + bool had_precision = false; + + while( par < num_of_args && pos < len ) + { // while + + use_par = par; + + if( pos >= len ) + break; + + if( format[pos] != '%' ) + { + s += format[pos]; + pos++; + continue; + } + + // % digit found + pos++; + + if( !(pos < len ) || (format[pos] == '%') ) + { + // %% -> % + s += format[pos]; + pos++; + continue; + } + + // format string found + + ST start = pos - 1; + CFormat f; + + // process flags + + while( (pos < len) ) + { + bool finished = false; + + switch( format[pos] ) + { + case '-' : f.adjust = CFormat::LEFT; break; + case '+' : f.sign = true; break; + case ' ' : f.zero = false; break; + case '#' : f.special = true; break; + case '\'': f.grouping = true; break; + case 'I' : f.conversion = true; break; + case '0' : f.zero = true; break; + default: finished = true; break; + } + + if( finished ) + break; + + pos++; + } // while( (pos < len) ) + + // get argument number + if( pos < len ) + { + // search for the $ digit + unsigned int dp = pos; + + while( dp < len && IS_DIGIT( format[dp] ) ) + dp++; + + if( dp < len && format[dp] == '$' ) + { + use_par = skip_atoi( format, pos, pos ) - 1; + pos = dp + 1; + } + } + + // get field with + if( pos < len ) + { + if( IS_DIGIT( format[pos] ) ) + f.width = skip_atoi( format, pos, pos ); + else if( format[pos] == '*' ) + { + pos++; + + // search for the $ digit + unsigned int dp = pos; + + while( dp < len && IS_DIGIT( format[dp] ) ) + dp++; + + if( dp < len && format[dp] == '$' ) + { + f.width = get_int_arg( skip_atoi( format, pos, pos ) - 1 ); + // skip $ sign + pos++; + } + else + { + f.width = get_int_arg( par ); + + if( use_par == par ) + use_par++; + + par++; + } + + if( f.width < 0 ) + { + f.width *= -1; + f.adjust = CFormat::LEFT; + } + } + } + + // precision + if( pos < len ) + { + if( format[pos] == '.' ) + { + pos++; + if( !(pos < len) ) + return; + + had_precision = true; + + if( IS_DIGIT( format[pos] ) ) + f.precision = skip_atoi( format, pos, pos ); + else if( format[pos] == '*' ) + { + pos++; + + + // search for the $ digit + unsigned int dp = pos; + + while( dp < len && IS_DIGIT( format[dp] ) ) + dp++; + + if( dp < len && format[dp] == '$' ) + { + f.precision = get_int_arg( skip_atoi( format, pos, pos ) - 1 ); + // skip $ sign + pos++; + } + else + { + f.precision = get_int_arg( par ); + + if( use_par == par ) + use_par++; + + par++; + } + + if( f.precision == 0) + f.precision_explicit = true; + + if( f.precision < 0 ) + f.precision = 0; + } + else + f.precision = 0; + } + + } + + // lenght modifier + /* + they will be ignored + cause we know the types of the parameter + */ + if( (pos < len) ) + { + bool hh = false; + bool ll = false; + bool found = false; + + switch( format[pos] ) + { + case 'h': hh = true; found = true; break; + case 'l': ll = true; found = true; break; + case 'L': + case 'q': + case 'j': + case 'z': + case 't': found = true; break; + default: break; + } + + if(found ) + { + pos++; + + if( pos < len ) + if( hh == true ) + { + if( format[pos] == 'h' ) + pos++; + } + else if( ll = true ) + if( format[pos] == 'l' ) + pos++; + } + } + + // conversion specifier + + if( pos < len ) + { + bool invalid = false; + switch( format[pos] ) + { + case 'd': + case 'i': + f.base = CFormat::DEC; + if( f.zero && (f.adjust != CFormat::LEFT) ) + f.internal = true; + break; + + case 'X': f.setupper = true; + case 'x': + f.base = CFormat::HEX; + if( f.special ) + f.showbase = true; + break; + + case 'o': + f.base = CFormat::OCT; + if( f.special ) + f.showbase = true; + break; + + + case 'E': + f.setupper = true; + + case 'e': + if( f.special ) + f.sign = true; + f.floating = CFormat::SCIENTIFIC; + break; + + case 'F': // not supported + case 'f': + if( f.special ) + f.sign = true; + f.floating = CFormat::FIXED; + break; + + case 's': + if( f.zero ) + f.zero = false; + break; + + + // unsupported modifiers + + + case 'G': + case 'g': + + case 'A': + case 'a': + + case 'c': + + case 'C': + case 'S': + case 'P': + case 'n': break; + + default: invalid = true; + } + + if( !invalid ) + f.valid = true; + } + + if( f.valid ) + { + std::string str; + int upar = par; + + if( use_par != par ) + upar = use_par; + + if( f.base == CFormat::HEX && had_precision && f.special ) + { + CFormat f2; + f2.base = f.base; + std::string s = use_arg( upar, f2 ); + f.strlength = s.size(); +// printf( "str: %s\n", s.c_str() ); + } + + str = use_arg( upar, f ); + + // cut string + if( had_precision && args[upar].is_string ) + str = str.substr( 0, f.precision ); + + s += str; + + if( use_par == par ) + par++; + } + else + { + // copy the invalid format string + for( ST i = start; i<= pos; i++ ) + if( i < len ) + s += format[i]; + } + + pos++; + + } // while + + if( pos < len ) + { + while( pos < len ) + { + s += format[pos]; + pos++; + } + } +} + +#else // ifndef NFORMAT + +/// converts anything to a string +templatestd::string x2s( T what ) +{ +#ifdef HAVE_STL_SSTREAM + + std::stringstream str; + + str << what; + std::string s( str.str() ); + return s; + +#else + + std::strstream str; + + str << what << std::ends; + + std::string s( str.str() ); + + str.freeze(0); + + return s; +#endif +} + +#include + +namespace Format +{ + +template const char* convert( T t ) { return x2s( t ).c_str(); } + +#define DEF( TYPE ) \ +inline TYPE convert( TYPE t ) { return t; } + + DEF( unsigned ) + DEF( int ) + DEF( char ) + DEF( char* ) + DEF( const char* ) + DEF( short ) + DEF( double ) + DEF( float ) + DEF( long ) + +#undef DEF + + +template +class Format +{ + std::string s; + + public: + Format( const std::string &format, A a, B b, C c, D d, E e, F f, unsigned int num_of_args ) + { +#define D( T ) convert( T ) + unsigned buffer_size = 256; + + bool cont = false; + + do { + + cont = false; + + char *buffer = new char[buffer_size]; + int n = 0; + + switch( num_of_args ) + { + case 1: n = std::sprintf( buffer, format.c_str(), + D( a ) ); break; + case 2: n = std::sprintf( buffer, format.c_str(), + D( a ), D( b ) ); break; + case 3: n = std::sprintf( buffer, format.c_str(), + D( a ), D( b ), D( c ) ); break; + case 4: n = std::sprintf( buffer, format.c_str(), + D( a ), D( b ), D( c ), D( d ) ); break; + case 5: n = std::sprintf( buffer, format.c_str(), + D( a ), D( b ), D( c ), D( d ), D( e ) ); break; + case 6: n = std::sprintf( buffer, format.c_str(), + D( a ), D( b ), D( c ), D( d ), D( e ), D( f ) ); break; + } + + + if( (unsigned) n >= buffer_size - 2 ) + { + buffer_size *= 2; + cont = true; + n = 0; + } + + for( int i = 0; i < n; i++ ) + s += buffer[i]; + + delete[] buffer; + + } while( cont ); +#undef D + } + + std::string get_string() const { return s; } +}; + +} + +#endif + +template +inline std::string format( std::string fs, A a, B b, C c, D d, E e, F f ) +{ + return Format::Format( fs, a, b, c, d, e, f, 6).get_string(); +} + +template +inline std::string format( std::string fs, A a, B b, C c, D d, E e ) +{ + return Format::Format( fs, a, b, c, d, e, 0, 5).get_string(); +} + +template +inline std::string format( std::string fs, A a, B b, C c, D d) +{ + return Format::Format( fs, a, b, c, d, 0, 0, 4).get_string(); +} + +template +inline std::string format( std::string fs, A a, B b, C c ) +{ + return Format::Format( fs, a, b, c, 0, 0, 0, 3).get_string(); +} + +template +inline std::string format( std::string fs, A a, B b ) +{ + return Format::Format( fs, a, b, 0, 0, 0, 0, 2).get_string(); +} + +template +inline std::string format( std::string fs, A a) +{ + return Format::Format( fs, a, 0, 0, 0, 0, 0, 1).get_string(); +} + + +namespace Format +{ + template class PrintF + { + private: + + int level; + int dlevel; + + int dmodule; + int module; + + + public: + + ostream &out; + + public: + + PrintF( ostream &out = std::cout, int module = -1, int debug_level = -1 ) + : level( debug_level ), dlevel( debug_level ), + dmodule( module), module( module), out( out ) + {} + + void set_debug_level( int dlevel_ ) { dlevel = dlevel_; } + void set_module( int module_ ) { dmodule = module_; } + + PrintF operator()( int module_ ) + { + PrintF printf( *this ); + printf.module = module_; + + return printf; + } + + PrintF operator[]( int level_ ) + { + PrintF printf( *this ); + printf.level = level_; + + return printf; + } + + template PrintF& operator<<( const T &t ) + { + if( check() ) + out << t; + + return *this; + } + + // io manipulator overloading + PrintF& operator<<(ostream& (*f)(ostream&)) + { + if( check() ) + out << f; + + return *this; + } + + PrintF& operator()( std::string fs ) + { + if( check() ) + out << fs; + + return *this; + } + + + template PrintF& operator()( std::string fs, const A &a ) + { + if( check() ) + out << format( fs, a ); + + return *this; + } + + template + PrintF& operator()( std::string fs, const A &a, const B &b ) + { + if( check() ) + out << format( fs, a, b ); + + return *this; + } + + template + PrintF& operator()( std::string fs, const A &a, const B &b, const C &c ) + { + if( check() ) + out << format( fs, a, b, c ); + + return *this; + } + + template + PrintF& operator()( std::string fs, const A &a, const B &b, const C &c, const D &d ) + { + if( check() ) + out << format( fs, a, b, c, d ); + + return *this; + } + + template + PrintF& operator()( std::string fs, const A &a, const B &b, const C &c, const D &d, const E &e ) + { + if( check() ) + out << format( fs, a, b, c, d, e ); + + return *this; + } + + template + PrintF& operator()( std::string fs, const A &a, const B &b, const C &c, const D &d, const E &e, const F &f ) + { + if( check() ) + out << format( fs, a, b, c, d, e, f ); + + return *this; + } + + bool check( int module, int level ) const + { + if( module == dmodule || dmodule == -1 ) + { + if( dlevel == -1 ) + return true; + + if( level <= dlevel ) + return true; + } + + return false; + } + + private: + + bool check() const { return check( module, level ); } + + }; + +} + + +#undef IS_DIGIT + +#endif diff --git a/engine/Utilities/sigslot.h b/engine/Utilities/sigslot.h new file mode 100644 index 0000000..97828b9 --- /dev/null +++ b/engine/Utilities/sigslot.h @@ -0,0 +1,2567 @@ +// sigslot.h: Signal/Slot classes +// +// Written by Sarah Thompson (sarah@telergy.com) 2002. +// +// License: Public domain. You are free to use this code however you like, with the proviso that +// the author takes on no responsibility or liability for any use. +// +// QUICK DOCUMENTATION +// +// (see also the full documentation at http://sigslot.sourceforge.net/) +// +// #define switches +// SIGSLOT_PURE_ISO - Define this to force ISO C++ compliance. This also disables +// all of the thread safety support on platforms where it is +// available. +// +// SIGSLOT_USE_POSIX_THREADS - Force use of Posix threads when using a C++ compiler other than +// gcc on a platform that supports Posix threads. (When using gcc, +// this is the default - use SIGSLOT_PURE_ISO to disable this if +// necessary) +// +// SIGSLOT_DEFAULT_MT_POLICY - Where thread support is enabled, this defaults to multi_threaded_global. +// Otherwise, the default is single_threaded. #define this yourself to +// override the default. In pure ISO mode, anything other than +// single_threaded will cause a compiler error. +// +// PLATFORM NOTES +// +// Win32 - On Win32, the WIN32 symbol must be #defined. Most mainstream +// compilers do this by default, but you may need to define it +// yourself if your build environment is less standard. This causes +// the Win32 thread support to be compiled in and used automatically. +// +// Unix/Linux/BSD, etc. - If you're using gcc, it is assumed that you have Posix threads +// available, so they are used automatically. You can override this +// (as under Windows) with the SIGSLOT_PURE_ISO switch. If you're using +// something other than gcc but still want to use Posix threads, you +// need to #define SIGSLOT_USE_POSIX_THREADS. +// +// ISO C++ - If none of the supported platforms are detected, or if +// SIGSLOT_PURE_ISO is defined, all multithreading support is turned off, +// along with any code that might cause a pure ISO C++ environment to +// complain. Before you ask, gcc -ansi -pedantic won't compile this +// library, but gcc -ansi is fine. Pedantic mode seems to throw a lot of +// errors that aren't really there. If you feel like investigating this, +// please contact the author. +// +// +// THREADING MODES +// +// single_threaded - Your program is assumed to be single threaded from the point of view +// of signal/slot usage (i.e. all objects using signals and slots are +// created and destroyed from a single thread). Behaviour if objects are +// destroyed concurrently is undefined (i.e. you'll get the occasional +// segmentation fault/memory exception). +// +// multi_threaded_global - Your program is assumed to be multi threaded. Objects using signals and +// slots can be safely created and destroyed from any thread, even when +// connections exist. In multi_threaded_global mode, this is achieved by a +// single global mutex (actually a critical section on Windows because they +// are faster). This option uses less OS resources, but results in more +// opportunities for contention, possibly resulting in more context switches +// than are strictly necessary. +// +// multi_threaded_local - Behaviour in this mode is essentially the same as multi_threaded_global, +// except that each signal, and each object that inherits has_slots, all +// have their own mutex/critical section. In practice, this means that +// mutex collisions (and hence context switches) only happen if they are +// absolutely essential. However, on some platforms, creating a lot of +// mutexes can slow down the whole OS, so use this option with care. +// +// USING THE LIBRARY +// +// See the full documentation at http://sigslot.sourceforge.net/ +// +// + +#ifndef SIGSLOT_H__ +#define SIGSLOT_H__ + +#include +#include + +#if defined(SIGSLOT_PURE_ISO) || (!defined(WIN32) && !defined(__GNUG__) && !defined(SIGSLOT_USE_POSIX_THREADS)) +# define _SIGSLOT_SINGLE_THREADED +#elif defined(WIN32) +# define _SIGSLOT_HAS_WIN32_THREADS +# include +#elif defined(__GNUG__) || defined(SIGSLOT_USE_POSIX_THREADS) +# define _SIGSLOT_HAS_POSIX_THREADS +# include +#else +# define _SIGSLOT_SINGLE_THREADED +#endif + +#ifndef SIGSLOT_DEFAULT_MT_POLICY +# ifdef _SIGSLOT_SINGLE_THREADED +# define SIGSLOT_DEFAULT_MT_POLICY single_threaded +# else +# define SIGSLOT_DEFAULT_MT_POLICY multi_threaded_local +# endif +#endif + + +namespace sigslot { + + class single_threaded + { + public: + single_threaded() + { + ; + } + + virtual ~single_threaded() + { + ; + } + + virtual void lock() + { + ; + } + + virtual void unlock() + { + ; + } + }; + +#ifdef _SIGSLOT_HAS_WIN32_THREADS + // The multi threading policies only get compiled in if they are enabled. + class multi_threaded_global + { + public: + multi_threaded_global() + { + static bool isinitialised = false; + + if(!isinitialised) + { + InitializeCriticalSection(get_critsec()); + isinitialised = true; + } + } + + multi_threaded_global(const multi_threaded_global&) + { + ; + } + + virtual ~multi_threaded_global() + { + ; + } + + virtual void lock() + { + EnterCriticalSection(get_critsec()); + } + + virtual void unlock() + { + LeaveCriticalSection(get_critsec()); + } + + private: + CRITICAL_SECTION* get_critsec() + { + static CRITICAL_SECTION g_critsec; + return &g_critsec; + } + }; + + class multi_threaded_local + { + public: + multi_threaded_local() + { + InitializeCriticalSection(&m_critsec); + } + + multi_threaded_local(const multi_threaded_local&) + { + InitializeCriticalSection(&m_critsec); + } + + virtual ~multi_threaded_local() + { + DeleteCriticalSection(&m_critsec); + } + + virtual void lock() + { + EnterCriticalSection(&m_critsec); + } + + virtual void unlock() + { + LeaveCriticalSection(&m_critsec); + } + + private: + CRITICAL_SECTION m_critsec; + }; +#endif // _SIGSLOT_HAS_WIN32_THREADS + +#ifdef _SIGSLOT_HAS_POSIX_THREADS + // The multi threading policies only get compiled in if they are enabled. + class multi_threaded_global + { + public: + multi_threaded_global() + { + pthread_mutex_init(get_mutex(), NULL); + } + + multi_threaded_global(const multi_threaded_global&) + { + ; + } + + virtual ~multi_threaded_global() + { + ; + } + + virtual void lock() + { + pthread_mutex_lock(get_mutex()); + } + + virtual void unlock() + { + pthread_mutex_unlock(get_mutex()); + } + + private: + pthread_mutex_t* get_mutex() + { + static pthread_mutex_t g_mutex; + return &g_mutex; + } + }; + + class multi_threaded_local + { + public: + multi_threaded_local() + { + pthread_mutex_init(&m_mutex, NULL); + } + + multi_threaded_local(const multi_threaded_local&) + { + pthread_mutex_init(&m_mutex, NULL); + } + + virtual ~multi_threaded_local() + { + pthread_mutex_destroy(&m_mutex); + } + + virtual void lock() + { + pthread_mutex_lock(&m_mutex); + } + + virtual void unlock() + { + pthread_mutex_unlock(&m_mutex); + } + + private: + pthread_mutex_t m_mutex; + }; +#endif // _SIGSLOT_HAS_POSIX_THREADS + + template + class lock_block + { + public: + mt_policy *m_mutex; + + lock_block(mt_policy *mtx) + : m_mutex(mtx) + { + m_mutex->lock(); + } + + ~lock_block() + { + m_mutex->unlock(); + } + }; + + template + class has_slots; + + template + class _connection_base0 + { + public: + virtual ~_connection_base0() {}; + virtual has_slots* getdest() const = 0; + virtual void emit() = 0; + virtual _connection_base0* clone() = 0; + virtual _connection_base0* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base1 + { + public: + virtual ~_connection_base1() {}; + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type) = 0; + virtual _connection_base1* clone() = 0; + virtual _connection_base1* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base2 + { + public: + virtual ~_connection_base2() {}; + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type, arg2_type) = 0; + virtual _connection_base2* clone() = 0; + virtual _connection_base2* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base3 + { + public: + virtual ~_connection_base3() {}; + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type) = 0; + virtual _connection_base3* clone() = 0; + virtual _connection_base3* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base4 + { + public: + virtual ~_connection_base4() {}; + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type) = 0; + virtual _connection_base4* clone() = 0; + virtual _connection_base4* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base5 + { + public: + virtual ~_connection_base5() {}; + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type) = 0; + virtual _connection_base5* clone() = 0; + virtual _connection_base5* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base6 + { + public: + virtual ~_connection_base6() {}; + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type) = 0; + virtual _connection_base6* clone() = 0; + virtual _connection_base6* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base7 + { + public: + virtual ~_connection_base7() {}; + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type, arg7_type) = 0; + virtual _connection_base7* clone() = 0; + virtual _connection_base7* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base8 + { + public: + virtual ~_connection_base8() {}; + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type, arg7_type, arg8_type) = 0; + virtual _connection_base8* clone() = 0; + virtual _connection_base8* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _signal_base : public mt_policy + { + public: + virtual void slot_disconnect(has_slots* pslot) = 0; + virtual void slot_duplicate(const has_slots* poldslot, has_slots* pnewslot) = 0; + }; + + template + class has_slots : public mt_policy + { + private: + typedef typename std::set<_signal_base *> sender_set; + typedef typename sender_set::const_iterator const_iterator; + + public: + has_slots() + { + ; + } + + has_slots(const has_slots& hs) + : mt_policy(hs) + { + lock_block lock(this); + const_iterator it = hs.m_senders.begin(); + const_iterator itEnd = hs.m_senders.end(); + + while(it != itEnd) + { + (*it)->slot_duplicate(&hs, this); + m_senders.insert(*it); + ++it; + } + } + + void signal_connect(_signal_base* sender) + { + lock_block lock(this); + m_senders.insert(sender); + } + + void signal_disconnect(_signal_base* sender) + { + lock_block lock(this); + m_senders.erase(sender); + } + + virtual ~has_slots() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_senders.begin(); + const_iterator itEnd = m_senders.end(); + + while(it != itEnd) + { + (*it)->slot_disconnect(this); + ++it; + } + + m_senders.erase(m_senders.begin(), m_senders.end()); + } + + private: + sender_set m_senders; + }; + + template + class _signal_base0 : public _signal_base + { + public: + typedef typename std::list<_connection_base0 *> connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base0() + { + ; + } + + _signal_base0(const _signal_base0& s) + : _signal_base(s) + { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + ~_signal_base0() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base1 : public _signal_base + { + public: + typedef typename std::list<_connection_base1 *> connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base1() + { + ; + } + + _signal_base1(const _signal_base1& s) + : _signal_base(s) + { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base1() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base2 : public _signal_base + { + public: + typedef typename std::list<_connection_base2 *> + connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base2() + { + ; + } + + _signal_base2(const _signal_base2& s) + : _signal_base(s) + { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base2() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base3 : public _signal_base + { + public: + typedef std::list<_connection_base3 *> + connections_list; + + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + _signal_base3() + { + ; + } + + _signal_base3(const _signal_base3& s) + : _signal_base(s) + { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base3() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base4 : public _signal_base + { + public: + typedef std::list<_connection_base4 *> connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base4() + { + ; + } + + _signal_base4(const _signal_base4& s) + : _signal_base(s) + { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base4() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + this->m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base5 : public _signal_base + { + public: + typedef std::list<_connection_base5 *> connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base5() + { + ; + } + + _signal_base5(const _signal_base5& s) + : _signal_base(s) + { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base5() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base6 : public _signal_base + { + public: + typedef std::list<_connection_base6 *> connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base6() + { + ; + } + + _signal_base6(const _signal_base6& s) + : _signal_base(s) + { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base6() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base7 : public _signal_base + { + public: + typedef std::list<_connection_base7 *> connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base7() + { + ; + } + + _signal_base7(const _signal_base7& s) + : _signal_base(s) + { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base7() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base8 : public _signal_base + { + public: + typedef std::list<_connection_base8 *> + connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base8() + { + ; + } + + _signal_base8(const _signal_base8& s) + : _signal_base(s) + { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base8() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + + template + class _connection0 : public _connection_base0 + { + public: + _connection0() + { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection0(dest_type* pobject, void (dest_type::*pmemfun)()) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base0* clone() + { + return new _connection0(*this); + } + + virtual _connection_base0* duplicate(has_slots* pnewdest) + { + return new _connection0((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit() + { + (m_pobject->*m_pmemfun)(); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(); + }; + + template + class _connection1 : public _connection_base1 + { + public: + _connection1() + { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection1(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection1() + { + } + + virtual _connection_base1* clone() + { + return new _connection1(*this); + } + + virtual _connection_base1* duplicate(has_slots* pnewdest) + { + return new _connection1((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1) + { + (m_pobject->*m_pmemfun)(a1); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type); + }; + + template + class _connection2 : public _connection_base2 + { + public: + _connection2() + { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection2(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection2() + { + } + + virtual _connection_base2* clone() + { + return new _connection2(*this); + } + + virtual _connection_base2* duplicate(has_slots* pnewdest) + { + return new _connection2((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2) + { + (m_pobject->*m_pmemfun)(a1, a2); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type); + }; + + template + class _connection3 : public _connection_base3 + { + public: + _connection3() + { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection3(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection3() + { + } + + virtual _connection_base3* clone() + { + return new _connection3(*this); + } + + virtual _connection_base3* duplicate(has_slots* pnewdest) + { + return new _connection3((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3) + { + (m_pobject->*m_pmemfun)(a1, a2, a3); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type); + }; + + template + class _connection4 : public _connection_base4 + { + public: + _connection4() + { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection4(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection4() + { + } + + virtual _connection_base4* clone() + { + return new _connection4(*this); + } + + virtual _connection_base4* duplicate(has_slots* pnewdest) + { + return new _connection4((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, + arg4_type a4) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, + arg4_type); + }; + + template + class _connection5 : public _connection_base5 + { + public: + _connection5() + { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection5(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection5() + { + } + + virtual _connection_base5* clone() + { + return new _connection5(*this); + } + + virtual _connection_base5* duplicate(has_slots* pnewdest) + { + return new _connection5((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type); + }; + + template + class _connection6 : public _connection_base6 + { + public: + _connection6() + { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection6(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection6() + { + } + + virtual _connection_base6* clone() + { + return new _connection6(*this); + } + + virtual _connection_base6* duplicate(has_slots* pnewdest) + { + return new _connection6((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type); + }; + + template + class _connection7 : public _connection_base7 + { + public: + _connection7() + { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection7(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, arg7_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection7() + { + } + + virtual _connection_base7* clone() + { + return new _connection7(*this); + } + + virtual _connection_base7* duplicate(has_slots* pnewdest) + { + return new _connection7((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type); + }; + + template + class _connection8 : public _connection_base8 + { + public: + _connection8() + { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection8(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type, arg8_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection8() + { + } + + virtual _connection_base8* clone() + { + return new _connection8(*this); + } + + virtual _connection_base8* duplicate(has_slots* pnewdest) + { + return new _connection8((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7, a8); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type); + }; + + template + class signal0 : public _signal_base0 + { + public: + typedef typename _signal_base0::connections_list::const_iterator const_iterator; + signal0() + { + ; + } + + signal0(const signal0& s) + : _signal_base0(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)()) + { + lock_block lock(this); + _connection0* conn = + new _connection0(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit() + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(); + + it = itNext; + } + } + + void operator()() + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(); + + it = itNext; + } + } + }; + + template + class signal1 : public _signal_base1 + { + public: + typedef typename _signal_base1::connections_list::const_iterator const_iterator; + signal1() + { + ; + } + + signal1(const signal1& s) + : _signal_base1(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type)) + { + lock_block lock(this); + _connection1* conn = + new _connection1(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1); + + it = itNext; + } + } + + void operator()(arg1_type a1) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1); + + it = itNext; + } + } + }; + + template + class signal2 : public _signal_base2 + { + public: + typedef typename _signal_base2::connections_list::const_iterator const_iterator; + signal2() + { + ; + } + + signal2(const signal2& s) + : _signal_base2(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type)) + { + lock_block lock(this); + _connection2* conn = new + _connection2(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2); + + it = itNext; + } + } + }; + + template + class signal3 : public _signal_base3 + { + public: + typedef typename _signal_base3::connections_list::const_iterator const_iterator; + signal3() + { + ; + } + + signal3(const signal3& s) + : _signal_base3(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type)) + { + lock_block lock(this); + _connection3* conn = + new _connection3(pclass, + pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3); + + it = itNext; + } + } + }; + + template + class signal4 : public _signal_base4 + { + public: + typedef typename _signal_base4::connections_list::const_iterator const_iterator; + signal4() + { + ; + } + + signal4(const signal4& s) + : _signal_base4(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type)) + { + lock_block lock(this); + _connection4* + conn = new _connection4(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4); + + it = itNext; + } + } + }; + + template + class signal5 : public _signal_base5 + { + public: + typedef typename _signal_base5::connections_list::const_iterator const_iterator; + signal5() + { + ; + } + + signal5(const signal5& s) + : _signal_base5(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type)) + { + lock_block lock(this); + _connection5* conn = new _connection5(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5); + + it = itNext; + } + } + }; + + + template + class signal6 : public _signal_base6 + { + public: + typedef typename _signal_base6::connections_list::const_iterator const_iterator; + signal6() + { + ; + } + + signal6(const signal6& s) + : _signal_base6(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type)) + { + lock_block lock(this); + _connection6* conn = + new _connection6(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6); + + it = itNext; + } + } + }; + + template + class signal7 : public _signal_base7 + { + public: + typedef typename _signal_base7::connections_list::const_iterator const_iterator; + signal7() + { + ; + } + + signal7(const signal7& s) + : _signal_base7(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type)) + { + lock_block lock(this); + _connection7* conn = + new _connection7(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7); + + it = itNext; + } + } + }; + + template + class signal8 : public _signal_base8 + { + public: + typedef typename _signal_base8::connections_list::const_iterator const_iterator; + signal8() + { + ; + } + + signal8(const signal8& s) + : _signal_base8(s) + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type, arg8_type)) + { + lock_block lock(this); + _connection8* conn = + new _connection8(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8); + + it = itNext; + } + } + }; + +}; // namespace sigslot + +#endif // SIGSLOT_H__ diff --git a/engine/data/DejaVuSans.ttf b/engine/data/DejaVuSans.ttf new file mode 100644 index 0000000000000000000000000000000000000000..6134e732e115a7d0ad7139169d1476fd3df868ec GIT binary patch literal 519412 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%0D^1&;_HCL7&QYLd%7q3~2;`aRLjP-6{8a2> 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

A6c`deaB)%Jcq@!3v`H*>7JA9wK+{lA7GLsp|x? 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

a3BWV8>t?NSYM8_m*!O3!s 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