From ddde05869d70875be11d1380ee5eb0e88375af79 Mon Sep 17 00:00:00 2001 From: Anonymous Date: Tue, 14 Jan 2014 10:50:02 -0700 Subject: [PATCH] Added sharedCollision library --- engine/shared/library/CMakeLists.txt | 1 + .../library/sharedCollision/CMakeLists.txt | 11 + .../public/sharedCollision/BarrierObject.h | 1 + .../public/sharedCollision/BaseClass.h | 1 + .../public/sharedCollision/BaseExtent.h | 1 + .../public/sharedCollision/BoxExtent.h | 1 + .../include/public/sharedCollision/BoxTree.h | 1 + .../public/sharedCollision/Bresenham.h | 1 + .../sharedCollision/CollideParameters.h | 1 + .../public/sharedCollision/Collision1d.h | 2 + .../public/sharedCollision/Collision2d.h | 1 + .../public/sharedCollision/CollisionBuckets.h | 1 + .../public/sharedCollision/CollisionDetect.h | 1 + .../public/sharedCollision/CollisionEnums.h | 1 + .../public/sharedCollision/CollisionInfo.h | 1 + .../public/sharedCollision/CollisionMesh.h | 1 + .../sharedCollision/CollisionNotification.h | 1 + .../sharedCollision/CollisionProperty.h | 1 + .../public/sharedCollision/CollisionResolve.h | 1 + .../public/sharedCollision/CollisionSurface.h | 1 + .../public/sharedCollision/CollisionUtils.h | 1 + .../public/sharedCollision/CollisionVolume.h | 1 + .../public/sharedCollision/CollisionWorld.h | 1 + .../public/sharedCollision/ComponentExtent.h | 1 + .../public/sharedCollision/CompositeExtent.h | 1 + .../sharedCollision/ConfigSharedCollision.h | 1 + .../public/sharedCollision/Contact3d.h | 1 + .../public/sharedCollision/ContactPoint.h | 1 + .../public/sharedCollision/Containment1d.h | 2 + .../public/sharedCollision/Containment2d.h | 2 + .../public/sharedCollision/Containment3d.h | 1 + .../public/sharedCollision/CylinderExtent.h | 1 + .../public/sharedCollision/DetailExtent.h | 1 + .../public/sharedCollision/Distance2d.h | 1 + .../public/sharedCollision/Distance3d.h | 1 + .../public/sharedCollision/DoorHelper.h | 1 + .../include/public/sharedCollision/DoorInfo.h | 2 + .../public/sharedCollision/DoorObject.h | 1 + .../include/public/sharedCollision/Extent.h | 1 + .../public/sharedCollision/ExtentList.h | 1 + .../sharedCollision/FirstSharedCollision.h | 1 + .../include/public/sharedCollision/Floor.h | 1 + .../sharedCollision/FloorContactShape.h | 1 + .../public/sharedCollision/FloorLocator.h | 1 + .../public/sharedCollision/FloorManager.h | 1 + .../public/sharedCollision/FloorMesh.h | 1 + .../include/public/sharedCollision/FloorTri.h | 1 + .../public/sharedCollision/Footprint.h | 1 + .../FootprintForceReattachManager.h | 1 + .../public/sharedCollision/Intersect1d.h | 1 + .../public/sharedCollision/Intersect2d.h | 2 + .../public/sharedCollision/Intersect3d.h | 1 + .../public/sharedCollision/MeshExtent.h | 1 + .../public/sharedCollision/MultiList.h | 1 + .../public/sharedCollision/NeighborObject.h | 1 + .../sharedCollision/OrientedCylinderExtent.h | 1 + .../public/sharedCollision/Overlap2d.h | 2 + .../public/sharedCollision/Overlap3d.h | 1 + .../public/sharedCollision/Resolve3d.h | 1 + .../sharedCollision/SetupSharedCollision.h | 1 + .../sharedCollision/SimpleCollisionMesh.h | 1 + .../public/sharedCollision/SimpleExtent.h | 1 + .../sharedCollision/SpaceAvoidanceManager.h | 1 + .../public/sharedCollision/SpatialDatabase.h | 1 + .../public/sharedCollision/TerrainExtent.h | 1 + .../sharedCollision/src/CMakeLists.txt | 156 + .../src/shared/core/BarrierObject.cpp | 220 + .../src/shared/core/BarrierObject.h | 44 + .../src/shared/core/BaseClass.h | 20 + .../src/shared/core/BoxTree.cpp | 730 +++ .../sharedCollision/src/shared/core/BoxTree.h | 116 + .../src/shared/core/Bresenham.cpp | 177 + .../src/shared/core/Bresenham.h | 69 + .../src/shared/core/CollideParameters.cpp | 97 + .../src/shared/core/CollideParameters.h | 65 + .../src/shared/core/Collision2d.cpp | 11 + .../src/shared/core/Collision2d.h | 111 + .../src/shared/core/CollisionBuckets.cpp | 500 +++ .../src/shared/core/CollisionBuckets.h | 86 + .../src/shared/core/CollisionEnums.cpp | 66 + .../src/shared/core/CollisionEnums.h | 266 ++ .../src/shared/core/CollisionInfo.h | 121 + .../src/shared/core/CollisionMesh.cpp | 1085 +++++ .../src/shared/core/CollisionMesh.h | 231 + .../src/shared/core/CollisionNotification.cpp | 177 + .../src/shared/core/CollisionNotification.h | 50 + .../src/shared/core/CollisionProperty.cpp | 1424 ++++++ .../src/shared/core/CollisionProperty.h | 459 ++ .../src/shared/core/CollisionResolve.cpp | 834 ++++ .../src/shared/core/CollisionResolve.h | 154 + .../src/shared/core/CollisionSurface.cpp | 21 + .../src/shared/core/CollisionSurface.h | 88 + .../src/shared/core/CollisionUtils.cpp | 1800 ++++++++ .../src/shared/core/CollisionUtils.h | 221 + .../src/shared/core/CollisionVolume.cpp | 8 + .../src/shared/core/CollisionVolume.h | 39 + .../src/shared/core/CollisionWorld.cpp | 2788 ++++++++++++ .../src/shared/core/CollisionWorld.h | 176 + .../src/shared/core/ConfigSharedCollision.cpp | 222 + .../src/shared/core/ConfigSharedCollision.h | 108 + .../src/shared/core/Contact3d.cpp | 355 ++ .../src/shared/core/Contact3d.h | 138 + .../src/shared/core/ContactPoint.cpp | 133 + .../src/shared/core/ContactPoint.h | 68 + .../src/shared/core/Containment1d.cpp | 165 + .../src/shared/core/Containment1d.h | 41 + .../src/shared/core/Containment2d.cpp | 148 + .../src/shared/core/Containment2d.h | 58 + .../src/shared/core/Containment3d.cpp | 818 ++++ .../src/shared/core/Containment3d.h | 202 + .../src/shared/core/Distance2d.cpp | 276 ++ .../src/shared/core/Distance2d.h | 89 + .../src/shared/core/Distance3d.cpp | 821 ++++ .../src/shared/core/Distance3d.h | 191 + .../src/shared/core/DoorHelper.cpp | 135 + .../src/shared/core/DoorHelper.h | 60 + .../src/shared/core/DoorInfo.h | 48 + .../src/shared/core/DoorObject.cpp | 612 +++ .../src/shared/core/DoorObject.h | 173 + .../src/shared/core/FirstSharedCollision.h | 18 + .../sharedCollision/src/shared/core/Floor.cpp | 663 +++ .../sharedCollision/src/shared/core/Floor.h | 184 + .../src/shared/core/FloorContactShape.cpp | 9 + .../src/shared/core/FloorContactShape.h | 34 + .../src/shared/core/FloorLocator.cpp | 457 ++ .../src/shared/core/FloorLocator.h | 232 + .../src/shared/core/FloorManager.cpp | 88 + .../src/shared/core/FloorManager.h | 52 + .../src/shared/core/FloorMesh.cpp | 3985 +++++++++++++++++ .../src/shared/core/FloorMesh.h | 349 ++ .../src/shared/core/FloorTri.cpp | 198 + .../src/shared/core/FloorTri.h | 285 ++ .../src/shared/core/Footprint.cpp | 1692 +++++++ .../src/shared/core/Footprint.h | 295 ++ .../core/FootprintForceReattachManager.cpp | 69 + .../core/FootprintForceReattachManager.h | 21 + .../src/shared/core/Intersect1d.cpp | 16 + .../src/shared/core/Intersect1d.h | 117 + .../src/shared/core/Intersect2d.cpp | 456 ++ .../src/shared/core/Intersect2d.h | 102 + .../src/shared/core/Intersect3d.cpp | 1701 +++++++ .../src/shared/core/Intersect3d.h | 193 + .../src/shared/core/MultiList.cpp | 579 +++ .../src/shared/core/MultiList.h | 376 ++ .../src/shared/core/NeighborObject.cpp | 28 + .../src/shared/core/NeighborObject.h | 37 + .../src/shared/core/Overlap2d.cpp | 92 + .../src/shared/core/Overlap2d.h | 37 + .../src/shared/core/Overlap3d.cpp | 761 ++++ .../src/shared/core/Overlap3d.h | 157 + .../src/shared/core/Resolve3d.cpp | 217 + .../src/shared/core/Resolve3d.h | 96 + .../src/shared/core/SetupSharedCollision.cpp | 76 + .../src/shared/core/SetupSharedCollision.h | 37 + .../src/shared/core/SimpleCollisionMesh.cpp | 286 ++ .../src/shared/core/SimpleCollisionMesh.h | 91 + .../src/shared/core/SpaceAvoidanceManager.cpp | 193 + .../src/shared/core/SpaceAvoidanceManager.h | 38 + .../src/shared/core/SpatialDatabase.cpp | 1314 ++++++ .../src/shared/core/SpatialDatabase.h | 224 + .../src/shared/extent/BaseExtent.cpp | 75 + .../src/shared/extent/BaseExtent.h | 90 + .../src/shared/extent/BoxExtent.cpp | 458 ++ .../src/shared/extent/BoxExtent.h | 240 + .../src/shared/extent/CollisionDetect.cpp | 688 +++ .../src/shared/extent/CollisionDetect.h | 113 + .../src/shared/extent/ComponentExtent.cpp | 221 + .../src/shared/extent/ComponentExtent.h | 62 + .../src/shared/extent/CompositeExtent.cpp | 225 + .../src/shared/extent/CompositeExtent.h | 74 + .../src/shared/extent/CylinderExtent.cpp | 275 ++ .../src/shared/extent/CylinderExtent.h | 100 + .../src/shared/extent/DetailExtent.cpp | 279 ++ .../src/shared/extent/DetailExtent.h | 64 + .../src/shared/extent/Extent.cpp | 423 ++ .../src/shared/extent/Extent.h | 174 + .../src/shared/extent/ExtentList.cpp | 223 + .../src/shared/extent/ExtentList.h | 57 + .../src/shared/extent/MeshExtent.cpp | 275 ++ .../src/shared/extent/MeshExtent.h | 97 + .../shared/extent/OrientedCylinderExtent.cpp | 230 + .../shared/extent/OrientedCylinderExtent.h | 96 + .../src/shared/extent/SimpleExtent.cpp | 277 ++ .../src/shared/extent/SimpleExtent.h | 95 + .../src/win32/FirstSharedCollision.cpp | 8 + 185 files changed, 38126 insertions(+) create mode 100644 engine/shared/library/sharedCollision/CMakeLists.txt create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/BarrierObject.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/BaseClass.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/BaseExtent.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/BoxExtent.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/BoxTree.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/Bresenham.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/CollideParameters.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/Collision1d.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/Collision2d.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionBuckets.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionDetect.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionEnums.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionInfo.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionMesh.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionNotification.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionProperty.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionResolve.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionSurface.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionUtils.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionVolume.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionWorld.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/ComponentExtent.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/CompositeExtent.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/ConfigSharedCollision.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/Contact3d.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/ContactPoint.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/Containment1d.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/Containment2d.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/Containment3d.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/CylinderExtent.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/DetailExtent.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/Distance2d.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/Distance3d.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/DoorHelper.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/DoorInfo.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/DoorObject.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/Extent.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/ExtentList.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/FirstSharedCollision.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/Floor.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/FloorContactShape.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/FloorLocator.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/FloorManager.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/FloorMesh.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/FloorTri.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/Footprint.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/FootprintForceReattachManager.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/Intersect1d.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/Intersect2d.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/Intersect3d.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/MeshExtent.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/MultiList.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/NeighborObject.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/OrientedCylinderExtent.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/Overlap2d.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/Overlap3d.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/Resolve3d.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/SetupSharedCollision.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/SimpleCollisionMesh.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/SimpleExtent.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/SpaceAvoidanceManager.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/SpatialDatabase.h create mode 100644 engine/shared/library/sharedCollision/include/public/sharedCollision/TerrainExtent.h create mode 100644 engine/shared/library/sharedCollision/src/CMakeLists.txt create mode 100644 engine/shared/library/sharedCollision/src/shared/core/BarrierObject.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/BarrierObject.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/BaseClass.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/BoxTree.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/BoxTree.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Bresenham.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Bresenham.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollideParameters.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollideParameters.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Collision2d.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Collision2d.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollisionBuckets.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollisionBuckets.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollisionEnums.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollisionEnums.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollisionInfo.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollisionMesh.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollisionMesh.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollisionNotification.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollisionNotification.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollisionProperty.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollisionProperty.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollisionResolve.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollisionResolve.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollisionSurface.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollisionSurface.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollisionUtils.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollisionUtils.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollisionVolume.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollisionVolume.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollisionWorld.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/CollisionWorld.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/ConfigSharedCollision.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/ConfigSharedCollision.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Contact3d.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Contact3d.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/ContactPoint.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/ContactPoint.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Containment1d.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Containment1d.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Containment2d.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Containment2d.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Containment3d.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Containment3d.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Distance2d.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Distance2d.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Distance3d.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Distance3d.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/DoorHelper.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/DoorHelper.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/DoorInfo.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/DoorObject.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/DoorObject.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/FirstSharedCollision.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Floor.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Floor.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/FloorContactShape.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/FloorContactShape.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/FloorLocator.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/FloorLocator.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/FloorManager.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/FloorManager.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/FloorMesh.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/FloorMesh.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/FloorTri.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/FloorTri.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Footprint.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Footprint.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/FootprintForceReattachManager.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/FootprintForceReattachManager.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Intersect1d.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Intersect1d.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Intersect2d.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Intersect2d.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Intersect3d.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Intersect3d.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/MultiList.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/MultiList.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/NeighborObject.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/NeighborObject.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Overlap2d.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Overlap2d.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Overlap3d.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Overlap3d.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Resolve3d.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/Resolve3d.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/SetupSharedCollision.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/SetupSharedCollision.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/SimpleCollisionMesh.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/SimpleCollisionMesh.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/SpaceAvoidanceManager.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/SpaceAvoidanceManager.h create mode 100644 engine/shared/library/sharedCollision/src/shared/core/SpatialDatabase.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/core/SpatialDatabase.h create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/BaseExtent.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/BaseExtent.h create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/BoxExtent.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/BoxExtent.h create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/CollisionDetect.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/CollisionDetect.h create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/ComponentExtent.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/ComponentExtent.h create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/CompositeExtent.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/CompositeExtent.h create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/CylinderExtent.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/CylinderExtent.h create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/DetailExtent.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/DetailExtent.h create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/Extent.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/Extent.h create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/ExtentList.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/ExtentList.h create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/MeshExtent.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/MeshExtent.h create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/OrientedCylinderExtent.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/OrientedCylinderExtent.h create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/SimpleExtent.cpp create mode 100644 engine/shared/library/sharedCollision/src/shared/extent/SimpleExtent.h create mode 100644 engine/shared/library/sharedCollision/src/win32/FirstSharedCollision.cpp diff --git a/engine/shared/library/CMakeLists.txt b/engine/shared/library/CMakeLists.txt index 9ec45bc5..b4660081 100644 --- a/engine/shared/library/CMakeLists.txt +++ b/engine/shared/library/CMakeLists.txt @@ -1,4 +1,5 @@ +add_subdirectory(sharedCollision) add_subdirectory(sharedCompression) add_subdirectory(sharedDebug) add_subdirectory(sharedFile) diff --git a/engine/shared/library/sharedCollision/CMakeLists.txt b/engine/shared/library/sharedCollision/CMakeLists.txt new file mode 100644 index 00000000..fea789e8 --- /dev/null +++ b/engine/shared/library/sharedCollision/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.8) + +project(sharedCollision) + +if(WIN32) + add_definitions(/D_CRT_SECURE_NO_WARNINGS) +endif() + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/public) + +add_subdirectory(src) diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/BarrierObject.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/BarrierObject.h new file mode 100644 index 00000000..e2d6e804 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/BarrierObject.h @@ -0,0 +1 @@ +#include "../../src/shared/core/BarrierObject.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/BaseClass.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/BaseClass.h new file mode 100644 index 00000000..82aae591 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/BaseClass.h @@ -0,0 +1 @@ +#include "../../src/shared/core/BaseClass.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/BaseExtent.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/BaseExtent.h new file mode 100644 index 00000000..3bc0866e --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/BaseExtent.h @@ -0,0 +1 @@ +#include "../../src/shared/extent/BaseExtent.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/BoxExtent.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/BoxExtent.h new file mode 100644 index 00000000..4d0c9d25 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/BoxExtent.h @@ -0,0 +1 @@ +#include "../../src/shared/extent/BoxExtent.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/BoxTree.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/BoxTree.h new file mode 100644 index 00000000..2f8adeb2 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/BoxTree.h @@ -0,0 +1 @@ +#include "../../src/shared/core/BoxTree.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/Bresenham.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/Bresenham.h new file mode 100644 index 00000000..75c14b41 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/Bresenham.h @@ -0,0 +1 @@ +#include "../../src/shared/core/Bresenham.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/CollideParameters.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollideParameters.h new file mode 100644 index 00000000..ceea34a1 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollideParameters.h @@ -0,0 +1 @@ +#include "../../src/shared/core/CollideParameters.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/Collision1d.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/Collision1d.h new file mode 100644 index 00000000..a5790374 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/Collision1d.h @@ -0,0 +1,2 @@ +#error obsolete + diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/Collision2d.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/Collision2d.h new file mode 100644 index 00000000..443abcc9 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/Collision2d.h @@ -0,0 +1 @@ +#include "../../src/shared/core/Collision2d.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionBuckets.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionBuckets.h new file mode 100644 index 00000000..73d6dad6 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionBuckets.h @@ -0,0 +1 @@ +#include "../../src/shared/core/CollisionBuckets.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionDetect.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionDetect.h new file mode 100644 index 00000000..92b52e97 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionDetect.h @@ -0,0 +1 @@ +#include "../../src/shared/extent/CollisionDetect.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionEnums.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionEnums.h new file mode 100644 index 00000000..31ba0c51 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionEnums.h @@ -0,0 +1 @@ +#include "../../src/shared/core/CollisionEnums.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionInfo.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionInfo.h new file mode 100644 index 00000000..1991368b --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionInfo.h @@ -0,0 +1 @@ +#include "../../src/shared/core/CollisionInfo.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionMesh.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionMesh.h new file mode 100644 index 00000000..d66cad19 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionMesh.h @@ -0,0 +1 @@ +#include "../../src/shared/core/CollisionMesh.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionNotification.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionNotification.h new file mode 100644 index 00000000..20b54fae --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionNotification.h @@ -0,0 +1 @@ +#include "../../src/shared/core/CollisionNotification.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionProperty.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionProperty.h new file mode 100644 index 00000000..75e4e123 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionProperty.h @@ -0,0 +1 @@ +#include "../../src/shared/core/CollisionProperty.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionResolve.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionResolve.h new file mode 100644 index 00000000..78250ab0 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionResolve.h @@ -0,0 +1 @@ +#include "../../src/shared/core/CollisionResolve.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionSurface.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionSurface.h new file mode 100644 index 00000000..df265f9f --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionSurface.h @@ -0,0 +1 @@ +#include "../../src/shared/core/CollisionSurface.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionUtils.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionUtils.h new file mode 100644 index 00000000..39c6aa1b --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionUtils.h @@ -0,0 +1 @@ +#include "../../src/shared/core/CollisionUtils.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionVolume.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionVolume.h new file mode 100644 index 00000000..ed720a8b --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionVolume.h @@ -0,0 +1 @@ +#include "../../src/shared/core/CollisionVolume.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionWorld.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionWorld.h new file mode 100644 index 00000000..c7b28b95 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/CollisionWorld.h @@ -0,0 +1 @@ +#include "../../src/shared/core/CollisionWorld.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/ComponentExtent.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/ComponentExtent.h new file mode 100644 index 00000000..7fabfa19 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/ComponentExtent.h @@ -0,0 +1 @@ +#include "../../src/shared/extent/ComponentExtent.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/CompositeExtent.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/CompositeExtent.h new file mode 100644 index 00000000..8bed2c44 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/CompositeExtent.h @@ -0,0 +1 @@ +#include "../../src/shared/extent/CompositeExtent.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/ConfigSharedCollision.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/ConfigSharedCollision.h new file mode 100644 index 00000000..416809e1 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/ConfigSharedCollision.h @@ -0,0 +1 @@ +#include "../../src/shared/core/ConfigSharedCollision.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/Contact3d.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/Contact3d.h new file mode 100644 index 00000000..06d43e34 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/Contact3d.h @@ -0,0 +1 @@ +#include "../../src/shared/core/Contact3d.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/ContactPoint.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/ContactPoint.h new file mode 100644 index 00000000..3f21a4e6 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/ContactPoint.h @@ -0,0 +1 @@ +#include "../../src/shared/core/ContactPoint.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/Containment1d.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/Containment1d.h new file mode 100644 index 00000000..5da72068 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/Containment1d.h @@ -0,0 +1,2 @@ +#include "../../src/shared/core/Containment1d.h" + diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/Containment2d.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/Containment2d.h new file mode 100644 index 00000000..0dc22270 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/Containment2d.h @@ -0,0 +1,2 @@ +#include "../../src/shared/core/Containment2d.h" + diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/Containment3d.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/Containment3d.h new file mode 100644 index 00000000..2ccf179f --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/Containment3d.h @@ -0,0 +1 @@ +#include "../../src/shared/core/Containment3d.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/CylinderExtent.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/CylinderExtent.h new file mode 100644 index 00000000..6dc329b4 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/CylinderExtent.h @@ -0,0 +1 @@ +#include "../../src/shared/extent/CylinderExtent.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/DetailExtent.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/DetailExtent.h new file mode 100644 index 00000000..c0353639 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/DetailExtent.h @@ -0,0 +1 @@ +#include "../../src/shared/extent/DetailExtent.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/Distance2d.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/Distance2d.h new file mode 100644 index 00000000..52ca9e9f --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/Distance2d.h @@ -0,0 +1 @@ +#include "../../src/shared/core/Distance2d.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/Distance3d.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/Distance3d.h new file mode 100644 index 00000000..1697232e --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/Distance3d.h @@ -0,0 +1 @@ +#include "../../src/shared/core/Distance3d.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/DoorHelper.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/DoorHelper.h new file mode 100644 index 00000000..6813aa34 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/DoorHelper.h @@ -0,0 +1 @@ +#include "../../src/shared/core/DoorHelper.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/DoorInfo.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/DoorInfo.h new file mode 100644 index 00000000..9e88c732 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/DoorInfo.h @@ -0,0 +1,2 @@ +#include "../../src/shared/core/DoorInfo.h" + diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/DoorObject.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/DoorObject.h new file mode 100644 index 00000000..5e85ed8d --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/DoorObject.h @@ -0,0 +1 @@ +#include "../../src/shared/core/DoorObject.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/Extent.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/Extent.h new file mode 100644 index 00000000..28f29ed3 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/Extent.h @@ -0,0 +1 @@ +#include "../../src/shared/extent/Extent.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/ExtentList.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/ExtentList.h new file mode 100644 index 00000000..d450c2e1 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/ExtentList.h @@ -0,0 +1 @@ +#include "../../src/shared/extent/ExtentList.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/FirstSharedCollision.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/FirstSharedCollision.h new file mode 100644 index 00000000..e25562f5 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/FirstSharedCollision.h @@ -0,0 +1 @@ +#include "../../src/shared/core/FirstSharedCollision.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/Floor.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/Floor.h new file mode 100644 index 00000000..66d3e221 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/Floor.h @@ -0,0 +1 @@ +#include "../../src/shared/core/Floor.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/FloorContactShape.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/FloorContactShape.h new file mode 100644 index 00000000..55df0629 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/FloorContactShape.h @@ -0,0 +1 @@ +#include "../../src/shared/core/FloorContactShape.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/FloorLocator.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/FloorLocator.h new file mode 100644 index 00000000..b4446ceb --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/FloorLocator.h @@ -0,0 +1 @@ +#include "../../src/shared/core/FloorLocator.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/FloorManager.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/FloorManager.h new file mode 100644 index 00000000..f7de3036 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/FloorManager.h @@ -0,0 +1 @@ +#include "../../src/shared/core/FloorManager.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/FloorMesh.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/FloorMesh.h new file mode 100644 index 00000000..97131508 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/FloorMesh.h @@ -0,0 +1 @@ +#include "../../src/shared/core/FloorMesh.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/FloorTri.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/FloorTri.h new file mode 100644 index 00000000..ed7d7d93 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/FloorTri.h @@ -0,0 +1 @@ +#include "../../src/shared/core/FloorTri.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/Footprint.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/Footprint.h new file mode 100644 index 00000000..52ed13af --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/Footprint.h @@ -0,0 +1 @@ +#include "../../src/shared/core/Footprint.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/FootprintForceReattachManager.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/FootprintForceReattachManager.h new file mode 100644 index 00000000..07ceff06 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/FootprintForceReattachManager.h @@ -0,0 +1 @@ +#include "../../src/shared/core/FootprintForceReattachManager.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/Intersect1d.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/Intersect1d.h new file mode 100644 index 00000000..d903c317 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/Intersect1d.h @@ -0,0 +1 @@ +#include "../../src/shared/core/Intersect1d.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/Intersect2d.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/Intersect2d.h new file mode 100644 index 00000000..0cabd347 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/Intersect2d.h @@ -0,0 +1,2 @@ +#include "../../src/shared/core/Intersect2d.h" + diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/Intersect3d.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/Intersect3d.h new file mode 100644 index 00000000..ce5a9620 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/Intersect3d.h @@ -0,0 +1 @@ +#include "../../src/shared/core/Intersect3d.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/MeshExtent.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/MeshExtent.h new file mode 100644 index 00000000..14733117 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/MeshExtent.h @@ -0,0 +1 @@ +#include "../../src/shared/extent/MeshExtent.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/MultiList.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/MultiList.h new file mode 100644 index 00000000..4067c2d4 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/MultiList.h @@ -0,0 +1 @@ +#include "../../src/shared/core/MultiList.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/NeighborObject.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/NeighborObject.h new file mode 100644 index 00000000..ebaa22bd --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/NeighborObject.h @@ -0,0 +1 @@ +#include "../../src/shared/core/NeighborObject.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/OrientedCylinderExtent.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/OrientedCylinderExtent.h new file mode 100644 index 00000000..a181c465 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/OrientedCylinderExtent.h @@ -0,0 +1 @@ +#include "../../src/shared/extent/OrientedCylinderExtent.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/Overlap2d.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/Overlap2d.h new file mode 100644 index 00000000..c52aa300 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/Overlap2d.h @@ -0,0 +1,2 @@ +#include "../../src/shared/core/Overlap2d.h" + diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/Overlap3d.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/Overlap3d.h new file mode 100644 index 00000000..ba31d22b --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/Overlap3d.h @@ -0,0 +1 @@ +#include "../../src/shared/core/Overlap3d.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/Resolve3d.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/Resolve3d.h new file mode 100644 index 00000000..6bfcddbb --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/Resolve3d.h @@ -0,0 +1 @@ +#include "../../src/shared/core/Resolve3d.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/SetupSharedCollision.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/SetupSharedCollision.h new file mode 100644 index 00000000..f5def578 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/SetupSharedCollision.h @@ -0,0 +1 @@ +#include "../../src/shared/core/SetupSharedCollision.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/SimpleCollisionMesh.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/SimpleCollisionMesh.h new file mode 100644 index 00000000..ef438659 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/SimpleCollisionMesh.h @@ -0,0 +1 @@ +#include "../../src/shared/core/SimpleCollisionMesh.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/SimpleExtent.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/SimpleExtent.h new file mode 100644 index 00000000..1b2b684a --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/SimpleExtent.h @@ -0,0 +1 @@ +#include "../../src/shared/extent/SimpleExtent.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/SpaceAvoidanceManager.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/SpaceAvoidanceManager.h new file mode 100644 index 00000000..23bb2fa5 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/SpaceAvoidanceManager.h @@ -0,0 +1 @@ +#include "../../src/shared/core/SpaceAvoidanceManager.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/SpatialDatabase.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/SpatialDatabase.h new file mode 100644 index 00000000..837ae8e8 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/SpatialDatabase.h @@ -0,0 +1 @@ +#include "../../src/shared/core/SpatialDatabase.h" diff --git a/engine/shared/library/sharedCollision/include/public/sharedCollision/TerrainExtent.h b/engine/shared/library/sharedCollision/include/public/sharedCollision/TerrainExtent.h new file mode 100644 index 00000000..841ac4c1 --- /dev/null +++ b/engine/shared/library/sharedCollision/include/public/sharedCollision/TerrainExtent.h @@ -0,0 +1 @@ +#error obsolete diff --git a/engine/shared/library/sharedCollision/src/CMakeLists.txt b/engine/shared/library/sharedCollision/src/CMakeLists.txt new file mode 100644 index 00000000..d43f63e2 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/CMakeLists.txt @@ -0,0 +1,156 @@ + +set(SHARED_SOURCES + shared/core/BarrierObject.cpp + shared/core/BarrierObject.h + shared/core/BaseClass.h + shared/core/BoxTree.cpp + shared/core/BoxTree.h + shared/core/Bresenham.cpp + shared/core/Bresenham.h + shared/core/CollideParameters.cpp + shared/core/CollideParameters.h + shared/core/Collision2d.cpp + shared/core/Collision2d.h + shared/core/CollisionBuckets.cpp + shared/core/CollisionBuckets.h + shared/core/CollisionEnums.cpp + shared/core/CollisionEnums.h + shared/core/CollisionInfo.h + shared/core/CollisionMesh.cpp + shared/core/CollisionMesh.h + shared/core/CollisionNotification.cpp + shared/core/CollisionNotification.h + shared/core/CollisionProperty.cpp + shared/core/CollisionProperty.h + shared/core/CollisionResolve.cpp + shared/core/CollisionResolve.h + shared/core/CollisionSurface.cpp + shared/core/CollisionSurface.h + shared/core/CollisionUtils.cpp + shared/core/CollisionUtils.h + shared/core/CollisionVolume.cpp + shared/core/CollisionVolume.h + shared/core/CollisionWorld.cpp + shared/core/CollisionWorld.h + shared/core/ConfigSharedCollision.cpp + shared/core/ConfigSharedCollision.h + shared/core/Contact3d.cpp + shared/core/Contact3d.h + shared/core/ContactPoint.cpp + shared/core/ContactPoint.h + shared/core/Containment1d.cpp + shared/core/Containment1d.h + shared/core/Containment2d.cpp + shared/core/Containment2d.h + shared/core/Containment3d.cpp + shared/core/Containment3d.h + shared/core/Distance2d.cpp + shared/core/Distance2d.h + shared/core/Distance3d.cpp + shared/core/Distance3d.h + shared/core/DoorHelper.cpp + shared/core/DoorHelper.h + shared/core/DoorInfo.h + shared/core/DoorObject.cpp + shared/core/DoorObject.h + shared/core/FirstSharedCollision.h + shared/core/FloorContactShape.cpp + shared/core/FloorContactShape.h + shared/core/Floor.cpp + shared/core/Floor.h + shared/core/FloorLocator.cpp + shared/core/FloorLocator.h + shared/core/FloorManager.cpp + shared/core/FloorManager.h + shared/core/FloorMesh.cpp + shared/core/FloorMesh.h + shared/core/FloorTri.cpp + shared/core/FloorTri.h + shared/core/Footprint.cpp + shared/core/Footprint.h + shared/core/FootprintForceReattachManager.cpp + shared/core/FootprintForceReattachManager.h + shared/core/Intersect1d.cpp + shared/core/Intersect1d.h + shared/core/Intersect2d.cpp + shared/core/Intersect2d.h + shared/core/Intersect3d.cpp + shared/core/Intersect3d.h + shared/core/MultiList.cpp + shared/core/MultiList.h + shared/core/NeighborObject.cpp + shared/core/NeighborObject.h + shared/core/Overlap2d.cpp + shared/core/Overlap2d.h + shared/core/Overlap3d.cpp + shared/core/Overlap3d.h + shared/core/Resolve3d.cpp + shared/core/Resolve3d.h + shared/core/SetupSharedCollision.cpp + shared/core/SetupSharedCollision.h + shared/core/SimpleCollisionMesh.cpp + shared/core/SimpleCollisionMesh.h + shared/core/SpaceAvoidanceManager.cpp + shared/core/SpaceAvoidanceManager.h + shared/core/SpatialDatabase.cpp + shared/core/SpatialDatabase.h + + shared/extent/BaseExtent.cpp + shared/extent/BaseExtent.h + shared/extent/BoxExtent.cpp + shared/extent/BoxExtent.h + shared/extent/CollisionDetect.cpp + shared/extent/CollisionDetect.h + shared/extent/ComponentExtent.cpp + shared/extent/ComponentExtent.h + shared/extent/CompositeExtent.cpp + shared/extent/CompositeExtent.h + shared/extent/CylinderExtent.cpp + shared/extent/CylinderExtent.h + shared/extent/DetailExtent.cpp + shared/extent/DetailExtent.h + shared/extent/Extent.cpp + shared/extent/Extent.h + shared/extent/ExtentList.cpp + shared/extent/ExtentList.h + shared/extent/MeshExtent.cpp + shared/extent/MeshExtent.h + shared/extent/OrientedCylinderExtent.cpp + shared/extent/OrientedCylinderExtent.h + shared/extent/SimpleExtent.cpp + shared/extent/SimpleExtent.h +) + +if(WIN32) + set(PLATFORM_SOURCES + win32/FirstSharedCollision.cpp + ) + + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/win32) +else() + set(PLATFORM_SOURCES "") +endif() + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/shared + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedDebug/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFile/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundation/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundationTypes/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedGame/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedLog/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMath/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMemoryManager/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedObject/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedRandom/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedTerrain/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedUtility/include/public + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/archive/include + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/localization/include + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/unicode/include +) + +add_library(sharedCollision STATIC + ${SHARED_SOURCES} + ${PLATFORM_SOURCES} +) diff --git a/engine/shared/library/sharedCollision/src/shared/core/BarrierObject.cpp b/engine/shared/library/sharedCollision/src/shared/core/BarrierObject.cpp new file mode 100644 index 00000000..204984ad --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/BarrierObject.cpp @@ -0,0 +1,220 @@ +// ====================================================================== +// +// BarrierObject.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/BarrierObject.h" + +#include "sharedCollision/CollisionProperty.h" +#include "sharedCollision/DoorObject.h" +#include "sharedCollision/SimpleExtent.h" +#include "sharedCollision/Extent.h" +#include "sharedCollision/Containment3d.h" +#include "sharedCollision/Distance3d.h" +#include "sharedCollision/CollisionUtils.h" + +#include "sharedMath/AxialBox.h" +#include "sharedMath/VectorArgb.h" + +#include "sharedObject/Appearance.h" +#include "sharedObject/Portal.h" +#include "sharedObject/CellProperty.h" + +static VectorArgb gs_barrierColor(0.85f,0.3f,0.02f,0.01f); + +// ---------------------------------------------------------------------- + +class BarrierCollisionProperty : public CollisionProperty +{ +public: + + explicit BarrierCollisionProperty( Object & owner ) + : CollisionProperty(owner) + { + } + + virtual bool canCollideWith ( CollisionProperty const * otherCollision ) const + { + if(otherCollision == NULL) + { + return false; + } + + BarrierObject const * barrier = safe_cast(&getOwner()); + + if(barrier == NULL) + { + return false; + } + + if(barrier->getParentCell() != otherCollision->getLastCell()) + { + return false; + } + + if(otherCollision->isPlayer()) + { + return barrier->isActive(); + } + else + { + return false; + } + } + + virtual bool blocksInteraction ( InteractionType interaction ) const + { + BarrierObject const * barrier = safe_cast(&getOwner()); + + bool barrierActive = barrier && barrier->isActive(); + + bool doorClosed = barrier && barrier->getDoor() && barrier->getDoor()->isClosed(); + + if(barrierActive) + { + // The barrier is active. All interactions are blocked except IT_See, which + // is blocked if the door is closed. + + if(interaction == IT_See) + { + return doorClosed; + } + else + { + return true; + } + } + else + { + // The barrier is inactive. + + return doorClosed; + } + } + + virtual bool blocksMovement () const + { + BarrierObject const * barrier = safe_cast(&getOwner()); + + bool barrierActive = barrier && barrier->isActive(); + + if(barrierActive) + { + return CollisionProperty::blocksMovement(); + } + else + { + return false; + } + } + + +protected: + +private: + + BarrierCollisionProperty(); + BarrierCollisionProperty(const BarrierCollisionProperty&); + BarrierCollisionProperty& operator= (const BarrierCollisionProperty&); +}; + + +// ---------------------------------------------------------------------- + +BarrierObject::BarrierObject(DoorObject const * door, bool needsAppearance) +: NeighborObject(), + m_door(door) +{ + setDebugName("Barrier object"); + + BarrierCollisionProperty * collision = new BarrierCollisionProperty(*this); //lint !e1524 new in constructor for class which has no explicit destructor + addProperty(*collision); + + createCollisionExtent(); + + if(needsAppearance) + { + createAppearance(); + } +}//lint !e429 custodial pointer collision has not been freed or returned // actually, addProperty takes custody of collision + +// ---------------------------------------------------------------------- + +void BarrierObject::createCollisionExtent ( void ) +{ + CollisionProperty * collision = getCollisionProperty(); + + if(collision) + { + AxialBox box = m_door->getPortal()->getLocalBox(); + + float r = 0.05f; + + box.setMax( box.getMax() + Vector(r,r,r) ); + box.setMin( box.getMin() - Vector(r,r,r) ); + + SimpleExtent * extent = new SimpleExtent( MultiShape(box) ); + + collision->attachSourceExtent(extent); + } +} + +// ---------------------------------------------------------------------- + +void BarrierObject::createAppearance ( void ) +{ + // Create the portal polygon and translate it + // to the origin so that it matches the door + // hardpoint + + VertexList verts; + + m_door->getPortal()->getLocalVerts(verts); + + Collision3d::MovePolyOnto(verts,Vector::zero); + + // Create our barrier appearance + + Appearance * appearance = CellProperty::createPortalBarrier(verts,gs_barrierColor); + + if(appearance != NULL) + { + setAppearance( appearance ); + + // Create an extent for the barrier appearance - without it the barrier won't be rendered + + Extent * appearanceExtent = new Extent( Containment3d::EncloseSphere(verts) ); + + IGNORE_RETURN(appearanceExtent->incrementReference()); // Extent::incrementReference() returns the ref count, not an error condition that needs to be checked + + appearance->setExtent( appearanceExtent ); + }//lint !e429 custodial pointer 'appearanceExtent' has not been freed or returned // setExtent takes custody +} + +// ---------------------------------------------------------------------- + +// ---------- +// Barriers are active if their doors don't allow passage + +bool BarrierObject::isActive ( void ) const +{ + return m_door && (!m_door->isPassageAllowed()); +} + +// Barriers should be rendered if their doors disallow passage in either +// direction + +bool BarrierObject::shouldRender ( void ) const +{ + return m_door && m_door->isOpen() && (!m_door->isPassageAllowed()); +} + +DoorObject const * BarrierObject::getDoor ( void ) const +{ + return m_door; +} + +// ---------------------------------------------------------------------- diff --git a/engine/shared/library/sharedCollision/src/shared/core/BarrierObject.h b/engine/shared/library/sharedCollision/src/shared/core/BarrierObject.h new file mode 100644 index 00000000..82d7fa9a --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/BarrierObject.h @@ -0,0 +1,44 @@ +// ====================================================================== +// +// BarrierObject.h +// Copyright 2002 Sony Online Entertainment +// All Rights Reserved. +// +// ====================================================================== + +#ifndef INCLUDED_BarrierObject_H +#define INCLUDED_BarrierObject_H + +#include "sharedCollision/NeighborObject.h" + +class DoorObject; + +// ---------------------------------------------------------------------- + +class BarrierObject : public NeighborObject +{ +public: + + BarrierObject(DoorObject const * door, bool needsAppearance); + + virtual bool isActive ( void ) const; + virtual bool shouldRender ( void ) const; + + DoorObject const * getDoor ( void ) const; + +protected: + + void createCollisionExtent ( void ); + void createAppearance ( void ); + + DoorObject const * m_door; + +private: + + BarrierObject( BarrierObject const & copy ); + BarrierObject & operator = ( BarrierObject const & copy ); +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/BaseClass.h b/engine/shared/library/sharedCollision/src/shared/core/BaseClass.h new file mode 100644 index 00000000..e0c96094 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/BaseClass.h @@ -0,0 +1,20 @@ +// ====================================================================== +// +// BaseClass.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_BaseClass_H +#define INCLUDED_BaseClass_H + +class BaseClass +{ +public: + + BaseClass() {} + virtual ~BaseClass() {} + +}; + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/BoxTree.cpp b/engine/shared/library/sharedCollision/src/shared/core/BoxTree.cpp new file mode 100644 index 00000000..1f046513 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/BoxTree.cpp @@ -0,0 +1,730 @@ +// ====================================================================== +// +// BoxTree.cpp +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/BoxTree.h" + +#include "sharedCollision/BaseClass.h" +#include "sharedCollision/Overlap3d.h" +#include "sharedCollision/Containment3d.h" +#include "sharedCollision/Distance3d.h" + +#include "sharedFile/Iff.h" + +#include "sharedFoundation/ExitChain.h" + +#include "sharedMath/VectorArgb.h" +#include "sharedMath/DebugShapeRenderer.h" +#include "sharedMath/Line3d.h" + +#include "sharedFoundation/MemoryBlockManagerMacros.h" +#include "sharedFoundation/MemoryBlockManager.h" + +#include + +const Tag TAG_BTRE = TAG(B,T,R,E); +const Tag TAG_NODS = TAG(N,O,D,S); + +// ====================================================================== + +class BoxTreeNode +{ + MEMORY_BLOCK_MANAGER_INTERFACE_WITH_INSTALL; + +public: + + BoxTreeNode(); + ~BoxTreeNode(); + + BoxTreeNode ( AxialBox const & box, int userId ); + BoxTreeNode ( BoxTreeNode * childA, BoxTreeNode * childB ); + + // ---------- + + void drawDebugShapes ( DebugShapeRenderer * renderer, VectorArgb color, int level ) const; + + int getNodeCount ( void ) const; + float calcWeight ( void ) const; + float calcBalance ( void ) const; + + void assignIndices ( void ); + + void recursePackInto ( BoxTreeNode * & nodeCursor, BoxTreeNode * base ) const; + void packInto ( BoxTreeNode * flatNode, BoxTreeNode * base ) const; + + void assignIndices ( int & counter ); + + void deleteChildren ( void ); + + void read ( Iff & iff, BoxTreeNode * base ); + void write ( Iff & iff, BoxTreeNode * base ); + + bool findClosest ( Vector const & V, float maxDistance, float & outDistance, int & outId ) const; + + float distanceTo ( Vector const & V ) const; + float childDistanceTo ( Vector const & V ) const; + + // ---------- + // This is stripped down to be 40 bytes, I can't really get it much smaller + // without majorly changing how the code works. + + AxialBox m_box; // The box. Can't live without this, though it could be made lower-precision. + + int m_index; // Used during packing to keep track of where in the packed array the node goes. + // Not absolutely necessary, but without it packing would be a pain. + + int m_userId; // A user-supplied integer + + BoxTreeNode * m_childA; + BoxTreeNode * m_childB; +}; + +MEMORY_BLOCK_MANAGER_IMPLEMENTATION_WITH_INSTALL(BoxTreeNode, true, 0, 0, 0); + +// ====================================================================== + +typedef std::vector BoxTreeNodePVec; + +typedef std::pair IntPair; + +IntPair findClosestPair( BoxTreeNodePVec const & nodes ) +{ + int nodeCount = nodes.size(); + + float minValue = REAL_MAX; + int minA = -1; + int minB = -1; + + for(int i = 0; i < nodeCount - 1; i++) + { + for(int j = i+1; j < nodeCount; j++) + { + AxialBox tempBox( nodes[i]->m_box, nodes[j]->m_box ); + + float tempValue = tempBox.getVolume(); + + if(tempValue < minValue) + { + minValue = tempValue; + minA = i; + minB = j; + } + } + } + + return IntPair(minA,minB); +} + +// ---------- +// Erase one or two elements from a vector in constant time, +// but this changes the order of the elements. + +inline void erase( BoxTreeNodePVec & nodes, int whichNode ) +{ + nodes[whichNode] = nodes.back(); + + nodes.resize(nodes.size()-1); +} + +// When erasing more than one element, we have to erase them in order +// from back to front. + +inline void erase( BoxTreeNodePVec & nodes, int nodeA, int nodeB ) +{ + if(nodeA == nodeB) + { + erase(nodes,nodeA); + } + else if(nodeA > nodeB) + { + erase(nodes,nodeA); + erase(nodes,nodeB); + } + else + { + erase(nodes,nodeB); + erase(nodes,nodeA); + } +} + +// ---------------------------------------------------------------------- + +BoxTreeNode::BoxTreeNode() +: m_box(), + m_index(-1), + m_userId(-1), + m_childA(NULL), + m_childB(NULL) +{ +} + +BoxTreeNode::BoxTreeNode ( AxialBox const & box, int userId ) +: m_box(box), + m_index(-1), + m_userId(userId), + m_childA(NULL), + m_childB(NULL) +{ +} + +BoxTreeNode::BoxTreeNode ( BoxTreeNode * childA, BoxTreeNode * childB ) +: m_box(), + m_index(-1), + m_userId(-1), + m_childA(childA), + m_childB(childB) +{ + if(m_childA) m_box.add(m_childA->m_box); + if(m_childB) m_box.add(m_childB->m_box); +} + +BoxTreeNode::~BoxTreeNode() +{ +} + +// ---------------------------------------------------------------------- + +void BoxTreeNode::drawDebugShapes ( DebugShapeRenderer * renderer, VectorArgb color, int level ) const +{ + UNREF(renderer); + UNREF(color); + UNREF(level); + +#ifdef _DEBUG + + if(m_childA == NULL) return; + if(m_childB == NULL) return; + + if(renderer == NULL) return; + + VectorArgb newColor( color.a, color.r * 0.9f, color.g * 0.9f, color.b * 0.9f ); + + if(m_childA) m_childA->drawDebugShapes(renderer,newColor,level+1); + if(m_childB) m_childB->drawDebugShapes(renderer,newColor,level+1); + + renderer->setColor(color); + renderer->drawBox(m_box); + +#endif +} + +// ---------------------------------------------------------------------- + +int BoxTreeNode::getNodeCount ( void ) const +{ + int accum = 1; + + if(m_childA) accum += m_childA->getNodeCount(); + if(m_childB) accum += m_childB->getNodeCount(); + + return accum; +} + +// ---------- + +float BoxTreeNode::calcWeight ( void ) const +{ + if((m_childA == NULL) && (m_childB == NULL)) return 1.0f; + + float accum = 0.0f; + + if(m_childA) accum += m_childA->calcWeight(); + if(m_childB) accum += m_childB->calcWeight(); + + return accum; +} + +// ---------- + +float BoxTreeNode::calcBalance ( void ) const +{ + if((m_childA == NULL) && (m_childB == NULL)) return 1.0f; + + if((m_childA != NULL) && (m_childB != NULL)) + { + return m_childA->calcWeight() / m_childB->calcWeight(); + } + else + { + return 0.0f; + } +} + +// ---------------------------------------------------------------------- + +void BoxTreeNode::assignIndices ( void ) +{ + int counter = 0; + + assignIndices(counter); +} + +void BoxTreeNode::assignIndices ( int & counter ) +{ + m_index = counter; + counter++; + + if(m_childA) m_childA->assignIndices(counter); + if(m_childB) m_childB->assignIndices(counter); +} + +// ---------------------------------------------------------------------- + +void BoxTreeNode::recursePackInto ( BoxTreeNode * & nodeCursor, BoxTreeNode * base ) const +{ + packInto(nodeCursor,base); + + nodeCursor++; + + if(m_childA) m_childA->recursePackInto(nodeCursor,base); + if(m_childB) m_childB->recursePackInto(nodeCursor,base); +} + +void BoxTreeNode::packInto ( BoxTreeNode * node, BoxTreeNode * base ) const +{ + node->m_box = m_box; + node->m_index = m_index; + + node->m_childA = m_childA ? (base + m_childA->m_index) : NULL; + node->m_childB = m_childB ? (base + m_childB->m_index) : NULL; + + node->m_userId = m_userId; +} + +// ---------------------------------------------------------------------- + +void BoxTreeNode::deleteChildren ( void ) +{ + if(m_childA) m_childA->deleteChildren(); + if(m_childB) m_childB->deleteChildren(); + + delete m_childA; + m_childA = NULL; + + delete m_childB; + m_childB = NULL; +} + +// ---------------------------------------------------------------------- + +void BoxTreeNode::write ( Iff & iff, BoxTreeNode * base ) +{ + iff.insertChunkFloatVector( m_box.getMax() ); + iff.insertChunkFloatVector( m_box.getMin() ); + + iff.insertChunkData(m_index); + iff.insertChunkData(m_userId); + + int indexA = ( m_childA ? m_childA - base : -1 ); + int indexB = ( m_childB ? m_childB - base : -1 ); + + iff.insertChunkData(indexA); + iff.insertChunkData(indexB); +} + +// ---------- + +void BoxTreeNode::read ( Iff & iff, BoxTreeNode * base ) +{ + Vector max = iff.read_floatVector(); + Vector min = iff.read_floatVector(); + + m_box = AxialBox(min,max); + + m_index = iff.read_int32(); + + m_userId = iff.read_int32(); + + int indexA = iff.read_int32(); + int indexB = iff.read_int32(); + + m_childA = ( indexA != -1 ? base + indexA : NULL ); + m_childB = ( indexB != -1 ? base + indexB : NULL ); +} + +// ---------------------------------------------------------------------- + +bool BoxTreeNode::findClosest ( Vector const & V, float maxDistance, float & outDistance, int & outIndex ) const +{ + float dist = Distance3d::Distance2PointABox(V,m_box); + + if(dist >= maxDistance) + { + // This node is too far away - don't recurse + + return false; + } + + // ---------- + // Leaf node case - this leaf node is closer + + if((m_childA == NULL) && (m_childB == NULL)) + { + outDistance = dist; + outIndex = m_userId; + + return true; + } + + // ---------- + // Branch node case - recurse through both children and return the closer (if there is one) + + float distanceA = REAL_MAX; + float distanceB = REAL_MAX; + + int indexA = -1; + int indexB = -1; + + bool closerA = m_childA->findClosest(V,maxDistance,distanceA,indexA); + + // reduce maxDistance for the child B search so we don't waste time searching through descendants of B that are farther than the closest descendant found in A + + if(closerA) maxDistance = distanceA; + + bool closerB = m_childB->findClosest(V,maxDistance,distanceB,indexB); + + if(closerA || closerB) + { + if(distanceA < distanceB) + { + outDistance = distanceA; + outIndex = indexA; + } + else + { + outDistance = distanceB; + outIndex = indexB; + } + + return true; + } + else + { + return false; + } +} + +// ====================================================================== + +void BoxTree::install() +{ + BoxTreeNode::install(); +} + +// ---------------------------------------------------------------------- + +BoxTree::BoxTree() +: m_flatNodes(NULL), + m_root(NULL), + m_testCounter(0) +{ +} + +BoxTree::~BoxTree() +{ + clear(); +} + +// ---------------------------------------------------------------------- + +inline BoxTreeNode const * BoxTree::getRoot ( void ) const +{ + return m_root; +} + +// ---------------------------------------------------------------------- + +void BoxTree::build ( BoxVec const & boxes ) +{ + if(boxes.empty()) return; + + // ---------- + + BoxTreeNodePVec nodes(boxes.size(),NULL); + + int boxcount = boxes.size(); + + for(int i = 0; i < boxcount; i++) + { + nodes[i] = new BoxTreeNode(boxes[i],i); + } + + // ---------- + // Build the tree, brute-force bottom-up (slooooow) + + while(nodes.size() > 1) + { + IntPair closestPair = findClosestPair(nodes); + + int A = closestPair.first; + int B = closestPair.second; + + BoxTreeNode * newNode = new BoxTreeNode( nodes[A], nodes[B] ); + + erase(nodes,A,B); + + nodes.push_back(newNode); + } + + // ---------- + + m_root = nodes[0]; + + pack(); + + m_root->deleteChildren(); + delete m_root; + m_root = &m_flatNodes->front(); +} + +// ---------------------------------------------------------------------- + +void BoxTree::drawDebugShapes ( DebugShapeRenderer * renderer ) const +{ + UNREF(renderer); + +#ifdef _DEBUG + + if(renderer == NULL) return; + + if(m_root) m_root->drawDebugShapes( renderer, VectorArgb::solidWhite, 0 ); + +#endif +} + +// ---------------------------------------------------------------------- + +int BoxTree::getNodeCount ( void ) const +{ + if(m_flatNodes) return m_flatNodes->size(); + + else if(m_root) return m_root->getNodeCount(); + + else return 0; +} + +// ---------- + +float BoxTree::calcWeight ( void ) const +{ + if(m_root) + return m_root->calcWeight(); + else + return 0.0f; +} + +// ---------- + +float BoxTree::calcBalance ( void ) const +{ + if(m_root) + return m_root->calcBalance(); + else + return 1.0f; +} + +// ---------------------------------------------------------------------- + +template< class TestShape > +static inline void templateTestOverlapRecurse( BoxTreeNode const * node, TestShape const & testShape, IdVec & outIds ) +{ + if(Overlap3d::Test(testShape,node->m_box)) + { + if(node->m_userId != -1) + { + outIds.push_back(node->m_userId); + } + + if(node->m_childA) templateTestOverlapRecurse(node->m_childA,testShape,outIds); + if(node->m_childB) templateTestOverlapRecurse(node->m_childB,testShape,outIds); + } +} + +template< class TestShape > +static inline bool templateTestOverlap( BoxTree const & tree, TestShape const & testShape, IdVec & outIds ) +{ + if(tree.getRoot() == NULL) return false; + + int oldSize = outIds.size(); + + templateTestOverlapRecurse( tree.getRoot(), testShape, outIds ); + + return oldSize != static_cast(outIds.size()); +} + +// ---------- +// Slightly different recursion - test the bounding box of the shape on nodes, +// only test against the actual shape on leaf nodes + +template< class TestShape > +static inline void templateTestOverlapRecurse2( BoxTreeNode const * node, TestShape const & testShape, AxialBox const & shapeBounds, IdVec & outIds ) +{ + if(Overlap3d::Test(shapeBounds,node->m_box)) + { + if(node->m_userId != -1) + { + if(Overlap3d::Test(testShape,node->m_box)) + { + outIds.push_back(node->m_userId); + } + } + + if(node->m_childA) templateTestOverlapRecurse(node->m_childA,testShape,outIds); + if(node->m_childB) templateTestOverlapRecurse(node->m_childB,testShape,outIds); + } +} + +template< class TestShape > +static inline bool templateTestOverlap2( BoxTree const & tree, TestShape const & testShape, IdVec & outIds ) +{ + if(tree.getRoot() == NULL) return false; + + int oldSize = outIds.size(); + + AxialBox shapeBounds = Containment3d::EncloseABox(testShape); + + templateTestOverlapRecurse( tree.getRoot(), testShape, shapeBounds, outIds ); + + return oldSize != outIds.size(); +} + +// ---------- + +bool BoxTree::testOverlap ( AxialBox const & box, IdVec & outIds ) const { return templateTestOverlap(*this,box,outIds); } +bool BoxTree::testOverlap ( Line3d const & line, IdVec & outIds ) const { return templateTestOverlap(*this,line,outIds); } +bool BoxTree::testOverlap ( Ray3d const & ray, IdVec & outIds ) const { return templateTestOverlap(*this,ray,outIds); } +bool BoxTree::testOverlap ( Segment3d const & segment, IdVec & outIds ) const { return templateTestOverlap(*this,segment,outIds); } + +// ---------------------------------------------------------------------- + +bool BoxTree::findClosest ( Vector const & V, float maxDistance, float & outDistance, int & outId ) const +{ + return getRoot()->findClosest(V,maxDistance,outDistance,outId); +} + +// ---------------------------------------------------------------------- + +void BoxTree::write ( Iff & iff ) +{ + iff.insertForm(TAG_BTRE); + + iff.insertForm(TAG_0000); + + // ---------- + + iff.insertChunk(TAG_NODS); + { + int nodeCount = m_flatNodes->size(); + + iff.insertChunkData(nodeCount); + + BoxTreeNode * base = &m_flatNodes->front(); + + for( int i = 0; i < nodeCount; i++ ) + { + m_flatNodes->at(i).write(iff,base); + } + } + iff.exitChunk(TAG_NODS); + + // ---------- + + iff.exitForm(TAG_0000); + + iff.exitForm(TAG_BTRE); +} + +// ---------------------------------------------------------------------- + +void BoxTree::read ( Iff & iff ) +{ + iff.enterForm(TAG_BTRE); + + switch (iff.getCurrentName()) + { + case TAG_0000: + read_0000(iff); + break; + + default: + FATAL(true,("FloorMesh::Invalid version")); + break; + } + + iff.exitForm(TAG_BTRE); + + if(m_flatNodes) + { + m_root = &m_flatNodes->front(); + } +} + +// ---------------------------------------------------------------------- + +void BoxTree::read_0000 ( Iff & iff ) +{ + clear(); + + iff.enterForm(TAG_0000); + + // ---------- + + iff.enterChunk(TAG_NODS); + { + int nodeCount = iff.read_int32(); + + m_flatNodes = new BoxTreeNodeVec(nodeCount); + + BoxTreeNode * base = &m_flatNodes->front(); + + for(int i = 0; i < nodeCount; i++) + { + m_flatNodes->at(i).read(iff,base); + } + } + iff.exitChunk(TAG_NODS); + + // ---------- + + iff.exitForm(TAG_0000); +} + +// ---------------------------------------------------------------------- + +void BoxTree::clear ( void ) +{ + if(m_root && !isFlat()) + { + m_root->deleteChildren(); + + delete m_root; + m_root = NULL; + } + + delete m_flatNodes; + m_flatNodes = NULL; +} + +// ---------------------------------------------------------------------- + +void BoxTree::pack ( void ) +{ + if(!m_root) return; + + if(m_flatNodes) return; + + int nodeCount = getNodeCount(); + + m_flatNodes = new BoxTreeNodeVec(); + m_flatNodes->resize(nodeCount); + + BoxTreeNode * nodeCursor = &m_flatNodes->front(); + + m_root->assignIndices(); + m_root->recursePackInto(nodeCursor,nodeCursor); +} + +// ---------------------------------------------------------------------- diff --git a/engine/shared/library/sharedCollision/src/shared/core/BoxTree.h b/engine/shared/library/sharedCollision/src/shared/core/BoxTree.h new file mode 100644 index 00000000..9a2d6545 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/BoxTree.h @@ -0,0 +1,116 @@ +// ====================================================================== +// +// BoxTree.h +// copyright (c) 2001 Sony Online Entertainment +// +// This is a very simple, limited bounding box hierarchy used for speeding +// up collision queries +// +// ====================================================================== + +#ifndef INCLUDED_BoxTree_H +#define INCLUDED_BoxTree_H + +#include "sharedMath/AxialBox.h" +#include "sharedFoundation/Tag.h" + +class BoxTreeNode; +class DebugShapeRenderer; + +class Line3d; +class Ray3d; +class Segment3d; +class VectorArgb; +class BoxTreeNode; +class Iff; + +typedef stdvector::fwd BoxTreeNodeVec; +typedef stdvector::fwd BoxVec; +typedef stdvector::fwd IdVec; + +// ---------------------------------------------------------------------- + +class BoxTree +{ +public: + + static void install(); + +public: + + BoxTree(); + ~BoxTree(); + + // ---------- + + BoxTreeNode const * getRoot ( void ) const; + + // ---------- + + void build ( BoxVec const & boxes ); + + void drawDebugShapes ( DebugShapeRenderer * renderer ) const; + + int getNodeCount ( void ) const; + float calcWeight ( void ) const; + float calcBalance ( void ) const; + + bool testOverlap ( AxialBox const & box, IdVec & outIds ) const; + bool testOverlap ( Line3d const & line, IdVec & outIds ) const; + bool testOverlap ( Ray3d const & ray, IdVec & outIds ) const; + bool testOverlap ( Segment3d const & segment, IdVec & outIds ) const; + + bool findClosest ( Vector const & V, float maxDistance, float & outDistance, int & outId ) const; + + void clearTestCounter( void ); + int getTestCounter ( void ) const; + + void write ( Iff & iff ); + void read ( Iff & iff ); + +protected: + + void clear ( void ); + + void read_0000 ( Iff & iff ); + + void pack ( void ); + + bool isFlat ( void ) const; + + // ---------- + + BoxTreeNodeVec * m_flatNodes; + + BoxTreeNode * m_root; + + int m_testCounter; + +private: + + BoxTree( BoxTree const & copy ); + BoxTree & operator = ( BoxTree const & copy ); +}; + +// ---------------------------------------------------------------------- + +inline void BoxTree::clearTestCounter ( void ) +{ + m_testCounter = 0; +} + +inline int BoxTree::getTestCounter ( void ) const +{ + return m_testCounter; +} + +// ---------- + +inline bool BoxTree::isFlat ( void ) const +{ + return m_flatNodes != NULL; +} + +// ====================================================================== + +#endif // #ifndef INCLUDED_BoxTree_H diff --git a/engine/shared/library/sharedCollision/src/shared/core/Bresenham.cpp b/engine/shared/library/sharedCollision/src/shared/core/Bresenham.cpp new file mode 100644 index 00000000..363d76ba --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Bresenham.cpp @@ -0,0 +1,177 @@ +// ====================================================================== +// +// Bresenham.cpp +// copyright (c) 2004 Sony Online Entertainment +// tford +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/Bresenham.h" + +#include +#include + +// ====================================================================== + +Bresenham::Point3D::Point3D(int const xx, int const yy, int const zz) +: x(xx) +, y(yy) +, z(zz) +{ +} + +// ====================================================================== + +Bresenham::PointSetter3d::PointSetter3d() +{ +} + +// ---------------------------------------------------------------------- + +Bresenham::PointSetter3d::~PointSetter3d() +{ +} + +// ---------------------------------------------------------------------- + +void Bresenham::PointSetter3d::set(Point3D const & point) +{ + realSet(point); +} + +// ====================================================================== + +void Bresenham::FindPointsAlong(Point3D const & begin, Point3D const & end, bool const sloppyStep, PointSetter3d & howTo) +{ + int const deltaX = end.x - begin.x; + int const deltaY = end.y - begin.y; + int const deltaZ = end.z - begin.z; + + int const lengthX = abs(deltaX); + int const lengthY = abs(deltaY); + int const lengthZ = abs(deltaZ); + + int const lengthX2 = lengthX * 2; + int const lengthY2 = lengthY * 2; + int const lengthZ2 = lengthZ * 2; + + int const incrementX = (deltaX < 0) ? -1 : 1; + int const incrementY = (deltaY < 0) ? -1 : 1; + int const incrementZ = (deltaZ < 0) ? -1 : 1; + + int x = begin.x; + int y = begin.y; + int z = begin.z; + + // check x is dominant + if (lengthX2 >= std::max(lengthY2, lengthZ2)) + { + int errorY = lengthY2 - lengthX; + int errorZ = lengthZ2 - lengthX; + + while (x != (end.x + incrementX)) + { + howTo.set(Point3D(x, y, z)); + + if (errorY >= 0) + { + if (sloppyStep) + { + howTo.set(Point3D(x + incrementX, y, z)); + howTo.set(Point3D(x, y + incrementY, z)); + } + y += incrementY; + errorY -= lengthX2; + } + + if (errorZ >= 0) + { + if (sloppyStep) + { + howTo.set(Point3D(x + incrementX, y, z)); + howTo.set(Point3D(x, y, z + incrementZ)); + } + z += incrementZ; + errorZ -= lengthX2; + } + + x += incrementX; + errorY += lengthY2; + errorZ += lengthZ2; + } + } + // check y is dominant + else if (lengthY2 >= std::max(lengthX2, lengthZ2)) + { + int errorX = lengthX2 - lengthY; + int errorZ = lengthZ2 - lengthY; + + while (y != (end.y + incrementY)) + { + howTo.set(Point3D(x, y, z)); + if (errorX >= 0) + { + if (sloppyStep) + { + howTo.set(Point3D(x, y + incrementY, z)); + howTo.set(Point3D(x + incrementX, y, z)); + } + x += incrementX; + errorX -= lengthY2; + } + + if (errorZ >= 0) + { + if (sloppyStep) + { + howTo.set(Point3D(x, y + incrementY, z)); + howTo.set(Point3D(x, y, z + incrementZ)); + } + z += incrementZ; + errorZ -= lengthY2; + } + + y += incrementY; + errorX += lengthX2; + errorZ += lengthZ2; + } + } + // check z is dominant + else if (lengthZ2 >= std::max(lengthX2, lengthY2)) + { + int errorX = lengthZ2 - lengthZ; + int errorY = lengthZ2 - lengthZ; + + while (z != (end.z + incrementZ)) + { + howTo.set(Point3D(x, y, z)); + if (errorX >= 0) + { + if (sloppyStep) + { + howTo.set(Point3D(x, y, z + incrementZ)); + howTo.set(Point3D(x + incrementX, y, z)); + } + x += incrementX; + errorX -= lengthY2; + } + + if (errorY >= 0) + { + if (sloppyStep) + { + howTo.set(Point3D(x, y, z + incrementZ)); + howTo.set(Point3D(x, y + incrementY, z)); + } + y += incrementY; + errorY -= lengthX2; + } + + z += incrementZ; + errorX += lengthX2; + errorY += lengthY2; + } + } +} + diff --git a/engine/shared/library/sharedCollision/src/shared/core/Bresenham.h b/engine/shared/library/sharedCollision/src/shared/core/Bresenham.h new file mode 100644 index 00000000..619cd427 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Bresenham.h @@ -0,0 +1,69 @@ +// ====================================================================== +// +// Bresenham.h +// copyright (c) 2004 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_Bresenham_H +#define INCLUDED_Bresenham_H + +// ====================================================================== + +class Bresenham +{ +public: + + //integer point class for use as parameters, etc. + struct Point3D + { + Point3D(int const xx, int const yy, int const zz); + int x; + int y; + int z; + }; + + // functor interface class for allowing FindPointsAlong to convey + // information back to the caller + class PointSetter3d + { + public: + PointSetter3d(); + virtual ~PointSetter3d(); + void set(Point3D const & point); + + private: + PointSetter3d(PointSetter3d const & copy); + PointSetter3d & operator=(PointSetter3d const & copy); + virtual void realSet(Point3D const & point) = 0; + }; + + // FindPointsAlong will march from begin to end using a 3D Bresenham + // line. for each 'pixel' that the line passes over, + // HowToSetPoint::set will be called with that pixels placement. + + // sloppyStep allows additional redundency whenever 'shifts' occur + // while creating the line. example lines based of the value of + // sloppyStep are below + // + // sloppyStep == false sloppyStep == true + // .x.... .x.... + // .x.... .xx... + // ..x... .xx... + // ..x... ..x... + // ..x... ..xx.. + // ...x.. ..xx.. + // ...x.. ...x.. + + static void FindPointsAlong(Point3D const & begin, Point3D const & end, bool const sloppyStep, PointSetter3d & howTo); + +private: + Bresenham(); + Bresenham(Bresenham const & copy); + Bresenham & operator=(Bresenham const & copy); +}; + + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollideParameters.cpp b/engine/shared/library/sharedCollision/src/shared/core/CollideParameters.cpp new file mode 100644 index 00000000..7eafe64b --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollideParameters.cpp @@ -0,0 +1,97 @@ +// ====================================================================== +// +// CollideParameters.cpp +// asommers +// +// copyright 2004, sony online entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/CollideParameters.h" + +// ====================================================================== + +CollideParameters const CollideParameters::cms_default; +CollideParameters const CollideParameters::cms_toolPickDefault(CollideParameters::Q_high, CollideParameters::T_collidable, true); + +// ====================================================================== + +CollideParameters::CollideParameters() : + m_quality(Q_high), + m_type(T_collidable), + m_isToolPickTest(false) +{ +} + +// ---------------------------------------------------------------------- + +CollideParameters::CollideParameters(CollideParameters::Quality q, CollideParameters::Type t, bool isToolPickTest) : + m_quality(q), + m_type(t), + m_isToolPickTest(isToolPickTest) +{ +} + +// ---------------------------------------------------------------------- + +CollideParameters::~CollideParameters() +{ +} + +// ---------------------------------------------------------------------- + +CollideParameters::Quality CollideParameters::getQuality() const +{ + return m_quality; +} + +// ---------------------------------------------------------------------- + +void CollideParameters::setQuality(Quality const quality) +{ + m_quality = quality; +} + +// ---------------------------------------------------------------------- + +CollideParameters::Type CollideParameters::getType() const +{ + return m_type; +} + +// ---------------------------------------------------------------------- + +void CollideParameters::setType(Type const type) +{ + m_type = type; +} + +// ---------------------------------------------------------------------- + +bool CollideParameters::getToolPickTest() const +{ + return m_isToolPickTest; +} + +// ---------------------------------------------------------------------- + +void CollideParameters::setToolPickTest(bool isToolPickTest) +{ + m_isToolPickTest = isToolPickTest; +} + +// ---------------------------------------------------------------------- + +bool CollideParameters::typeShouldCollide(bool const collidable, bool const opaqueSolid) const +{ + if (m_type == T_collidable && collidable) + return true; + + if (m_type == T_opaqueSolid && opaqueSolid) + return true; + + return false; +} + +// ====================================================================== diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollideParameters.h b/engine/shared/library/sharedCollision/src/shared/core/CollideParameters.h new file mode 100644 index 00000000..624d2127 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollideParameters.h @@ -0,0 +1,65 @@ +// ====================================================================== // +// +// CollideParameters.h +// asommers +// +// copyright 2004, sony online entertainment +// +// ====================================================================== + +#ifndef INCLUDED_CollideParameters_H +#define INCLUDED_CollideParameters_H + +// ====================================================================== + +class CollideParameters +{ +public: + + static CollideParameters const cms_default; + static CollideParameters const cms_toolPickDefault; + +public: + + enum Quality + { + Q_high, + Q_medium, + Q_low + }; + + enum Type + { + T_collidable, + T_opaqueSolid + }; + +public: + + //-- Default constructed CollideParameters will choose the first enum + CollideParameters(); + CollideParameters(Quality q, Type t, bool isToolPickTest); + ~CollideParameters(); + + Quality getQuality() const; + void setQuality(Quality quality); + + Type getType() const; + void setType(Type type); + + bool getToolPickTest() const; + void setToolPickTest(bool isToolPickTest); + + bool typeShouldCollide(bool collidable, bool opaqueSolid) const; + +private: + + Quality m_quality; + Type m_type; + + bool m_isToolPickTest; +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/Collision2d.cpp b/engine/shared/library/sharedCollision/src/shared/core/Collision2d.cpp new file mode 100644 index 00000000..c720e3e0 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Collision2d.cpp @@ -0,0 +1,11 @@ +// ====================================================================== +// +// Collision2d.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/Collision2d.h" + +const real gkInvertEpsilon = 1.0e-6f; diff --git a/engine/shared/library/sharedCollision/src/shared/core/Collision2d.h b/engine/shared/library/sharedCollision/src/shared/core/Collision2d.h new file mode 100644 index 00000000..47db3ddc --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Collision2d.h @@ -0,0 +1,111 @@ +// ====================================================================== +// +// Collision2d.h +// copyright (c) 2001 Sony Online Entertainment +// +// ---------------------------------------------------------------------- + +#ifndef INCLUDED_Collision2d_H +#define INCLUDED_Collision2d_H + +#include "sharedMath/Vector.h" +#include "sharedMath/Vector2d.h" + +class Triangle2d; + +// ---------------------------------------------------------------------- +//@todo - Move this to its own class! + +extern const real gkInvertEpsilon; + +struct Matrix2 +{ + real m00,m01,m10,m11; + + Matrix2() + { + } + + Matrix2( real a, real b, real c, real d ) + : m00(a), m01(b), m10(c), m11(d) + { + } + + // ---------- + + static bool Invert ( Matrix2 const & mat, Matrix2 & out ) + { + real det = mat.m00 * mat.m11 - mat.m01 * mat.m10; + + if(fabs(det) < gkInvertEpsilon) + { + return false; + } + + real invDet = 1.0f / det; + + out.m00 = mat.m11 * invDet; + out.m01 = -mat.m01 * invDet; + out.m10 = -mat.m10 * invDet; + out.m11 = mat.m00 * invDet; + + return true; + } + + static Matrix2 fromColumns( Vector2d const & A, Vector2d const & B ) + { + return Matrix2(A.x,B.x,A.y,B.y); + } + + Matrix2 inverted ( void ) + { + // compiler complains if this isn't initialized + Matrix2 temp(0,0,0,0); + + Invert(*this,temp); + + return temp; + } + + bool solveFor ( Vector2d const & V, Vector2d & result ) + { + Matrix2 inv(0,0,0,0); + + if(Invert(*this,inv)) + { + result = inv * V; + + return true; + } + else + { + return false; + } + } + + Vector2d operator * ( Vector2d const & V ) + { + Vector2d temp; + + temp.x = m00 * V.x + m01 * V.y; + temp.y = m10 * V.x + m11 * V.y; + + return temp; + } +}; + +// ---------------------------------------------------------------------- +// 2d operations - work in the X-Y plane + +namespace Collision2d +{ + +inline Vector flatten ( Vector const & V ) { return Vector(V.x,0.0f,V.z); } +inline Vector2d flatten2d ( Vector const & V ) { return Vector2d(V.x,V.z); } +inline Vector expand ( Vector2d const & V ) { return Vector(V.x, 0.0f, V.y); } + +}; // namespace Collision2d + +// ---------------------------------------------------------------------- + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollisionBuckets.cpp b/engine/shared/library/sharedCollision/src/shared/core/CollisionBuckets.cpp new file mode 100644 index 00000000..af528e5f --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollisionBuckets.cpp @@ -0,0 +1,500 @@ +// ====================================================================== +// +// CollisionBuckets.cpp +// copyright (c) 2001 Sony Online Entertainment +// tford +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/CollisionBuckets.h" + +#include "sharedCollision/Bresenham.h" +#include "sharedCollision/FloorTri.h" // for IndexedTri +#include "sharedCollision/Intersect2d.h" +#include "sharedCollision/Intersect3d.h" +#include "sharedMath/DebugShapeRenderer.h" +#include "sharedMath/IndexedTriangleList.h" +#include "sharedMath/Ray3d.h" + +namespace CollisionBucketsNamespace +{ + float const cs_maximumNodeSpan = 100.0f; + float const cs_minimumNodeSpan = 10.0f; + unsigned int const cs_framesToRenderEachBucket = 50; + bool s_renderTravelingNode = true; + bool s_renderNodesAroundPath = true; + + // ---------------------------------------------------------------------- + + class NodeChooser : public Bresenham::PointSetter3d + { + public: + + typedef std::vector PointVector; + + explicit NodeChooser(Bresenham::Point3D const & maximumDimension); + PointVector const & getResultPointVector() const; + + private: + + NodeChooser(); + NodeChooser(NodeChooser const & copy); + NodeChooser & operator=(NodeChooser const & copy); + + virtual void realSet(Bresenham::Point3D const & point); + + private: + + Bresenham::Point3D const m_maximumDimension; + PointVector m_results; + }; +} + +// ====================================================================== + +CollisionBucketsNamespace::NodeChooser::NodeChooser(Bresenham::Point3D const & maximumDimension) +: PointSetter3d() +, m_maximumDimension(maximumDimension) +, m_results() +{ } + +// ---------------------------------------------------------------------- + +CollisionBucketsNamespace::NodeChooser::PointVector const & CollisionBucketsNamespace::NodeChooser::getResultPointVector() const +{ + return m_results; +} + +// ---------------------------------------------------------------------- + +void CollisionBucketsNamespace::NodeChooser::realSet(Bresenham::Point3D const & point) +{ + if (point.x >= m_maximumDimension.x) return; + if (point.y >= m_maximumDimension.y) return; + if (point.z >= m_maximumDimension.z) return; + if (point.x < 0) return; + if (point.y < 0) return; + if (point.z < 0) return; + m_results.push_back(point); +} + +// ====================================================================== + +using namespace CollisionBucketsNamespace; + +// ====================================================================== + +CollisionBuckets::Node::Node(AxialBox const & bounds) +: m_indices() +, m_bounds(bounds) +{ +} + +// ====================================================================== + +CollisionBuckets::CollisionBuckets(IndexedTriangleList * const indexedTriangleList) +: m_indexedTriangleList(indexedTriangleList) +, m_bounds() +, m_spanOfBounds() +, m_sizeOfNode() +, m_bucketsAlongX(0) +, m_bucketsAlongY(0) +, m_bucketsAlongZ(0) +, m_nodeMatrix(0) +#ifdef _DEBUG +, m_renderXX(0) +, m_renderYY(0) +, m_renderZZ(0) +, m_timesRendered(0) +, m_lastTestedNodes() +, m_hitTriangle() +#endif +{ +} + +// ---------------------------------------------------------------------- + +CollisionBuckets::~CollisionBuckets() +{ + destroy(); + // destroy will delete m_nodeMatrix + m_nodeMatrix = 0; +} + +// ---------------------------------------------------------------------- + +void CollisionBuckets::build(Vector const & minimumBounds, Vector const & maximumBounds) +{ + if (m_nodeMatrix != 0) + { + return; + } + + typedef std::vector Vertices; + typedef std::vector Indices; + Vertices const & vertices = m_indexedTriangleList->getVertices(); + Indices const & indices = m_indexedTriangleList->getIndices(); + + m_bounds.setMin(minimumBounds); + m_bounds.setMax(maximumBounds); + m_spanOfBounds = m_bounds.getMax() - m_bounds.getMin(); + + m_bucketsAlongX = ((m_spanOfBounds.x > cs_maximumNodeSpan) + ? static_cast(m_spanOfBounds.x / cs_maximumNodeSpan) + : static_cast(m_spanOfBounds.x / cs_minimumNodeSpan)) + 1; + m_bucketsAlongY = ((m_spanOfBounds.y > cs_maximumNodeSpan) + ? static_cast(m_spanOfBounds.y / cs_maximumNodeSpan) + : static_cast(m_spanOfBounds.y / cs_minimumNodeSpan)) + 1; + m_bucketsAlongZ = ((m_spanOfBounds.z > cs_maximumNodeSpan) + ? static_cast(m_spanOfBounds.z / cs_maximumNodeSpan) + : static_cast(m_spanOfBounds.z / cs_minimumNodeSpan)) + 1; + + m_sizeOfNode.x = m_spanOfBounds.x / static_cast(m_bucketsAlongX); + m_sizeOfNode.y = m_spanOfBounds.y / static_cast(m_bucketsAlongY); + m_sizeOfNode.z = m_spanOfBounds.z / static_cast(m_bucketsAlongZ); + + { // size the matrix + m_nodeMatrix = new NodeMatrix; + (*m_nodeMatrix).resize(m_bucketsAlongX); + for (unsigned int xx = 0; xx < m_bucketsAlongX; ++xx) + { + (*m_nodeMatrix)[xx].resize(m_bucketsAlongY); + for (unsigned int yy = 0; yy < m_bucketsAlongY; ++yy) + { + // note that nodes are initialized to NULL + (*m_nodeMatrix)[xx][yy].resize(m_bucketsAlongZ, 0); + } + } + } + + unsigned int numberOfFaces = indices.size() / 3; + for (unsigned int i = 0, index = 0; i < numberOfFaces; ++i) + { + int const index1 = indices[index++]; + int const index2 = indices[index++]; + int const index3 = indices[index++]; + + Vector const & point1 = vertices[static_cast(index1)]; + Vector const & point2 = vertices[static_cast(index2)]; + Vector const & point3 = vertices[static_cast(index3)]; + + for (unsigned int xx = 0; xx < m_bucketsAlongX; ++xx) + { + for (unsigned int yy = 0; yy < m_bucketsAlongY; ++yy) + { + for (unsigned int zz = 0; zz < m_bucketsAlongZ; ++zz) + { + Vector const minimumNodeOffset(m_sizeOfNode.x * xx, m_sizeOfNode.y * yy, m_sizeOfNode.z * zz); + Vector const minimumNodeBounds(minimumNodeOffset + minimumBounds); + Vector const maximumNodeBounds(minimumNodeBounds + m_sizeOfNode); + + AxialBox const bounds(minimumNodeBounds, maximumNodeBounds); + + if (Intersect3d::intersectTriangleBox(bounds, point1, point2, point3)) + { + if ((*m_nodeMatrix)[xx][yy][zz] == 0) + { + (*m_nodeMatrix)[xx][yy][zz] = new Node(bounds); + } + + Node * const node = (*m_nodeMatrix)[xx][yy][zz]; + node->m_indices.push_back(index1); + node->m_indices.push_back(index2); + node->m_indices.push_back(index3); + } + } + } + } + } +} + +// ---------------------------------------------------------------------- + +void CollisionBuckets::destroy() +{ + if (m_nodeMatrix != 0) + { + unsigned int const bucketsPerDimensionX = (*m_nodeMatrix).size(); + for (unsigned int xx = 0; xx < bucketsPerDimensionX; ++xx) + { + unsigned int const bucketsPerDimensionY = (*m_nodeMatrix)[xx].size(); + for (unsigned int yy = 0; yy < bucketsPerDimensionY; ++yy) + { + unsigned int const bucketsPerDimensionZ = (*m_nodeMatrix)[xx][yy].size(); + for (unsigned int zz = 0; zz < bucketsPerDimensionZ; ++zz) + { + delete (*m_nodeMatrix)[xx][yy][zz]; + } + } + } + delete m_nodeMatrix; + m_nodeMatrix = 0; + } +} + +// ---------------------------------------------------------------------- + +bool CollisionBuckets::intersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, float * time) const +{ + Vector direction(end - begin); + float const magnitude = direction.magnitude(); + + bool hit = false; + + if (direction.normalize()) + { + Ray3d const ray(begin, direction); + Intersect3d::ResultData data; + if ((m_bounds.contains(begin)) || (Intersect3d::intersectRayABoxWithData(ray, m_bounds, &data))) + { + float const lengthToFront = data.m_length; + data.m_length = 0.0f; + + // the comment below explains the reason for doing the reverse test + if ((m_bounds.contains(end)) || (Intersect3d::intersectRayABoxWithData(Ray3d(end, -direction), m_bounds, &data))) + { + float const lengthToBack = data.m_length; + BucketNodeList nodesToTest; + + { + // closeBegin and closeEnd need to be calculated for + // extreme distances (~1000 units) to help keep the + // Bresenham traversal from losing precision + Vector const closeBegin(ray.atParam(lengthToFront)); + Vector const closeEnd(ray.atParam(magnitude - lengthToBack)); + findCandidateBucketsToTestClosestToFarthest(closeBegin, closeEnd, nodesToTest); + } + +#ifdef _DEBUG + m_lastTestedNodes = nodesToTest; + m_hitTriangle = Triangle3d(); +#endif + + if (!nodesToTest.empty()) + { + float bestTime = magnitude; + Triangle3d bestTriangle;; + + typedef std::vector Vertices; + Vertices const & vertices = m_indexedTriangleList->getVertices(); + + BucketNodeList::const_iterator ii = nodesToTest.begin(); + BucketNodeList::const_iterator iiEnd = nodesToTest.end(); + for (bool done = false; ((ii != iiEnd) && (!done)); ++ii) + { + Node const * const node = *ii; + + // pretest each bucket for early rejection + if ((node != 0) && (Intersect3d::intersectRayABoxWithData(ray, node->m_bounds, &data))) + { + // no point testing beyond best time if the remaining + // boxes are past this also + done = (data.m_length > bestTime); + if (!done) + { + Node::Indices const & indices = node->m_indices; + + unsigned int const numberOfFaces = indices.size() / 3; + for (unsigned int i = 0, index = 0; i < numberOfFaces; ++i) + { + int const index1 = indices[index++]; + int const index2 = indices[index++]; + int const index3 = indices[index++]; + + Vector const & point1 = vertices[static_cast(index1)]; + Vector const & point2 = vertices[static_cast(index2)]; + Vector const & point3 = vertices[static_cast(index3)]; + + Triangle3d const triangle(point1, point2, point3); + Vector hitPoint; + real t; + + if (Intersect3d::IntersectRayTriSided(ray, triangle, hitPoint, t)) + { + if (t < bestTime) + { + bestTriangle = triangle; +#ifdef _DEBUG + m_hitTriangle = bestTriangle; +#endif + bestTime = t; + hit = true; + } + } + } + } + } + } + + if (hit) + { + if (surfaceNormal != 0) + { + *surfaceNormal = bestTriangle.getNormal(); + } + + if (time != 0) + { + *time = bestTime / magnitude; + } + } + } + } + } + } + return hit; +} + +// ---------------------------------------------------------------------- + +void CollisionBuckets::findCandidateBucketsToTestClosestToFarthest(Vector const & begin, Vector const & end, BucketNodeList & bucketNodeList) const +{ + if (m_nodeMatrix != 0) + { + int const beginIndexX = static_cast((begin.x - m_bounds.getMin().x) / m_sizeOfNode.x); + int const beginIndexY = static_cast((begin.y - m_bounds.getMin().y) / m_sizeOfNode.y); + int const beginIndexZ = static_cast((begin.z - m_bounds.getMin().z) / m_sizeOfNode.z); + + int const endIndexX = static_cast((end.x - m_bounds.getMin().x) / m_sizeOfNode.x); + int const endIndexY = static_cast((end.y - m_bounds.getMin().y) / m_sizeOfNode.y); + int const endIndexZ = static_cast((end.z - m_bounds.getMin().z) / m_sizeOfNode.z); + + Bresenham::Point3D const beginIndex(beginIndexX, beginIndexY, beginIndexZ); + Bresenham::Point3D const endIndex(endIndexX, endIndexY, endIndexZ); + + NodeChooser howToPick(Bresenham::Point3D(static_cast(m_bucketsAlongX), static_cast(m_bucketsAlongY), static_cast(m_bucketsAlongZ))); + + Bresenham::FindPointsAlong(beginIndex, endIndex, true, howToPick); + + bucketNodeList.reserve(howToPick.getResultPointVector().size()); + + NodeChooser::PointVector::const_iterator ii = howToPick.getResultPointVector().begin(); + NodeChooser::PointVector::const_iterator iiEnd = howToPick.getResultPointVector().end(); + + for (; ii != iiEnd; ++ii) + { + Bresenham::Point3D const & point = *ii; + + unsigned int const xx = static_cast(point.x); + unsigned int const yy = static_cast(point.y); + unsigned int const zz = static_cast(point.z); + + Node * node = (*m_nodeMatrix)[xx][yy][zz]; + if (node != 0) + { + bucketNodeList.push_back(node); + } + } + } +} + +// ---------------------------------------------------------------------- + +void CollisionBuckets::drawDebugShapes(DebugShapeRenderer * const renderer) const +{ + UNREF(renderer); +#ifdef _DEBUG + if (renderer != 0) + { + typedef std::vector Vertices; + + if (s_renderNodesAroundPath) + { + renderer->setColor(VectorArgb::solidYellow); + + BucketNodeList::const_iterator ii = m_lastTestedNodes.begin(); + BucketNodeList::const_iterator iiEnd = m_lastTestedNodes.end(); + + for (; ii != iiEnd; ++ii) + { + Node const * const node = *ii; + if (node != 0) + { + renderer->drawBox(node->m_bounds); + } + } + + if (!m_lastTestedNodes.empty()) + { + Vertices lineList; + lineList.push_back(m_hitTriangle.getCornerA()); + lineList.push_back(m_hitTriangle.getCornerB()); + lineList.push_back(m_hitTriangle.getCornerB()); + lineList.push_back(m_hitTriangle.getCornerC()); + lineList.push_back(m_hitTriangle.getCornerC()); + lineList.push_back(m_hitTriangle.getCornerA()); + renderer->setColor(VectorArgb::solidRed); + renderer->drawLineList(lineList); + } + } + + if ((s_renderTravelingNode) && (m_nodeMatrix != 0)) + { + // draw the current selected box + renderer->setColor(VectorArgb::solidBlack); + + Node const * const node = (*m_nodeMatrix)[m_renderXX][m_renderYY][m_renderZZ]; + if (node != 0) + { + renderer->drawBox(node->m_bounds); + + // draw the triangles in the selected box + renderer->setColor(VectorArgb::solidRed); + + Node::Indices const & indices = node->m_indices; + Vertices const & vertices = m_indexedTriangleList->getVertices(); + + Vertices lineList; + unsigned int const numberOfFaces = indices.size() / 3; + for (unsigned int i = 0, index = 0; i < numberOfFaces; ++i) + { + int const index1 = indices[index++]; + int const index2 = indices[index++]; + int const index3 = indices[index++]; + + Vector const & point1 = vertices[static_cast(index1)]; + Vector const & point2 = vertices[static_cast(index2)]; + Vector const & point3 = vertices[static_cast(index3)]; + lineList.push_back(point1); + lineList.push_back(point2); + lineList.push_back(point2); + lineList.push_back(point3); + lineList.push_back(point3); + lineList.push_back(point1); + } + renderer->drawLineList(lineList); + } + else + { + m_timesRendered = cs_framesToRenderEachBucket; + } + + // select the next box to show + ++m_timesRendered; + if (m_timesRendered > cs_framesToRenderEachBucket) + { + m_timesRendered = 0; + ++m_renderZZ; + + if (m_renderZZ >= m_bucketsAlongZ) + { + m_renderZZ = 0; + ++m_renderYY; + + if (m_renderYY >= m_bucketsAlongY) + { + m_renderYY = 0; + ++m_renderXX; + + if (m_renderXX >= m_bucketsAlongX) + { + m_renderXX = 0; + } + } + } + } + } + } +#endif +} diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollisionBuckets.h b/engine/shared/library/sharedCollision/src/shared/core/CollisionBuckets.h new file mode 100644 index 00000000..9c79042c --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollisionBuckets.h @@ -0,0 +1,86 @@ +// ====================================================================== +// +// CollisionBuckets.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_CollisionBuckets_H +#define INCLUDED_CollisionBuckets_H + +// ====================================================================== + +#include "sharedCollision/CollisionSurface.h" + +#include "sharedMath/AxialBox.h" +#include "sharedMath/Triangle3d.h" + +#include + +class DebugShapeRenderer; +class IndexedTriangleList; + +// ====================================================================== + +class CollisionBuckets +{ +public: + + explicit CollisionBuckets(IndexedTriangleList * const indexedTriangleList); + ~CollisionBuckets(); + + void build(Vector const & minimumBounds, Vector const & maximumBounds); + void destroy(); + void drawDebugShapes(DebugShapeRenderer * const renderer) const; + bool intersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, float * time) const; + +private: + + CollisionBuckets(); + CollisionBuckets(const CollisionBuckets & source); + CollisionBuckets & operator=(const CollisionBuckets & source); + + struct Node; + typedef std::vector BucketNodeList; + void findCandidateBucketsToTestClosestToFarthest(Vector const & begin, Vector const & end, BucketNodeList & bucketNodeList) const; + +private: + + IndexedTriangleList const * const m_indexedTriangleList; + AxialBox m_bounds; + Vector m_spanOfBounds; + Vector m_sizeOfNode; + + unsigned int m_bucketsAlongX; + unsigned int m_bucketsAlongY; + unsigned int m_bucketsAlongZ; + + struct Node + { + Node(AxialBox const & bounds); + + typedef std::vector Indices; + Indices m_indices; + AxialBox m_bounds; + }; + + typedef std::vector NodeRow; + typedef std::vector NodeColumn; + typedef std::vector NodeMatrix; + NodeMatrix * m_nodeMatrix; + +#ifdef _DEBUG + mutable unsigned int m_renderXX; + mutable unsigned int m_renderYY; + mutable unsigned int m_renderZZ; + mutable unsigned int m_timesRendered; + mutable BucketNodeList m_lastTestedNodes; + mutable Triangle3d m_hitTriangle; +#endif + +}; + + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollisionEnums.cpp b/engine/shared/library/sharedCollision/src/shared/core/CollisionEnums.cpp new file mode 100644 index 00000000..a7be5913 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollisionEnums.cpp @@ -0,0 +1,66 @@ +// ====================================================================== +// +// CollisionEnums.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/CollisionEnums.h" + +namespace Containment +{ + +// Used to turn containment results for two orthogonal axes into a single containment result + +ContainmentResult ComposeAxisTests ( ContainmentResult A, ContainmentResult B ) +{ + if(A == B) + { + return A; + } + else if((A == CR_Outside) || (B == CR_Outside)) + { + return CR_Outside; + } + else if ((A == CR_TouchingOutside) || (B == CR_TouchingOutside)) + { + return CR_TouchingOutside; + } + else if ((A == CR_Overlap) || (B == CR_Overlap)) + { + if((A == CR_Boundary) || (B == CR_Boundary)) + { + // One exception to this pattern - if the test volume overlaps the boundary + // along one axis and is in the boundary along the other axis, then the test + // volume touches the outside of the boundary + + return CR_TouchingOutside; + } + else + { + return CR_Boundary; + } + } + else if((A == CR_Boundary) || (B == CR_Boundary)) + { + return CR_Boundary; + } + else if((A == CR_TouchingInside) || (B == CR_TouchingInside)) + { + return CR_TouchingInside; + } + else + { + return CR_Inside; + } +} + +ContainmentResult ComposeAxisTests ( ContainmentResult A, ContainmentResult B, ContainmentResult C ) +{ + return Containment::ComposeAxisTests(ComposeAxisTests(A,B),C); +} + +// ---------------------------------------------------------------------- + +} // namespace Containment diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollisionEnums.h b/engine/shared/library/sharedCollision/src/shared/core/CollisionEnums.h new file mode 100644 index 00000000..0296e71a --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollisionEnums.h @@ -0,0 +1,266 @@ +// ====================================================================== +// +// CollisionEnums.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_CollisionEnums_H +#define INCLUDED_CollisionEnums_H + +// ====================================================================== + +enum CanMoveResult +{ + CMR_MoveOK, + CMR_HitFloorEdge, + CMR_HitObstacle, + CMR_MissedGoal, + CMR_Error, + + CMR_Invalid +}; + +enum EdgeIntersectResult +{ + EIR_MissedEdge, + EIR_HitEdge, + EIR_PassesEdge, + EIR_GrazesEdge, + + EIR_Invalid +}; + +enum ContainmentResult +{ + CR_Outside, // Test point/shape is totally outside the boundary + CR_Inside, // Test point/shape is totally contained by the boundary + CR_Boundary, // Test point/shape is entirely within epsilon of the boundary of the containing shape, + CR_Overlap, // Test shape is on both sides of the boundary + CR_TouchingInside, // Test shape has points inside the boundary and within epsilon of the boundary + CR_TouchingOutside, // Test shape has points outside the boundary and within epsilon of the boundary + + CR_Invalid, +}; + +namespace Containment +{ + // True if the object has no points outside of the boundary + + inline bool isContainment( ContainmentResult C ) + { + return (C == CR_Inside) || (C == CR_TouchingInside) || (C == CR_Boundary); + } + + // True if the object has no points inside the boundary + + inline bool isNonContainment( ContainmentResult C ) + { + return (C == CR_Outside) || (C == CR_TouchingOutside) || (C == CR_Boundary); + } + + // True if the object has points on the boundary + + inline bool isContact( ContainmentResult C ) + { + return (C == CR_Boundary) || (C == CR_Overlap) || (C == CR_TouchingInside) || (C == CR_TouchingOutside); + } + + ContainmentResult ComposeAxisTests ( ContainmentResult A, ContainmentResult B ); + ContainmentResult ComposeAxisTests ( ContainmentResult A, ContainmentResult B, ContainmentResult C ); +} + +// ---------- + +enum ResolutionResult +{ + RR_NoCollision, // The objects don't collide + RR_DontKnowHow, // We don't know how to resolve collisions between those types of objects + RR_Past, // The collision happened in the past, we can't resolve it. + RR_Future, // The collision happens too far in the future, we can't resolve it + RR_Resolved, // The objects collided, and the collision has been resolved + RR_Failed, // The objects collided, but we were unable to resolve the collision + + RR_Invalid +}; + +// ---------- + +enum FloorEdgeType +{ + FET_Uncrossable = 0, + FET_Crossable = 1, + FET_WallBase = 2, + FET_WallTop = 3, + + FET_Invalid, +}; + +// ---------- + +enum HitResult +{ + HR_Miss, // Primitive does not hit the test shape + HR_Hit, // Primitive hits the test shape + + HR_HitFront, // Primitive hits the front side of the test shape + HR_HitBack, // Primtiive hits the back side of the test shape + HR_TouchFront, // Segment's begin point is in front of the shape and its end point is on the shape + HR_TouchBack, // Segment's begin point is in front of the shape and its end point is on the shape + HR_LeaveFront, // Segment/ray begins on the shape and goes in front of the shape + HR_LeaveBack, // Segment/ray begins on the shape and goes behind the shape + HR_Coplanar, // Primitive is coplanar with the test shape + + HR_Invalid, +}; + +// ---------- + +enum CollinearResult +{ + CLR_LeftSide, // (2D) Point lies on the left (counterclockwise) side of AB + CLR_RightSide, // (2D) Point lies on the right (clockwise) side of AB + + CLR_CoincidentA, // Point is coincident with A + CLR_CoincidentB, // Point is coincident with B + + CLR_OverlapLeft, // Point is collinear with AB, forms line V-A-B + CLR_OverlapCenter, // Point is collinear with AB, forms line A-V-B + CLR_OverlapRight, // Point is collinear with AB, forms line A-B-V + + // ---------- + + CLR_Disjoint, // (3D) Point does not lie on the line + CLR_Overlap, // OverlapLeft || OverlapRight || OverlapCenter || CoincidentA || CoincidentB +}; + +// ---------- + +/* +enum OverlapResult +{ + OR_Identical, + OR_AContainsB, + OR_BContainsA, + OR_Overlap, + OR_Disjoint, + + OR_Invalid +}; + +*/ + +// ---------- +// The values for these two enums are taken from SharedObjectTemplate.h +// If they change, this file needs to change too + +// Why did I duplicate them? Well, since their definition is nested inside +// SharedObjectTemplate, there's no way to get to them without #including +// SharedObjectTemplate.h, and including that file creates a bunch of dependencies +// that tend to break builds since there are two different files named +// SharedObjectTemplate.h, one in sharedTemplate and one in sharedGame. + +enum InteractionType +{ + IT_See = 0x0001, + IT_Target = 0x0002, + IT_CombatRanged = 0x0004, + IT_CombatMelee = 0x0008, + IT_Manipulate = 0x0010, + IT_Talk = 0x0020, // // Sound doesn't travel in straight lines, but... + IT_RadialDamage = 0x0040, // // Explosions cause radial damage + IT_Camera = 0x0080, // // This is a bit different from the other actions - if an object blocks CAT_Camera it means the object blocks the camera's view of the target. + + IT_All = IT_See | IT_Target | IT_CombatRanged | IT_CombatMelee | IT_Manipulate | IT_Talk | IT_RadialDamage | IT_Camera, + + IT_Any = 0xFFFFFFFF, +}; + +enum MaterialType +{ + MT_Solid = 0x0001, // // Generic solid 'stuff' + MT_Meat = 0x0002, // // Living things are made of meat + MT_Metal = 0x0004, // // Droids and machines are made of metal + MT_Energy = 0x0008, // // Ghosts are made of energy + MT_Brush = 0x0010, // // Bushes are made of brush + MT_Door = 0x0020, // // Doors are made of door + MT_Monster = 0x0040, // // Monsters are made of monster + MT_Player = 0x0080, // // Players are made of player + MT_NPC = 0x0100, // // NPCs are made of NPC + + MT_Any = 0xFFFFFFFF, +}; + +// ---------- + +// Update the database name table in CollisionWorld if this changes + +enum SpatialDatabaseType +{ + SDT_Floors, + SDT_Statics, + SDT_Flora, + + SDT_Invalid, +}; + +// ---------- + +enum ExtentType +{ + ET_Sphere, + ET_Cylinder, + ET_Box, + ET_Detail, + ET_Component, + ET_Mesh, + ET_Terrain, + ET_OrientedBox, + ET_OrientedCylinder, + ET_Simple, + ET_Null, // Dummy extent - doesn't collide with anything + ET_Floor, + + ET_ExtentTypeCount, +}; + +// ---------- + +enum PathWalkResult +{ + PWR_MissedStartTri, // The walk failed because the path didn't enter the mesh at the starting triangle + PWR_CantEnter, // The walk failed because the path couldn't enter the starting triangle + PWR_DoesntEnter, // The walk didn't enter the floor + PWR_HitBeforeEnter, // The footprint circle hit an edge of the floor before the center entered the floor + PWR_WalkOk, // The walk succeeded + PWR_WalkFailed, // The walk failed (probably an error) + PWR_HitEdge, // The walk hit an edge of the mesh + PWR_HitPortalEdge, // The walk hit a portal-adjacent edge of the mesh + PWR_HitPast, // The walk hit an edge some time in the past + PWR_InContact, // The walk was unable to move because the point/circle was already in contact with something + PWR_ExitedMesh, // The walk exited the mesh + PWR_CenterHitEdge, // The center of the circle hit an edge + PWR_CenterInContact, // The center of the circle is in contact with an edge + PWR_StartLocInvalid, // bad starting point for the walk (embedded in a wall or something) + + PWR_Invalid, +}; + +// ---------- + +enum QueryInteractionResult +{ + QIR_None, + QIR_HitTerrain, + QIR_HitObjectExtent, + QIR_HitObjectFloor, + QIR_HitCellExtent, + QIR_HitCellFloor, + QIR_MissedTarget, + + QIR_Invalid, +}; + +// ====================================================================== + +#endif //#ifndef INCLUDED_CollisionEnums_H diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollisionInfo.h b/engine/shared/library/sharedCollision/src/shared/core/CollisionInfo.h new file mode 100644 index 00000000..22ddf817 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollisionInfo.h @@ -0,0 +1,121 @@ +//=================================================================== +// +// CollisionInfo.h +// asommers 2-10-99 +// +// copyright 1999, bootprint entertainment +// copyright 2002, sony online entertainment +// +//=================================================================== + +#ifndef INCLUDED_CollisionInfo_H +#define INCLUDED_CollisionInfo_H + +//=================================================================== + +#include "sharedMath/Vector.h" + +class Object; + +//=================================================================== + +class CollisionInfo +{ +public: + + CollisionInfo (); + ~CollisionInfo (); + + const Object* getObject () const; + void setObject (const Object* object); + const Vector& getPoint () const; + void setPoint (const Vector& point); + const Vector& getNormal () const; + void setNormal (const Vector& normal); + float getTime () const; + void setTime (float time); + +private: + + const Object* m_object; + Vector m_point; + Vector m_normal; + float m_time; +}; + +//=================================================================== + +inline CollisionInfo::CollisionInfo () : + m_object (0), + m_point (), + m_normal (), + m_time (0.f) +{ +} + +//------------------------------------------------------------------- + +inline CollisionInfo::~CollisionInfo () +{ + m_object = 0; +} + +//------------------------------------------------------------------- + +inline const Object* CollisionInfo::getObject () const +{ + return m_object; +} + +//------------------------------------------------------------------- + +inline void CollisionInfo::setObject (const Object* const object) +{ + m_object = object; +} + +//------------------------------------------------------------------- + +inline const Vector& CollisionInfo::getPoint () const +{ + return m_point; +} + +//------------------------------------------------------------------- + +inline void CollisionInfo::setPoint (const Vector& point) +{ + m_point = point; +} + +//------------------------------------------------------------------- + +inline const Vector& CollisionInfo::getNormal () const +{ + return m_normal; +} + +//------------------------------------------------------------------- + +inline void CollisionInfo::setNormal (const Vector& normal) +{ + m_normal = normal; +} + +//------------------------------------------------------------------- + +inline float CollisionInfo::getTime () const +{ + return m_time; +} + +//------------------------------------------------------------------- + +inline void CollisionInfo::setTime (const float time) +{ + m_time = time; +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollisionMesh.cpp b/engine/shared/library/sharedCollision/src/shared/core/CollisionMesh.cpp new file mode 100644 index 00000000..80a0e930 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollisionMesh.cpp @@ -0,0 +1,1085 @@ +// ====================================================================== +// +// CollisionMesh.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/CollisionMesh.h" + +#include "sharedCollision/SimpleExtent.h" +#include "sharedCollision/FloorTri.h" +#include "sharedCollision/Intersect3d.h" +#include "sharedCollision/Overlap3d.h" +#include "sharedCollision/Distance3d.h" +#include "sharedCollision/CollisionUtils.h" +#include "sharedCollision/ConfigSharedCollision.h" +#include "sharedCollision/BoxTree.h" +#include "sharedCollision/FloorLocator.h" + +#include "sharedMath/Triangle3d.h" +#include "sharedMath/AxialBox.h" +#include "sharedMath/Transform.h" +#include "sharedMath/IndexedTriangleList.h" +#include "sharedMath/Sphere.h" +#include "sharedMath/Ray3d.h" +#include "sharedMath/Range.h" +#include "sharedMath/Line3d.h" +#include "sharedMath/Segment3d.h" +#include "sharedMath/ShapeUtils.h" + +#include +#include +#include + +namespace CollisionMeshNamespace +{ + +static std::vector gs_tempResultVec; + +} + +using namespace CollisionMeshNamespace; + +// ---------------------------------------------------------------------- +// Accessories + +int CollisionMesh::FindEdgeDirected ( IndexedTri const & T, int index1, int index2 ) +{ + for(int i = 0; i < 3; i++) + { + if( (T.getCornerIndex(i) == index1) && + (T.getCornerIndex(i+1) == index2) ) + { + return i; + } + } + + return -1; +} + +// ---------- + +int CollisionMesh::FindEdgeUndirected ( IndexedTri const & T, int index1, int index2 ) +{ + for(int i = 0; i < 3; i++) + { + if( (T.getCornerIndex(i) == index1) && + (T.getCornerIndex(i+1) == index2) ) + { + return i; + } + + if( (T.getCornerIndex(i+1) == index1) && + (T.getCornerIndex(i) == index2) ) + { + return i; + } + } + + return -1; +} + +// ---------- + +void CollisionMesh::LinkTris( ConnectedTri & A, ConnectedTri & B) +{ + for(int edgeA = 0; edgeA < 3; edgeA++) + { + int index1 = A.getCornerIndex(edgeA); + int index2 = A.getCornerIndex(edgeA+1); + + int edgeB = FindEdgeDirected(B,index2,index1); + + if(edgeB != -1) + { + A.setNeighborIndex(edgeA,B.getIndex()); + B.setNeighborIndex(edgeB,A.getIndex()); + } + } +} + +// ---------------------------------------------------------------------- + +CollisionMesh::CollisionMesh() +: CollisionSurface(), + m_id(0), + m_version(1), + m_boxTree(NULL), + m_extent(NULL), + m_boundsDirty(true) +{ + m_extent = new SimpleExtent( MultiShape(AxialBox()) ); +} + +CollisionMesh::~CollisionMesh() +{ + delete m_boxTree; + m_boxTree = NULL; + + delete m_extent; + m_extent = NULL; +} + +// ---------------------------------------------------------------------- + +Transform const & CollisionMesh::getTransform_o2p ( void ) const +{ + return Transform::identity; +} + +Transform const & CollisionMesh::getTransform_o2w ( void ) const +{ + return Transform::identity; +} + +float CollisionMesh::getScale ( void ) const +{ + return 1.0f; +} + +BaseExtent const * CollisionMesh::getExtent_l ( void ) const +{ + return m_extent; +} + +BaseExtent const * CollisionMesh::getExtent_p ( void ) const +{ + return m_extent; +} + +// ---------------------------------------------------------------------- + +void CollisionMesh::clear ( void ) +{ + delete m_boxTree; + m_boxTree = NULL; +} + +// ---------------------------------------------------------------------- +// Brute-force vertex merge + +void CollisionMesh::merge ( void ) +{ + int nVerts = getVertexCount(); + int nTris = getTriCount(); + + real epsilon = 0.0000000000001f; + + // ---------- + // A temporary table that we'll use to remap vertex indices when + // we're done with the merge. + + IntVector vertRemap(nVerts); + + for(int iRemap = 0; iRemap < nVerts; iRemap++) + { + vertRemap[iRemap] = static_cast(iRemap); + } + + // ---------- + + for(int iVert = 0; iVert < nVerts; iVert++) + { + // If this vertex has already been remapped, skip it. + + if(vertRemap[iVert] != static_cast(iVert)) continue; + + // ---------- + // Otherwise, remap all vertices in the rest of the array that are + // mergeable with this vertex so that they reference this vertex + + for (int jVert = iVert+1; jVert < nVerts; jVert++) + { + Vector const & A = getVertex(iVert); + Vector const & B = getVertex(jVert); + + if(A == B) + { + vertRemap[jVert] = static_cast(iVert); + } + else if(A.magnitudeBetween(B) <= epsilon) + { + vertRemap[jVert] = static_cast(iVert); + } + } + } + + // ---------- + // Done building the remapping table, remap all our floor tris + + for(int iTri = 0; iTri < nTris; iTri++) + { + IndexedTri I = getIndexedTri(iTri); + + int iA = vertRemap[ I.getCornerIndex(0) ]; + int iB = vertRemap[ I.getCornerIndex(1) ]; + int iC = vertRemap[ I.getCornerIndex(2) ]; + + I.setCornerIndex( 0, iA ); + I.setCornerIndex( 1, iB ); + I.setCornerIndex( 2, iC ); + + setIndexedTri(iTri,I); + } + + setBoundsDirty(true); +} + +// ---------------------------------------------------------------------- +// Remove all degenerate triangles from the mesh + +int CollisionMesh::scrub ( void ) +{ + IntVector badTris; + + for(int iTri = 0; iTri < getTriCount(); iTri++) + { + if(getTriangle(iTri).isDegenerate()) + { + badTris.push_back(iTri); + } + } + + int nBadTris = static_cast(badTris.size()); + + deleteTris(badTris); + + setBoundsDirty(true); + + return nBadTris; +} + +// ---------------------------------------------------------------------- + +void CollisionMesh::calcBounds() const +{ + AxialBox bounds; + + for(int i = 0; i < getVertexCount(); i++) + { + bounds.add( getVertex(i) ); + } + + m_extent->setShape( MultiShape(bounds) ); + + setBoundsDirty(false); +} + +// ---------------------------------------------------------------------- + +void CollisionMesh::setBoundsDirty(bool const dirty) const +{ + bool const finalizeTheData = m_boundsDirty && !dirty; + m_boundsDirty = dirty; + + if (finalizeTheData) + const_cast(this)->finalizeData(); +} + +// ---------------------------------------------------------------------- + +AxialBox CollisionMesh::getBoundingABox ( void ) const +{ + if(m_boundsDirty) + { + calcBounds(); + } + + return m_extent->getShape().getAxialBox(); +} + +// ---------- + +Sphere CollisionMesh::getBoundingSphere ( void ) const +{ + if(m_boundsDirty) + { + calcBounds(); + } + + return m_extent->getShape().getBoundingSphere(); +} + +// ---------------------------------------------------------------------- + +// This has the potential to be a little slow depending on the size of the +// polygon soup. If that becomes the case we can look at storing the soup +// more efficiently (octree, etc) +bool CollisionMesh::intersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, float * time) const +{ + Vector direction(end - begin); + + bool hit = false; + + if (direction.normalize()) + { + Ray3d const ray(begin, direction); + + real bestTime = REAL_MAX; + int bestTriangle = -1; + + int const nTris = getTriCount(); + + for(int i = 0; i < nTris; ++i) + { + // be nice if getTriangle returned a pointer or a const ref + // I'm hesitant to change it at this moment -- tmf + Triangle3d const triangle(getTriangle(i)); + Vector hitPoint; + real t; + + if (Intersect3d::IntersectRayTriSided(ray, triangle, hitPoint, t)) + { + if (t < bestTime) + { + bestTime = t; + bestTriangle = i; + } + hit = true; + } + } + + if (hit) + { + if (surfaceNormal) + { + *surfaceNormal = getTriangle(bestTriangle).getNormal(); + } + if (time) + { + float const magnitude = (end - begin).magnitude(); + *time = bestTime / magnitude; + } + } + } + + return(hit); +} + +bool CollisionMesh::testIntersect ( Line3d const & L, int triId ) const { return Overlap3d::TestSided(L,getTriangle(triId)); } +bool CollisionMesh::testIntersect ( Ray3d const & R, int triId ) const { return Overlap3d::TestSided(R,getTriangle(triId)); } +bool CollisionMesh::testIntersect ( Segment3d const & S, int triId ) const { return Overlap3d::TestSided(S,getTriangle(triId)); } + +bool CollisionMesh::testIntersectUnsided ( Line3d const & L, int triId ) const { return Overlap3d::TestUnsided(L,getTriangle(triId)); } +bool CollisionMesh::testIntersectUnsided ( Ray3d const & R, int triId ) const { return Overlap3d::TestUnsided(R,getTriangle(triId)); } +bool CollisionMesh::testIntersectUnsided ( Segment3d const & S, int triId ) const { return Overlap3d::TestUnsided(S,getTriangle(triId)); } + +// ---------------------------------------------------------------------- + +template +static inline bool templateTestIntersectSided ( TestShape const & shape, CollisionMesh const & mesh, int triId ) +{ + return Overlap3d::TestSided(shape,mesh.getTriangle(triId)); +} + +template +static inline bool templateTestIntersectUnsided ( TestShape const & shape, CollisionMesh const & mesh, int triId ) +{ + return Overlap3d::TestUnsided(shape,mesh.getTriangle(triId)); +} + +// ---------- + +template +static inline bool templateTestIntersectSided_slow ( TestShape const & shape, CollisionMesh const & mesh ) +{ + int nTris = mesh.getTriCount(); + + for(int triId = 0; triId < nTris; triId++) + { + if(templateTestIntersectSided(shape,mesh,triId)) + { + return true; + } + } + + return false; +} + +// ---------- + +template +static inline bool templateTestIntersectSided_fast ( TestShape const & shape, CollisionMesh const & mesh ) +{ + BoxTree const * tree = mesh.getBoxTree(); + + if(!tree) + { + return templateTestIntersectSided_slow(shape,mesh); + } + + // ---------- + + IntVector & tempResults = mesh.getTempResultVector(); + + tempResults.clear(); + + tree->testOverlap(shape,tempResults); + + int resultCount = tempResults.size(); + + for(int i = 0; i < resultCount; i++) + { + int triId = tempResults[i]; + + if(templateTestIntersectSided(shape,mesh,triId)) + { + return true; + } + } + + return false; +} + +// ---------- + +template +static inline bool templateTestIntersectSided ( TestShape const & shape, CollisionMesh const & mesh ) +{ +#if DEBUG_LEVEL == DEBUG_LEVEL_DEBUG + + if(ConfigSharedCollision::getEnableCollisionValidate()) + { + bool hitFast = templateTestIntersectSided_fast(shape,mesh); + bool hitSlow = templateTestIntersectSided_slow(shape,mesh); + + DEBUG_FATAL(hitFast != hitSlow,("CollisionMesh::test - Fast and slow versions disagree\n")); + + return hitFast; + } + else + { + return templateTestIntersectSided_fast(shape,mesh); + } + +#else + + return templateTestIntersectSided_fast(shape,mesh); + +#endif +} + +// ---------- + +bool CollisionMesh::testIntersect ( Line3d const & line ) const { return templateTestIntersectSided(line,*this); } +bool CollisionMesh::testIntersect ( Ray3d const & ray ) const { return templateTestIntersectSided(ray,*this); } +bool CollisionMesh::testIntersect ( Segment3d const & segment ) const { return templateTestIntersectSided(segment,*this); } + +// ---------------------------------------------------------------------- + +void CollisionMesh::makeResult ( bool hit, int id, Vector const & pos, float param, ContactPoint & result ) const +{ + UNREF(hit); + + result = ContactPoint(this,pos,id,param); +} + +void CollisionMesh::makeResult ( bool hit, int id, Vector const & pos, float param, LineIntersectResult & result ) const +{ + UNREF(hit); + + result = LineIntersectResult( true, param, ContactPoint(this,pos,id,param) ); +} + +void CollisionMesh::makeResult ( bool hit, int id, Vector const & pos, float param, bool & result ) const +{ + result = hit; + + UNREF(id); + UNREF(pos); + UNREF(param); +} + +// ---------- + +template< class TestShape, class ResultType > +static inline bool templateTestIntersectSided ( TestShape const & shape, CollisionMesh const & mesh, int triId, ResultType & result ) +{ + Vector newPos; + real param; + + bool hit = Intersect3d::IntersectSided(shape,mesh.getTriangle(triId),newPos,param); + + if(hit) + { + mesh.makeResult(hit,triId,newPos,param,result); + } + + return hit; +} + +template< class TestShape, class ResultType > +static inline bool templateTestIntersectUnsided ( TestShape const & shape, CollisionMesh const & mesh, int triId, ResultType & result ) +{ + Vector newPos; + real param; + + bool hit = Intersect3d::IntersectUnsided(shape,mesh.getTriangle(triId),newPos,param); + + if(hit) + { + mesh.makeResult(hit,triId,newPos,param,result); + } + + return hit; +} + +// ---------- + +bool CollisionMesh::testIntersect ( Line3d const & L, int triId, ContactPoint & result ) const { return templateTestIntersectSided(L,*this,triId,result); } +bool CollisionMesh::testIntersect ( Ray3d const & R, int triId, ContactPoint & result ) const { return templateTestIntersectSided(R,*this,triId,result); } +bool CollisionMesh::testIntersect ( Segment3d const & S, int triId, ContactPoint & result ) const { return templateTestIntersectSided(S,*this,triId,result); } + +bool CollisionMesh::testIntersectUnsided ( Line3d const & L, int triId, ContactPoint & result ) const { return templateTestIntersectUnsided(L,*this,triId,result); } +bool CollisionMesh::testIntersectUnsided ( Ray3d const & R, int triId, ContactPoint & result ) const { return templateTestIntersectUnsided(R,*this,triId,result); } +bool CollisionMesh::testIntersectUnsided ( Segment3d const & S, int triId, ContactPoint & result ) const { return templateTestIntersectUnsided(S,*this,triId,result); } + +bool CollisionMesh::testIntersect ( Line3d const & L, int triId, LineIntersectResult & result ) const { return templateTestIntersectSided(L,*this,triId,result); } +bool CollisionMesh::testIntersect ( Ray3d const & R, int triId, LineIntersectResult & result ) const { return templateTestIntersectSided(R,*this,triId,result); } +bool CollisionMesh::testIntersect ( Segment3d const & S, int triId, LineIntersectResult & result ) const { return templateTestIntersectSided(S,*this,triId,result); } + +// ---------------------------------------------------------------------- + +template< class TestShape, class ResultType > +static inline bool templateIntersectAny_slow ( TestShape const & shape, CollisionMesh const & mesh, ResultType & result ) +{ + int nTris = mesh.getTriCount(); + + for(int i = 0; i < nTris; i++) + { + if(templateTestIntersectSided(shape,mesh,i,result)) + { + return true; + } + } + + return false; +} + +// ---------- + +template< class TestShape, class ResultType > +static inline bool templateIntersectAny_fast ( TestShape const & shape, CollisionMesh const & mesh, ResultType & result ) +{ + BoxTree const * tree = mesh.getBoxTree(); + + if(!tree) + { + return templateIntersectAny_slow(shape,mesh,result); + } + + // ---------- + + IntVector & tempResults = mesh.getTempResultVector(); + + tempResults.clear(); + + tree->testOverlap(shape,tempResults); + + int resultCount = tempResults.size(); + + for(int i = 0; i < resultCount; i++) + { + int triId = tempResults[i]; + + if(templateTestIntersectSided(shape,mesh,triId,result)) + { + return true; + } + } + + return false; +} + +// ---------- + +template< class TestShape, class ResultType > +static inline bool templateIntersectAny ( TestShape const & shape, CollisionMesh const & mesh, ResultType & result ) +{ +#if DEBUG_LEVEL == DEBUG_LEVEL_DEBUG + + if(ConfigSharedCollision::getEnableCollisionValidate()) + { + bool hitFast = templateIntersectAny_fast(shape,mesh,result); + bool hitSlow = templateIntersectAny_slow(shape,mesh,result); + + DEBUG_FATAL(hitFast != hitSlow,("CollisionMesh::test - Fast and slow versions disagree\n")); + + return hitFast; + } + else + { + return templateIntersectAny_fast(shape,mesh,result); + } + +#else + + return templateIntersectAny_fast(shape,mesh,result); + +#endif +} + +// ---------- + +bool CollisionMesh::intersectAny ( Line3d const & L, ContactPoint & result ) const { return templateIntersectAny(L,*this,result); } +bool CollisionMesh::intersectAny ( Ray3d const & R, ContactPoint & result ) const { return templateIntersectAny(R,*this,result); } +bool CollisionMesh::intersectAny ( Segment3d const & S, ContactPoint & result ) const { return templateIntersectAny(S,*this,result); } + +// ---------------------------------------------------------------------- + +bool CollisionMesh::intersectClosest ( Line3d const & line, ContactPoint & closest ) const +{ + if(!testBounds(line)) + { + return false; + } + + // ---------- + + FloorLocator closestFront; + FloorLocator closestBack; + + IGNORE_RETURN( findClosestPair(line,-1,closestFront,closestBack) ); + + if(closestFront.isAttached()) + { + closest = closestFront; + return true; + } + else if(closestBack.isAttached()) + { + closest = closestBack; + return true; + } + else + { + closest = FloorLocator::invalid; + return false; + } +} + +// ---------- + +bool CollisionMesh::intersectClosest ( Ray3d const & ray, ContactPoint & closest ) const +{ + Line3d line(ray.getPoint(),ray.getNormal()); + + FloorLocator closestFront; + FloorLocator closestBack; + + IGNORE_RETURN( findClosestPair(line,-1,closestFront,closestBack) ); + + if(closestFront.isAttached()) + { + closest = closestFront; + return true; + } + else + { + closest = FloorLocator::invalid; + return false; + } +} + +// ---------- + +bool CollisionMesh::intersectClosest ( Segment3d const & seg, ContactPoint & closest ) const +{ + Line3d line(seg.getBegin(),seg.getDelta()); + + FloorLocator closestFront; + FloorLocator closestBack; + + IGNORE_RETURN( findClosestPair(line,-1,closestFront,closestBack) ); + + if(closestFront.isAttached() && (closestFront.getOffset() <= 1.0f)) + { + closest = closestFront; + return true; + } + else + { + closest = FloorLocator::invalid; + return false; + } +} + +// ---------------------------------------------------------------------- + +template< class TestShape, class ResultType > +static inline int templateIntersectAll_slow ( TestShape const & shape, CollisionMesh const & mesh, std::vector & results ) +{ + ResultType result; + int counter = 0; + + for(int i = 0; i < mesh.getTriCount(); i++) + { + if(templateTestIntersectSided(shape,mesh,i,result)) + { + results.push_back( result ); + counter++; + } + } + + return counter; +} + +// ---------- + +template< class TestShape, class ResultType > +static inline int templateIntersectAll_fast ( TestShape const & shape, CollisionMesh const & mesh, std::vector & results ) +{ + BoxTree const * tree = mesh.getBoxTree(); + + if(!tree) + { + return templateIntersectAll_slow(shape,mesh,results); + } + + // ---------- + + IntVector & tempResults = mesh.getTempResultVector(); + + tempResults.clear(); + + tree->testOverlap(shape,tempResults); + + ResultType result; + int counter = 0; + + int resultCount = tempResults.size(); + + for(int i = 0; i < resultCount; i++) + { + int triId = tempResults[i]; + + if(templateTestIntersectSided(shape,mesh,triId,result)) + { + results.push_back( result ); + counter++; + } + } + + return counter; +} + +// ---------- + +template< class TestShape, class ResultVector > +static inline int templateIntersectAll ( TestShape const & shape, CollisionMesh const & mesh, ResultVector & results ) +{ +#if DEBUG_LEVEL == DEBUG_LEVEL_DEBUG + + if(ConfigSharedCollision::getEnableCollisionValidate()) + { + ResultVector tempResults; + + int hitFast = templateIntersectAll_fast(shape,mesh,results); + int hitSlow = templateIntersectAll_slow(shape,mesh,tempResults); + + DEBUG_FATAL(hitFast != hitSlow,("CollisionMesh::intersectRange - Fast and slow versions disagree\n")); + + return hitFast; + } + else + { + return templateIntersectAll_fast(shape,mesh,results); + } + +#else + + return templateIntersectAll_fast(shape,mesh,results); + +#endif +} + +// ---------- + +int CollisionMesh::intersectAll ( Line3d const & L, LineIntersectResultVec & results ) const { return templateIntersectAll(L,*this,results); } +int CollisionMesh::intersectAll ( Ray3d const & R, LineIntersectResultVec & results ) const { return templateIntersectAll(R,*this,results); } +int CollisionMesh::intersectAll ( Segment3d const & S, LineIntersectResultVec & results ) const { return templateIntersectAll(S,*this,results); } + +// ---------------------------------------------------------------------- + +template< class TestShape > +static inline Range templateIntersectRange_slow( CollisionMesh const & mesh, TestShape const & shape ) +{ + ContactPoint contact; + + Range range( Range::empty ); + + int triCount = mesh.getTriCount(); + + for(int i = 0; i < triCount; i++) + { + if(templateTestIntersectUnsided(shape,mesh,i,contact)) + { + range = Range::enclose( range, contact.getOffset() ); + } + } + + return range; +} + +// ---------- + +template< class TestShape > +static inline Range templateIntersectRange_fast( CollisionMesh const & mesh, TestShape const & shape ) +{ + BoxTree const * tree = mesh.getBoxTree(); + + if(!tree) + { + return templateIntersectRange_slow(mesh,shape); + } + + // ---------- + + IntVector & tempResults = mesh.getTempResultVector(); + + tempResults.clear(); + + tree->testOverlap(shape,tempResults); + + ContactPoint contact; + + Range range( Range::empty ); + + int resultCount = tempResults.size(); + + for(int i = 0; i < resultCount; i++) + { + int triId = tempResults[i]; + + if(templateTestIntersectUnsided(shape,mesh,triId,contact)) + { + range = Range::enclose( range, contact.getOffset() ); + } + } + + return range; +} + +// ---------- + +template< class TestShape > +static inline Range templateIntersectRange( CollisionMesh const & mesh, TestShape const & shape ) +{ +#if DEBUG_LEVEL == DEBUG_LEVEL_DEBUG + + if(ConfigSharedCollision::getEnableCollisionValidate()) + { + Range hitFast = templateIntersectRange_fast(mesh,shape); + Range hitSlow = templateIntersectRange_slow(mesh,shape); + + DEBUG_FATAL(hitFast != hitSlow,("CollisionMesh::intersectRange - Fast and slow versions disagree\n")); + + return hitFast; + } + else + { + return templateIntersectRange_fast(mesh,shape); + } + +#else + + return templateIntersectRange_fast(mesh,shape); + +#endif +} + +Range CollisionMesh::intersectRange ( Line3d const & L ) const { return templateIntersectRange(*this,L); } +Range CollisionMesh::intersectRange ( Ray3d const & R ) const { return templateIntersectRange(*this,R); } +Range CollisionMesh::intersectRange ( Segment3d const & S ) const { return templateIntersectRange(*this,S); } + +// ---------------------------------------------------------------------- + +bool CollisionMesh::findClosestPair ( FloorLocator const & testLoc, + Vector const & down, + FloorLocator & outClosestAbove, + FloorLocator & outClosestBelow ) const +{ + Line3d line(testLoc.getPosition_l(),-down); + int ignoreId = testLoc.getId(); + + return findClosestPair( line, ignoreId, outClosestAbove, outClosestBelow ); +} + +// ---------------------------------------------------------------------- +// Find the pair of triangles that intersect the line and are closest +// to the line's origin. + +bool CollisionMesh::findClosestPair ( Line3d const & line, + int ignoreId, + FloorLocator & outClosestFront, + FloorLocator & outClosestBack ) const +{ + outClosestFront = FloorLocator( this, Vector::zero, -1, REAL_MAX, 0.0f ); + outClosestBack = FloorLocator( this, Vector::zero, -1, -REAL_MAX, 0.0f ); + + if(!testBounds(line)) + { + return false; + } + + static LineIntersectResultVec results; + + results.clear(); + + if(intersectAll(line,results)) + { + // ---------- + // Search the triangles for one that is below the test point + + int resultCount = results.size(); + + for(int i = 0; i < resultCount; i++) + { + LineIntersectResult & result = results[i]; + + ContactPoint & contact = result.m_hitLoc; + + if(contact.getId() == ignoreId) continue; + + // ---------- + // Found a triangle under/above the point. + + if(contact.getOffset() > 0) + { + if(contact.getOffset() < outClosestFront.getOffset()) + { + outClosestFront = contact; + } + } + else + { + if(contact.getOffset() > outClosestBack.getOffset()) + { + outClosestBack = contact; + } + } + } + + return outClosestBack.isAttached() || outClosestFront.isAttached(); + } + else + { + return false; + } +} + +// ---------------------------------------------------------------------- + +bool CollisionMesh::testBounds ( Line3d const & line ) const +{ + return Overlap3d::Test( line, getBoundingABox() ); +} + +bool CollisionMesh::testBounds ( Segment3d const & seg ) const +{ + return Overlap3d::Test( seg, getBoundingABox() ); +} + +bool CollisionMesh::testBounds ( Ray3d const & ray ) const +{ + return Overlap3d::Test( ray, getBoundingABox() ); +} + +// ---------------------------------------------------------------------- + +void CollisionMesh::transform( CollisionMesh const * sourceMesh, Transform const & tform, float scale ) +{ + DEBUG_FATAL( sourceMesh->getVertexCount() != getVertexCount(), ("MeshExtent::transform - Source extent has a different number of vertices")); + DEBUG_FATAL( sourceMesh->getTriCount() != getTriCount(), ("MeshExtent::transform - Source extent has a different number of indices")); + + // ---------- + + for(int i = 0; i < sourceMesh->getVertexCount(); i++) + { + Vector newVert = tform.rotateTranslate_l2p( sourceMesh->getVertex(i) * scale); + + setVertex(i,newVert); + } + + setBoundsDirty(true); +} + +// ---------------------------------------------------------------------- + +bool CollisionMesh::hasBoxTree ( void ) const +{ + return m_boxTree != NULL; +} + +// ---------- + +void CollisionMesh::buildBoxTree ( void ) +{ + typedef std::vector BoxVec; + + BoxVec tempBoxes; + + for(int i = 0; i < getTriCount(); i++) + { + Triangle3d tri = getTriangle(i); + + AxialBox box(tri.getCornerA(), tri.getCornerB()); + box.add(tri.getCornerC()); + + // Inflate the box by a centimeter to avoid possible numerical inaccuracy + + box = ShapeUtils::inflate(box,0.01f); + + tempBoxes.push_back(box); + } + + delete m_boxTree; + m_boxTree = new BoxTree(); + + m_boxTree->build(tempBoxes); +} + +// ---------------------------------------------------------------------- + +IntVector & CollisionMesh::getTempResultVector ( void ) const +{ + return gs_tempResultVec; +} + +// ---------------------------------------------------------------------- + +void CollisionMesh::getVertices ( VectorVector & outVerts ) const +{ + int nVerts = getVertexCount(); + + outVerts.clear(); + outVerts.reserve(nVerts); + + for(int i = 0; i < nVerts; i++) + { + outVerts.push_back( getVertex(i) ); + } +} + +void CollisionMesh::getIndices ( IntVector & outIndices ) const +{ + int nTris = getTriCount(); + + outIndices.clear(); + outIndices.reserve(nTris * 3); + + for(int i = 0; i < nTris; i++) + { + IndexedTri const & tri = getIndexedTri(i); + + outIndices.push_back(tri.getCornerIndex(0)); + outIndices.push_back(tri.getCornerIndex(1)); + outIndices.push_back(tri.getCornerIndex(2)); + } +} + +void CollisionMesh::finalizeData() +{ +} diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollisionMesh.h b/engine/shared/library/sharedCollision/src/shared/core/CollisionMesh.h new file mode 100644 index 00000000..4fc93148 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollisionMesh.h @@ -0,0 +1,231 @@ +// ====================================================================== +// +// CollisionMesh.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_CollisionMesh_H +#define INCLUDED_CollisionMesh_H + +// ====================================================================== + +#include "sharedCollision/CollisionSurface.h" + +class Iff; +class IndexedTri; +class ConnectedTri; +class Triangle3d; +class IndexedTriangleList; +class AxialBox; +class SimpleExtent; +class Sphere; +class Vector; +class Line3d; +class Ray3d; +class Segment3d; +class Range; +class BoxTree; +class FloorLocator; + +typedef stdvector::fwd VectorVector; +typedef stdvector::fwd IntVector; + +// ====================================================================== + +class CollisionMesh : public CollisionSurface +{ +public: + + explicit CollisionMesh(); + virtual ~CollisionMesh(); + + // ---------- + // Interface inherited from CollisionSurface + + virtual Transform const & getTransform_o2p ( void ) const; + virtual Transform const & getTransform_o2w ( void ) const; + virtual float getScale ( void ) const; + + virtual BaseExtent const * getExtent_l ( void ) const; + virtual BaseExtent const * getExtent_p ( void ) const; + + // ---------- + + virtual void clear ( void ); + virtual void merge ( void ); + virtual int scrub ( void ); + + virtual void calcBounds ( void ) const; + + virtual bool getBoundsDirty ( void ) const; + virtual void setBoundsDirty ( bool dirty ) const; + + virtual AxialBox getBoundingABox ( void ) const; + virtual Sphere getBoundingSphere ( void ) const; + + // ---------- + // Basic interface - accessors + + virtual int getVertexCount ( void ) const = 0; + virtual Vector const & getVertex ( int whichVertex ) const = 0; + virtual void setVertex ( int whichVertex, Vector const & newPoint ) = 0; + + virtual int getTriCount ( void ) const = 0; + virtual IndexedTri const & getIndexedTri ( int whichTri ) const = 0; + virtual void setIndexedTri ( int whichTri, IndexedTri const & newTri ) = 0; + + virtual Triangle3d getTriangle ( int whichTri ) const = 0; + + virtual void deleteVertex ( int whichVertex ) = 0; + virtual void deleteVertices ( IntVector const & vertIndices ) = 0; + + virtual void deleteTri ( int whichTri ) = 0; + virtual void deleteTris ( IntVector const & triIndices ) = 0; + + // ---------- + // Single-triangle intersection methods + + virtual bool intersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, float * time ) const; + + virtual bool testIntersect ( Line3d const & L, int triId ) const; + virtual bool testIntersect ( Ray3d const & R, int triId ) const; + virtual bool testIntersect ( Segment3d const & S, int triId ) const; + + virtual bool testIntersectUnsided ( Line3d const & L, int triId ) const; + virtual bool testIntersectUnsided ( Ray3d const & R, int triId ) const; + virtual bool testIntersectUnsided ( Segment3d const & S, int triId ) const; + + virtual bool testIntersect ( Line3d const & L, int triId, ContactPoint & outLoc ) const; + virtual bool testIntersect ( Ray3d const & R, int triId, ContactPoint & outLoc ) const; + virtual bool testIntersect ( Segment3d const & S, int triId, ContactPoint & outLoc ) const; + + virtual bool testIntersectUnsided ( Line3d const & L, int triId, ContactPoint & outLoc ) const; + virtual bool testIntersectUnsided ( Ray3d const & R, int triId, ContactPoint & outLoc ) const; + virtual bool testIntersectUnsided ( Segment3d const & S, int triId, ContactPoint & outLoc ) const; + + virtual bool testIntersect ( Line3d const & L, int triId, LineIntersectResult & result ) const; + virtual bool testIntersect ( Ray3d const & R, int triId, LineIntersectResult & result ) const; + virtual bool testIntersect ( Segment3d const & S, int triId, LineIntersectResult & result ) const; + + // ---------- + // Whole-mesh intersection methods + + // Search for any intersection with the mesh, return true if there is one + + virtual bool testIntersect ( Line3d const & line ) const; + virtual bool testIntersect ( Ray3d const & ray ) const; + virtual bool testIntersect ( Segment3d const & seg ) const; + + // Search for any point of intersection with the linear primitive + + virtual bool intersectAny ( Line3d const & line, ContactPoint & outLoc ) const; + virtual bool intersectAny ( Ray3d const & ray, ContactPoint & outLoc ) const; + virtual bool intersectAny ( Segment3d const & seg, ContactPoint & outLoc ) const; + + // Search for the point of intersection with the linear primitive that has the minimum + // parametric intersection time. + + virtual bool intersectClosest( Line3d const & line, ContactPoint & outLoc ) const; + virtual bool intersectClosest( Ray3d const & ray, ContactPoint & outLoc ) const; + virtual bool intersectClosest( Segment3d const & seg, ContactPoint & outLoc) const; + + // Find all intersection points with the linear primitive. Return value is the number of hits + + virtual int intersectAll ( Line3d const & line, LineIntersectResultVec & results ) const; + virtual int intersectAll ( Ray3d const & ray, LineIntersectResultVec & results ) const; + virtual int intersectAll ( Segment3d const & seg, LineIntersectResultVec & results ) const; + + // Return the parametric intersection time range with the linear primitive + + virtual Range intersectRange ( Line3d const & line ) const; + virtual Range intersectRange ( Ray3d const & ray ) const; + virtual Range intersectRange ( Segment3d const & seg ) const; + + // ---------- + + virtual bool findClosestPair ( Line3d const & line, + int ignoreId, + FloorLocator & outClosestFront, + FloorLocator & outClosestBack ) const; + + virtual bool findClosestPair ( FloorLocator const & testLoc, + Vector const & down, + FloorLocator & outClosestAbove, + FloorLocator & outClosestBelow ) const; + + + // ---------- + + virtual bool testBounds ( Line3d const & line ) const; + virtual bool testBounds ( Ray3d const & ray ) const; + virtual bool testBounds ( Segment3d const & seg ) const; + + // ---------- + + virtual void transform ( CollisionMesh const * sourceMesh, Transform const & tform, float scale ); + + virtual bool hasBoxTree ( void ) const; + virtual void buildBoxTree ( void ); + + virtual BoxTree const * getBoxTree ( void ) const; + + IntVector & getTempResultVector ( void ) const; + + void getVertices ( VectorVector & outVerts ) const; + void getIndices ( IntVector & outIndices ) const; + + // ---------- + // Utility functions + + //@todo - These could be moved elsewhere + + static int FindEdgeDirected ( IndexedTri const & T, int index1, int index2 ); + static int FindEdgeUndirected ( IndexedTri const & T, int index1, int index2 ); + static void LinkTris ( ConnectedTri & A, ConnectedTri & B ); + + void makeResult ( bool hit, int id, Vector const & pos, float param, ContactPoint & result ) const; + void makeResult ( bool hit, int id, Vector const & pos, float param, LineIntersectResult & result ) const; + void makeResult ( bool hit, int id, Vector const & pos, float param, bool & result ) const; + +protected: + + // Disable copying + + CollisionMesh(const CollisionMesh &source); + CollisionMesh & operator =(const CollisionMesh &source); + +private: + + virtual void finalizeData(); + +protected: + + int m_id; + int m_version; + BoxTree * m_boxTree; + + mutable SimpleExtent * m_extent; + +private: + + mutable bool m_boundsDirty; +}; + +// ---------------------------------------------------------------------- + +inline bool CollisionMesh::getBoundsDirty ( void ) const +{ + return m_boundsDirty; +} + +// ---------- + +inline BoxTree const * CollisionMesh::getBoxTree ( void ) const +{ + return m_boxTree; +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollisionNotification.cpp b/engine/shared/library/sharedCollision/src/shared/core/CollisionNotification.cpp new file mode 100644 index 00000000..6fba343e --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollisionNotification.cpp @@ -0,0 +1,177 @@ +// ====================================================================== +// +// CollisionWorld.cpp +// copyright (c) 2002 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/CollisionNotification.h" + +#include "sharedCollision/CollisionProperty.h" +#include "sharedCollision/CollisionWorld.h" + +#include "sharedObject/Object.h" + +#include "sharedFoundation/Watcher.h" + +#include + +// ---------- + +struct AddRemoveQueueEntry +{ + bool m_added; + Watcher m_object; +}; + +// ---------- + +namespace CollisionNotificationNamespace +{ + +CollisionNotification g_collisionNotification; + +std::vector g_addRemoveQueue; + +}; + +using namespace CollisionNotificationNamespace; + +// ---------------------------------------------------------------------- + +CollisionNotification::CollisionNotification() +: ObjectNotification() +{ +} + +CollisionNotification::~CollisionNotification() +{ +} + +// ---------- + +CollisionNotification & CollisionNotification::getInstance ( void ) +{ + return g_collisionNotification; +} + +// ---------- + +int CollisionNotification::getPriority() const +{ + return 4; +} + +// ---------------------------------------------------------------------- + +void CollisionNotification::purgeQueue ( void ) +{ + int count = g_addRemoveQueue.size(); + + for(int i = 0; i < count; i++) + { + AddRemoveQueueEntry & entry = g_addRemoveQueue[i]; + + Object * object = entry.m_object; + + if(object == NULL) continue; + + if(entry.m_added) + { + CollisionWorld::addObject(object); + } + else + { + CollisionWorld::removeObject(object); + } + } + + g_addRemoveQueue.clear(); +} + +// ---------------------------------------------------------------------- + +void CollisionNotification::visibilityDataChanged ( Object & object ) const +{ + CollisionWorld::appearanceChanged(&object); +} + +// ---------- + +void CollisionNotification::extentChanged ( Object & object ) const +{ + CollisionWorld::appearanceChanged(&object); +} + +// ---------- + +void CollisionNotification::addToWorld ( Object & object ) const +{ + if(CollisionWorld::isUpdating()) + { + AddRemoveQueueEntry entry; + + entry.m_added = true; + entry.m_object = &object; + + g_addRemoveQueue.push_back(entry); + } + else + { + CollisionWorld::addObject(&object); + } +} + +// ---------- + +void CollisionNotification::removeFromWorld( Object & object ) const +{ + if(CollisionWorld::isUpdating()) + { + AddRemoveQueueEntry entry; + + entry.m_added = false; + entry.m_object = &object; + + g_addRemoveQueue.push_back(entry); + } + else + { + CollisionWorld::removeObject(&object); + } +} + +// ---------- + +bool CollisionNotification::positionChanged( Object & object, bool, Vector const & ) const +{ + CollisionWorld::moveObject(&object); + + return true; +} + +// ---------- + +void CollisionNotification::rotationChanged( Object & object, bool ) const +{ + CollisionWorld::moveObject(&object); +} + +// ---------- + +bool CollisionNotification::positionAndRotationChanged ( Object & object, bool, Vector const & ) const +{ + CollisionWorld::moveObject(&object); + + return true; +} + +// ---------- + +void CollisionNotification::cellChanged( Object & object, bool ) const +{ + CollisionWorld::cellChanged(&object); +} + +// ---------------------------------------------------------------------- diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollisionNotification.h b/engine/shared/library/sharedCollision/src/shared/core/CollisionNotification.h new file mode 100644 index 00000000..f1ebd34c --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollisionNotification.h @@ -0,0 +1,50 @@ +// ====================================================================== +// +// CollisionNotification.h +// copyright (c) 2001 Sony Online Entertainment +// +// ---------------------------------------------------------------------- + +#ifndef INCLUDED_CollisionNotification_H +#define INCLUDED_CollisionNotification_H + +#include "sharedObject/ObjectNotification.h" +#include "sharedMath/Vector.h" + +// ====================================================================== + +class CollisionNotification : public ObjectNotification +{ +public: + + CollisionNotification(); + virtual ~CollisionNotification(); + + static CollisionNotification & getInstance ( void ); + + static void purgeQueue ( void ); + + virtual int getPriority ( void ) const; + + virtual void visibilityDataChanged ( Object & object ) const; + virtual void extentChanged ( Object & object ) const; + + virtual void addToWorld ( Object & object ) const; + virtual void removeFromWorld ( Object & object ) const; + + virtual bool positionChanged ( Object & object, bool dueToParentChange, Vector const & oldPosition) const; + virtual void rotationChanged ( Object & object, bool dueToParentChange ) const; + virtual bool positionAndRotationChanged ( Object & object, bool dueToParentChange, Vector const & oldPosition ) const; + virtual void cellChanged ( Object & object, bool dueToParentChange ) const; + +protected: + +private: + + CollisionNotification(const CollisionNotification &); + CollisionNotification &operator =(const CollisionNotification &); +}; + +// ====================================================================== + +#endif // #ifndef INCLUDED_CollisionNotification_H diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollisionProperty.cpp b/engine/shared/library/sharedCollision/src/shared/core/CollisionProperty.cpp new file mode 100644 index 00000000..5e22c064 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollisionProperty.cpp @@ -0,0 +1,1424 @@ +// ====================================================================== +// +// CollisionProperty.h.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/CollisionProperty.h" + +#include "sharedCollision/BarrierObject.h" +#include "sharedCollision/BoxExtent.h" +#include "sharedCollision/CollisionNotification.h" +#include "sharedCollision/CollisionUtils.h" +#include "sharedCollision/CollisionWorld.h" +#include "sharedCollision/ComponentExtent.h" +#include "sharedCollision/ConfigSharedCollision.h" +#include "sharedCollision/Containment3d.h" +#include "sharedCollision/CylinderExtent.h" +#include "sharedCollision/DetailExtent.h" +#include "sharedCollision/Distance3d.h" +#include "sharedCollision/DoorObject.h" +#include "sharedCollision/Extent.h" +#include "sharedCollision/Floor.h" +#include "sharedCollision/FloorManager.h" +#include "sharedCollision/Footprint.h" +#include "sharedCollision/MeshExtent.h" +#include "sharedCollision/SimpleExtent.h" + +#include "sharedGame/GameObjectTypes.h" +#include "sharedGame/SharedCreatureObjectTemplate.h" +#include "sharedGame/SharedObjectTemplate.h" +#include "sharedGame/SharedShipObjectTemplate.h" + +#include "sharedMath/DebugShapeRenderer.h" +#include "sharedMath/SphereTree.h" + +#include "sharedObject/Appearance.h" +#include "sharedObject/AppearanceTemplate.h" +#include "sharedObject/CellProperty.h" +#include "sharedObject/PortalProperty.h" +#include "sharedObject/PortalPropertyTemplate.h" +#include "sharedObject/World.h" + +namespace CollisionPropertyNamespace +{ + CollisionProperty * ms_activeListHead = NULL; +}; + +using namespace CollisionPropertyNamespace; + +class CollisionSphereAccessor; + +// ---------------------------------------------------------------------- +// active/idle list methods + +void CollisionProperty::detachList ( void ) +{ + if(m_prev) m_prev->m_next = m_next; + if(m_next) m_next->m_prev = m_prev; + + if(this == ms_activeListHead) + { + ms_activeListHead = m_next; + } + + m_prev = NULL; + m_next = NULL; +} + +// ---------- + +void CollisionProperty::attachList ( CollisionProperty * & head ) +{ + detachList(); + + if(head) head->m_prev = this; + + m_prev = NULL; + m_next = head; + + head = this; +} + +// ---------- + +void CollisionProperty::updateIdle ( void ) +{ + m_idleCounter++; + + if(m_idleCounter >= 3) + { + if(m_footprint) + { + storePosition(); + } + + setIdle(true); + } +} + +// ---------- + +void CollisionProperty::setIdle ( bool idle ) +{ + if(isMobile()) + { + if(idle) + detachList(); + else + { + m_idleCounter = std::min(m_idleCounter,0); + + attachList(ms_activeListHead); + } + } + + modifyFlags(F_idle, idle); +} + +// ---------- + +CollisionProperty * CollisionProperty::getActiveHead ( void ) +{ + return ms_activeListHead; +} + +// ---------------------------------------------------------------------- + +Transform getTransform_o2c( Object const * object ) +{ + if(object == NULL) return Transform::identity; + + // If this object is a cell, its o2c transform is the identity transform + + if( object->getCellProperty() ) + { + return Transform::identity; + } + + // Otherwise this object's o2c transform is the product of its o2p transform + // and its parent's o2c transform. + + Transform objectToParent = object->getTransform_o2p(); + Transform parentToCell = getTransform_o2c( object->getAttachedTo() ); + + Transform objectToCell; + + objectToCell.multiply( parentToCell, objectToParent ); + + return objectToCell; +} + +// ====================================================================== + +PropertyId CollisionProperty::getClassPropertyId ( void ) +{ + return PROPERTY_HASH(Collision, 0x01D26659); +} + +// ---------------------------------------------------------------------- + +CollisionProperty::CollisionProperty( Object & owner ) +: Property( CollisionProperty::getClassPropertyId(), owner ), + m_lastTransform_w(Transform::identity), + m_lastTransform_p(Transform::identity), + m_lastCellObject(NULL), + m_stepHeight(0.0f), + m_defaultRadius(0.0f), + m_offsetX(0.0f), + m_offsetZ(0.0f), + m_extentsDirty(true), + m_extent_l(NULL), + m_extent_p(NULL), + m_sphere_l(), + m_sphere_w(), + m_scale(owner.getScale().x), + m_spatialSubdivisionHandle(NULL), + m_floor(NULL), + m_footprint(NULL), + m_idleCounter(3), + m_next(NULL), + m_prev(NULL), + m_flags(F_collidable), + m_spatialDatabaseStorageType(SpatialDatabase::Q_None) +{ + owner.addNotification(CollisionNotification::getInstance()); + + SharedObjectTemplate const * objTemplate = owner.getObjectTemplate() ? owner.getObjectTemplate()->asSharedObjectTemplate() : 0; + + initFromTemplate(objTemplate); + + initFloor(); + + if(objTemplate) + { + char const * const templateName = objTemplate->getName(); + + if(templateName) + { + char const * const found = strstr(templateName,"lair"); + + if(found != NULL) + { + setCollidable(false); + } + } + } +} + +// ---------- + +CollisionProperty::CollisionProperty( Object & owner, SharedObjectTemplate const * objTemplate ) +: Property( CollisionProperty::getClassPropertyId(), owner ), + m_lastTransform_w(Transform::identity), + m_lastTransform_p(Transform::identity), + m_lastCellObject(NULL), + m_stepHeight(0.0f), + m_defaultRadius(0.0f), + m_offsetX(0.0f), + m_offsetZ(0.0f), + m_extentsDirty(true), + m_extent_l(NULL), + m_extent_p(NULL), + m_sphere_l(), + m_sphere_w(), + m_scale(owner.getScale().x), + m_spatialSubdivisionHandle(NULL), + m_floor(NULL), + m_footprint(NULL), + m_idleCounter(3), + m_next(NULL), + m_prev(NULL), + m_flags(F_collidable), + m_spatialDatabaseStorageType(SpatialDatabase::Q_None) +{ + owner.addNotification(CollisionNotification::getInstance()); + + initFromTemplate(objTemplate); + + initFloor(); + + if(objTemplate) + { + char const * const templateName = objTemplate->getName(); + + if(templateName) + { + char const * const found = strstr(templateName,"lair"); + + if(found != NULL) + { + setCollidable(false); + } + } + } +} + +// ---------- + +void CollisionProperty::initFromTemplate( SharedObjectTemplate const * objTemplate ) +{ + if (!objTemplate) + { + m_spatialDatabaseStorageType = SpatialDatabase::Q_Static; + + return; + } + + SharedCreatureObjectTemplate const * creatureTemplate = dynamic_cast(objTemplate); + + if(creatureTemplate) + { + setMobile(true); + + m_spatialDatabaseStorageType = SpatialDatabase::Q_Dynamic; + + if(strstr(objTemplate->getCrcName().getString(), "player") != 0) + { + setPlayerControlled(true); + } + + m_stepHeight = creatureTemplate->getStepHeight(); + m_defaultRadius = creatureTemplate->getCollisionRadius(); + m_offsetX = creatureTemplate->getCollisionOffsetX(); + m_offsetZ = creatureTemplate->getCollisionOffsetZ(); + + const float waterHeight = creatureTemplate->getSwimHeight (); + + m_footprint = new Footprint( getOwner().getPosition_p(), getCollisionRadius(), this, waterHeight ); + } + else + { + if (objTemplate->getForceNoCollision()) + { + setCollidable(false); + } + + int const gameObjectType = objTemplate->getGameObjectType(); + if (GameObjectTypes::isTypeOf(gameObjectType, SharedObjectTemplate::GOT_ship)) + { + setShip(true); + + SharedShipObjectTemplate const * const shipObjectTemplate = dynamic_cast(objTemplate); + if (shipObjectTemplate) + { + setMobile(true); + + if (shipObjectTemplate->getPlayerControlled()) + setPlayerControlled(true); + } + + // ships don't have a radius specified in the template, but we'll give them one once we've updated + // the extents. to start, just give them a 1 meter default radius. + + m_stepHeight = 0.0f; + m_defaultRadius = 1.0f; + m_offsetX = 0.0f; + m_offsetZ = 0.0f; + + if (GameObjectTypes::isTypeOf(gameObjectType, SharedObjectTemplate::GOT_ship_station)) + { + m_spatialDatabaseStorageType = SpatialDatabase::Q_Static; + } + else if (GameObjectTypes::isTypeOf(gameObjectType, SharedObjectTemplate::GOT_ship_capital)) + { + // this is a hack, putting the capital ships in the static database + // this is done for the server to lessen the load... + m_spatialDatabaseStorageType = SpatialDatabase::Q_Static; + } + else if (GameObjectTypes::isTypeOf(gameObjectType, SharedObjectTemplate::GOT_ship_transport)) + { + // this is a hack, putting the transport ships in the static database + // this is done for the server to lessen the load... + m_spatialDatabaseStorageType = SpatialDatabase::Q_Static; + } + else if (GameObjectTypes::isTypeOf(gameObjectType, SharedObjectTemplate::GOT_ship_mining_asteroid_static)) + { + m_spatialDatabaseStorageType = SpatialDatabase::Q_Static; + } + else if (GameObjectTypes::isTypeOf(gameObjectType, SharedObjectTemplate::GOT_ship_mining_asteroid_dynamic)) + { + m_spatialDatabaseStorageType = SpatialDatabase::Q_Dynamic; + } + else + { + // all other ships + m_spatialDatabaseStorageType = SpatialDatabase::Q_Dynamic; + } + } + + else if (GameObjectTypes::isTypeOf(gameObjectType, SharedObjectTemplate::GOT_ship_component)) + { + m_spatialDatabaseStorageType = SpatialDatabase::Q_Dynamic; + } + + else // everything else should be static + { + m_spatialDatabaseStorageType = SpatialDatabase::Q_Static; + } + } +} + +// ---------- + +CollisionProperty::~CollisionProperty() +{ + // do this first and hope that the required data hasn't been deleted when we remove the object + + if(m_spatialSubdivisionHandle) + { + WARNING_STRICT_FATAL(true,("CollisionProperty::~CollisionProperty - Deleting a collision property that's still referenced by the collision sphere tree\n")); + + static_cast::NodeHandle *>(m_spatialSubdivisionHandle)->removeObject(); + + m_spatialSubdivisionHandle = NULL; + } + + FATAL(CollisionWorld::isUpdating(),("CollisionProperty::~CollisionProperty - Trying to destroy a collision property while the collision world is updating. This is baaaad\n")); + getOwner().removeNotification(CollisionNotification::getInstance()); + + detachList(); + + delete m_extent_l; + m_extent_l = NULL; + + delete m_extent_p; + m_extent_p = NULL; + + delete m_floor; + m_floor = NULL; + + delete m_footprint; + m_footprint = NULL; + +} + +// ---------------------------------------------------------------------- + +void CollisionProperty::attachSourceExtent ( BaseExtent * newSourceExtent ) const +{ + if(m_extent_l != newSourceExtent) + { + delete m_extent_l; + m_extent_l = newSourceExtent; + + delete m_extent_p; + m_extent_p = NULL; + + m_extentsDirty = true; + } +} + +// ---------------------------------------------------------------------- + +void CollisionProperty::setMount ( bool mount ) +{ + if (!boolEqual(isMount(), mount)) + { + modifyFlags(F_mount, mount); + + if(m_footprint) m_footprint->updateContactRadii(); + } +} + +// ---------------------------------------------------------------------- + +void CollisionProperty::setShip ( bool ship ) +{ + modifyFlags(F_ship, ship); +} + +// ---------------------------------------------------------------------- + +void CollisionProperty::initFloor ( void ) +{ + if (isMobile()) + return; + + if(m_floor != NULL) + return; + + // ---------- + + char const *floorName = NULL; + + Appearance * appearance = getOwner().getAppearance(); + if(appearance) + floorName = appearance->getFloorName(); + + //@todo - HACK - Server-side POBs don't currently load appearances, which means + // that they don't load their building skirts. If we couldn't get a floor from + // the appearance, try to get one from the portal property instead. + if (!floorName) + { + PortalProperty * portalProperty = getOwner().getPortalProperty(); + if(portalProperty) + floorName = portalProperty->getExteriorFloorName(); + } + + if (floorName) + { + m_floor = FloorManager::createFloor(floorName,&getOwner(),appearance,true); + WARNING(!m_floor, ("Could not load exterior floor %s", floorName)); + + if (appearance) + appearance->setShadowBlobAllowed(); + } +} + +// ---------------------------------------------------------------------- + +void CollisionProperty::addToCollisionWorld ( void ) +{ + modifyFlags(F_inCollisionWorld, true); + + setIdle(false); + m_idleCounter = -10; // Make AI collision not be flagged as idle for a short while after being added to the world. + + storePosition(); + + setExtentsDirty(true); + + if(m_footprint) + { + //-- cached objects don't get footprints because we don't want to drop them to the floors/ground etc... + if (getOwner().getNetworkId() < NetworkId::cms_invalid) + { + delete m_footprint; + m_footprint = NULL; + } + + if (m_footprint) + m_footprint->addToWorld(); + } +} + +// ---------- + +void CollisionProperty::removeFromCollisionWorld ( void ) +{ + detachList(); + + if(m_footprint) m_footprint->detach(); + + modifyFlags(F_inCollisionWorld, false); +} + +// ---------- + +bool CollisionProperty::isInCollisionWorld ( void ) const +{ + return hasFlags(F_inCollisionWorld); +} + +// ---------------------------------------------------------------------- + +Transform const & CollisionProperty::getTransform_o2p ( void ) const +{ + return getOwner().getTransform_o2p(); +} + +Transform const & CollisionProperty::getTransform_o2w ( void ) const +{ + return getOwner().getTransform_o2w(); +} + +Transform CollisionProperty::getTransform_o2c ( void ) const +{ + return ::getTransform_o2c( &getOwner() ); +} + +// ---------------------------------------------------------------------- + +BaseExtent * convertToSimpleExtent ( BaseExtent const * sourceExtent ) +{ + if(!sourceExtent) return NULL; + + switch(sourceExtent->getType()) + { + case ET_Simple: + { + SimpleExtent const * extent = safe_cast(sourceExtent); + + return new SimpleExtent( MultiShape( extent->getShape() ) ); + } + + case ET_Sphere: + { + Extent const * extent = safe_cast(sourceExtent); + + if(!extent) return NULL; + + return new SimpleExtent( MultiShape( extent->getShape() ) ); + } + + case ET_Cylinder: + { + CylinderExtent const * extent = safe_cast(sourceExtent); + + if(!extent) return NULL; + + return new SimpleExtent( MultiShape( extent->getShape() ) ); + } + + case ET_Box: + { + BoxExtent const * extent = safe_cast(sourceExtent); + + if(!extent) return NULL; + + return new SimpleExtent( MultiShape( extent->getShape() ) ); + } + + case ET_Mesh: + { + MeshExtent const * extent = safe_cast(sourceExtent); + + if(!extent) return NULL; + + return extent->clone(); + } + + case ET_Detail: + { + DetailExtent const * extent = safe_cast(sourceExtent); + + if(!extent) return NULL; + + DetailExtent * newExtent = new DetailExtent(); + + for(int i = 0; i < extent->getExtentCount(); i++) + { + BaseExtent * newChild = convertToSimpleExtent( extent->getExtent(i) ); + + newExtent->attachExtent( newChild ); + } + + return newExtent; + } + + case ET_Component: + { + ComponentExtent const * extent = safe_cast(sourceExtent); + + if(!extent) return NULL; + + ComponentExtent * newExtent = new ComponentExtent(); + + for(int i = 0; i < extent->getExtentCount(); i++) + { + BaseExtent * newChild = convertToSimpleExtent( extent->getExtent(i) ); + + newExtent->attachExtent( newChild ); + } + + return newExtent; + } + + case ET_Terrain: + case ET_OrientedBox: + case ET_OrientedCylinder: + case ET_Null: + case ET_ExtentTypeCount: + default: + return NULL; + } +} + + +// ---------------------------------------------------------------------- + +void CollisionProperty::cellChanged() +{ + setExtentsDirty(true); + + if(m_footprint) m_footprint->cellChanged(); +} + +// ---------------------------------------------------------------------- + +extern int extentUpdateCount; + +void CollisionProperty::updateExtents ( void ) const +{ + extentUpdateCount++; + + // We can't get an onScaleChanged callback, so detect if it changed ourselves + // and if so rebuild the extents. + + float newScale = getOwner().getScale().x; + + if(m_scale != newScale) + { + delete m_extent_l; + m_extent_l = NULL; + + delete m_extent_p; + m_extent_p = NULL; + + m_scale = newScale; + } + + if(!m_extent_l) + { + Appearance const * appearance = getOwner().getAppearance(); + + if(appearance) + { + AppearanceTemplate const * appTemplate = appearance->getAppearanceTemplate(); + + if(appTemplate) + { + BaseExtent * source = convertToSimpleExtent( appTemplate->getCollisionExtent() ); + + if(source) + { + BaseExtent * scaled = source->clone(); + + scaled->transform(source,Transform::identity,m_scale); + + attachSourceExtent(scaled); + } + + delete source; + } + } + + if(!m_extent_l) + { + PortalProperty const * portalProperty = getOwner().getPortalProperty(); + + if(portalProperty) + { + BaseExtent * source = convertToSimpleExtent( portalProperty->getPortalPropertyTemplate().getCell(0).getCollisionExtent() ); + + if(source) + { + BaseExtent * scaled = source->clone(); + + scaled->transform(source,Transform::identity,m_scale); + + attachSourceExtent(scaled); + } + + delete source; + } + } + + if(!m_extent_l) + { + if(isMobile()) + { + Vector center = Vector(m_offsetX, getCollisionHeight() + getCollisionRadius(), m_offsetZ) * m_scale; + float radius = getCollisionRadius() * m_scale; + + Sphere defaultSphere( center, radius ); + + attachSourceExtent( new SimpleExtent( MultiShape(defaultSphere) ) ); + + if(m_footprint) + { + m_footprint->setRadius(radius); + } + } + } + + // ---------- + // minor hack to make sure ships have a default radius even though it's not specified in the template + + if(isShip() && m_extent_l) + { + const_cast(this)->m_defaultRadius = m_extent_l->getRadius(); + } + } + + // ---------- + + if(!m_extent_p) + { + if(m_extent_l) + { + m_extent_p = m_extent_l->clone(); + } + } + + // ---------- + + if(m_extent_p && m_extent_l) + { + Transform t = getTransform_o2c(); + + if(isMobile() && !isShip()) + { + Vector pos = t.getPosition_p(); + + t = Transform::identity; + + t.move_p(pos); + } + + m_extent_p->transform( m_extent_l, t, 1.0f ); + } + + // ---------- + + if(m_floor) + { + m_floor->updateExtent(); + } + + // ---------- + // local sphere + + { + if(m_extent_l && m_floor) + { + m_sphere_l = Containment3d::EncloseSphere(m_extent_l->getBoundingSphere(),m_floor->getBoundingSphere_l()); + } + else if(m_extent_l) + { + m_sphere_l = m_extent_l->getBoundingSphere(); + } + else if (m_floor) + { + m_sphere_l = m_floor->getBoundingSphere_l(); + } + else + { + Appearance const * appearance = getOwner().getAppearance(); + + if(appearance) + { + m_sphere_l = appearance->getSphere(); + } + else + { + m_sphere_l = Sphere::zero; + } + } + } + + // ---------- + // world-space sphere + + { + if(m_extent_p && m_floor) + { + Sphere extentSphere_w = CollisionUtils::transformToWorld(getCell(),m_extent_p->getBoundingSphere()); + + m_sphere_w = Containment3d::EncloseSphere(extentSphere_w,m_floor->getBoundingSphere_w()); + } + else if(m_floor) + { + m_sphere_w = m_floor->getBoundingSphere_w(); + } + else if(m_extent_p) + { + Sphere sphere_p = m_extent_p->getBoundingSphere(); + + m_sphere_w = CollisionUtils::transformToWorld(getCell(),sphere_p); + } + else + { + m_sphere_w = getOwner().rotateTranslate_o2w(m_sphere_l); + } + } + + // ---------- + + m_extentsDirty = false; +} + +// ---------------------------------------------------------------------- + +Sphere const & CollisionProperty::getBoundingSphere_l ( void ) const +{ + if(m_extentsDirty) updateExtents(); + +#ifdef _DEBUG + + if(ConfigSharedCollision::getEnableCollisionValidate()) + { + Sphere temp; + + if(m_extent_l && m_floor) + { + temp = Containment3d::EncloseSphere(m_extent_l->getBoundingSphere(),m_floor->getBoundingSphere_l()); + } + else if(m_extent_l) + { + temp = m_extent_l->getBoundingSphere(); + } + else if (m_floor) + { + temp = m_floor->getBoundingSphere_l(); + } + else + { + Appearance const * appearance = getOwner().getAppearance(); + + if(appearance) + { + temp = appearance->getSphere(); + } + else + { + temp = Sphere::zero; + } + } + + if(!CollisionUtils::epsilonEqual(temp,m_sphere_l,0.001f)) + { + DEBUG_WARNING(true,("CollisionProperty::getBoundingSphere_l() - current sphere is different from cached sphere\n")); + } + } + +#endif + + return m_sphere_l; + +} + +// ---------- + +Sphere const & CollisionProperty::getBoundingSphere_w ( void ) const +{ + if(m_extentsDirty) updateExtents(); + +#ifdef _DEBUG + + if(ConfigSharedCollision::getEnableCollisionValidate()) + { + Sphere temp; + + if(m_extent_p && m_floor) + { + Sphere extentSphere_w = CollisionUtils::transformToWorld(getCell(),m_extent_p->getBoundingSphere()); + + temp = Containment3d::EncloseSphere(extentSphere_w,m_floor->getBoundingSphere_w()); + } + else if(m_floor) + { + temp = m_floor->getBoundingSphere_w(); + } + else if(m_extent_p) + { + Sphere sphere_p = m_extent_p->getBoundingSphere(); + + temp = CollisionUtils::transformToWorld(getCell(),sphere_p); + } + else + { + temp = getOwner().rotateTranslate_o2w(m_sphere_l); + } + + if(!CollisionUtils::epsilonEqual(temp,m_sphere_w,0.001f)) + { + DEBUG_WARNING(true,("CollisionProperty::getBoundingSphere_w() - current sphere is different from cached sphere\n")); + } + } + +#endif + + return m_sphere_w; +} + +// ---------- + +Capsule CollisionProperty::getQueryCapsule_w ( void ) const +{ + Sphere sphere_w = getBoundingSphere_w(); + + Vector delta = getOwner().getPosition_w() - getLastPos_w(); + + // stretch the capsule's radius out by a meter to help catch glancing collisions + + Sphere oldSphere_w ( sphere_w.getCenter() - delta, sphere_w.getRadius() + 1.0f); + + return Capsule(oldSphere_w,delta); +} + +// ---------------------------------------------------------------------- + +float CollisionProperty::getCollisionRadius ( void ) const +{ + return m_defaultRadius; +} + +float CollisionProperty::getCollisionHeight ( void ) const +{ + return m_stepHeight; +} + +// ---------------------------------------------------------------------- + +void CollisionProperty::drawDebugShapes ( DebugShapeRenderer * renderer ) const +{ + UNREF(renderer); + +#ifdef _DEBUG + + if(renderer == NULL) return; + + if(ConfigSharedCollision::getDrawExtents()) + { + renderer->setApplyScale(false); + + if(isMobile()) + { + renderer->setWorldSpace(true); + + renderer->setColor(VectorArgb::solidYellow); + + renderer->drawSphere(m_sphere_w); + + renderer->setWorldSpace(false); + } + else + { + if(m_extent_l) + { + m_extent_l->drawDebugShapes(renderer); + } + } + + if(m_footprint) + { + m_footprint->drawDebugShapes(renderer); + } + + renderer->setApplyScale(true); + } + + if(m_floor) + { + m_floor->drawDebugShapes(renderer,true); + } + +#endif +} + +// ---------------------------------------------------------------------- + +void CollisionProperty::setServerSide ( bool serverSide ) +{ + modifyFlags(F_serverSide, serverSide); +} + +// ---------- + +bool CollisionProperty::isServerSide ( void ) const +{ + return hasFlags(F_serverSide); +} + +// ---------- + +int CollisionProperty::elevatorMove ( int nFloors, Transform & outTransform ) +{ + if(m_footprint) + { + return m_footprint->elevatorMove(nFloors,outTransform); + } + else + { + return 0; + } +} + +// ---------------------------------------------------------------------- + +bool CollisionProperty::canCollideWith( CollisionProperty const * otherCollision ) const +{ + if(!otherCollision) + { + return false; + } + + if(!isCollidable()) + { + return false; + } + + if(!otherCollision->isCollidable()) + { + return false; + } + + if(!otherCollision->isMobile()) + { + DoorObject const * const door = dynamic_cast(&otherCollision->getOwner()); + + if(door) + { + // Everybody always triggers doors + return true; + } + + bool const isBarrier = (dynamic_cast(&otherCollision->getOwner()) != 0); + + // ---------- + + CellProperty const * thisCell = getOwner().getParentCell(); + CellProperty const * otherCell = otherCollision->getOwner().getParentCell(); + + if(thisCell && !thisCell->isAdjacentTo(otherCell)) + { + return false; + } + + if(otherCell && (otherCell != CellProperty::getWorldCellProperty())) + { + PortalProperty const * otherPortalProperty = otherCell->getPortalProperty(); + + if(otherPortalProperty) + { + Object const * otherBuilding = &otherPortalProperty->getOwner(); + + IsPlayerHouseHook hook = ConfigSharedCollision::getIsPlayerHouseHook(); + + if(hook && hook(otherBuilding)) + { + // Target object is inside a player house, and thus is non-collidable + + if(isBarrier) + { + return true; + } + + return false; + } + } + } + + if(isBarrier) + { + return true; + } + } + + if(isMobile() && otherCollision->isMobile()) + { + if(isPlayer()) + { + if(otherCollision->isPlayerControlled()) + { + // Players collide with each other if they're both in combat + + return isInCombat() && otherCollision->isInCombat(); + } + else + { + // Players always collide with monsters + + return true; + } + } + else + { + // Monsters don't collide with anything + + return true; + } + } + else + { + return true; + } +} + +// ---------- + +bool CollisionProperty::blocksInteraction (InteractionType) const +{ + return isCollidable(); +} + +// ---------- + +bool CollisionProperty::blocksMovement () const +{ + //@todo - HACK - force mobiles to block movement so we can collide with + // them + + if(isMobile()) + return true; + else + { + return isCollidable(); + } +} + +// ---------------------------------------------------------------------- + +void CollisionProperty::objectWarped ( void ) +{ + setIdle(false); + + storePosition(); + + Footprint * foot = getFootprint(); + + if(foot) foot->objectWarped(); +} + +// ---------------------------------------------------------------------- + +void CollisionProperty::storePosition ( void ) +{ + setLastPos( getCell(), getTransform_o2p() ); +} + +// ---------------------------------------------------------------------- + +void CollisionProperty::hit ( CollisionProperty * staticCollider ) +{ + UNREF(staticCollider); + + if(isMobile()) + { + setIdle(false); + } +} + +void CollisionProperty::hitBy ( CollisionProperty * dynamicCollider ) +{ + UNREF(dynamicCollider); + + if(isMobile()) + { + setIdle(false); + } +} + +// ---------------------------------------------------------------------- + +CellProperty * CollisionProperty::getCell ( void ) +{ + return getOwner().getParentCell(); +} + +CellProperty const * CollisionProperty::getCell ( void ) const +{ + return getOwner().getParentCell(); +} + +// ---------------------------------------------------------------------- + +Vector CollisionProperty::getLastPos_w ( void ) const +{ + return getLastTransform_w().getPosition_p();; +} + +// ---------- + +Vector CollisionProperty::getLastPos_p ( void ) const +{ + return getLastTransform_p().getPosition_p(); +} + +// ---------- + +Transform const & CollisionProperty::getLastTransform_w ( void ) const +{ + return m_lastTransform_w; +} + +// ---------- + +Transform const & CollisionProperty::getLastTransform_p ( void ) const +{ + if(m_lastCellObject) + { + return m_lastTransform_p; + } + else + { + return m_lastTransform_w; + } +} + +// ---------- + +CellProperty * CollisionProperty::getLastCell ( void ) +{ + if(m_lastCellObject) + { + return m_lastCellObject->getCellProperty(); + } + else + { + return CellProperty::getWorldCellProperty(); + } +} + +// ---------- + +CellProperty const * CollisionProperty::getLastCell ( void ) const +{ + if(m_lastCellObject) + { + return m_lastCellObject->getCellProperty(); + } + else + { + return CellProperty::getWorldCellProperty(); + } +} + +// ---------- + +void CollisionProperty::setLastPos ( CellProperty * cell, Transform const & transform_p ) +{ + NAN_CHECK(transform_p); + + if(cell == NULL) + { + m_lastCellObject = NULL; + m_lastTransform_p = transform_p; + m_lastTransform_w = transform_p; + } + else + { + m_lastCellObject = &(cell->getOwner()); + m_lastTransform_p = transform_p; + m_lastTransform_w = CollisionUtils::transformToCell(cell,transform_p,CellProperty::getWorldCellProperty()); + } +} + +// ---------------------------------------------------------------------- + +Object const * CollisionProperty::getStandingOn ( void ) const +{ + if(m_footprint) + { + return m_footprint->getStandingOn(); + } + else + { + return NULL; + } +} + +// ---------------------------------------------------------------------- + +void getDistance ( Vector const & point_p, BaseExtent const * extent, float maxDistance, float & minDistance ); + +// ---------- + +void getDistance ( Vector const & point_p, SimpleExtent const * extent, float maxDistance, float & minDistance ) +{ + UNREF(maxDistance); + + float distance = Distance3d::DistancePointShape(point_p,extent->getShape()); + + if(distance < minDistance) + { + minDistance = distance; + } +} + +// ---------- + +void getDistance ( Vector const & point_p, ComponentExtent const * extent, float maxDistance, float & minDistance ) +{ + //@todo - don't recurse if the bounds of this extent is farther away than minDistance + + int extentCount = extent->getExtentCount(); + + for(int i = 0; i < extentCount; i++) + { + getDistance(point_p,extent->getExtent(i),maxDistance,minDistance); + } +} + +// ---------- + +void getDistance ( Vector const & point_p, DetailExtent const * extent, float maxDistance, float & minDistance ) +{ + //@todo - don't recurse if the bounds of this extent is farther away than minDistance + + int extentCount = extent->getExtentCount(); + + getDistance(point_p,extent->getExtent(extentCount-1),maxDistance,minDistance); +} + +// ---------- + +void getDistance ( Vector const & point_p, BaseExtent const * extent, float maxDistance, float & minDistance ) +{ + NOT_NULL(extent); + + ExtentType type = extent->getType(); + + // ---------- + + if (type == ET_Simple) getDistance( point_p, safe_cast(extent), maxDistance, minDistance ); + else if(type == ET_Component) getDistance( point_p, safe_cast(extent), maxDistance, minDistance ); + else if(type == ET_Detail) getDistance( point_p, safe_cast(extent), maxDistance, minDistance ); +} + +// ---------- + +bool CollisionProperty::getDistance ( Vector const & point_w, float maxDistance, float & outDistance ) const +{ + if(Distance3d::DistancePointSphere(point_w,getBoundingSphere_w()) >= maxDistance) + { + return false; + } + + // ---------- + + float distance = maxDistance; + + Vector point_p = CollisionUtils::transformFromWorld(point_w,getCell()); + + ::getDistance(point_p,getExtent_p(),maxDistance,distance); + + if(distance < maxDistance) + { + outDistance = distance; + return true; + } + else + { + return false; + } +} + +// ---------------------------------------------------------------------- + +SpatialSubdivisionHandle * CollisionProperty::getSpatialSubdivisionHandle ( void ) +{ + return m_spatialSubdivisionHandle; +} + +void CollisionProperty::setSpatialSubdivisionHandle ( SpatialSubdivisionHandle * newHandle ) +{ + if(getDisableCollisionWorldAddRemove()) + { + FATAL(newHandle,("CollisionProperty::setSpatialSubdivisionHandle - Can't add a CollisionProperty flagged for disabled add/remove to a sphere tree\n")); + } + + m_spatialSubdivisionHandle = newHandle; +} + +// ---------- + +bool CollisionProperty::getDisableCollisionWorldAddRemove () const +{ + return hasFlags(F_disableCollisionWorldAddRemove); +} + +void CollisionProperty::setDisableCollisionWorldAddRemove (bool disable) +{ + if(m_spatialSubdivisionHandle) + { + FATAL(disable,("CollisionProperty::setDisableCollisionWorldAddRemove - Can't disable add/remove for an object already in a collision sphere tree\n")); + } + + modifyFlags(F_disableCollisionWorldAddRemove, disable); +} + diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollisionProperty.h b/engine/shared/library/sharedCollision/src/shared/core/CollisionProperty.h new file mode 100644 index 00000000..1d75fc8b --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollisionProperty.h @@ -0,0 +1,459 @@ +// ====================================================================== +// +// CollisionProperty.h +// +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_CollisionProperty_H +#define INCLUDED_CollisionProperty_H + +#include "sharedCollision/SpatialDatabase.h" +#include "sharedObject/Property.h" +#include "sharedMath/Transform.h" +#include "sharedMath/Vector.h" +#include "sharedMath/Sphere.h" +#include "sharedMath/Capsule.h" +#include "sharedFoundation/Watcher.h" + +class BaseExtent; +class CellProperty; +class CollisionProperty; +class DebugShapeRenderer; +class Floor; +class Footprint; +class SharedObjectTemplate; +class SpatialDatabase; +class SpatialSubdivisionHandle; +class Transform; + +typedef stdvector::fwd ColliderList; + +// ====================================================================== + +class CollisionProperty : public Property +{ +public: + + static PropertyId getClassPropertyId ( void ); + + enum Flags + { + F_mobile = 0x0001, + F_flora = 0x0002, + F_player = 0x0004, + F_mount = 0x0008, + F_ship = 0x0010, + F_playerControlled = 0x0020, + F_collidable = 0x0040, + F_serverSide = 0x0080, + F_inCombat = 0x0100, + F_inCollisionWorld = 0x0200, + F_idle = 0x0400, + F_disableCollisionWorldAddRemove = 0x0800 + }; + +public: + + CollisionProperty( Object & owner ); + CollisionProperty( Object & owner, SharedObjectTemplate const * objTemplate ); + + virtual ~CollisionProperty(); + + void initFloor ( void ); + void initFromTemplate ( SharedObjectTemplate const * objTemplate ); + + // ---------- + + void addToCollisionWorld ( void ); + void removeFromCollisionWorld ( void ); + bool isInCollisionWorld ( void ) const; + + // ---------- + + void setDisableCollisionWorldAddRemove (bool disable); + bool getDisableCollisionWorldAddRemove () const; + + // ---------- + // Accessors + + Transform const & getTransform_o2p ( void ) const; + Transform const & getTransform_o2w ( void ) const; + Transform getTransform_o2c ( void ) const; + + // ---------- + + CellProperty * getCell ( void ); + CellProperty const * getCell ( void ) const; + + Vector getLastPos_p ( void ) const; + Vector getLastPos_w ( void ) const; + + Transform const & getLastTransform_p ( void ) const; + Transform const & getLastTransform_w ( void ) const; + + CellProperty * getLastCell ( void ); + CellProperty const * getLastCell ( void ) const; + + void setLastPos ( CellProperty * cell, Transform const & transform_p ); + + // ---------- + + Floor * getFloor ( void ); + Floor const * getFloor ( void ) const; + + Footprint * getFootprint ( void ); + Footprint const * getFootprint ( void ) const; + + // ---------- + // Extents + + bool getExtentsDirty ( void ) const; + void setExtentsDirty ( bool dirty ) const; + + BaseExtent const * getExtent_l ( void ) const; + BaseExtent const * getExtent_p ( void ) const; + + Sphere const & getBoundingSphere_l ( void ) const; + Sphere const & getBoundingSphere_w ( void ) const; + + Capsule getQueryCapsule_w ( void ) const; + + float getCollisionRadius ( void ) const; + float getCollisionHeight ( void ) const; + + void attachSourceExtent ( BaseExtent * newSourceExtent ) const; + + // ---------- + + float getScale ( void ) const; + + // ---------- + // Flags + + void setMobile ( bool mobile ); + void setFlora ( bool flora ); + void setPlayer ( bool player ); + void setMount ( bool mount ); + void setShip ( bool ship ); + void setPlayerControlled ( bool playerControlled ); + void setCollidable ( bool collidable ); + void setServerSide ( bool serverSide ); + void setInCombat ( bool inCombat ); + + bool isMobile ( void ) const; + bool isFlora ( void ) const; + bool isPlayer ( void ) const; + bool isMount ( void ) const; + bool isShip ( void ) const; + bool isPlayerControlled ( void ) const; + bool isCollidable ( void ) const; + bool isServerSide ( void ) const; + bool isInCombat ( void ) const; + + // ---------- + + bool isIdle ( void ) const; + + void updateIdle ( void ); + void setIdle ( bool active ); + + void attachList ( CollisionProperty * & head ); + void detachList ( void ); + + CollisionProperty * getPrev ( void ); + CollisionProperty * getNext ( void ); + + static CollisionProperty * getActiveHead ( void ); + + // ---------- + + virtual bool canCollideWith ( CollisionProperty const * otherCollision ) const; + virtual bool blocksInteraction (InteractionType interaction) const; + virtual bool blocksMovement () const; + virtual void objectWarped ( void ); + + void storePosition ( void ); + + // ---------- + + SpatialSubdivisionHandle * getSpatialSubdivisionHandle ( void ); + void setSpatialSubdivisionHandle ( SpatialSubdivisionHandle * newHandle ); + + // ---------- + // Misc + + void drawDebugShapes ( DebugShapeRenderer * renderer ) const; + void cellChanged ( void ); + void updateExtents ( void ) const; + int elevatorMove ( int nFloors, Transform & outTransform ); + + virtual void hit ( CollisionProperty * staticCollider ); + virtual void hitBy ( CollisionProperty * dynamicCollider ); + + Object const * getStandingOn ( void ) const; + + bool getDistance ( Vector const & point_w, float maxDistance, float & outDistance ) const; + + int getFlags() const; + void setFlags(int flags); + bool hasFlags(int flags) const; + void modifyFlags(int flags, bool onOff); + + // ---------- + + SpatialDatabase::Query getSpatialDatabaseStorageType() const; + +protected: + + typedef Watcher CellWatcher; + + Transform m_lastTransform_w; + Transform m_lastTransform_p; + CellWatcher m_lastCellObject; + + // ---------- + // Since I can't get to the shared object template from shared code when running on the server, + // I have to cache these four variables from the template here. + + float m_stepHeight; + float m_defaultRadius; + float m_offsetX; + float m_offsetZ; + + // ---------- + + mutable bool m_extentsDirty; + + mutable BaseExtent * m_extent_l; + mutable BaseExtent * m_extent_p; + + mutable Sphere m_sphere_l; + mutable Sphere m_sphere_w; + + mutable float m_scale; + + // ---------- + + SpatialSubdivisionHandle * m_spatialSubdivisionHandle; + + Floor * m_floor; + + Footprint * m_footprint; + + int m_idleCounter; + + CollisionProperty * m_next; + CollisionProperty * m_prev; + + bool m_disableCollisionWorldAddRemove; + + uint16 m_flags; + +private: + + CollisionProperty(); + CollisionProperty(const CollisionProperty&); + CollisionProperty& operator= (const CollisionProperty&); + +private: + + SpatialDatabase::Query m_spatialDatabaseStorageType; // SpatialDatabase::Query values + +}; + +// ---------------------------------------------------------------------- + +inline Floor * CollisionProperty::getFloor ( void ) +{ + return m_floor; +} + +inline Floor const * CollisionProperty::getFloor ( void ) const +{ + return m_floor; +} + +// ---------- + +inline Footprint * CollisionProperty::getFootprint ( void ) +{ + return m_footprint; +} + +inline Footprint const * CollisionProperty::getFootprint ( void ) const +{ + return m_footprint; +} + +// ---------- +// Extents + +inline bool CollisionProperty::getExtentsDirty ( void ) const +{ + return m_extentsDirty; +} + +inline void CollisionProperty::setExtentsDirty ( bool dirty ) const +{ + m_extentsDirty = dirty; +} + +inline BaseExtent const * CollisionProperty::getExtent_l ( void ) const +{ + if(getExtentsDirty()) updateExtents(); + + return m_extent_l; +} + +inline BaseExtent const * CollisionProperty::getExtent_p ( void ) const +{ + if(getExtentsDirty()) updateExtents(); + + return m_extent_p; +} + +// ---------- + +inline float CollisionProperty::getScale ( void ) const +{ + return m_scale; +} + +// ---------- +// Flags + +inline void CollisionProperty::setMobile ( bool mobile ) +{ + modifyFlags(F_mobile, mobile); +} + +inline void CollisionProperty::setFlora ( bool flora ) +{ + modifyFlags(F_flora, flora); +} + +inline void CollisionProperty::setPlayer ( bool player ) +{ + modifyFlags(F_player, player); +} + +inline void CollisionProperty::setPlayerControlled ( bool playerControlled ) +{ + modifyFlags(F_playerControlled, playerControlled); +} + +inline void CollisionProperty::setCollidable ( bool collidable ) +{ + modifyFlags(F_collidable, collidable); +} + +inline void CollisionProperty::setInCombat ( bool inCombat ) +{ + modifyFlags(F_inCombat, inCombat); +} + +// ---------- + +inline bool CollisionProperty::isMobile ( void ) const +{ + return hasFlags(F_mobile); +} + +inline bool CollisionProperty::isFlora ( void ) const +{ + return hasFlags(F_flora); +} + +inline bool CollisionProperty::isPlayer ( void ) const +{ + return hasFlags(F_player); +} + +inline bool CollisionProperty::isMount ( void ) const +{ + return hasFlags(F_mount); +} + +inline bool CollisionProperty::isShip ( void ) const +{ + return hasFlags(F_ship); +} + +inline bool CollisionProperty::isPlayerControlled ( void ) const +{ + return hasFlags(F_playerControlled); +} + +inline bool CollisionProperty::isCollidable ( void ) const +{ + return hasFlags(F_collidable); +} + +inline bool CollisionProperty::isInCombat ( void ) const +{ + return hasFlags(F_inCombat); +} + +// ---------- + +inline bool CollisionProperty::isIdle ( void ) const +{ + return hasFlags(F_idle); +} + +// ---------- + +inline CollisionProperty * CollisionProperty::getPrev ( void ) +{ + return m_prev; +} + +inline CollisionProperty * CollisionProperty::getNext ( void ) +{ + return m_next; +} + +// ---------- + +inline SpatialDatabase::Query CollisionProperty::getSpatialDatabaseStorageType() const +{ + return m_spatialDatabaseStorageType; +} + +//---------------------------------------------------------------------- + +inline int CollisionProperty::getFlags() const +{ + return m_flags; +} + +//---------------------------------------------------------------------- + +inline void CollisionProperty::setFlags(int flags) +{ + m_flags = static_cast(flags); +} + +//---------------------------------------------------------------------- + +inline bool CollisionProperty::hasFlags(int flags) const +{ + return (m_flags & flags) == flags; +} + +//---------------------------------------------------------------------- + +inline void CollisionProperty::modifyFlags(int flags, bool onOff) +{ + if (onOff) + m_flags |= flags; + else + m_flags &= ~flags; +} + +// ====================================================================== + +#endif // #ifndef INCLUDED_CollisionProperty_H + diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollisionResolve.cpp b/engine/shared/library/sharedCollision/src/shared/core/CollisionResolve.cpp new file mode 100644 index 00000000..0638c13c --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollisionResolve.cpp @@ -0,0 +1,834 @@ +// ====================================================================== +// +// CollisionResolve.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/CollisionResolve.h" + +#include "sharedCollision/CollisionProperty.h" +#include "sharedCollision/ConfigSharedCollision.h" +#include "sharedCollision/Resolve3d.h" +#include "sharedCollision/Contact3d.h" +#include "sharedCollision/SimpleExtent.h" +#include "sharedCollision/ComponentExtent.h" +#include "sharedCollision/DetailExtent.h" +#include "sharedCollision/CollisionUtils.h" +#include "sharedCollision/Footprint.h" +#include "sharedCollision/FloorContactShape.h" +#include "sharedCollision/Floor.h" +#include "sharedCollision/FloorMesh.h" + +#include "sharedMath/Cylinder.h" +#include "sharedMath/ShapeUtils.h" + +#include "sharedObject/Object.h" +#include "sharedObject/CellProperty.h" + +#include + +typedef std::vector FloorContactList; + +typedef MultiListDataIterator ContactIterator; + +// ====================================================================== + +Vector MoveSegment::getBegin ( CellProperty const * relativeCell ) const +{ + return CollisionUtils::transformToCell(m_cellA,m_pointA,relativeCell); +} + +Vector MoveSegment::getEnd ( CellProperty const * relativeCell ) const +{ + return CollisionUtils::transformToCell(m_cellB,m_pointB,relativeCell); +} + +Vector MoveSegment::getDelta ( CellProperty const * relativeCell ) const +{ + return getEnd(relativeCell) - getBegin(relativeCell); +} + +Vector MoveSegment::getDelta_b ( void ) const +{ + return m_pointB - getBegin(m_cellB); +} + +Vector MoveSegment::getDelta_a ( void ) const +{ + return getEnd(m_cellA) - m_pointA; +} + +Vector MoveSegment::atParam ( CellProperty const * relativeCell, float time ) const +{ + Vector begin = getBegin(relativeCell); + Vector end = getEnd(relativeCell); + + return begin + (end-begin)*time; +} + +float MoveSegment::lengthSquared ( void ) const +{ + return getDelta_a().magnitudeSquared(); +} + +// ====================================================================== + +namespace CollisionResolveNamespace +{ + int ms_colliderCount = 0; + int ms_obstacleCount = 0; + int ms_collisionCount = 0; + int ms_bounceCount = 0; + + ObstacleList ms_obstacleList; + ContactList ms_contactList; + + FloorContactList ms_floorContactList; + + Floor const * ms_ignoreFloor = NULL; + int ms_ignoreTriId = -1; + int ms_ignoreEdge = -1; + Vector ms_ignoreNormal = Vector::zero; + float ms_ignoreTime = REAL_MAX; + + bool ms_debugBackupContacts = false; + bool ms_debugTestClear = false; + bool ms_debugRedoResolve = false; + +}; + +using namespace CollisionResolveNamespace; + +// ---------------------------------------------------------------------- + +void CollisionResolve::install ( void ) +{ +} + +void CollisionResolve::remove ( void ) +{ +} + +// ---------------------------------------------------------------------- +// Resolve all collisions in the collision world + +int CollisionResolve::getColliderCount ( void ) +{ + return ms_colliderCount; +} + +int CollisionResolve::getObstacleCount ( void ) +{ + return ms_obstacleCount; +} + +int CollisionResolve::getCollisionCount ( void ) +{ + return ms_collisionCount; +} + +int CollisionResolve::getBounceCount ( void ) +{ + return ms_bounceCount; +} + +void CollisionResolve::resetCounters ( void ) +{ + ms_colliderCount = 0; + ms_obstacleCount = 0; + ms_collisionCount = 0; + ms_bounceCount = 0; +} + +// ---------------------------------------------------------------------- +// @todo - handle extents in cells + +void translateExtent ( CellProperty const * cell, BaseExtent * extent, Vector const & delta ) +{ + UNREF(cell); + + Transform tform = Transform::identity; + + tform.move_p(delta); + + extent->transform(extent,tform,1.0f); +} + +void translateExtent ( CellProperty const * cell, BaseExtent * extent, MoveSegment const & moveSeg, float moveParam ) +{ + Transform tform = Transform::identity; + + Vector delta = moveSeg.getDelta(cell); + + delta *= moveParam; + + tform.move_p(delta); + + extent->transform(extent,tform,1.0f); +} + +void translateFloorContacts ( FloorContactList & floorContacts, MoveSegment const & moveSeg, float moveParam, int ignoreTriId, int ignoreEdge ) +{ + int floorContactCount = ms_floorContactList.size(); + + for(int i = 0; i < floorContactCount; i++) + { + FloorContactShape * shape = floorContacts[i]; + + FloorLocator & loc = shape->m_contact; + + Floor const * floor = loc.getFloor(); + + CellProperty const * floorCell = loc.getCell(); + + Vector begin = loc.getPosition_p(); + Vector end = moveSeg.getEnd(floorCell); + + Vector delta = end - begin; + + Vector goal = begin + delta * moveParam; + + if(floor) + { + FloorLocator tempLoc; + + IGNORE_RETURN( floor->moveLocator( loc, goal, ignoreTriId, ignoreEdge, tempLoc ) ); + + loc = tempLoc; + } + else + { + Vector end = moveSeg.getEnd(loc.getCell()); + + loc.setPosition_p(end); + } + } +} + +// ---------------------------------------------------------------------- + +void moveObjectAlong ( Object * object, CellPositionList const & positions ) +{ + int count = positions.size(); + + for(int i = 0; i < count; i++) + { + CellProperty * const objectCell = object->getParentCell(); + + CellProperty const * newCell = positions[i].m_cell; + Vector const & newEnd = positions[i].m_position; + + Vector localEnd = CollisionUtils::transformToCell(newCell,newEnd,objectCell); + + object->setPosition_p( localEnd ); + } +} + +// ---------- + +void CollisionResolve::translateEverything ( CellProperty const * cell, SimpleExtent * extent, FloorContactList & floorContacts, MoveSegment const & moveSeg, float moveParam, int ignoreTriId, int ignoreEdge ) +{ + translateExtent(cell,extent,moveSeg,moveParam); + translateFloorContacts(floorContacts,moveSeg,moveParam,ignoreTriId,ignoreEdge); +} + +// ---------------------------------------------------------------------- +// Explode the collidee list into a list of simple extents, move the +// collider back to the beginning of the timestep, resolve all collisions +// during the timestep, and then move the collider's object to the +// resolved position. + +ResolutionResult CollisionResolve::resolveCollisions(CollisionProperty * colliderA, ColliderList const & colliderList) +{ + NOT_NULL(colliderA); + + if (colliderList.empty()) + { + Footprint * foot = colliderA->getFootprint(); + + NOT_NULL(foot); + + if(!foot->hasContacts()) + { + return RR_NoCollision; + } + } + + Object * objectA = &(colliderA->getOwner()); + + NOT_NULL(objectA); + + MoveSegment moveSeg; + + moveSeg.m_cellA = colliderA->getLastCell(); + moveSeg.m_pointA = colliderA->getLastPos_p(); + moveSeg.m_cellB = objectA->getParentCell(); + moveSeg.m_pointB = objectA->getPosition_p(); + + if(moveSeg.lengthSquared() == 0.0f) + { + return RR_NoCollision; + } + + ResolutionResult result = resolveCollisions(colliderA, moveSeg, colliderList); + + return result; +} + +// ---------------------------------------------------------------------- + +ResolutionResult CollisionResolve::resolveCollisions(CollisionProperty * colliderA, MoveSegment const & moveSeg, ColliderList const & colliderList) +{ + Object * objectA = &(colliderA->getOwner()); + + BaseExtent const * baseExtentA = colliderA->getExtent_p(); + if (!baseExtentA) + return RR_NoCollision; + + CellProperty const * objectCell = objectA->getParentCell(); + SimpleExtent const * objectExtent = safe_cast(baseExtentA); + + NOT_NULL(objectExtent); + if (!objectExtent) + return RR_NoCollision; + + explodeCollider(colliderA, colliderList); + + // ---------- + + SimpleExtent * tempExtent = safe_cast(objectExtent->clone()); + + translateExtent(objectCell,tempExtent,-moveSeg.getDelta(objectCell)); + + static CellPositionList positions; + + positions.clear(); + + ResolutionResult result = resolveCollisions(objectCell,tempExtent,ms_floorContactList,moveSeg,&ms_obstacleList,positions, colliderA->getFootprint()); + + delete tempExtent; + + if(result == RR_Resolved) + { + colliderA->hitBy(NULL); + colliderA->setExtentsDirty(true); + + Vector resetPos = moveSeg.getBegin(moveSeg.m_cellB); + + objectA->setPosition_p(resetPos); + + moveObjectAlong(objectA,positions); + } + + return result; +} + +// ---------------------------------------------------------------------- + +ResolutionResult CollisionResolve::resolveCollisions ( CellProperty const * cellA, SimpleExtent * extentA, FloorContactList & floorContactsA, + MoveSegment const & startMoveSeg, + ObstacleList * obstacleList, + CellPositionList & outList, Footprint * footprint ) +{ + CellProperty const * startCell = startMoveSeg.m_cellA; + Vector startDelta = startMoveSeg.getDelta(startCell); + + MoveSegment moveSeg = startMoveSeg; + + const int iterationCount = 8; + + ms_ignoreFloor = NULL; + ms_ignoreTriId = -1; + ms_ignoreEdge = -1; + ms_ignoreTime = REAL_MAX; + ms_ignoreNormal = Vector::zero; + + for(int i = 0; i < iterationCount; i++) + { + int minIndex = -1; + Contact contact; + + bool foundContact = findFirstContact(cellA,extentA,moveSeg,obstacleList,minIndex,contact, footprint); + + if(!foundContact) + { + outList.push_back( CellPosition(moveSeg.m_cellB,moveSeg.m_pointB) ); + + translateEverything(cellA,extentA,floorContactsA,moveSeg,1.0f,ms_ignoreTriId,ms_ignoreEdge); + + if(i == 0) + { + return RR_NoCollision; + } + else + { + return RR_Resolved; + } + } + + if(!contact.m_resolvable) + { + return RR_DontKnowHow; + } + + if(!contact.m_exists) + { + return RR_Resolved; + } + + // ---------- + + ms_bounceCount++; + + CellProperty const * contactCell = contact.m_cell; + + Vector contactPoint = moveSeg.atParam(contactCell,contact.m_time); + + outList.push_back( CellPosition(contactCell,contactPoint) ); + + MoveSegment secondSeg; + + secondSeg.m_cellA = contactCell; + secondSeg.m_pointA = contactPoint; + + secondSeg.m_cellB = moveSeg.m_cellB; + secondSeg.m_pointB = moveSeg.m_pointB; + + if(contact.m_time != 0.0f) + { + translateEverything(cellA,extentA,floorContactsA,moveSeg,contact.m_time,ms_ignoreTriId,ms_ignoreEdge); + } + + // ---------- + + int contactCount = ms_contactList.size() + 1; + + if(contactCount == 1) + { + Vector delta = secondSeg.getDelta(contactCell); + + Vector slidDelta = Collision3d::SingleSlide(delta,contact.m_normal); + + // Stop resolution if the velocity starts to move backwards + + Vector localStartDelta = CollisionUtils::rotateToCell(startCell,startDelta,contactCell); + + if(slidDelta.dot(localStartDelta) <= 0.0f) + { + return RR_Resolved; + } + + ms_contactList.push_back(contact); + + moveSeg.m_cellA = contactCell; + moveSeg.m_pointA = contactPoint; + + moveSeg.m_cellB = contactCell; + moveSeg.m_pointB = contactPoint + slidDelta; + } + else if (contactCount == 2) + { + Vector delta = secondSeg.getDelta(contactCell); + + Contact contactA = contact; + Contact contactB = ms_contactList.front(); + + Vector normalA = contactA.m_normal; + Vector normalB = CollisionUtils::rotateToCell(contactB.m_cell,contactB.m_normal,contactA.m_cell); + + Vector slidDelta = Collision3d::DoubleSlide(delta,normalA,normalB); + + // Stop resolution if the velocity starts to move backwards + + Vector localStartDelta = CollisionUtils::rotateToCell(startCell,startDelta,contactCell); + + if(slidDelta.dot(localStartDelta) <= 0.0f) + { + return RR_Resolved; + } + + // ---------- + // Keep only the contacts that the new velocity is not moving away + // from + + ms_contactList.clear(); + + if(slidDelta.dot(normalA) < 0.0001f) + { + ms_contactList.push_back(contactA); + } + + if(slidDelta.dot(normalB) < 0.0001f) + { + ms_contactList.push_back(contactB); + } + + // ---------- + + moveSeg.m_cellA = contactCell; + moveSeg.m_pointA = contactPoint; + + moveSeg.m_cellB = contactCell; + moveSeg.m_pointB = contactPoint + slidDelta; + } + else + { + // triple-contact - stop movement because this is too much of a pain + // to resolve correctly. + + return RR_Resolved; + } + + // ---------- + // Remove the hit obstacle from the extent list + + if(obstacleList->at(minIndex).m_extent != NULL) + { + obstacleList->at(minIndex) = obstacleList->back(); + obstacleList->resize(obstacleList->size()-1); + + ms_ignoreFloor = NULL; + ms_ignoreTriId = -1; + ms_ignoreEdge = -1; + ms_ignoreTime = REAL_MAX; + ms_ignoreNormal = Vector::zero; + } + else + { + ms_ignoreFloor = obstacleList->at(minIndex).m_floorContact->m_contact.getFloor(); + ms_ignoreTriId = contact.m_surfaceId1; + ms_ignoreEdge = contact.m_surfaceId2; + ms_ignoreTime = contact.m_time; + ms_ignoreNormal = contact.m_normal; + } + } + + // For all practical purposes the code should never get here. + + DEBUG_WARNING(true,("CollisionResolve::resolveCollisions - Resolution went through 8 iterations without success")); + + outList.push_back( CellPosition(moveSeg.m_cellA,moveSeg.m_pointA) ); + + return RR_Resolved; +} + +// ---------------------------------------------------------------------- + +void CollisionResolve::explodeExtent ( CellProperty const * cell, SimpleExtent const * extent, ObstacleList & list ) +{ + list.push_back( ObstacleInfo(cell,extent) ); +} + +// ---------- + +void CollisionResolve::explodeExtent ( CellProperty const * cell, ComponentExtent const * extent, ObstacleList & list ) +{ + int extentCount = extent->getExtentCount(); + + for(int i = 0; i < extentCount; i++) + { + explodeExtent(cell,extent->getExtent(i),list); + } +} + +// ---------- + +void CollisionResolve::explodeExtent ( CellProperty const * cell, DetailExtent const * extent, ObstacleList & list ) +{ + int extentCount = extent->getExtentCount(); + + explodeExtent(cell,extent->getExtent(extentCount-1),list); +} + +// ---------- + +void CollisionResolve::explodeExtent ( CellProperty const * cell, BaseExtent const * extent, ObstacleList & list ) +{ + NOT_NULL(extent); + + ExtentType type = extent->getType(); + + // ---------- + + if (type == ET_Simple) explodeExtent(cell,safe_cast(extent),list); + else if(type == ET_Component) explodeExtent(cell,safe_cast(extent),list); + else if(type == ET_Detail) explodeExtent(cell,safe_cast(extent),list); +} + +// ---------- + +void CollisionResolve::explodeFootprint ( Footprint * foot, ObstacleList & obstacleList, FloorContactList & floorContacts ) +{ + if(foot == NULL) return; + + for(ContactIterator it(foot->getFloorList()); it; ++it) + { + FloorContactShape * contact = (*it); + + if(contact == NULL) + continue; + + obstacleList.push_back( ObstacleInfo(contact->m_contact.getCell(),contact) ); + floorContacts.push_back(contact); + } +} + +// ---------- +// Explode the extents of all colliders into SimpleExtents, accumuluate +// the extents into ms_obstacleList + +void CollisionResolve::explodeCollider(CollisionProperty * colliderA, ColliderList const & colliderList) +{ + NOT_NULL(colliderA); + + ms_obstacleList.clear(); + ms_contactList.clear(); + ms_floorContactList.clear(); + + ColliderList::const_iterator ii = colliderList.begin(); + ColliderList::const_iterator iiEnd = colliderList.end(); + + for(; ii != iiEnd; ++ii) + { + CollisionProperty const * const colliderB = *ii; + + if(colliderB == NULL) continue; + + BaseExtent const * const extentB = colliderB->getExtent_p(); + + explodeExtent(colliderB->getOwner().getParentCell(), extentB, ms_obstacleList); + } + + if(!(colliderA->isServerSide() && colliderA->isPlayerControlled()) && ConfigSharedCollision::getEnableFloorCollision()) + { + explodeFootprint(colliderA->getFootprint(),ms_obstacleList,ms_floorContactList); + } + else + { + Footprint * const foot = colliderA->getFootprint(); + + for(ContactIterator it(foot->getFloorList()); it; ++it) + { + FloorContactShape * const contact = (*it); + + if(contact == NULL) continue; + + ms_floorContactList.push_back(contact); + } + } + + ms_obstacleCount += ms_obstacleList.size(); +} + +// ---------------------------------------------------------------------- + +bool CollisionResolve::findFirstContact ( CellProperty const * cellA, SimpleExtent const * extentA, MoveSegment const & moveSeg, ObstacleList * obstacleList, int & outMinIndex, Contact & outContact, Footprint * footprint ) +{ + int extentCount = obstacleList->size(); + + outContact = Contact::noContact; + outMinIndex = -1; + + for(int i = 0; i < extentCount; i++) + { + ObstacleInfo & obstacle = obstacleList->at(i); + + Contact contact = findContactWithObstacle(cellA,extentA,moveSeg,obstacle, footprint); + + if(!contact.m_resolvable) + { + continue; + } + else if(contact.m_exists && (contact.m_time < outContact.m_time)) + { + if(contact.m_time < 0.0f) + { + Vector delta = moveSeg.getDelta(contact.m_cell); + + float stepDistance = delta.magnitude(); + + float contactDistance = stepDistance * -contact.m_time; + + if(contactDistance > 0.05f) continue; + } + + outContact = contact; + outMinIndex = i; + } + } + + return outContact.m_exists; +} + +// ---------------------------------------------------------------------- + +Contact CollisionResolve::findContactWithExtent ( CellProperty const * cellA, SimpleExtent const * extentA, MoveSegment const & moveSeg, CellProperty const * cellB, SimpleExtent const * extentB ) +{ + NOT_NULL(extentA); + NOT_NULL(extentB); + + MultiShape const & shapeA = extentA->getShape(); + MultiShape const & shapeB = extentB->getShape(); + + MultiShape tempShapeA = shapeA; + + if(cellA != cellB) + { + tempShapeA = CollisionUtils::transformToCell(cellA,shapeA,cellB); + } + + Vector delta = moveSeg.getDelta(cellA); + + Contact temp = Contact3d::FindContact(tempShapeA,delta,shapeB); + + temp.m_cell = cellB; + temp.m_extent = extentB; + + Vector normV = delta; + + IGNORE_RETURN(normV.normalize()); + + float dot = normV.dot(temp.m_normal); + + if(dot < 0.0f) + { + return temp; + } + else + { + return Contact::noContact; + } +} + +// ---------------------------------------------------------------------- + +Contact CollisionResolve::findContactWithFloor ( FloorContactShape * floorContact, MoveSegment const & moveSeg, Footprint * footprint ) +{ + NOT_NULL(floorContact); + + FloorLocator & loc = floorContact->m_contact; + FloorLocator & result = floorContact->m_result; + + Floor const * floor = loc.getFloor(); + + if(floor == NULL) return Contact::noContact; + + CellProperty const * floorCell = loc.getCell(); + + Vector begin = loc.getPosition_p(); + Vector end = moveSeg.getEnd(floorCell); + + Vector delta = end - begin; + + // The Y component must be zeroed out or floor contacts far above/below the avatar can + // produce erroneous results + + delta.y = 0.0f; + + PathWalkResult walkResult; + + if(delta.magnitudeSquared() == 0.0f) + { + return Contact::noContact; + } + + if (footprint) + { + if (!footprint->isFloorWithinThreshold(*floorContact)) + return Contact::noContact; + } + + if((ms_ignoreFloor == NULL) || (floor != ms_ignoreFloor)) + { + walkResult = floor->canMove(loc,delta,-1,-1,result); + } + else + { + walkResult = floor->canMove(loc,delta,ms_ignoreTriId,ms_ignoreEdge,result); + } + + Contact tempContact = Contact::noContact; + + if(walkResult != PWR_WalkOk) + { + if((walkResult == PWR_HitEdge) || (walkResult == PWR_InContact) || (walkResult == PWR_HitBeforeEnter) || (walkResult == PWR_HitPast) || (walkResult == PWR_CenterHitEdge) || (walkResult == PWR_CenterInContact)) + { + Vector normV = delta; + + IGNORE_RETURN(normV.normalize()); + + float dot = normV.dot(result.getContactNormal()); + + if(dot < 0.0f) + { + if((result.getContactNormal() != ms_ignoreNormal) || (result.getTime() != ms_ignoreTime)) + { + tempContact.m_exists = true; + tempContact.m_resolvable = true; + + tempContact.m_time = result.getTime(); + tempContact.m_point = Vector::zero; //result.clipPoint; + tempContact.m_normal = result.getContactNormal(); + + tempContact.m_surfaceId1 = result.getHitTriId(); + tempContact.m_surfaceId2 = result.getHitEdgeId(); + + tempContact.m_cell = floorCell; + tempContact.m_extent = NULL; + tempContact.m_surface = result.getFloor(); + } + } + } + } + + return tempContact; +} + +// ---------------------------------------------------------------------- + +Contact CollisionResolve::findContactWithObstacle ( CellProperty const * cellA, SimpleExtent const * extentA, MoveSegment const & seg, ObstacleInfo const & obstacle, Footprint * footprint ) +{ + NOT_NULL(extentA); + + if(obstacle.m_extent) + { + return findContactWithExtent(cellA,extentA,seg,obstacle.m_cell,obstacle.m_extent); + } + else if(obstacle.m_floorContact) + { + return findContactWithFloor(obstacle.m_floorContact,seg, footprint); + } + else + { + return Contact::noContact; + } +} + +// ---------------------------------------------------------------------- + +/* +ResolutionResult CollisionResolve::resolveCollisionAsCylinders( SimpleExtent const * extentA, + Vector const & delta, + SimpleExtent const * extentB, + Vector & outDelta ) +{ + NOT_NULL(extentA); + NOT_NULL(extentB); + + MultiShape shapeA = extentA->getShape(); + MultiShape shapeB = extentB->getShape(); + + // Stretch the cylinders vertically so that players can't squeeze under creatures + + shapeA.setExtentY( shapeA.getExtentY() * 5.0f ); + shapeB.setExtentY( shapeB.getExtentY() * 5.0f ); + + return Resolve3d::Resolve(shapeA.getCylinder(),delta,shapeB.getCylinder(),outDelta); +} +*/ + + +// ---------------------------------------------------------------------- + diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollisionResolve.h b/engine/shared/library/sharedCollision/src/shared/core/CollisionResolve.h new file mode 100644 index 00000000..7e4b9e44 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollisionResolve.h @@ -0,0 +1,154 @@ +// ====================================================================== +// +// CollisionResolve.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_CollisionResolve_H +#define INCLUDED_CollisionResolve_H + +#include "sharedCollision/CollisionEnums.h" +#include "sharedMath/Vector.h" + +class CollisionProperty; +class CellProperty; +class BaseExtent; +class Vector; +class Contact; +class BaseExtent; +class SimpleExtent; +class ComponentExtent; +class DetailExtent; +class Footprint; +class FloorContactShape; +class Floor; + +struct ObstacleInfo +{ + ObstacleInfo() + : m_cell(NULL), + m_extent(NULL), + m_floorContact(NULL) + { + } + + ObstacleInfo ( CellProperty const * cell, SimpleExtent const * extent ) + : m_cell(cell), + m_extent(extent), + m_floorContact(NULL) + { + } + + ObstacleInfo ( CellProperty const * cell, FloorContactShape * contact ) + : m_cell(cell), + m_extent(NULL), + m_floorContact(contact) + { + } + + CellProperty const * m_cell; + SimpleExtent const * m_extent; + FloorContactShape * m_floorContact; +}; + +struct CellPosition +{ + CellPosition( CellProperty const * cell, Vector const & position ) + { + m_cell = cell; + m_position = position; + } + + CellProperty const * m_cell; + Vector m_position; +}; + + +typedef stdvector::fwd ColliderList; +typedef stdvector::fwd ObstacleList; +typedef stdvector::fwd ContactList; +typedef stdvector::fwd FloorContactList; +typedef stdvector::fwd CellPositionList; + +struct MoveSegment +{ + CellProperty const * m_cellA; + Vector m_pointA; + CellProperty const * m_cellB; + Vector m_pointB; + + + Vector getBegin ( CellProperty const * relativeCell ) const; + Vector getEnd ( CellProperty const * relativeCell ) const; + + Vector getDelta ( CellProperty const * relativeCell ) const; + + Vector getDelta_a ( void ) const; + + Vector getDelta_b ( void ) const; + + Vector atParam ( CellProperty const * relativeCell, float time ) const; + + float lengthSquared ( void ) const; +}; + +// ====================================================================== + +class CollisionResolve +{ +public: + + static void install ( void ); + static void remove ( void ); + + static void resolveAllCollisions ( void ); + + static int getColliderCount ( void ); + static int getObstacleCount ( void ); + static int getCollisionCount ( void ); + static int getBounceCount ( void ); + + static void resetCounters ( void ); + + static ResolutionResult resolveCollisions(CollisionProperty * colliderA, ColliderList const & colliderList); + +protected: + + static Contact findContactWithObstacle ( CellProperty const * cellA, SimpleExtent const * extentA, MoveSegment const & moveSeg, ObstacleInfo const & obstacle, Footprint * footprint); + static Contact findContactWithExtent ( CellProperty const * cellA, SimpleExtent const * extentA, MoveSegment const & moveSeg, CellProperty const * cellB, SimpleExtent const * extentB ); + static Contact findContactWithFloor ( FloorContactShape * floorContact, MoveSegment const & moveSeg, Footprint * footprint); + + // ---------- + + static ResolutionResult resolveCollisions(CollisionProperty * colliderA, MoveSegment const & moveSeg, ColliderList const & colliderList); + + static ResolutionResult resolveCollisions ( CellProperty const * cellA, SimpleExtent * extentA, FloorContactList & floorContactsA, + MoveSegment const & moveSeg, + ObstacleList * extentList, + CellPositionList & outList, Footprint * footprint ); + + static bool findFirstContact ( CellProperty const * cellA, SimpleExtent const * extentA, + MoveSegment const & moveSeg, + ObstacleList * ObstacleList, + int & outMinIndex, Contact & outContact, Footprint * footprint); + + // ---------- + + static void explodeExtent ( CellProperty const * cell, SimpleExtent const * extent, ObstacleList & list ); + static void explodeExtent ( CellProperty const * cell, ComponentExtent const * extent, ObstacleList & list ); + static void explodeExtent ( CellProperty const * cell, DetailExtent const * extent, ObstacleList & list ); + static void explodeExtent ( CellProperty const * cell, BaseExtent const * extent, ObstacleList & list ); + + static void explodeFootprint ( Footprint * foot, ObstacleList & obstacleList, FloorContactList & floorContacts ); + + static void explodeCollider(CollisionProperty * colliderA, ColliderList const & colliderList); + + static void translateEverything ( CellProperty const * cell, SimpleExtent * extent, FloorContactList & floorContacts, MoveSegment const & moveSeg, float moveParam, int ignoreTriId, int ignoreEdge ); + +}; + +// ====================================================================== + +#endif // #ifndef INCLUDED_CollisionResolve_H + diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollisionSurface.cpp b/engine/shared/library/sharedCollision/src/shared/core/CollisionSurface.cpp new file mode 100644 index 00000000..0d87527c --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollisionSurface.cpp @@ -0,0 +1,21 @@ +// ====================================================================== +// +// CollisionSurface.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/CollisionSurface.h" + + +CollisionSurface::CollisionSurface() +{ +} + +// ---------- + +CollisionSurface::~CollisionSurface() +{ +} + diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollisionSurface.h b/engine/shared/library/sharedCollision/src/shared/core/CollisionSurface.h new file mode 100644 index 00000000..2d245655 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollisionSurface.h @@ -0,0 +1,88 @@ +// ====================================================================== +// +// CollisionSurface.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_CollisionSurface_H +#define INCLUDED_CollisionSurface_H + +#include "sharedCollision/ContactPoint.h" + +// ---------- + +class Transform; +class BaseExtent; +class Vector; +class Line3d; +class Segment3d; + +// ---------- + +struct LineIntersectResult +{ + LineIntersectResult () + : m_hit(false), + m_hitTime(0.0f), + m_hitLoc() + { + } + + LineIntersectResult ( bool hit, float hitTime, ContactPoint const & contact ) + : m_hit(hit), + m_hitTime(hitTime), + m_hitLoc(contact) + { + } + + // ---------- + + static LineIntersectResult null; + + // ---------- + + bool operator < ( LineIntersectResult const & result ) const + { + return m_hitTime < result.m_hitTime; + } + + bool m_hit; + float m_hitTime; + ContactPoint m_hitLoc; +}; + +typedef stdvector::fwd LineIntersectResultVec; + +// ====================================================================== + +class CollisionSurface +{ +public: + + CollisionSurface(); + virtual ~CollisionSurface(); + + // ---------- + + virtual Transform const & getTransform_o2p ( void ) const = 0; + virtual Transform const & getTransform_o2w ( void ) const = 0; + virtual float getScale ( void ) const = 0; + + virtual BaseExtent const * getExtent_l ( void ) const = 0; + virtual BaseExtent const * getExtent_p ( void ) const = 0; + + // ---------- + // Intersection methods + +protected: + + // Disable copying + + CollisionSurface(const CollisionSurface &source); + CollisionSurface & operator =(const CollisionSurface &source); +}; + +// ---------------------------------------------------------------------- + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollisionUtils.cpp b/engine/shared/library/sharedCollision/src/shared/core/CollisionUtils.cpp new file mode 100644 index 00000000..f9a485d1 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollisionUtils.cpp @@ -0,0 +1,1800 @@ +// ====================================================================== +// +// CollisionUtils.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/CollisionUtils.h" + +#include "sharedCollision/Overlap2d.h" +#include "sharedCollision/Overlap3d.h" +#include "sharedCollision/Containment3d.h" +#include "sharedCollision/Intersect2d.h" +#include "sharedCollision/Intersect3d.h" +#include "sharedCollision/Distance3d.h" +#include "sharedCollision/Containment2d.h" +#include "sharedCollision/CollisionProperty.h" +#include "sharedCollision/Floor.h" +#include "sharedCollision/SimpleExtent.h" +#include "sharedCollision/ComponentExtent.h" +#include "sharedCollision/DetailExtent.h" +#include "sharedCollision/CollisionEnums.h" +#include "sharedCollision/CollisionDetect.h" + +#include "sharedObject/CellProperty.h" + +#include "sharedMath/AxialBox.h" +#include "sharedMath/YawedBox.h" +#include "sharedMath/OrientedBox.h" +#include "sharedMath/Triangle3d.h" +#include "sharedMath/Segment3d.h" +#include "sharedMath/Ribbon3d.h" +#include "sharedMath/Line3d.h" +#include "sharedMath/Plane3d.h" +#include "sharedMath/Circle.h" +#include "sharedMath/RangeLoop.h" +#include "sharedMath/Sphere.h" +#include "sharedMath/Cylinder.h" +#include "sharedMath/MultiShape.h" +#include "sharedMath/Vector.h" +#include "sharedMath/Ray3d.h" + +#include +#include + +using namespace Intersect3d; +using namespace Overlap3d; + +// ---------------------------------------------------------------------- + +const real Collision::gs_planarEpsilon = 0.000000001f; +const real Collision::gs_equalityEpsilon = 0.000000001f; +const real Collision::gs_spatialEpsilon = 0.000000001f; +const real Collision::gs_twistEpsilon = 0.000000001f; + +// ---------------------------------------------------------------------- + +Matrix3 Matrix3::fromRows ( Vector const & row0, Vector const & row1, Vector const & row2 ) +{ + Matrix3 temp; + + temp.m00 = row0.x; temp.m01 = row0.y; temp.m02 = row0.z; + temp.m10 = row1.x; temp.m11 = row1.y; temp.m12 = row1.z; + temp.m20 = row2.x; temp.m21 = row2.y; temp.m22 = row2.z; + + return temp; +} + +// ---------- + +Matrix3 Matrix3::fromColumns ( Vector const & col0, Vector const & col1, Vector const & col2 ) +{ + Matrix3 temp; + + temp.m00 = col0.x; temp.m01 = col1.x; temp.m02 = col2.x; + temp.m10 = col0.y; temp.m11 = col1.y; temp.m12 = col2.y; + temp.m20 = col0.z; temp.m21 = col1.z; temp.m22 = col2.z; + + return temp; +} + +// ---------- + +Vector Matrix3::row0 ( void ) const { return Vector(m00,m01,m02); } +Vector Matrix3::row1 ( void ) const { return Vector(m10,m11,m12); } +Vector Matrix3::row2 ( void ) const { return Vector(m20,m21,m22); } + +Vector Matrix3::col0 ( void ) const { return Vector(m00,m10,m20); } +Vector Matrix3::col1 ( void ) const { return Vector(m01,m11,m21); } +Vector Matrix3::col2 ( void ) const { return Vector(m02,m12,m22); } + +// ---------- + +bool Matrix3::invert ( Matrix3 & out ) const +{ + Matrix3 t; + + t.m00 = ( m11*m22 - m12*m21 ); + t.m01 = - ( m10*m22 - m12*m20 ); + t.m02 = ( m10*m21 - m11*m20 ); + + t.m10 = - ( m01*m22 - m02*m21 ); + t.m11 = ( m00*m22 - m02*m20 ); + t.m12 = - ( m00*m21 - m01*m20 ); + + t.m20 = ( m01*m12 - m02*m11 ); + t.m21 = - ( m00*m12 - m02*m10 ); + t.m22 = ( m00*m11 - m01*m10 ); + + float det = m00 * t.m00 + m01 * t.m01 + m02 * t.m02; + + if ( fabs(det) <= 0.00000001f ) return false; + + float inv = 1.0f/det; + + out.m00 = t.m00 * inv; + out.m01 = t.m10 * inv; + out.m02 = t.m20 * inv; + + out.m10 = t.m01 * inv; + out.m11 = t.m11 * inv; + out.m12 = t.m21 * inv; + + out.m20 = t.m02 * inv; + out.m21 = t.m12 * inv; + out.m22 = t.m22 * inv; + + return true; +} + +// ---------- + +Matrix3 Matrix3::multiply ( Matrix3 const & A ) const +{ + Matrix3 T; + + T.m00 = m00 * A.m00 + m01 * A.m10 + m02 * A.m20; + T.m01 = m00 * A.m01 + m01 * A.m11 + m02 * A.m21; + T.m02 = m00 * A.m02 + m01 * A.m12 + m02 * A.m22; + + T.m10 = m10 * A.m00 + m11 * A.m10 + m12 * A.m20; + T.m11 = m10 * A.m01 + m11 * A.m11 + m12 * A.m21; + T.m12 = m10 * A.m02 + m11 * A.m12 + m12 * A.m22; + + T.m20 = m20 * A.m00 + m21 * A.m10 + m22 * A.m20; + T.m21 = m20 * A.m01 + m21 * A.m11 + m22 * A.m21; + T.m22 = m20 * A.m02 + m21 * A.m12 + m22 * A.m22; + + return T; +} + +// ---------- + +Vector Matrix3::operator * ( Vector const & V ) const +{ + float x = m00 * V.x + m01 * V.y + m02 * V.z; + float y = m10 * V.x + m11 * V.y + m12 * V.z; + float z = m20 * V.x + m21 * V.y + m22 * V.z; + + return Vector(x,y,z); +} + +// ---------------------------------------------------------------------- + +namespace Collision2d +{ + +// ---------- + +float ComponentAlong( Vector const & V2, Vector const & N2 ) +{ + Vector V(V2.x,0.0f,V2.z); + Vector N(N2.x,0.0f,N2.z); + + float nmag2 = N.magnitudeSquared(); + + if(nmag2 > 0.0f) + { + return V.dot(N) / nmag2; + } + else + { + return 0.0f; + } +} + +// ---------- + +} // namespace Collision2d + +// ---------------------------------------------------------------------- + +namespace Collision3d +{ + +// ---------------------------------------------------------------------- +// Return the magnitude of the component of V in the direction of N + +// if t = componentAlong(V,N), + +// V - (N * t) is perpendicular to N. + +float ComponentAlong( Vector const & V, Vector const & N ) +{ + float nmag2 = N.magnitudeSquared(); + + if(nmag2 > 0.0f) + { + return V.dot(N) / nmag2; + } + else + { + return 0.0f; + } +} + +// ---------------------------------------------------------------------- +// Given a movement velocity, a collision normal, and a + +Vector SlideAlong ( Vector const & V, Vector const & N, float t ) +{ + float nmag2 = N.magnitudeSquared(); + + if(nmag2 > 0.0f) + { + real d = V.dot(N); + + if(d < 0.0f) + { + real mag = (-d * (1-t)) / nmag2; + + return V + N*mag; + } + else + { + return V; + } + } + else + { + return V; + } +} + +// ---------------------------------------------------------------------- + +Vector SingleSlide ( Vector V, Vector N ) +{ + if(V.dot(N) > 0.0f) return V; + + // build normal-binormal-tangent vectors + + Vector up(0.0f,1.0f,0.0f); + + static float angleEpsilon = 0.99996192306417128873735516482698f; // cos(0.5 degrees) + + float cosTheta = N.y; + + if(cosTheta > angleEpsilon) + { + // Surface is almost exactly horizontal + + V.y = 0.0f; + + return V; + } + + Vector B = N.cross(up); + Vector T = B.cross(N); + + IGNORE_RETURN(N.normalize()); + IGNORE_RETURN(B.normalize()); + IGNORE_RETURN(T.normalize()); + + float x = V.dot(B); + float y = V.dot(T); + float z = V.dot(N); + + if(z < 0.0f) z = 0.0f; + + if(N.y < 0.0f) + { + // Surface is facing down. + + if(N.y < -0.86602540378443864676372317075294f) // -cos(30 degrees) + { + // Surface is tilted down a lot. Since we don't know what the terrain is doing + // at this point, we can't allow any movement because we might end up putting + // the character under the terrain. + + return Vector::zero; + } + } + else + { + // Surface is facing up. Do not allow upward Y movement on slopes or players will + // be able to climb up rocks and such. + + // Very shallow slopes are OK, otherwise players can get stuck on top of extents. + // Very steep slops are OK, otherwise players can slide down under the terrain + + static float angleTolerance1 = 0.9659258262890682867497431997289f; // cos(15 degrees) + static float angleTolerance2 = 0.08715574274765817355806427083747f; // cos(85 degrees) + + if((cosTheta < angleTolerance1) && (cosTheta > angleTolerance2)) + { + if(y > 0.0f) y = 0.0f; + } + } + + Vector out = (B * x) + (T * y) + (N * z); + + return out; +} + +Vector DoubleSlide ( Vector V, Vector A, Vector B ) +{ + Vector C = A.cross(B); + + IGNORE_RETURN(A.normalize()); + IGNORE_RETURN(B.normalize()); + IGNORE_RETURN(C.normalize()); + + float a = A.dot(V); + float b = B.dot(V); + + Vector V2; + + if((a > 0.0f) && (b > 0.0f)) + { + // Velocity is moving away from both contacts + V2 = V; + } + else + { + float c = 0.00174532836589830883577820272085f; // 1/10 of 1 degree + + if( A.dot(B) < c) + { + // The corner is acute, we can't resolve this as a slide along one wall + + V2 = Vector::zero; + } + else + { + // The corner is obtuse, this could be resolved as either no motion + // or a slide along one of the walls + + Vector A2 = C.cross(A); + Vector B2 = B.cross(C); + + IGNORE_RETURN(A2.normalize()); + IGNORE_RETURN(B2.normalize()); + + float a2 = A2.dot(V); + float b2 = B2.dot(V); + + if((a2 < 0.0f) && (b2 < 0.0f)) + { + return Vector::zero; + } + else if (a2 > 0.0f) + { + V2 = V - A * a; + } + else if (b2 > 0.0f) + { + V2 = V - B * b; + } + else + { + // should never get here + + V2 = V; + } + } + } + + return V2; +} + +// ---------------------------------------------------------------------- +// Returns true if the lines twist in a clockwise (left hand) +// fashion + +// This little function has given me no end of grief. + +// Logically, twist(A,B) == twist(B,A) == -twist(-A,B), but due to +// numerical inaccuracies this isn't always the case. + +// In order to enforce these identities, we have to impose an arbitrary +// ordering on the arguments so that the return values are always consistent, +// even if their values are iffy due to precision issues. + +inline bool vectorLess ( Vector const & A, Vector const & B ) +{ + if(A.x != B.x) return (A.x < B.x); + + if(A.y != B.y) return (A.y < B.y); + + if(A.z != B.z) return (A.z < B.z); + + return false; +} + + +// This is the original version of the test. It's short, simple, and wrong. +// It doesn't handle degenerate cases and can return inconsistent results if +// the lines are swapped or the line directions are negated. + +/* +bool TestLineLineTwist ( Line3d const & A, Line3d const & B ) +{ + Vector D = B.getPoint() - A.getPoint(); + + return D.cross(A.getNormal()).dot(B.getNormal()) < 0.0f; +} +*/ + +#ifdef _MSC_VER + +inline void faster_normalize( Vector & V ) +{ + float t = (V.x * V.x) + (V.y * V.y) + (V.z * V.z); + + __asm fld t; + __asm fsqrt; + __asm fstp t; + + t = 1.0f / t; + + V.x *= t; + V.y *= t; + V.z *= t; +} + +int TestLineTwist4 ( Line3d const & A, Line3d const & B ) +{ + float epsilon = 0.00005f; + + Vector AN = A.getNormal(); + Vector BN = B.getNormal(); + + faster_normalize(AN); + faster_normalize(BN); + + Vector N = AN.cross(BN); + + Vector D = B.getPoint() - A.getPoint(); + + real V = D.dot(N); + + if ( V > epsilon ) return 1; + else if ( V < -epsilon ) return -1; + else return 0; +} + +#else + +int TestLineTwist4 ( Line3d const & A, Line3d const & B ) +{ + float epsilon = 0.00005f; + + Vector AN = A.getNormal(); + Vector BN = B.getNormal(); + + IGNORE_RETURN(AN.normalize()); + IGNORE_RETURN(BN.normalize()); + + Vector N = AN.cross(BN); + + Vector D = B.getPoint() - A.getPoint(); + + real V = D.dot(N); + + if ( V > epsilon ) return 1; + else if ( V < -epsilon ) return -1; + else return 0; +} + +#endif + +int TestLineLineTwist ( Line3d const & inA, Line3d const & inB ) +{ + Line3d A = inA; + Line3d B = inB; + + bool flipped = false; + + if(vectorLess(B.getPoint(),A.getPoint())) + { + std::swap(A,B); + } + + if(vectorLess(A.getNormal(),Vector::zero)) + { + A.flip(); + + flipped = !flipped; + } + + if(vectorLess(B.getNormal(),Vector::zero)) + { + B.flip(); + + flipped = !flipped; + } + + int result = TestLineTwist4(A,B); + + return flipped ? -result : result; +} + + +// ---------- + +int TestSegLineTwist ( Segment3d const & A, Line3d const & B ) +{ + if(vectorLess(A.getEnd(),A.getBegin())) + { + return -TestLineLineTwist(A.getReverseLine(),B); + } + else + { + return TestLineLineTwist(A.getLine(),B); + } +} + +int TestLineSegTwist ( Line3d const & A, Segment3d const & B ) +{ + if(vectorLess(B.getEnd(),B.getBegin())) + { + return -TestLineLineTwist(A,B.getReverseLine()); + } + else + { + return TestLineLineTwist(A,B.getLine()); + } +} + +int TestSegSegTwist ( Segment3d const & A, Segment3d const & B ) +{ + if(vectorLess(A.getEnd(),A.getBegin())) + { + return -TestLineSegTwist(A.getReverseLine(),B); + } + else + { + return TestLineSegTwist(A.getLine(),B); + } +} + +//---------------------------------------------------------------------------- + +CollinearResult testCollinear ( Vector const & V, Vector const & A, Vector const & B ) +{ + Vector dA = A - V; + Vector dB = B - V; + + float a2 = dA.dot(dA); + float b2 = dB.dot(dB); + + float d = dB.x*dA.y - dB.y*dA.x; + + float d2 = d*d; + + float test = 1.0e-6f * b2 * a2; + + if ( d2 > test ) + { + if ( d > 0.0f ) + { + return CLR_LeftSide; + } + else if ( d < 0.0f ) + { + return CLR_RightSide; + } + } + + float t = dA.dot(dB); + + if ( t < 0.0f ) + { + // V - A - B + + return CLR_OverlapLeft; + } + else if (t <= b2) + { + // A - V - B + + return CLR_OverlapCenter; + } + else + { + // A - B - V + + return CLR_OverlapRight; + } +} + +// ---------------------------------------------------------------------- +// Returns true if the line passing through V and poly[index] is left- +// tangent (touches on the counterclockwise side) to the polygon + +bool tangentPointL ( Vector const & V, VertexList const & poly, int index ) +{ + int size = poly.size(); + + int iA = (index > 0) ? index - 1 : size - 1; + int iB = index; + int iC = (index < size - 1) ? index + 1 : 0; + + Vector const & A = poly[iA]; + Vector const & B = poly[iB]; + Vector const & C = poly[iC]; + + if(testCollinear(V,B,C) == CLR_RightSide) + { + if(testCollinear(V,A,B) != CLR_LeftSide) + { + return true; + } + } + + return false; +} + +// ---------- +// Returns true if the line passing through V and poly[index] is right- +// tangent (touches on the clockwise side) to the polygon + +bool tangentPointR ( Vector const & V, VertexList const & poly, int index ) +{ + int size = poly.size(); + + int iA = (index > 0) ? index - 1 : size - 1; + int iB = index; + int iC = (index < size - 1) ? index + 1 : 0; + + Vector const & A = poly[iA]; + Vector const & B = poly[iB]; + Vector const & C = poly[iC]; + + if(testCollinear(V,A,B) == CLR_LeftSide) + { + if(testCollinear(V,B,C) != CLR_RightSide) + { + return true; + } + } + + return false; +} + +// ---------------------------------------------------------------------- + +void rightTangent ( Vector const & point, VertexList const & poly, int & index ) +{ + int size = poly.size(); + + for(int i = 0; i < size; i++) + { + if(tangentPointR(point,poly,i)) + { + index = i; + return; + } + } + + FATAL( true, ("CollisionUtils::rightTangent(point,poly) - couldn't find a valid tangent\n")); +} + +void leftTangent ( Vector const & point, VertexList const & poly, int & index ) +{ + int size = poly.size(); + + for(int i = 0; i < size; i++) + { + if(tangentPointL(point,poly,i)) + { + index = i; + return; + } + } + + FATAL( true, ("CollisionUtils::leftTangent(point,poly) - couldn't find a valid tangent\n")); +} + +// ---------------------------------------------------------------------- +// Find the pair of vertices A, B such that A is the left tangent for +// polyA from B, and B is the right tangent for poly B from A. + +// This is a variation on the code from Magic Software, but this one +// works for any pair of starting indices + +void rightTangent ( VertexList const & polyA, VertexList const & polyB, int & indexA, int & indexB ) +{ + int sizeA = polyA.size(); + int sizeB = polyB.size(); + + int i(0); + + indexA = 0; + indexB = 0; + + for (i = 0; i < sizeA+sizeB; i++) + { + bool stepped = false; + + // step around poly A until we find the tangent point for B + + Vector const & B = polyB[indexB]; + + while(!tangentPointR( B, polyA, indexA)) + { + indexA = (indexA < sizeA - 1) ? indexA + 1 : 0; + stepped = true; + } + + // step around poly B until we find the tangent point for A + + Vector const & A = polyA[indexA]; + + while(!tangentPointL( A, polyB, indexB)) + { + indexB = (indexB > 0) ? indexB - 1 : sizeB - 1; + stepped = true; + } + + // if we didn't go anywhere then both points are valid tangents and we're done. + + if(stepped == false) break; + } + + FATAL( i >= sizeA+sizeB, ("CollisionUtils::rightTangent(poly,poly) - iterated too far without finding a valid tangent\n")); +} + +//---------------------------------------------------------------------------- + +void appendPoly ( VertexList const & poly, int min, int max, VertexList & outPoly ) +{ + int cursor = min; + int size = poly.size(); + + do + { + outPoly.push_back( poly[cursor] ); + + cursor++; + if(cursor == size) cursor = 0; + } + while (cursor != max); + + outPoly.push_back(poly[max]); +} + +// ---------- + +void joinPolyPoly ( VertexList const & polyA, int minA, int maxA, + VertexList const & polyB, int minB, int maxB, + VertexList & outPoly ) +{ + appendPoly(polyA,minA,maxA,outPoly); + appendPoly(polyB,minB,maxB,outPoly); +} + +// ---------- + +void joinPointPoly ( Vector const & point, + VertexList const & poly, int min, int max, + VertexList & outPoly ) +{ + outPoly.push_back(point); + appendPoly(poly,min,max,outPoly); +} + +// ---------- + +void joinPointPoint ( Vector const & pointA, + Vector const & pointB, + VertexList & outPoly ) +{ + outPoly.push_back(pointA); + outPoly.push_back(pointB); +} + +// ---------------------------------------------------------------------- + +void MergePointPoint ( Vector const & pointA, Vector const & pointB, VertexList & outPoly ) +{ + outPoly.push_back(pointA); + outPoly.push_back(pointB); +} + +// ---------- + +void MergePointLine ( Vector const & pointA, VertexList const & lineB, VertexList & outPoly ) +{ + CollinearResult result = testCollinear( pointA, lineB[0], lineB[1] ); + + switch ( result ) + { + case CLR_RightSide: + outPoly.push_back(lineB[1]); + outPoly.push_back(lineB[0]); + outPoly.push_back(pointA); + break; + + case CLR_LeftSide: + outPoly.push_back(lineB[0]); + outPoly.push_back(lineB[1]); + outPoly.push_back(pointA); + break; + + case CLR_OverlapLeft: + outPoly.push_back(pointA); + outPoly.push_back(lineB[1]); + break; + + case CLR_OverlapRight: + outPoly.push_back(lineB[0]); + outPoly.push_back(pointA); + break; + + case CLR_OverlapCenter: + outPoly.push_back(lineB[0]); + outPoly.push_back(lineB[1]); + break; + + default: + break; + } +} + +// ---------------------------------------------------------------------- + +void MergePointPoly ( Vector const & pointA, VertexList const & polyB, VertexList & outPoly ) +{ + int sizeB = polyB.size(); + + if(sizeB == 0) { outPoly.push_back(pointA); return; } + if(sizeB == 1) { MergePointPoint(pointA,polyB[0],outPoly); return; } + if(sizeB == 2) { MergePointLine(pointA,polyB,outPoly); return; } + + int minB(0); + int maxB(0); + + rightTangent(pointA,polyB,minB); + leftTangent(pointA,polyB,maxB); + + joinPointPoly(pointA,polyB,minB,maxB,outPoly); +} + +// ---------------------------------------------------------------------- + +void MergePolyPoly ( VertexList & polyA, VertexList & polyB, VertexList & outPoly ) +{ + int sizeA = polyA.size(); + int sizeB = polyB.size(); + + if(sizeA == 0) { outPoly = polyB; return; } + if(sizeA == 1) { MergePointPoly(polyA[0],polyB,outPoly); return; } + + if(sizeB == 0) { outPoly = polyA; return; } + if(sizeB == 1) { MergePointPoly(polyB[0],polyA,outPoly); return; } + + // ---------- + + int minA, maxA, minB, maxB; + + rightTangent(polyA,polyB,maxA,minB); + rightTangent(polyB,polyA,maxB,minA); + + joinPolyPoly(polyA,minA,maxA,polyB,minB,maxB,outPoly); +} + +// ---------------------------------------------------------------------- + +void BuildConvexHull ( Vector const * sortedVerts, int vertCount, VertexList & outPoly ) +{ + if(sortedVerts == NULL) return; + + if(vertCount <= 0) return; + if(vertCount == 1) { outPoly.push_back(sortedVerts[0]); return; } + if(vertCount == 2) { outPoly.push_back(sortedVerts[0]); outPoly.push_back(sortedVerts[1]); return; } + + VertexList polyA; + VertexList polyB; + + int center = vertCount / 2; + + BuildConvexHull(sortedVerts,center,polyA); + BuildConvexHull(&sortedVerts[center],vertCount - center, polyB); + + MergePolyPoly(polyA,polyB,outPoly); +} + +// ---------- + +bool VertexSorterX ( Vector const & A, Vector const & B ) +{ + return A.x < B.x; +} + +void BuildConvexHull ( VertexList const & verts, VertexList & outPoly ) +{ + VertexList sortedVerts = verts; + + std::sort(sortedVerts.begin(),sortedVerts.end(),VertexSorterX); + + int count = verts.size(); + + BuildConvexHull( sortedVerts.begin(), count, outPoly ); +} + +// ---------------------------------------------------------------------- +// 3-d exit test + +// This is kinda freaky to try and visualize... + +// This test does the same basic thing as projecting the segment onto the triangle +// and determining which edge the segment exited. + +// It does it in a slightly different way than normal though - instead of doing a +// projection and then doing segment intersection, it creates a ribbon from the +// given segment and the "down" vector, and determines which edge of the triangle +// goes through that ribbon from front to back. + +int TestExitTri ( Ribbon3d const & R, Triangle3d const & tri ) +{ + if(TestSegRibbon(Segment3d(tri.getCornerA(),tri.getCornerB()),R) == HR_HitBack) return 0; + if(TestSegRibbon(Segment3d(tri.getCornerB(),tri.getCornerC()),R) == HR_HitBack) return 1; + if(TestSegRibbon(Segment3d(tri.getCornerC(),tri.getCornerA()),R) == HR_HitBack) return 2; + + return -1; +} + +int TestEntranceTri ( Ribbon3d const & R, Triangle3d const & tri ) +{ + if(TestSegRibbon(Segment3d(tri.getCornerA(),tri.getCornerB()),R) == HR_HitFront) return 0; + if(TestSegRibbon(Segment3d(tri.getCornerB(),tri.getCornerC()),R) == HR_HitFront) return 1; + if(TestSegRibbon(Segment3d(tri.getCornerC(),tri.getCornerA()),R) == HR_HitFront) return 2; + + return -1; +} + +// ---------- + +Vector MoveIntoTriangle ( Vector const & inPoint, Triangle3d const & tri, real desiredDist ) +{ + Vector outPoint = inPoint; + + for(int i = 0; i < 3; i++) + { + Vector planePoint = tri.getCorner(i); + + Vector edge = tri.getCorner(i+1) - tri.getCorner(i); + Vector normal = tri.getNormal(); + Vector planeNormal = normal.cross(edge); + + IGNORE_RETURN(planeNormal.normalize()); + + real dist = Distance3d::DistancePointPlane(outPoint,Plane3d(planePoint,planeNormal)); + + if(dist < desiredDist) + { + outPoint += planeNormal * (desiredDist - dist); + } + } + + return outPoint; +} + +// ---------------------------------------------------------------------- +// Assumes that the two triangles are neighbors and that they share +// the edges given by edgeA and edgeB + +bool IsEdgeConcave ( Triangle3d const & triA, int edgeA, + Triangle3d const & triB ) +{ + return Containment3d::TestPointTri( triA.getCorner(edgeA+2), triB ) == CR_Outside; +} + +// ---------------------------------------------------------------------- +// Translate the polygon the minimum amount to make it coincident with +// the given vertex + +void MovePolyOnto ( VertexList & verts, Vector V ) +{ + Vector close = Distance3d::ClosestPointPoly(V,verts); + + for(uint i = 0; i < verts.size(); i++) + { + verts[i] = verts[i] - close; + } +} + +// ---------------------------------------------------------------------- +// Avoidance theta calculation + +RangeLoop CalcAvoidanceThetas_Point ( Vector const & A, Vector const & B ) +{ + Vector D = B - A; + D.y = 0.0f; + + if(D.magnitudeSquared() < 0.000001f) return RangeLoop::empty; + + float theta = atan2(D.z,D.x) / PI_TIMES_2; + + return RangeLoop(theta,theta); +} + +// ---------- + +RangeLoop CalcAvoidanceThetas ( Vector const & A, Sphere const & S ) +{ + Vector D = S.getCenter() - A; + D.y = 0.0f; + + float dist = D.magnitude(); + float radius = S.getRadius(); + + if(dist < 0.000001f) return RangeLoop::empty; + + float thetaA = atan2(D.z,D.x) / PI_TIMES_2; + + float thetaB; + + if(dist < radius) + { + thetaB = 0.25f; + } + else + { + thetaB = asin( radius/dist ) / PI_TIMES_2; + } + + return RangeLoop( thetaA - thetaB, thetaA + thetaB ); +} + +// ---------- + +RangeLoop CalcAvoidanceThetas_Point ( Sphere const & S, Vector const & V ) +{ + return CalcAvoidanceThetas ( S.getCenter(), Sphere(V,S.getRadius()) ); +} + +// ---------- + +template< class RoundThing > +RangeLoop CalcAvoidanceThetas_Round ( Sphere const & A, RoundThing const & B ) +{ + return CalcAvoidanceThetas ( A.getCenter(), Sphere(B.getCenter(), A.getRadius() + B.getRadius()) ); +} + +// ---------- + +template< class RoundThing > +RangeLoop CalcAvoidanceThetas_Round ( Vector const & A, RoundThing const & B ) +{ + return CalcAvoidanceThetas ( A, Sphere(B.getCenter(), B.getRadius()) ); +} + +// ---------- + +template +RangeLoop CalcAvoidanceThetas_Box ( Thing const & S, Box const & box ) +{ + RangeLoop range = RangeLoop::empty; + + for(int i = 0; i < 8; i++) + { + RangeLoop temp = CalcAvoidanceThetas_Point(S,box.getCorner(i)); + + range = RangeLoop::enclose(range,temp); + } + + return range; +} + +// ---------- + +template +RangeLoop CalcAvoidanceThetas ( Thing const & T, MultiShape const & S ) +{ + MultiShape::ShapeType type = S.getShapeType(); + + // ---------- + + if (type == MultiShape::MST_AxialBox) { return CalcAvoidanceThetas_Box( T, S.getAxialBox() ); } + else if(type == MultiShape::MST_YawedBox) { return CalcAvoidanceThetas_Box( T, S.getYawedBox() ); } + else if(type == MultiShape::MST_OrientedBox) { return CalcAvoidanceThetas_Box( T, S.getOrientedBox() ); } + else if(type == MultiShape::MST_Cylinder) { return CalcAvoidanceThetas_Round( T, S.getCylinder() ); } + else if(type == MultiShape::MST_Sphere) { return CalcAvoidanceThetas_Round( T, S.getSphere() ); } + else { return RangeLoop::empty; } +} + +// ---------- + +RangeLoop CalcAvoidanceThetas ( Sphere const & S, BaseExtent const * extent ); + +RangeLoop CalcAvoidanceThetas ( Sphere const & S, SimpleExtent const * extent ) +{ + if(extent == NULL) return RangeLoop::empty; + + return CalcAvoidanceThetas(S,extent->getShape()); +} + +// ---------- + +RangeLoop CalcAvoidanceThetas ( Sphere const & S, ComponentExtent const * extent ) +{ + if(extent == NULL) return RangeLoop::empty; + + RangeLoop range = RangeLoop::empty; + + int count = extent->getExtentCount(); + + for(int i = 0; i < count; i++) + { + BaseExtent const * child = extent->getExtent(i); + + if(child == NULL) continue; + + RangeLoop temp = CalcAvoidanceThetas(S,child); + + range = RangeLoop::enclose(range,temp); + } + + return range; +} + +// ---------- + +RangeLoop CalcAvoidanceThetas ( Sphere const & S, DetailExtent const * extent ) +{ + if(extent == NULL) return RangeLoop::empty; + + int count = extent->getExtentCount(); + + BaseExtent const * child = extent->getExtent(count - 1); + + if(child == NULL) return RangeLoop::empty; + + return CalcAvoidanceThetas(S,child); +} + +// ---------- + +RangeLoop CalcAvoidanceThetas ( Sphere const & S, BaseExtent const * extent ) +{ + if(extent == NULL) return RangeLoop::empty; + + ExtentType type = extent->getType(); + + // ---------- + + if (type == ET_Simple) return CalcAvoidanceThetas(S,safe_cast(extent)); + else if(type == ET_Component) return CalcAvoidanceThetas(S,safe_cast(extent)); + else if(type == ET_Detail) return CalcAvoidanceThetas(S,safe_cast(extent)); + else return RangeLoop::empty; +} + +// ---------- + +RangeLoop CalcAvoidanceThetas ( Sphere const & S, ExtentVec const & extents ) +{ + RangeLoop range = RangeLoop::empty; + + int count = extents.size(); + + for(int i = 0; i < count; i++) + { + BaseExtent const * child = extents[i]; + + if(child == NULL) continue; + + RangeLoop temp = CalcAvoidanceThetas(S,child); + + range = RangeLoop::enclose(range,temp); + } + + return range; +} + +// ---------------------------------------------------------------------- + +bool intersectRays ( Vector const & A, float thetaA, Vector const & B, float thetaB, Vector & out ) +{ + thetaA *= PI_TIMES_2; + thetaB *= PI_TIMES_2; + + Vector nA( cos(thetaA), 0.0f, sin(thetaA) ); + Vector nB( cos(thetaB), 0.0f, sin(thetaB) ); + + return Intersect2d::IntersectRayRay( Ray3d(A,nA), Ray3d(B,nB), out ); +} + +// ---------- + +bool CalcAvoidancePoint ( Vector const & A, Vector const & B, RangeLoop const & rangeA, RangeLoop const & rangeB, Vector & out ) +{ + Vector delta = B - A; + + delta.y = 0.0f; + + float thetaA = atan2(delta.z,delta.x) / PI_TIMES_2; + float thetaB = thetaA + 0.5f; + + // if the ranges are empty, then we have no collision - return the destination + if(rangeA.isEmpty() || rangeB.isEmpty()) + { + out = B; + return true; + } + + if(rangeB.getSize() > 0.6f) return false; // goal is inside the thing we're trying to avoid + + // if either range contains exclusively the respective theta, we can't find a good path + if(!rangeA.containsExclusive(thetaA) || !rangeB.containsExclusive(thetaB)) return false; + + float minA = rangeA.getMin(); + float maxA = rangeA.getMax(); + float minB = rangeB.getMin(); + float maxB = rangeB.getMax(); + + Vector C = Vector::zero; + Vector D = Vector::zero; + + bool foundC = intersectRays( A, minA, B, maxB, C ); + bool foundD = intersectRays( A, maxA, B, minB, D ); + + float distAC = A.magnitudeBetween(C); + float distBC = B.magnitudeBetween(C); + + float distAD = A.magnitudeBetween(D); + float distBD = B.magnitudeBetween(D); + + bool gotoC = true; + + if(foundC && foundD) + { + gotoC = (distAC + distBC) < (distAD + distBD); + } + else if(foundC) + { + gotoC = true; + } + else if(foundD) + { + gotoC = false; + } + else + { + // both start and end are partially inside the obstacle. + // pick a silhouette theta and walk towards it + + float diffThetaA = RangeLoop(minA,maxB).getSize(); + float diffThetaB = RangeLoop(minB,maxA).getSize(); + + if(diffThetaA > diffThetaB) + { + float t = minA * PI_TIMES_2; + + Vector offset( cos(t), 0.0f, sin(t) ); + + out = A + offset; + } + else + { + float t = maxA * PI_TIMES_2; + + Vector offset( cos(t), 0.0f, sin(t) ); + + out = A + offset; + } + + return true; + } + + // Explanation - In some cases obstacle avoidance can produce an avoidance point that's + // really really really close to the start point. Since moving there doesn't get the mob + // to the goal effectively, in those cases we scoot the avoidance point out a bit + // to keep the mob from getting stuck. + + const float cornerScoot = 0.3f; + + if(gotoC) + { + if(distAC >= cornerScoot) + { + out = C; + } + else + { + // Avoidance point is too close to starting point - move it away from the obstacle + float bisectTheta = RangeLoop(maxB,minA).atParam(0.5f) * PI_TIMES_2; + + Vector offset( cos(bisectTheta), 0.0f, sin(bisectTheta) ); + + offset *= cornerScoot - distAC; + + out = C + offset; + } + } + else + { + if(distAD >= cornerScoot) + { + out = D; + } + else + { + float bisectTheta = RangeLoop(maxA,minB).atParam(0.5f) * PI_TIMES_2; + + Vector offset( cos(bisectTheta), 0.0f, sin(bisectTheta) ); + + offset *= cornerScoot - distAD; + + out = D + offset; + } + } + + return true; +} + +// ---------------------------------------------------------------------- + +bool CalcAvoidancePoint ( Sphere const & A, Vector const & delta, BaseExtent const * E, Vector & out ) +{ + Sphere B(A.getCenter() + delta,A.getRadius()); + + RangeLoop rangeA = CalcAvoidanceThetas(A,E); + RangeLoop rangeB = CalcAvoidanceThetas(B,E); + + return CalcAvoidancePoint(A.getCenter(),B.getCenter(),rangeA,rangeB,out); +} + +bool CalcAvoidancePoint ( Sphere const & A, Vector const & delta, ExtentVec const & extents, Vector & out ) +{ + Sphere B(A.getCenter() + delta,A.getRadius()); + + RangeLoop rangeA = CalcAvoidanceThetas(A,extents); + RangeLoop rangeB = CalcAvoidanceThetas(B,extents); + + return CalcAvoidancePoint(A.getCenter(),B.getCenter(),rangeA,rangeB,out); +} + +// ---------------------------------------------------------------------- +// Obstacle exploding - break down an obstacle list into a set of simple +// extents that need to be avoided + +void explodeObstacle ( Sphere const & sphere, Vector const & delta, BaseExtent const * extent, ExtentVec & outList ); + +void explodeObstacle ( Sphere const & sphere, Vector const & delta, SimpleExtent const * extent, ExtentVec & outList ) +{ + DetectResult result = CollisionDetect::testSphereExtent(sphere,delta,extent); + + if(result.collided) + { + outList.push_back(extent); + } +} + +void explodeObstacle ( Sphere const & sphere, Vector const & delta, DetailExtent const * extent, ExtentVec & outList ) +{ + if(extent == NULL) return; + + int count = extent->getExtentCount(); + + explodeObstacle(sphere,delta,extent->getExtent(count-1),outList); +} + +void explodeObstacle ( Sphere const & sphere, Vector const & delta, ComponentExtent const * extent, ExtentVec & outList ) +{ + if(extent == NULL) return; + + int count = extent->getExtentCount(); + + for(int i = 0; i < count; i++) + { + explodeObstacle(sphere,delta,extent->getExtent(i),outList); + } +} + +void explodeObstacle ( Sphere const & sphere, Vector const & delta, BaseExtent const * extent, ExtentVec & outList ) +{ + if(extent == NULL) return; + + ExtentType type = extent->getType(); + + if (type == ET_Simple) { explodeObstacle(sphere,delta,safe_cast(extent),outList); } + else if(type == ET_Component) { explodeObstacle(sphere,delta,safe_cast(extent),outList); } + else if(type == ET_Detail) { explodeObstacle(sphere,delta,safe_cast(extent),outList); } +} + +void explodeObstacle ( Sphere const & sphere, Vector const & delta, ExtentVec const & extentVec, ExtentVec & outList ) +{ + int count = extentVec.size(); + + for(int i = 0; i < count; i++) + { + explodeObstacle(sphere,delta,extentVec[i],outList); + } +} + +// ---------------------------------------------------------------------- + +bool CalcAvoidancePoint ( Sphere const & sphere, Transform const & sphereTransform_p2w, Vector const & delta, CollisionProperty const * obstacle, Vector & out ) +{ + if(obstacle == NULL) return false; + + static ExtentVec tempExtents; + + BaseExtent const * obstacleExtent = obstacle->getExtent_p(); + if(obstacleExtent) + { + // Use the normal obstacle extents. + explodeObstacle(sphere,delta,obstacleExtent,tempExtents); + } + else + { + // The object doesn't have a set of extents. See if the object has a floor. + Floor const *const floor = obstacle->getFloor(); + if (!floor) + return false; + + // Build a SimpleExtent out of the floor surrounding sphere. + Sphere const floorSphere_w = floor->getBoundingSphere_w(); + + // Build an obstacle sphere in the same parent space as the sphere (mob) parameter. + Vector const floorSphereCenter_mobP = sphereTransform_p2w.rotateTranslate_p2l(floorSphere_w.getCenter()); + Sphere const floorSphere_mobP(floorSphereCenter_mobP, floorSphere_w.getRadius()); + + // Build a SimpleExtent. This needs to be static since tempExtents stores a pointer to it. + static SimpleExtent floorExtent; + floorExtent.setShape(MultiShape(floorSphere_mobP)); + + // Add shape to temp extents. + explodeObstacle(sphere, delta, &floorExtent, tempExtents); + } + + + bool result = CalcAvoidancePoint(sphere,delta,tempExtents,out); + + tempExtents.clear(); + + return result; +} + +// ---------- + +bool CalcAvoidancePoint ( CollisionProperty const * mob, Vector const & delta, CollisionProperty const * obstacle, Vector & out ) +{ + if(mob == NULL) return false; + if(obstacle == NULL) return false; + + BaseExtent const * mobExtent = mob->getExtent_p(); + + if(mobExtent == NULL) return false; + + Sphere mobSphere = mobExtent->getBoundingSphere(); + + return CalcAvoidancePoint(mobSphere,mob->getOwner().getTransform_p2w(),delta,obstacle,out); +} + +// ---------- + +bool CalcAvoidancePoint ( Object const * mob, Vector const & delta, Object const * obstacle, Vector & out ) +{ + if(mob == NULL) return false; + if(obstacle == NULL) return false; + + CollisionProperty const * mobCollision = mob->getCollisionProperty(); + CollisionProperty const * obstacleCollision = obstacle->getCollisionProperty(); + + return CalcAvoidancePoint(mobCollision,delta,obstacleCollision,out); +} + +// ---------------------------------------------------------------------- +// normalized obstacle space - ship at the origin moving along positive Z + +bool CalcAvoidancePoints3d ( Sphere const & obstacle, Vector & avoidA, Vector & avoidB ) +{ + Vector center = obstacle.getCenter(); + float radius = obstacle.getRadius(); + + Vector delta = -center; + + delta.z = 0.0f; + + delta.normalize(); + + delta *= radius; + + avoidA = Vector( center.x + delta.x, center.y + delta.y, center.z - radius ); + avoidB = Vector( center.x + delta.x, center.y + delta.y, center.z + radius ); + + return true; +} + +// ---------------------------------------------------------------------- + +bool CalcAvoidancePoints3d ( Sphere const & mob, Vector const & delta, Sphere const & obstacle, Vector & avoidA, Vector & avoidB ) +{ + Vector K = delta; + + K.normalize(); + + Vector I,J; + + if(abs(K.y) > 0.95) + { + // line A-B is almost vertical, build orthonormal basis using X axis + + J = K.cross(Vector::unitX); + J.normalize(); + + I = J.cross(K); + I.normalize(); + } + else + { + I = Vector::unitY.cross(K); + I.normalize(); + + J = K.cross(I); + J.normalize(); + } + + Transform T; + + T.setLocalFrameIJK_p(I,J,K); + T.setPosition_p(mob.getCenter()); + + Sphere normalizedObstacle(T.rotateTranslate_p2l(obstacle.getCenter()),obstacle.getRadius() + mob.getRadius()); + + bool calcOk = CalcAvoidancePoints3d ( normalizedObstacle, avoidA, avoidB ); + + if(!calcOk) return false; + + avoidA = T.rotateTranslate_l2p(avoidA); + avoidB = T.rotateTranslate_l2p(avoidB); + + return true; +} + +// ---------------------------------------------------------------------- + +} // namespace Collision3d + +// ---------------------------------------------------------------------- + +namespace CollisionUtils +{ + +Vector transformToWorld ( CellProperty const * cellA, Vector const & point_A ) +{ + return transformToCell( cellA, point_A, CellProperty::getWorldCellProperty() ); +} + +Sphere transformToWorld ( CellProperty const * cellA, Sphere const & sphere_A ) +{ + return transformToCell( cellA, sphere_A, CellProperty::getWorldCellProperty() ); +} + +Capsule transformToWorld ( CellProperty const * cellA, Capsule const & capsule_A ) +{ + return transformToCell( cellA, capsule_A, CellProperty::getWorldCellProperty() ); +} + +Transform transformToWorld ( CellProperty const * cellA, Transform const & transform_A ) +{ + return transformToCell( cellA, transform_A, CellProperty::getWorldCellProperty() ); +} + +Vector transformFromWorld ( Vector const & point_w, CellProperty const * cellB ) +{ + return transformToCell( CellProperty::getWorldCellProperty(), point_w, cellB ); +} + +Sphere transformFromWorld ( Sphere const & sphere_w, CellProperty const * cellB ) +{ + return transformToCell ( CellProperty::getWorldCellProperty(), sphere_w, cellB ); +} + +Capsule transformFromWorld ( Capsule const & capsule_w, CellProperty const * cellB ) +{ + return transformToCell ( CellProperty::getWorldCellProperty(), capsule_w, cellB ); +} + +Transform transformFromWorld ( Transform const & transform_w, CellProperty const * cellB ) +{ + return transformToCell( CellProperty::getWorldCellProperty(), transform_w, cellB ); +} + +Vector transformToCell ( CellProperty const * cellA, Vector const & point_A, CellProperty const * cellB ) +{ + CellProperty const * worldCell = CellProperty::getWorldCellProperty(); + + if(cellA == NULL) cellA = worldCell; + if(cellB == NULL) cellB = worldCell; + + if(cellA == cellB) return point_A; + + if(cellA == CellProperty::getWorldCellProperty()) + { + return cellB->getOwner().rotateTranslate_w2o(point_A); + } + else if(cellB == CellProperty::getWorldCellProperty()) + { + return cellA->getOwner().rotateTranslate_o2w(point_A); + } + else + { + Vector point_w = cellA->getOwner().rotateTranslate_o2w(point_A); + Vector point_B = cellB->getOwner().rotateTranslate_w2o(point_w); + + return point_B; + } +} + +Vector rotateToCell ( CellProperty const * cellA, Vector const & point_A, CellProperty const * cellB ) +{ + CellProperty const * worldCell = CellProperty::getWorldCellProperty(); + + if(cellA == NULL) cellA = worldCell; + if(cellB == NULL) cellB = worldCell; + + if(cellA == cellB) return point_A; + + if(cellA == CellProperty::getWorldCellProperty()) + { + return cellB->getOwner().rotate_w2o(point_A); + } + else if(cellB == CellProperty::getWorldCellProperty()) + { + return cellA->getOwner().rotate_o2w(point_A); + } + else + { + Vector point_w = cellA->getOwner().rotate_o2w(point_A); + Vector point_B = cellB->getOwner().rotate_w2o(point_w); + + return point_B; + } +} + +// ---------------------------------------------------------------------- + +Transform transformToCell ( CellProperty const * cellA, Transform const & transform_A, CellProperty const * cellB ) +{ + CellProperty const * worldCell = CellProperty::getWorldCellProperty(); + + if(cellA == NULL) cellA = worldCell; + if(cellB == NULL) cellB = worldCell; + + if(cellA == cellB) return transform_A; + + if(cellA == worldCell) + { + Transform const & objectToWorld = transform_A; + + Transform cellAToWorld = cellB->getOwner().getTransform_o2w(); + + Transform worldToCellA; + worldToCellA.invert(cellAToWorld); + + Transform objectToCellA; + objectToCellA.multiply(worldToCellA, objectToWorld); + + return objectToCellA; + } + else if (cellB == worldCell) + { + Transform const & objectToCellA = transform_A; + + Transform cellAToWorld = cellA->getOwner().getTransform_o2w(); + + Transform objectToWorld; + objectToWorld.multiply(cellAToWorld, objectToCellA); + + return objectToWorld; + } + else + { + Transform const & objectToCellA = transform_A; + + Transform cellAToWorld = cellA->getOwner().getTransform_o2w(); + + Transform objectToWorld; + objectToWorld.multiply(cellAToWorld,objectToCellA); + + Transform cellBToWorld = cellB->getOwner().getTransform_o2w(); + + Transform worldToCellB; + worldToCellB.invert(cellBToWorld); + + Transform objectToCellB; + objectToCellB.multiply(worldToCellB,objectToWorld); + + return objectToCellB; + } +} + +// ---------------------------------------------------------------------- + +Sphere transformToCell ( CellProperty const * cellA, Sphere const & sphere_A, CellProperty const * cellB) +{ + Vector center_B = transformToCell(cellA,sphere_A.getCenter(),cellB); + + return Sphere(center_B,sphere_A.getRadius()); +} + +// ---------------------------------------------------------------------- + +Capsule transformToCell ( CellProperty const * cellA, Capsule const & capsule_A, CellProperty const * cellB) +{ + Vector pointA_B = transformToCell(cellA,capsule_A.getPointA(),cellB); + Vector pointB_B = transformToCell(cellA,capsule_A.getPointB(),cellB); + + return Capsule(pointA_B,pointB_B,capsule_A.getRadius()); +} +// ---------------------------------------------------------------------- + +MultiShape transformToCell ( CellProperty const * cellA, MultiShape const & shape, CellProperty const * cellB ) +{ + Vector center = transformToCell(cellA,shape.getCenter(),cellB); + + Vector axisX = rotateToCell(cellA,shape.getAxisX(),cellB); + Vector axisY = rotateToCell(cellA,shape.getAxisY(),cellB); + Vector axisZ = rotateToCell(cellA,shape.getAxisZ(),cellB); + + return MultiShape ( shape.getBaseType(), + center, axisX, axisY, axisZ, + shape.getExtentX(), shape.getExtentY(), shape.getExtentZ() ); +} + +// ---------------------------------------------------------------------- + +bool testPortalVis ( CellProperty const * cellA, Vector const & pointA, CellProperty const * cellB, Vector const & pointB ) +{ + if(cellA == NULL) return false; + if(cellB == NULL) return false; + + float hitTime = 0.0f; + + // ---------- + // If the two points are in the same cell, they're visible if the line between them doesn't hit any portals in the cell + + if(cellA == cellB) + { + return cellA->getDestinationCell(pointA,pointB,hitTime) == NULL; + } + + // ---------- + // The two points are in different cells + + // Find point B's location in this cell, and determine if the line between point A + // and the local point B hits a portal + + Vector localB = transformToCell(cellB,pointB,cellA); + CellProperty *newCellA = cellA->getDestinationCell(pointA,localB,hitTime); + if(newCellA) + { + // If it does, re-run the query starting in the cell on the other side of the + // portal, starting from the intersection of the segment and the portal + + Vector hitPoint = pointA + (localB - pointA) * hitTime; + Vector newPointA = transformToCell(cellA,hitPoint,newCellA); + return testPortalVis( newCellA, newPointA, cellB, pointB ); + } + + // The segment never leaves this cell, so it can't possibly get to point B + return false; +} +// ---------------------------------------------------------------------- + +bool epsilonEqual ( Sphere const & A, Sphere const & B, float epsilon ) +{ + if(abs(A.getRadius() - B.getRadius()) > epsilon) return false; + + Vector cA = A.getCenter(); + Vector cB = B.getCenter(); + + if(abs(cA.x - cB.x) > epsilon) return false; + if(abs(cA.y - cB.y) > epsilon) return false; + if(abs(cA.z - cB.z) > epsilon) return false; + + return true; +} + +// ---------------------------------------------------------------------- + +} // namespace CollisionUtils + +// ====================================================================== diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollisionUtils.h b/engine/shared/library/sharedCollision/src/shared/core/CollisionUtils.h new file mode 100644 index 00000000..6f39dab5 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollisionUtils.h @@ -0,0 +1,221 @@ +// ====================================================================== +// +// CollisionUtils.h +// copyright (c) 2001 Sony Online Entertainment +// +// ---------------------------------------------------------------------- + +#ifndef INCLUDED_CollisionUtils_H +#define INCLUDED_CollisionUtils_H + +#include "sharedMath/Vector.h" +#include "sharedMath/Transform.h" + +class Vector; +class ABox; +class Vector; +class Triangle3d; +class Line3d; +class Segment3d; +class Ribbon3d; +class Circle; +class Sphere; +class Capsule; +class Transform; +class CellProperty; +class Object; +class CollisionProperty; +class MultiShape; +class BaseExtent; + +typedef stdvector::fwd VectorVector; +typedef stdvector::fwd VertexList; +typedef stdvector::fwd ExtentVec; + +// ---------------------------------------------------------------------- + +#ifdef _MSC_VER + +inline bool isNan ( float const & x ) +{ + if(_isnan(x)) return true; + + if(!_finite(x)) return true; + + return false; +} + +inline bool isNan ( Vector const & v ) +{ + if(isNan(v.x)) return true; + if(isNan(v.y)) return true; + if(isNan(v.z)) return true; + + return false; +} + +inline bool isNan ( Transform const & t ) +{ + return t.isNaN(); +} + +#define NAN_CHECK(x) DEBUG_FATAL(isNan(x),("Floating-point value is not a number")) + +#else + +#define NAN_CHECK(x) + +#endif + +// ---------------------------------------------------------------------- + +struct Matrix3 +{ + static Matrix3 fromRows( Vector const & row0, Vector const & row1, Vector const & row2); + static Matrix3 fromColumns ( Vector const & col0, Vector const & col1, Vector const & col2 ); + + // ---------- + + Vector row0 ( void ) const; + Vector row1 ( void ) const; + Vector row2 ( void ) const; + + Vector col0 ( void ) const; + Vector col1 ( void ) const; + Vector col2 ( void ) const; + + bool invert ( Matrix3 & out ) const; + + Matrix3 multiply ( Matrix3 const & A ) const; + + Vector operator * ( Vector const & V ) const; + + // ---------- + + float m00, m01, m02; + float m10, m11, m12; + float m20, m21, m22; +}; + +// ---------------------------------------------------------------------- + +namespace Collision +{ + extern const real gs_equalityEpsilon; + extern const real gs_planarEpsilon; + extern const real gs_spatialEpsilon; + extern const real gs_twistEpsilon; +}; + +// ---------------------------------------------------------------------- +// 2d operations + +namespace Collision2d +{ + +float ComponentAlong ( Vector const & V, Vector const & N ); + +}; // namespace Collision2d + +// ---------------------------------------------------------------------- +// 3d operations + +namespace Collision3d +{ + +// ---------------------------------------------------------------------- +// Side/twist tests + +int TestLineLineTwist ( Line3d const & A, Line3d const & B ); + +int TestLineSegTwist ( Line3d const & A, Segment3d const & B ); +int TestSegLineTwist ( Segment3d const & A, Line3d const & B ); + +int TestSegSegTwist ( Segment3d const & A, Segment3d const & B); + +// ---------------------------------------------------------------------- +// Miscellaneous + +// The idea of an exit test for a segment exiting a triangle in 3d is a +// little wonky. This does effectively the same thing as projecting the +// segment down onto the triangle and figuring out which edge the projected +// segment exits. + +int TestExitTri ( Ribbon3d const & R, Triangle3d const & tri ); +int TestEntranceTri ( Ribbon3d const & R, Triangle3d const & tri ); + +Vector ClipPath ( Vector const & begin, + Vector const & delta, + Triangle3d const & tri, + int whichEdge, + Vector const & up ); + +float ComponentAlong ( Vector const & V, Vector const & N ); +Vector SlideAlong ( Vector const & V, Vector const & N, float t ); + +Vector SingleSlide ( Vector V, Vector A ); +Vector DoubleSlide ( Vector V, Vector A, Vector B ); + +bool IsEdgeConcave ( Triangle3d const & triA, int edgeA, + Triangle3d const & triB ); + +Vector MoveIntoTriangle ( Vector const & point, + Triangle3d const & tri, + real desiredDist ); + +void MovePolyOnto ( VertexList & verts, Vector V ); + +bool FindTangentPoint ( Vector A, Circle C, bool right, Vector & outTangent ); +bool FindAvoidancePoint ( Vector A, Vector B, Circle C, Vector & outPoint ); + +bool FindAvoidanceThetas ( Vector A, MultiShape const & shape, float & outThetaA, float & outDistA, float & outThetaB, float & outDistB ); + +bool CalcAvoidancePoint ( Object const * mob, Vector const & delta, Object const * obstacle, Vector & out ); + +bool CalcAvoidancePoints3d ( Sphere const & mob, Vector const & delta, Sphere const & obstacle, Vector & avoidA, Vector & avoidB ); + +// ---------------------------------------------------------------------- + +}; // namespace Collision3d + +// ---------------------------------------------------------------------- + +namespace CollisionUtils +{ + +Vector transformToWorld ( CellProperty const * cellA, Vector const & point_A ); +Sphere transformToWorld ( CellProperty const * cellA, Sphere const & sphere_A ); +Capsule transformToWorld ( CellProperty const * cellA, Capsule const & capsule_A ); +Transform transformToWorld ( CellProperty const * cellA, Transform const & transform_A ); + +Vector transformFromWorld ( Vector const & point_w, CellProperty const * cellB ); +Sphere transformFromWorld ( Sphere const & sphere_w, CellProperty const * cellB ); +Capsule transformFromWorld ( Capsule const & capsule_w, CellProperty const * cellB ); +Transform transformFromWorld ( Transform const & transform_w, CellProperty const * cellB ); + +// ---------- + +Vector transformToCell ( CellProperty const * cellA, Vector const & point, CellProperty const * cellB ); +Transform transformToCell ( CellProperty const * cellA, Transform const & transform, CellProperty const * cellB ); +Sphere transformToCell ( CellProperty const * cellA, Sphere const & sphere, CellProperty const * cellB ); +Capsule transformToCell ( CellProperty const * cellA, Capsule const & capsule, CellProperty const * cellB ); +MultiShape transformToCell ( CellProperty const * cellA, MultiShape const & shape, CellProperty const * cellB ); + +Vector rotateToCell ( CellProperty const * cellA, Vector const & dir, CellProperty const * cellB ); + +// ---------- + +bool testPortalVis ( CellProperty const * cellA, Vector const & pointA, CellProperty const * cellB, Vector const & pointB ); + +// ---------- + +bool epsilonEqual ( Sphere const & A, Sphere const & B, float epsilon ); + + +}; + + + +// ---------------------------------------------------------------------- + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollisionVolume.cpp b/engine/shared/library/sharedCollision/src/shared/core/CollisionVolume.cpp new file mode 100644 index 00000000..f8d3ef00 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollisionVolume.cpp @@ -0,0 +1,8 @@ +// ====================================================================== +// +// CollisionVolume.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollisionVolume.h b/engine/shared/library/sharedCollision/src/shared/core/CollisionVolume.h new file mode 100644 index 00000000..afb2324e --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollisionVolume.h @@ -0,0 +1,39 @@ +// ====================================================================== +// +// CollisionVolume.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_CollisionVolume_H +#define INCLUDED_CollisionVolume_H + +// ====================================================================== + +class Vector; + +// ====================================================================== + +class CollisionVolume +{ +public: + + CollisionVolume(); + virtual ~CollisionVolume(); + + // ---------- + // Generic interface + + bool contains ( Vector const & V ); + +protected: + + // Disable copying + + CollisionVolume(const CollisionVolume &source); + CollisionVolume & operator =(const CollisionVolume &source); +}; + +// ---------------------------------------------------------------------- + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollisionWorld.cpp b/engine/shared/library/sharedCollision/src/shared/core/CollisionWorld.cpp new file mode 100644 index 00000000..5e5471cf --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollisionWorld.cpp @@ -0,0 +1,2788 @@ +// ====================================================================== +// +// CollisionWorld.cpp +// copyright (c) 2002 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/CollisionWorld.h" + +#include "sharedCollision/BarrierObject.h" +#include "sharedCollision/CollisionDetect.h" +#include "sharedCollision/CollisionInfo.h" +#include "sharedCollision/CollisionProperty.h" +#include "sharedCollision/CollisionResolve.h" +#include "sharedCollision/CollisionUtils.h" +#include "sharedCollision/ConfigSharedCollision.h" +#include "sharedCollision/Containment3d.h" +#include "sharedCollision/Distance2d.h" +#include "sharedCollision/DoorObject.h" +#include "sharedCollision/Floor.h" +#include "sharedCollision/FloorManager.h" +#include "sharedCollision/FloorMesh.h" +#include "sharedCollision/FloorTri.h" +#include "sharedCollision/Footprint.h" +#include "sharedCollision/FootprintForceReattachManager.h" +#include "sharedCollision/Intersect1d.h" +#include "sharedCollision/SetupSharedCollision.h" +#include "sharedCollision/SimpleExtent.h" +#include "sharedCollision/SpatialDatabase.h" +#include "sharedDebug/PerformanceTimer.h" +#include "sharedDebug/Profiler.h" +#include "sharedDebug/Report.h" +#include "sharedFoundation/ExitChain.h" +#include "sharedFoundation/FloatingPointUnit.h" +#include "sharedLog/Log.h" +#include "sharedMath/AxialBox.h" +#include "sharedMath/Circle.h" +#include "sharedMath/Cylinder.h" +#include "sharedMath/DebugShapeRenderer.h" +#include "sharedMath/Line3d.h" +#include "sharedMath/MultiShape.h" +#include "sharedMath/Ray3d.h" +#include "sharedMath/Rectangle2d.h" +#include "sharedMath/Segment3d.h" +#include "sharedMath/Sphere.h" +#include "sharedObject/Appearance.h" +#include "sharedObject/CellProperty.h" +#include "sharedTerrain/TerrainObject.h" +#include + +// ---------- + +namespace CollisionWorldNamespace +{ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + typedef CollisionWorld::WarpWarningCallback WarpWarningCallback; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + void defaultNearWarpWarning(Object const &object, Vector const &oldPosition_w, Vector const &newPosition_w, int segmentCount); + void defaultFarWarpWarning(Object const &object, Vector const &oldPosition_w, Vector const &newPosition_w, int segmentCount); + + bool testFloorCollision(FloorLocator const &startFloorLocator, Vector const &deltaPosition_w, Vector &collisionLocation_w, Object const *&collisionObject); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + int const cs_nearWarpCollisionSegmentCount = 20; + int const cs_farWarpCollisionSegmentCount = 250; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + SpatialDatabase * ms_database = NULL; + + bool ms_updating = false; + bool ms_serverSide = false; + + bool ms_forceReattachContactsPostResolve = false; + + WarpWarningCallback s_nearWarpWarningCallback; + WarpWarningCallback s_farWarpWarningCallback; + + char const * ms_sptatialDatabaseNames[] = { + "None", + "Static", + "Dynamic", + "Barriers", + "Doors" + }; + + //---------------------------------------------------------------------- + + bool testPassableTerrain(Vector const & startPos, Vector const & delta, float & collisionTime) + { + TerrainObject const *const terrainObject = TerrainObject::getConstInstance(); + if (NULL != terrainObject) + { + float totalDistance = delta.magnitude(); + float distanceTraversed = 0.0f; + Vector deltaNormal = delta; + deltaNormal.normalize(); + + for ( ; distanceTraversed < totalDistance; ) + { + float distance = std::min(distanceTraversed + 2.0f, totalDistance); + Vector const & endPos = startPos + (deltaNormal * distance); + + //-- is the target point in an impassable region? + if (!terrainObject->isPassableForceChunkCreation(endPos)) + { + collisionTime = distanceTraversed; + return false; + } + + distanceTraversed = distance; + } + } + return true; + } + + //---------------------------------------------------------------------- + + void handleTerrainImpassability(CollisionProperty & collider, bool const allowSlide) + { + PROFILER_AUTO_BLOCK_DEFINE("handleTerrainImpassability"); + + Object & object = collider.getOwner(); + + CellProperty const * const cellA = collider.getLastCell(); + CellProperty const * const cellB = object.getParentCell(); + + if (cellA != CellProperty::getWorldCellProperty() || cellB != CellProperty::getWorldCellProperty()) + return; + + TerrainObject const *const terrainObject = TerrainObject::getConstInstance(); + if (NULL != terrainObject && terrainObject->hasPassableAffectors()) + { + + // pointA_w is *not* the previous world position, but actually the world + // position for the previous position in the cell, in respect to the + // current position of the previous cell. This has the effect that a + // stationary object in a moving cell appears to not be moving as far + // as collision is concerned. + + Vector const & pointA_w = collider.getLastPos_p(); + Vector const & pointB_w = object.getPosition_w(); + + Vector const & locomotionTranslation_w = pointB_w - pointA_w; + Vector targetPosition_w = pointB_w; + //-- is the target point in an impassable region? + if (!terrainObject->isPassableForceChunkCreation(targetPosition_w)) + { + if (allowSlide) + { + //-- the passable boundaries are always axis-aligned, so try moving along one or the other world axes + //-- try moving along the X axis + Vector const locomotionTranslation_w_x(locomotionTranslation_w.x, locomotionTranslation_w.y, 0.0f); + targetPosition_w = pointA_w + locomotionTranslation_w_x; + if (!terrainObject->isPassableForceChunkCreation(targetPosition_w)) + { + //-- try moving along the Z axis + Vector const locomotionTranslation_w_z(0.0f, locomotionTranslation_w.y, locomotionTranslation_w.z); + targetPosition_w = pointA_w + locomotionTranslation_w_z; + if (!terrainObject->isPassableForceChunkCreation(targetPosition_w)) + { + targetPosition_w = pointA_w; + } + } + } + else + { + targetPosition_w = pointA_w; + } + + object.setPosition_w(targetPosition_w); + } + } + } + + //---------------------------------------------------------------------- + + CollisionWorld::CanTestCollisionDetectionOnObjectThisFrameFunction ms_canTestCollisionDetectionOnObjectThisFrameFunction = 0; + CollisionWorld::NoCollisionDetectionThisFrameFunction ms_noCollisionDetectionThisFrameFunction = 0; + CollisionWorld::CollisionDetectionOnHitFunction ms_collisionDetectionOnHitFunction = 0; + CollisionWorld::DoCollisionWithTerrainFunction ms_doCollisionWithTerrainFunction = 0; + +} + +using namespace CollisionWorldNamespace; + +// ---------- +// Debugging / profiling data + +float collisionDetectTime = 0.0f; +float floorUpdateTime = 0.0f; +float footprintUpdateTime = 0.0f; +float terrainHeightTime = 0.0f; +float getCellTime = 0.0f; +float resolveCollisionsTime = 0.0f; +float totalCollisionTime = 0.0f; +float postUpdateTime = 0.0f; +int canMoveCount = 0; +float canMoveTime = 0.0f; +float maxCanMoveTime = 0.0f; +float pathSearchTime = 0.0f; +float pathBuildTime = 0.0f; +float pathfindingTime = 0.0f; +int footprintUpdateCounter = 0; +int pathAllocations = 0; +float pathScrubTime = 0.0f; +int extentUpdateCount = 0; +int behaviorAlterCount = 0; +float behaviorTotalTime = 0.0f; +float behaviorMaxTime = 0.0f; +float ringTotalTime = 0.0f; +int aiControllerAlterCount = 0; +float aiControllerTotalTime = 0.0f; +float aiControllerMaxTime = 0.0f; + +std::string ms_reportString; + +// ---------- + +void terrainChangedCallback ( Rectangle2d const & rect ) +{ + AxialBox box( Vector(rect.x0,-2000.0f,rect.y0), Vector(rect.x1,2000.0f,rect.y1) ); + + CollisionWorld::environmentChanged( MultiShape(box) ); +} + +// ====================================================================== +// namespace CollisionWorldNamespace +// ====================================================================== + +void CollisionWorldNamespace::defaultNearWarpWarning(Object const &object, Vector const &oldPosition_w, Vector const &newPosition_w, int segmentCount) +{ + WARNING(ConfigSharedCollision::getReportWarnings(), + ("CollisionWorld::update() had %d segments for object id=[%s], template=[%s], start position=[%.2f,%.2f,%.2f], end position=[%.2f,%.2f,%.2f], object probably should have warped but collision system is not warping it.", + segmentCount, + object.getNetworkId().getValueString().c_str(), + object.getObjectTemplateName(), + oldPosition_w.x, + oldPosition_w.y, + oldPosition_w.z, + newPosition_w.x, + newPosition_w.y, + newPosition_w.z + )); +} + +// ---------------------------------------------------------------------- + +void CollisionWorldNamespace::defaultFarWarpWarning(Object const &object, Vector const &oldPosition_w, Vector const &newPosition_w, int segmentCount) +{ + WARNING(ConfigSharedCollision::getReportWarnings(), + ("CollisionWorld::update() had %d segments for object id=[%s], template=[%s], start position=[%.2f,%.2f,%.2f], end position=[%.2f,%.2f,%.2f], collision system will consider this a warp and adjust accordingly.", + segmentCount, + object.getNetworkId().getValueString().c_str(), + object.getObjectTemplateName(), + oldPosition_w.x, + oldPosition_w.y, + oldPosition_w.z, + newPosition_w.x, + newPosition_w.y, + newPosition_w.z + )); +} + +// ---------------------------------------------------------------------- +/** + * Test if a footprint is capable of walking across the floor in a given direction. + * + * @param startFloorLocator the floor locator for the footprint we want to test. + * @param deltaPosition_w the world space delta position vector indicating the direction in which + * the footprint will be tested. + * @param collisionLocation_w if a collision is detected, this return value will be filled with the + * world space location where the collision occurred. + * @param collisionObject if a collision is detected, the object associated with the floor will + * be returned. + * + * @return true if the footprint succeeded in walking across the floor when moving in the specified direction; false otherwise. + */ + +bool CollisionWorldNamespace::testFloorCollision(FloorLocator const &startFloorLocator, Vector const &deltaPosition_w, Vector &collisionLocation_w, Object const *&collisionObject) +{ + // Get the floor from the floor locator. + Floor const *const floor = startFloorLocator.getFloor(); + if (!floor) + { + // For some reason this solid floor does not have an attached floor. No collision. + WARNING(true, ("testFloorCollision(): attempted to test floor with a starting floor locator that had a NULL floor, unexpected. Check calling code's assumptions.")); + return true; + } + + // Test if we can walk across the floor. + FloorLocator destinationLocator; + + // @todo verify that we want deltaPosition in world space here. Look at other floor collision resolution + // code. The parameter name suggests this but the usage suggests it should be in some kind of "floor space". + PathWalkResult const pathWalkResult = floor->canMove(startFloorLocator, deltaPosition_w, -1, -1, destinationLocator); + + if ((pathWalkResult == PWR_DoesntEnter) + || (pathWalkResult == PWR_WalkOk) + || (pathWalkResult == PWR_ExitedMesh)) + { + // In these cases, indicate there is no collision with the floor. + return false; + } + + // A collision occurred with the floor. + collisionLocation_w = destinationLocator.getPosition_w(); + + Floor const *const destinationFloor = destinationLocator.getFloor(); + if (!destinationFloor) + { + // Hmm, we think we collided with a floor but the destination collision floor location does not have an associated floor. + // This most likely means that the pathWalkResult that was issued indicates a non-collision and should be caught in the + // if statement above. + WARNING(true, + ("testFloorCollision(): floor collision occurred while checking for floor collisions for floor of object id [%s] but collision floor location has no associated floor. Calling this a non-collision. PathWalkResult was [%d].", + floor->getOwner() ? floor->getOwner()->getNetworkId().getValueString().c_str() : "", + static_cast(pathWalkResult) + )); + return false; + } + + collisionObject = destinationFloor->getOwner(); + if (!collisionObject) + { + // We had a collision on a floor, but the floor had a NULL owner. Consider this a non-collision. + WARNING(true, ("testFloorCollision(): floor collision occurred, destination collision floor location had a floor but floor reported a NULL owner. Calling this a non-collision.")); + return false; + } + + return true; +} + + +// ---------------------------------------------------------------------- + +void CollisionWorld::registerCanTestCollisionDetectionOnObjectThisFrame(CanTestCollisionDetectionOnObjectThisFrameFunction function) +{ + ms_canTestCollisionDetectionOnObjectThisFrameFunction = function; +} + +// ---------------------------------------------------------------------- + +bool CollisionWorld::canTestCollisionDetectionOnObjectThisFrame(Object * const object) +{ + if (ms_canTestCollisionDetectionOnObjectThisFrameFunction != 0) + { + return (*ms_canTestCollisionDetectionOnObjectThisFrameFunction)(object); + } + return true; +} + +// ---------------------------------------------------------------------- + +void CollisionWorld::registerNoCollisionDetectionThisFrame(NoCollisionDetectionThisFrameFunction function) +{ + ms_noCollisionDetectionThisFrameFunction = function; +} + +// ---------------------------------------------------------------------- + +void CollisionWorld::noCollisionDetectionThisFrame(Object * const object) +{ + if (ms_noCollisionDetectionThisFrameFunction != 0) + { + (*ms_noCollisionDetectionThisFrameFunction)(object); + } +} + +// ---------------------------------------------------------------------- + +void CollisionWorld::registerDoCollisionDetectionOnHit(CollisionDetectionOnHitFunction function) +{ + ms_collisionDetectionOnHitFunction = function; +} + +// ---------------------------------------------------------------------- + +bool CollisionWorld::doCollisionDetectionOnHit(Object * const object, Object * const wasHitByThisObject) +{ + if (ms_collisionDetectionOnHitFunction != 0) + { + return(*ms_collisionDetectionOnHitFunction)(object, wasHitByThisObject); + } + + return false; +} + +// ---------------------------------------------------------------------- + +void CollisionWorld::registerDoCollisionWithTerrain(DoCollisionWithTerrainFunction function) +{ + ms_doCollisionWithTerrainFunction = function; +} + +// ---------------------------------------------------------------------- + +bool CollisionWorld::doCollisionWithTerrain(Object * const object) +{ + if (ms_doCollisionWithTerrainFunction != 0) + { + return(*ms_doCollisionWithTerrainFunction)(object); + } + + return false; +} + +// ====================================================================== +// class CollisionWorld: PUBLIC STATIC +// ====================================================================== + +void CollisionWorld::install ( bool serverSide ) +{ + DEBUG_REPORT_LOG(true,("CollisionWorld::install()\n")); + + s_nearWarpWarningCallback = defaultNearWarpWarning; + s_farWarpWarningCallback = defaultFarWarpWarning; + + ConfigSharedCollision::install(); + FloorMesh::install(); + CollisionResolve::install(); + PerformanceTimer::install(); + + ms_serverSide = serverSide; + + if(!serverSide) + { + TerrainObject::addTerrainChangedFunction( terrainChangedCallback ); + } + + ms_database = new SpatialDatabase(); + + ExitChain::add(CollisionWorld::remove,"CollisionWorld::remove()"); +} + +// ---------- + +void CollisionWorld::remove ( void ) +{ + DEBUG_REPORT_LOG(true,("CollisionWorld::remove()\n")); + + CollisionResolve::remove(); + FloorMesh::remove(); + + s_nearWarpWarningCallback = NULL; + s_farWarpWarningCallback = NULL; + + delete ms_database; + ms_database = NULL; +} + +// ---------------------------------------------------------------------- + +void CollisionWorld::update ( float time ) +{ + CollisionNotification::purgeQueue(); + + // ---------- + + ms_updating = true; + + floorUpdateTime = 0.0f; + footprintUpdateTime = 0.0f; + collisionDetectTime = 0.0f; + resolveCollisionsTime = 0.0f; + postUpdateTime = 0.0f; + terrainHeightTime = 0.0f; + + PerformanceTimer timer; + timer.start(); + +#ifdef _MSC_VER + + FloatingPointUnit::Precision oldPrecision = FloatingPointUnit::getPrecision(); + FloatingPointUnit::setPrecision(FloatingPointUnit::P_64); + +#endif + + // ---------- + + CollisionResolve::resetCounters(); + + typedef std::vector CollisionPropertyVector; + static CollisionPropertyVector active; + + active.clear(); + + CollisionProperty * cursor = CollisionProperty::getActiveHead(); + + while(cursor) + { + active.push_back(cursor); + + cursor = cursor->getNext(); + } + + CollisionPropertyVector::size_type const count = active.size(); + + if(count > 3000) + { + WARNING(true,("CollisionWorld::update - Updating more than 3000 objects this frame - something's probably wrong\n")); + } + + for(CollisionPropertyVector::size_type i = 0; i < count; i++) + { + update(active[i], time); + } + + footprintUpdateCounter = static_cast(count); + + // ---------- + + +#ifdef _MSC_VER + + FloatingPointUnit::setPrecision(oldPrecision); + +#endif + + timer.stop(); + totalCollisionTime = timer.getElapsedTime(); + + // ---------- + // Post-update, where test code goes + + updateReportString(); + + if(totalCollisionTime >= 0.1f && ConfigSharedCollision::getLogLongFrames()) + { + // Collision took more than 100 milliseconds - log the report + + LOG("Collision",(ms_reportString.c_str())); + } + + ms_updating = false; +} + +// ---------------------------------------------------------------------- + +bool CollisionWorld::spatialSweepAndResolve(CollisionProperty * collider) +{ + Object * object = &collider->getOwner(); + NOT_NULL(object); + + bool hadACollisionThisFrame = false; + + if (canTestCollisionDetectionOnObjectThisFrame(object)) + { + CellProperty const * const cell_w = CellProperty::getWorldCellProperty(); + Capsule const capsule_w(collider->getQueryCapsule_w()); + ColliderList collidedWith; + + int const queryMask = ConfigSharedCollision::getSpatialSweepAndResolveDefaultMask(); + + ms_database->queryFor(queryMask, cell_w, true, capsule_w, collidedWith); + + int const potentialCount = static_cast(collidedWith.size()); + + for(int i = 0; i < potentialCount; ++i) + { + CollisionProperty * const toTestCollision = collidedWith.at(static_cast(i)); + + if ((toTestCollision) && (toTestCollision != collider)) + { + CollisionProperty * const wasHitByThisCollision = collidedWith.at(i); + Object * const wasHitByThisObject = &wasHitByThisCollision->getOwner(); + + if (doCollisionDetectionOnHit(object, wasHitByThisObject)) + { + hadACollisionThisFrame = true; + } + } + } + + if (doCollisionWithTerrain(object)) + { + hadACollisionThisFrame = true; + } + + if (!hadACollisionThisFrame) + { + noCollisionDetectionThisFrame(object); + } + } + + return(hadACollisionThisFrame); +} + +// ---------------------------------------------------------------------- + +void CollisionWorld::update(CollisionProperty * collider, float time) +{ + FATAL(!collider, ("CollisionWorld::update(): collider is NULL.")); + + PROFILER_AUTO_BLOCK_DEFINE("CollisionWorld::update"); + + Object * object = &collider->getOwner(); + + Footprint * foot = collider->getFootprint(); + + if(foot == NULL) + { + PROFILER_AUTO_BLOCK_DEFINE("CollisionWorld::update: foot == NULL"); + + IGNORE_RETURN (CollisionWorld::spatialSweepAndResolve(collider)); + collider->storePosition(); + collider->updateIdle(); + + return; + } + + // ---------- + + CellProperty * cellA = collider->getLastCell(); + CellProperty * cellB = object->getParentCell(); + + // pointA_w is *not* the previous world position, but actually the world + // position for the previous position in the cell, in respect to the + // current position of the previous cell. This has the effect that a + // stationary object in a moving cell appears to not be moving as far + // as collision is concerned. + + Vector pointA_w = CollisionUtils::transformToWorld(cellA, collider->getLastPos_p()); + Vector pointB_w = object->getPosition_w(); + + Vector delta = pointB_w - pointA_w; + + delta.y = 0.0f; + + float moveLength = delta.magnitude(); + + // 2 meters per move segment + + int segments = static_cast(ceil(moveLength / 2.0f)); + + if(segments <= 1) + { + PROFILER_AUTO_BLOCK_DEFINE("CollisionWorld::update: segments <= 1"); + + updateSegment(collider,time); + } + else + { + PROFILER_AUTO_BLOCK_DEFINE("CollisionWorld::update: segments > 1"); + + // Break the move into pieces and resolve each separately + + if(segments > cs_nearWarpCollisionSegmentCount) + { + PROFILER_AUTO_BLOCK_DEFINE("CollisionWorld::update: segments > cs_nearWarpCollisionSegmentCount"); + + // we can't actually warp the player, because that creats an exploit where they can cause themselves to get warped into bad places + if (s_nearWarpWarningCallback) + (*s_nearWarpWarningCallback)(collider->getOwner(), pointA_w, pointB_w, segments); + } + + if(segments > cs_farWarpCollisionSegmentCount) + { + PROFILER_AUTO_BLOCK_DEFINE("CollisionWorld::update: segments > cs_farWarpCollisionSegmentCount"); + + if (s_farWarpWarningCallback) + (*s_farWarpWarningCallback)(collider->getOwner(), pointA_w, pointB_w, segments); + + CollisionWorld::objectWarped(&collider->getOwner()); + + segments = 1; + } + + // Move the player back to where they were at the start of the last frame, + // in the context of parent transforms this frame, so we can start resolving collisions + + // This version just moves them, & hopefully they'll end up in the same cell as they were last frame + + Vector pointA_B = CollisionUtils::transformFromWorld(pointA_w,cellB); + + NAN_CHECK(pointA_B); + + object->setPosition_p(pointA_B); + + if(object->getParentCell() != cellA) + { + REPORT_LOG(ConfigSharedCollision::getReportMessages(),("CollisionWorld::update - Tried to move the object back to where it was last frame, but it ended up in a different cell\n")); + } + + // ---------- + + float segmentTime = time / static_cast(segments); + Vector segmentDelta_w = (pointB_w-pointA_w) / static_cast(segments); + + for(int i = 0; i < segments; i++) + { + Vector segmentDelta_p = CollisionUtils::rotateToCell(CellProperty::getWorldCellProperty(),segmentDelta_w,object->getParentCell()); + + Vector goal = object->getPosition_p() + segmentDelta_p; + + NAN_CHECK(goal); + + object->setPosition_p(goal); + + updateSegment(collider,segmentTime); + } + } + + collider->updateIdle(); + + CellProperty const * finalCell = object->getParentCell(); + + if(finalCell && (finalCell != CellProperty::getWorldCellProperty())) + { + if(!foot->isAttached()) + { + PROFILER_AUTO_BLOCK_DEFINE("CollisionWorld::update: !foot->isAttached()"); + + IGNORE_RETURN(foot->snapToCellFloor()); + } + } +} + +// ---------------------------------------------------------------------- + +void CollisionWorld::updateSegment ( CollisionProperty * collider, float time ) +{ + PROFILER_AUTO_BLOCK_DEFINE("CollisionWorld::updateSegment"); + + Object * object = &collider->getOwner(); + + Footprint * foot = collider->getFootprint(); + + // This is done in the parent space of the destination point, because the cell we were in + // may have moved, and we need to do collision relative to the potentially moving cells. + Vector pointA_p = CollisionUtils::transformToCell(collider->getLastCell(), collider->getLastPos_p(), object->getParentCell()); + Vector pointB_p = object->getPosition_p(); + + Vector delta = pointB_p - pointA_p; + + if((delta.x != 0.0f) || (delta.y != 0.0f)) + { + Vector footPos_p = foot->getPosition_p(); + Vector groundNormal_p = foot->getGroundNormal_p(); + + if(groundNormal_p.y != 0.0f) + { + float dx = pointB_p.x - footPos_p.x; + float dz = pointB_p.z - footPos_p.z; + + float dydx = -(groundNormal_p.x/groundNormal_p.y); + float dydz = -(groundNormal_p.z/groundNormal_p.y); + + float groundOffset = (dx * dydx) + (dz * dydz); + + pointB_p.y += groundOffset; + + NAN_CHECK(pointB_p); + + object->setPosition_p(pointB_p); + } + } + + // ---------- + // do the actual collision resolution step + + IGNORE_RETURN(updateStep(collider,time)); + + // and now repeatedly snap the object and re-run collision resolution until + // there's no collision or we give up + + int j; + + for(j = 0; j < 10; j++) + { + IGNORE_RETURN(foot->snapObjectToGround()); + + ResolutionResult result = updateStep(collider,0.0f); + + if(result == RR_NoCollision) + { + break; + } + } +} + +// ---------------------------------------------------------------------- +// push the object away from anything it's colliding with + +void CollisionWorld::shoveAway(CollisionProperty * const collisionProperty, ColliderList const & colliderList, float time) +{ + Object * obj = &collisionProperty->getOwner(); + + Vector accum; + + Vector objectPos_p = obj->getPosition_p(); + + ColliderList::const_iterator ii = colliderList.begin(); + ColliderList::const_iterator iiEnd = colliderList.end(); + + for (; ii != iiEnd; ++ii) + { + CollisionProperty * const collisionPropertyToTest = *ii; + Vector obstaclePos_p = collisionPropertyToTest->getOwner().getPosition_p(); + + Vector delta = objectPos_p - obstaclePos_p; + + delta.y = 0.0f; + + IGNORE_RETURN(delta.normalize()); + + accum += delta; + } + + IGNORE_RETURN(accum.normalize()); + + accum *= 8.0f * time; + + Vector newPos = objectPos_p + accum; + + NAN_CHECK(newPos); + + obj->setPosition_p(newPos); + +} + +// ---------------------------------------------------------------------- +// returns true if any collisions were resolved + +ResolutionResult CollisionWorld::updateStep ( CollisionProperty * collider, float time ) +{ + PROFILER_AUTO_BLOCK_DEFINE("CollisionWorld::updateStep"); + + NOT_NULL(collider); + + Footprint * foot = collider->getFootprint(); + + NOT_NULL(foot); + + if(foot->isFloating()) + { + Vector newPos = foot->m_addToWorldPos; + + NAN_CHECK(newPos); + + collider->getOwner().setPosition_p(newPos); + + foot->updateFloating(); + + return RR_NoCollision; + } + + + //---------------------------------------------------------------------- + + handleTerrainImpassability(*collider, true); + + // ---------- + + ResolutionResult result; + + PerformanceTimer timer; + + static ColliderList colliderList; + + { + PROFILER_AUTO_BLOCK_DEFINE("update floor database"); + timer.start(); + + ms_database->updateFloorCollision(collider,false); + + timer.stop(); + floorUpdateTime += timer.getElapsedTime(); + } + + { + PROFILER_AUTO_BLOCK_DEFINE("update collisions"); + timer.start(); + + ms_database->updateStaticCollision(collider, colliderList); + + timer.stop(); + collisionDetectTime += timer.getElapsedTime(); + } + + { + PROFILER_AUTO_BLOCK_DEFINE("update footprints"); + timer.start(); + + foot->updatePreResolve(time); + + timer.stop(); + footprintUpdateTime += timer.getElapsedTime(); + } + + { + PROFILER_AUTO_BLOCK_DEFINE("resolve collisions"); + timer.start(); + + result = CollisionResolve::resolveCollisions(collider, colliderList); + + timer.stop(); + resolveCollisionsTime += timer.getElapsedTime(); + } + + { + PROFILER_AUTO_BLOCK_DEFINE("post update"); + timer.start(); + + foot->updatePostResolve(time); + + if (ms_forceReattachContactsPostResolve) + foot->forceReattachContacts(); + + timer.stop(); + postUpdateTime += timer.getElapsedTime(); + } + + handleTerrainImpassability(*collider, false); + + collider->storePosition(); + colliderList.clear(); + + return result; +} + +// ---------------------------------------------------------------------- + +bool CollisionWorld::isUpdating ( void ) +{ + return ms_updating; +} + +// ---------------------------------------------------------------------- + +void CollisionWorld::updateReportString ( void ) +{ + ms_reportString.clear(); + + char buffer[256]; + + sprintf(buffer,"CollsionWorld::update -\n"); + ms_reportString += buffer; + + const int staticObjectCount = ms_database->getObjectCount(SpatialDatabase::Q_Static); + const int dynamicObjectCount = ms_database->getObjectCount(SpatialDatabase::Q_Dynamic); + const int floorCount = ms_database->getFloorCount(); + + sprintf(buffer,"Database : %d S / %d D / %d F\n",staticObjectCount,dynamicObjectCount,floorCount); + + ms_reportString += buffer; + + int colliderCount = CollisionResolve::getColliderCount(); + int obstacleCount = CollisionResolve::getObstacleCount(); + int collisionCount = CollisionResolve::getCollisionCount(); + int bounceCount = CollisionResolve::getBounceCount(); + + sprintf(buffer,"Resolution : %d colliders, %d obstacles, %d collisions, %d bounces\n",colliderCount,obstacleCount,collisionCount,bounceCount); + ms_reportString += buffer; + + ms_reportString += "C E F P L T O\n"; + + ms_reportString += buffer; + + float behaviorAverage = behaviorTotalTime / static_cast(behaviorAlterCount); + + sprintf(buffer,"Behavior : %d, %1.3f total, %1.3f avg, %1.3f max\n",behaviorAlterCount,behaviorTotalTime * 1000.0f,behaviorAverage * 1000.0f,behaviorMaxTime * 1000.0f); + ms_reportString += buffer; + + float aiControllerAverage = aiControllerTotalTime / static_cast(aiControllerAlterCount); + + sprintf(buffer,"AiController : %d, %1.3f total, %1.3f avg, %1.3f max\n",aiControllerAlterCount, aiControllerTotalTime * 1000.0f, aiControllerAverage * 1000.0f, aiControllerMaxTime * 1000.0f); + ms_reportString += buffer; + + float canMoveAverage = canMoveTime / static_cast(canMoveCount); + + sprintf(buffer,"Can move : %d, %1.3f total, %1.3f avg, %1.3f max\n",canMoveCount, canMoveTime * 1000.0f, canMoveAverage * 1000.0f, maxCanMoveTime * 1000.0f); + ms_reportString += buffer; + + sprintf(buffer,"Extent updates : %d\n",extentUpdateCount); + ms_reportString += buffer; + + sprintf(buffer,"Path scrub time : %1.3f msec\n",pathScrubTime * 1000.0f); + ms_reportString += buffer; + + sprintf(buffer,"Pathfinding time : %1.3f msec\n",pathfindingTime * 1000.0f); + ms_reportString += buffer; + + sprintf(buffer,"Path build time : %1.3f msec\n",pathBuildTime * 1000.0f); + ms_reportString += buffer; + + sprintf(buffer,"Path search time : %1.3f msec\n",pathSearchTime * 1000.0f); + ms_reportString += buffer; + + sprintf(buffer,"Path search alloc : %d allocs\n",pathAllocations); + ms_reportString += buffer; + + sprintf(buffer,"Footprint updates : %d updates\n",footprintUpdateCounter); + ms_reportString += buffer; + + sprintf(buffer,"All collision : %1.3f msec\n",totalCollisionTime * 1000.0f); + ms_reportString += buffer; + + sprintf(buffer,"Floor update : %1.3f msec\n",floorUpdateTime * 1000.0f); + ms_reportString += buffer; + + sprintf(buffer,"Footprint update : %1.3f msec\n",footprintUpdateTime * 1000.0f); + ms_reportString += buffer; + + sprintf(buffer,"Collision detection : %1.3f msec\n",collisionDetectTime * 1000.0f); + ms_reportString += buffer; + + sprintf(buffer,"Collision resolution : %1.3f msec\n",resolveCollisionsTime * 1000.0f); + ms_reportString += buffer; + + sprintf(buffer,"Terrain height : %1.3f msec\n",terrainHeightTime * 1000.0f); + ms_reportString += buffer; + + sprintf(buffer,"Get cell : %1.3f msec\n",getCellTime * 1000.0f); + ms_reportString += buffer; + + sprintf(buffer,"Post-update : %1.3f msec\n",postUpdateTime * 1000.0f); + ms_reportString += buffer; + + extentUpdateCount = 0; + + behaviorAlterCount = 0; + behaviorTotalTime = 0.0f; + behaviorMaxTime = 0.0f; + + ringTotalTime = 0.0f; + + canMoveCount = 0; + canMoveTime = 0.0f; + maxCanMoveTime = 0.0f; + + aiControllerAlterCount = 0; + aiControllerTotalTime = 0.0f; + aiControllerMaxTime = 0.0f; +} + +// ---------------------------------------------------------------------- + +void CollisionWorld::reportCallback( void ) +{ +#if _DEBUG + + if(!ConfigSharedCollision::getReportStatus()) return; + + DEBUG_REPORT_PRINT(true,(ms_reportString.c_str())); + +#endif +} + +// ---------------------------------------------------------------------- + +void CollisionWorld::setNearWarpWarningCallback(WarpWarningCallback callback) +{ + if (callback) + s_nearWarpWarningCallback = callback; + else + s_nearWarpWarningCallback = defaultNearWarpWarning; +} + +// ---------------------------------------------------------------------- + +void CollisionWorld::setFarWarpWarningCallback(WarpWarningCallback callback) +{ + if (callback) + s_farWarpWarningCallback = callback; + else + s_farWarpWarningCallback = defaultFarWarpWarning; +} + +// ---------------------------------------------------------------------- + +void CollisionWorld::addObject ( Object * object ) +{ + if(object == NULL) return; + + CollisionProperty * collision = object->getCollisionProperty(); + + if((collision == NULL) || collision->getDisableCollisionWorldAddRemove()) return; + + if(collision->isInCollisionWorld()) return; + + // ---------- + + collision->addToCollisionWorld(); + + SpatialDatabase::Query query = collision->getSpatialDatabaseStorageType(); + bool added = false; + + if(collision->isMobile()) + { + added = ms_database->addObject(query, object); + } + else + { + Floor * pFloor = collision->getFloor(); + + if(pFloor && !pFloor->isCellFloor()) + { + added = ms_database->addFloor(pFloor); + } + + if(collision->getExtent_l()) + { + if (dynamic_cast(object) != 0) + { + query = SpatialDatabase::Q_Doors; + } + else if (dynamic_cast(object) != 0) + { + query = SpatialDatabase::Q_Barriers; + } + + added = ms_database->addObject(query, object) || added; + } + + environmentChanged(MultiShape(collision->getBoundingSphere_w())); + } + + if(added) + { + char const * name = object->getObjectTemplateName(); + + if(name == NULL) + { + Appearance const * appearance = object->getAppearance(); + + if(appearance) + { + name = appearance->getAppearanceTemplateName(); + } + } + + int index = 0; + switch (query) + { + case SpatialDatabase::Q_Static: + index = 1; + break; + case SpatialDatabase::Q_Dynamic: + index = 2; + break; + case SpatialDatabase::Q_Barriers: + index = 3; + break; + case SpatialDatabase::Q_Doors: + index = 4; + break; + default: + break; + } + + DEBUG_REPORT_LOG(ConfigSharedCollision::getReportChanges(),("Added object %s to collision world [%s] at (%f,%f,%f)\n",name, ms_sptatialDatabaseNames[index], object->getPosition_w().x,object->getPosition_w().y,object->getPosition_w().z)); + } +} + +// ---------- + +void CollisionWorld::removeObject ( Object * object ) +{ + if(object == NULL) return; + + CollisionProperty * collision = object->getCollisionProperty(); + + if((collision == NULL) || collision->getDisableCollisionWorldAddRemove()) return; + + if(!collision->isInCollisionWorld()) return; + + // ---------- + + bool removed = false; + + if(collision->isMobile()) + { + removed = ms_database->removeObject(SpatialDatabase::Q_Dynamic, object); + } + else + { + Floor * pFloor = collision->getFloor(); + + if(pFloor && !pFloor->isCellFloor()) + { + removed = ms_database->removeFloor(pFloor); + } + + if(collision->getExtent_l()) + { + SpatialDatabase::Query query = SpatialDatabase::Q_Static; + + if (dynamic_cast(object) != 0) + { + query = SpatialDatabase::Q_Doors; + } + else if (dynamic_cast(object) != 0) + { + query = SpatialDatabase::Q_Barriers; + } + + removed = ms_database->removeObject(SpatialDatabase::Q_Static, object) || removed; + } + + environmentChanged(MultiShape(collision->getBoundingSphere_w())); + } + + if(removed) + { + DEBUG_REPORT_LOG(ConfigSharedCollision::getReportChanges(),("Removed object %s from collision world\n",object->getObjectTemplateName())); + } + + collision->removeFromCollisionWorld(); +} + +// ---------- + +void CollisionWorld::moveObject (Object * object) +{ + if(object == NULL) return; + + CollisionProperty * collision = object->getCollisionProperty(); + + if(collision == NULL) return; + + if(!collision->isInCollisionWorld()) return; + + // ---------- + + if(object->getTransform_o2w() == collision->getLastTransform_w()) + { + return; + } + + collision->setIdle(false); + collision->setExtentsDirty(true); + + IGNORE_RETURN(ms_database->moveObject(collision)); + + if(!collision->isMobile()) + { + environmentChanged(MultiShape(collision->getBoundingSphere_w())); + } +} + +// ---------- + +void CollisionWorld::cellChanged ( Object * object ) +{ + if(object == NULL) return; + + CollisionProperty * collision = object->getCollisionProperty(); + + if(collision == NULL) return; + + if(!collision->isInCollisionWorld()) return; + + // ---------- + + collision->setIdle(false); + collision->cellChanged(); +} + +// ---------- + +void CollisionWorld::appearanceChanged ( Object * object ) +{ + if(object == NULL) return; + + CollisionProperty * collision = object->getCollisionProperty(); + + if(collision == NULL) return; + + if(!collision->isInCollisionWorld()) return; + + // ---------- + + if(!collision->isMobile()) + { + IGNORE_RETURN(ms_database->moveObject(collision)); + + collision->initFloor(); + } +} + +// ---------------------------------------------------------------------- +// Something about the environment (terrain, static objects) has changed. +// Wake up all collision properties in the changed area. + +void CollisionWorld::environmentChanged ( MultiShape const & shape ) +{ + static ObjectVec mobs; + + mobs.clear(); + + IGNORE_RETURN(ms_database->queryDynamics(shape,&mobs)); + + ObjectVec::size_type const count = mobs.size(); + + if(count > 500) + { + Vector c = shape.getCenter(); + float x = shape.getExtentX() * 2.0f; + float y = shape.getExtentY() * 2.0f; + float z = shape.getExtentZ() * 2.0f; + + WARNING(true,("CollisionWorld::environmentChanged - Changing environment wakes up over 500 AIs. This is probably bad. (%f,%f,%f), (%f,%f,%f)\n",c.x,c.y,c.z,x,y,z)); + } + + for(ObjectVec::size_type i = 0; i < count; i++) + { + Object * mob = mobs[i]; + + NOT_NULL(mob); + + CollisionProperty * mobCollision = mob->getCollisionProperty(); + + NOT_NULL(mobCollision); + + mobCollision->setIdle(false); + } +} + +// ---------------------------------------------------------------------- + +SpatialDatabase * CollisionWorld::getDatabase ( void ) +{ + return ms_database; +} + +// ---------------------------------------------------------------------- + +void CollisionWorld::objectWarped ( Object * object ) +{ + if(object == NULL) return; + + CollisionProperty * collision = object->getCollisionProperty(); + + // object does not have a CollisionProperty + if(collision == NULL) return; + + // object is not in CollisionWorld + if (!collision->isInCollisionWorld()) return; + + collision->objectWarped(); +} + +// ---------------------------------------------------------------------- +// useTerrainLos indicates whether to check for terrain LOS +// +// generateTerrainLos indicates whether to generate missing terrain for +// terrain LOS (only valid if useTerrainLos is true) +// +// terrainLosMinDistance and terrainLosMaxDistance specifies the required +// range of the 2 objects before terrain LOS is done (only valid +// if useTerrainLos is true) + +QueryInteractionResult CollisionWorld::queryInteraction ( CellProperty const * cellA, Vector const & pointA, + CellProperty const * cellB, Vector const & pointB, + Object const * ignoreObject, + bool const useTerrainLos, + bool const generateTerrainLos, + float const terrainLosMinDistance, + float const terrainLosMaxDistance, + float & outHitTime, Object const * & outHitObject ) +{ + PROFILER_AUTO_BLOCK_DEFINE("CollisionWorld::queryInteraction"); + Vector localB = CollisionUtils::transformToCell(cellB,pointB,cellA); + + Segment3d inSeg(pointA,localB); + + // ---------- + // Test portals first. If the query line hits a portal, we'll ignore any collision after that hit. + + float hitPortalTime = REAL_MAX; + + CellProperty const * nextCell = cellA->getDestinationCell(pointA,localB,hitPortalTime); + + // ---------- + // Test all extents first + + BaseExtent const * cellExtent = cellA->getCollisionExtent(); + + if(cellExtent) + { + Range hitRange = cellExtent->rangedIntersect(inSeg); + + if(!hitRange.isEmpty() && (hitRange.getMin() < hitPortalTime)) + { + outHitTime = hitRange.getMin(); + outHitObject = &cellA->getOwner(); + + return QIR_HitCellExtent; + } + } + + // ---------- + // Test the extents of all objects in the cell + + Object const * hitObject; + float hitTime = 0.0f; + + if(ms_database->queryInteraction(cellA,inSeg,ignoreObject,hitObject,hitTime)) + { + if(hitTime < hitPortalTime) + { + outHitTime = hitTime; + outHitObject = hitObject; + + return QIR_HitObjectExtent; + } + } + + // ---------- + // If we're in the world cell, test terrain. + + if (useTerrainLos) + { + if ((inSeg.getLength() >= terrainLosMinDistance) && (inSeg.getLength() <= terrainLosMaxDistance)) + { + if(cellA == CellProperty::getWorldCellProperty()) + { + PROFILER_AUTO_BLOCK_DEFINE("CollisionWorld::queryInteraction::Terrain"); + + TerrainObject * terrain = TerrainObject::getInstance(); + + CollisionInfo info; + + bool terrainCollide = false; + + if (terrain) + { + if (generateTerrainLos) + { + // do terrain LOS check, generating any missing terrain between the 2 points + terrainCollide = terrain->collideForceChunkCreation(pointA,localB,info); + } + else + { + // do terrain LOS check, using only the generated terrain between the 2 points + terrainCollide = terrain->collide(pointA,localB,info); + } + } + + if (terrainCollide) + { + // info doesn't have the correct hit time, so compute it using the hit point + + Vector hitPoint = info.getPoint(); + + Vector hitDelta = hitPoint - pointA; + + Vector delta = localB - pointA; + + float hitTime = Collision3d::ComponentAlong(hitDelta,delta); + + if(hitTime < hitPortalTime) + { + outHitTime = hitTime; + outHitObject = NULL; + + return QIR_HitTerrain; + } + } + } + } + } + + + // ---------- + // If we did not hit a portal, the query ends in this cell. + // If this cell is not the goal cell, then the query fails. + + if(nextCell == NULL) + { + if(cellA != cellB) + { + outHitTime = 1.0f; + outHitObject = NULL; + + return QIR_MissedTarget; + } + else + { + return QIR_None; + } + } + + // ---------- + // If we hit a portal, recurse through it + + else + { + Vector clipPoint = pointA + (localB - pointA) * hitPortalTime; + + // Don't bother recursing if the segment we'd test in the next cell is tiny + + if(clipPoint.magnitudeBetween(localB) <= 0.01f) + { + return QIR_None; + } + + Vector nextPoint = CollisionUtils::transformToCell(cellA,clipPoint,nextCell); + + // ---------- + // infinite recursion sentinel + + static int recursed = 0; + + if(recursed >= 10) + { + return QIR_None; + } + + // ---------- + + recursed++; + + QueryInteractionResult recurseResult = queryInteraction(nextCell, + nextPoint, + cellB, + pointB, + ignoreObject, + useTerrainLos, + generateTerrainLos, + terrainLosMinDistance, + terrainLosMaxDistance, + outHitTime, + outHitObject); + + recursed--; + + float lastLength = pointA.magnitudeBetween(localB); + float nextLength = clipPoint.magnitudeBetween(localB); + + float timeScale = nextLength / lastLength; + + outHitTime = hitPortalTime + outHitTime * timeScale; + + return recurseResult; + } + +} + +// ---------------------------------------------------------------------- + +bool CollisionWorld::query( Line3d const & line, ObjectVec * outList ) +{ + return ms_database->queryStatics(line,outList); +} + +// ---------- + +bool CollisionWorld::query( Sphere const & sphere, ObjectVec * outList ) +{ + return ms_database->queryStatics(sphere,outList); +} + +// ---------------------------------------------------------------------- +// Given startLoc and goalLoc in the same cell, determine if startLoc +// can reach goalLoc + +CanMoveResult CollisionWorld::canMoveInCell ( Object const * object, + CellProperty const * startCell, + FloorLocator const & startLoc, + Vector const & goalPos, + bool checkY, + bool checkFlora, + bool checkFauna, + FloorLocator & outLoc, + int & outHitPortalId ) +{ + FloorLocator goalLoc; + + if(checkY) + { + makeLocator(startCell,goalPos,goalLoc); + } + else + { + goalLoc.setFloor( startCell->getFloor() ); + goalLoc.setPosition_p( goalPos ); + } + + return canMoveInCell(object, startCell,startLoc,goalLoc,checkY,checkFlora,checkFauna,outLoc,outHitPortalId ); +} + +CanMoveResult CollisionWorld::canMoveInCell ( Object const * object, + CellProperty const * startCell, + FloorLocator const & startLoc, + FloorLocator const & goalLoc, + bool checkY, + bool checkFlora, + bool checkFauna, + FloorLocator & outLoc, + int & outHitPortalId ) +{ + // ---------- + // Test collision extents + + Vector startPos = startLoc.getPosition_p(); + Vector goalPos = goalLoc.getPosition_p(); + + Segment3d querySeg(startPos,goalPos); + + if(startCell == CellProperty::getWorldCellProperty()) + { + float radius = startLoc.getRadius(); + + Sphere sphere(startPos + Vector(0.0f,radius,0.0f),radius); + Vector delta = goalPos - startPos; + float collisionTime = 0.0f; + + if (!testPassableTerrain(sphere.getCenter(), delta, collisionTime)) + { + //-- hit impassable terrain + return CMR_HitObstacle; + } + + if(ms_database->queryMaterial(startCell,sphere,delta,MT_Solid,checkFlora,checkFauna,object,collisionTime)) + { + float epsilon = 0.05f / delta.magnitude(); + + if(collisionTime > -epsilon) + { + return CMR_HitObstacle; + } + } + } + + // ---------- + // The move didn't hit an edge on the current floor or any collision extents. + + // Test for collisions on other floors. These will be expensive, since we have to search + // for a start point on each floor (which is slow). + + // ---------- + // Test object floors + + bool hasOutLoc = false; + + if(startCell == CellProperty::getWorldCellProperty()) + { + if(ms_database) + { + FloorVec floors; + + IGNORE_RETURN(ms_database->queryFloors(startCell,querySeg,&floors)); + + for(uint i = 0; i < floors.size(); i++) + { + Floor const * floor = floors[i]; + + CanMoveResult result = canMoveOnFloor(object,startLoc,floor,goalLoc,checkY,checkFlora,checkFauna,outLoc,outHitPortalId); + + if(result == CMR_MoveOK) + { + if(outHitPortalId != -1) + { + hasOutLoc = true; + } + } + else + { + return result; + } + } + } + } + + // Last but not least, test the cell floor (if there is one) + // Test cell floor + + Floor const * cellFloor = startCell->getFloor(); + + if(cellFloor) + { + return canMoveOnFloor(object,startLoc,cellFloor,goalLoc,checkY,checkFlora,checkFauna,outLoc,outHitPortalId); + } + else + { + if(!hasOutLoc) outLoc = goalLoc; + + return CMR_MoveOK; + } +} + +// ---------------------------------------------------------------------- + +CanMoveResult CollisionWorld::canMove ( CellProperty const * startCell, + Vector const & startPos, + Vector const & goalPos, + float moveRadius, + bool checkY, + bool checkFlora, + bool checkFauna ) +{ + canMoveCount++; + + FloorLocator startLoc(startPos,moveRadius); + + FloorLocator dummy; + + return canMove( NULL, startCell, startLoc, goalPos, checkY, checkFlora, checkFauna, dummy ); +} + +// ---------- + +CanMoveResult CollisionWorld::canMove ( Object const * object, + Vector const & goalPos, + float moveRadius, + bool checkY, + bool checkFlora, + bool checkFauna ) +{ + if(object == NULL) + { + return CMR_Error; + } + else + { + CollisionProperty const * collision = object->getCollisionProperty(); + + if(collision) + { + Footprint const * foot = collision->getFootprint(); + + if(foot && foot->isObjectInSync()) + { + FloorLocator const * footLoc = foot->getAnyContact(); + + if(footLoc && footLoc->isAttached()) + { + FloorLocator objectLoc = *footLoc; + + objectLoc.setPosition_p( object->getPosition_p() ); + + objectLoc.setRadius(moveRadius); + + CellProperty const * startCell = object->getParentCell(); + + FloorLocator dummy; + + return canMove( object, startCell, objectLoc, goalPos, checkY, checkFlora, checkFauna, dummy ); + } + } + } + } + + FloorLocator startLoc(object->getPosition_p(),moveRadius); + + FloorLocator dummy; + + return canMove( object, object->getParentCell(), startLoc, goalPos, checkY, checkFlora, checkFauna, dummy ); +} + +// ---------------------------------------------------------------------- + +CanMoveResult CollisionWorld::canMove ( Object const * object, + CellProperty const * startCell, + FloorLocator const & startLoc, + Vector const & goalPos, + bool checkY, + bool checkFlora, + bool checkFauna, + FloorLocator & endLoc ) +{ + if(startCell == NULL) + { + startCell = CellProperty::getWorldCellProperty(); + } + + // ---------- + + int hitPortalId = -1; + + CanMoveResult cellResult = canMoveInCell(object,startCell,startLoc,goalPos,checkY,checkFlora,checkFauna,endLoc,hitPortalId); + + if(cellResult != CMR_MoveOK) + { + // We hit something in this cell + + return cellResult; + } + + if(hitPortalId == -1) + { + // We made it to the goal without hitting a portal - the move + // succeeds + + return CMR_MoveOK; + } + + // ---------- + // We crossed a portal-adjacent floor edge on our way to the goal + + Floor const * floor = endLoc.getFloor(); + + if(!floor) + { + return CMR_Error; + } + + Object const * owner = floor->getOwner(); + + if(!owner) + { + return CMR_Error; + } + + CellProperty * newCell = startCell->getDestinationCell(owner,hitPortalId); + + if(!newCell) + { + return CMR_Error; + } + + // so restart the move in the new cell at the point where we crossed + // the portal-adjacent edge + + Vector hitPortalPos = endLoc.getPosition_p(); + + Vector newStartPos = CollisionUtils::transformToCell(startCell,hitPortalPos,newCell); + + FloorLocator newStartLoc(newStartPos,startLoc.getRadius()); + + Vector newGoalPos = CollisionUtils::transformToCell(startCell,goalPos,newCell); + + float dist2 = newStartPos.magnitudeBetweenSquared(newGoalPos); + + if(dist2 < 0.0000000001f) + { + return CMR_MoveOK; + } + else + { + static int recursionCounter = 0; + + if(recursionCounter == 10) + { + Vector s = startLoc.getPosition_w(); + + WARNING_STRICT_FATAL(ConfigSharedCollision::getReportWarnings(),("CollisionWorld::canMove - infinite recursion detected (%f,%f,%f)",s.x,s.y,s.z)); + + endLoc = FloorLocator::invalid; + return CMR_Error; + } + + recursionCounter++; + + CanMoveResult result = canMove(object,newCell,newStartLoc,newGoalPos,checkY,checkFlora,checkFauna,endLoc); + + recursionCounter--; + + return result; + } +} + +// ---------- +// A NULL goal cell means we don't care what cell we end up in as long as we make it to the goal + +CanMoveResult CollisionWorld::canMove ( Object const * object, + CellProperty const * startCell, + FloorLocator const & startLoc, + CellProperty const * goalCell, + FloorLocator const & goalLoc, + bool checkY, + bool checkFlora, + bool checkFauna ) +{ + // ---------- + + if(startCell == NULL) + { + startCell = CellProperty::getWorldCellProperty(); + } + + if(goalCell == NULL) + { + FloorLocator dummy; + + return canMove(object,startCell,startLoc,goalLoc.getPosition_p(),checkY,checkFlora,checkFauna,dummy); + } + + // ---------- + + int hitPortalId = -1; + FloorLocator endLoc; + + if(startCell == goalCell) + { + // We're in the right cell. The move succeeds if it makes it to the goal + // without hitting a portal on the way there. + + CanMoveResult cellResult = canMoveInCell(object,startCell,startLoc,goalLoc,checkY,checkFlora,checkFauna,endLoc,hitPortalId); + + if((cellResult == CMR_MoveOK) && (hitPortalId != -1)) + { + return CMR_MissedGoal; + } + else + { + return cellResult; + } + } + + // ---------- + + // We're not in our goal cell. Try to move as far as we can towards the goal + // in our current cell + + Vector const & goalPos = goalLoc.getPosition_p(); + + Vector localGoalPos = CollisionUtils::transformToCell(goalCell,goalPos,startCell); + + CanMoveResult cellResult = canMoveInCell(object,startCell,startLoc,localGoalPos,checkY,checkFlora,checkFauna,endLoc,hitPortalId); + + if(cellResult != CMR_MoveOK) + { + // We hit something while trying to get out of this cell - the move fails. + + return cellResult; + } + + if(hitPortalId == -1) + { + // We can move towards the goal from the current cell, but we don't exit the cell via a portal-adjacent + // floor edge and we're not in the right goal cell - we can never get to the goal cell, + // so the move fails + + return CMR_MissedGoal; + } + + // Otherwise, restart the query in the cell on the other side of the portal-adjacent edge + + Floor const * floor = endLoc.getFloor(); + + if(!floor) + { + return CMR_Error; + } + + Object const * owner = floor->getOwner(); + + if(!owner) + { + return CMR_Error; + } + + CellProperty * newCell = startCell->getDestinationCell(owner,hitPortalId); + + if(!newCell) + { + return CMR_Error; + } + + Vector hitPortalPos = endLoc.getPosition_p(); + + Vector newStartPos = CollisionUtils::transformToCell(startCell,hitPortalPos,newCell); + + FloorLocator newStartLoc(newStartPos,startLoc.getRadius()); + + newStartLoc.setRadius(startLoc.getRadius()); + + return canMove(object,newCell,newStartLoc,goalCell,goalLoc,checkY,checkFlora,checkFauna); +} + +// ---------------------------------------------------------------------- + +CanMoveResult CollisionWorld::canMoveOnFloor ( Object const * object, + FloorLocator const & startLoc, + Floor const * floor, + FloorLocator const & goalLoc, + bool checkY, + bool checkFlora, + bool checkFauna, + FloorLocator & outLoc, + int & outHitPortalId ) +{ + FloorMesh const * floorMesh = floor->getFloorMesh(); + + Vector const & startPos = startLoc.getPosition_p() + Vector(0.0f,startLoc.getOffset(),0.0f); + Vector const & goalPos = goalLoc.getPosition_p() + Vector(0.0f,goalLoc.getOffset(),0.0f); + + Vector localGoalPos = goalPos; + + float radius = startLoc.getRadius(); + + Vector delta = localGoalPos - startPos; + + delta.y = 0.0f; + + if(!startLoc.isAttachedTo(floor)) + { + // Starting location isn't on this floor, run the canMoveOnFloor version that does an + // attachment test instead + + return canMoveOnFloor(object,startPos,radius,floor,goalLoc,checkY,checkFlora,checkFauna,outLoc,outHitPortalId); + } + + // ---------- + + FloorLocator result; + + PathWalkResult walkResult = floor->canMove(startLoc,delta,-1,-1,result); + + outLoc = result; + + outHitPortalId = -1; + + if((result.getTriId() >= 0) && (result.getEdgeId() >= 0)) + { + int const exitTriId = result.getTriId(); + + FloorTri const & exitTri = floorMesh->getFloorTri(exitTriId); + + outHitPortalId = exitTri.getPortalId(result.getEdgeId()); + } + + + // ---------- + // The move has clipped to a crossable edge of the floor. + // Check to see if the move re-enters this floor, and if it does + // test the move there. + + // Since this is a recursive call, it'll catch an arbitrary number of reentrances + + if(walkResult == PWR_HitPortalEdge) + { + // Exiting the mesh along a portal-adjacent edge is always a successful move. + + return CMR_MoveOK; + } + else if(walkResult == PWR_ExitedMesh) + { + if(floor->isCellFloor()) + { + // Trying to walk off a ledge inside a cell isn't allowed at the moment. + + return CMR_HitFloorEdge; + } + + Vector reenterDelta = delta - (delta * result.getTime()); + + FloorLocator reenterLoc; + + if(floor->findEntryPoint(result,reenterDelta,false,reenterLoc)) + { + if(reenterLoc.getTime() > 0.0f) + { + Vector reenterPos = reenterLoc.getPosition_p(); + + UNREF(reenterPos); + + reenterLoc.setRadius(radius); + + static int recursionCounter = 0; + + if(recursionCounter == 5) + { + Vector s = startLoc.getPosition_w(); + + WARNING_STRICT_FATAL(ConfigSharedCollision::getReportWarnings(),("CollisionWorld::canMoveOnFloor - infinite recursion detected (%f,%f,%f)",s.x,s.y,s.z)); + outLoc = FloorLocator::invalid; + outHitPortalId = -1; + return CMR_Error; + } + + recursionCounter++; + CanMoveResult floorResult = canMoveOnFloor(object,reenterLoc,floor,goalLoc,checkY,checkFlora,checkFauna,outLoc,outHitPortalId); + + recursionCounter--; + + return floorResult; + } + } + + // Failure to reenter a floor in the world cell isn't a collision because + // it means you're back on the terrain. + + return CMR_MoveOK; + } + else if(walkResult == PWR_WalkOk) + { + if((floor->getCell() == CellProperty::getWorldCellProperty())) + { + if(result.isFallthrough()) + { + return CMR_MoveOK; + } + } + + outLoc = result; + outHitPortalId = -1; + + if ((floor == startLoc.getFloor()) && (floor == goalLoc.getFloor())) + { + // The start and goal locs are on this floor - if the move didn't get to + // the goal tri, the move failed. + + if(checkY && (goalLoc.getId() != -1) && (result.getId() != goalLoc.getId())) + { + return CMR_MissedGoal; + } + else + { + return CMR_MoveOK; + } + } + else + { + if(result.getFloor() == floor) + { + Vector resultPos = result.getPosition_p(); + + if((resultPos.y - goalPos.y) > 3.0f) + { + return CMR_MissedGoal; + } + else + { + return CMR_MoveOK; + } + } + else + { + return CMR_MoveOK; + } + } + } + else + { + return CMR_HitFloorEdge; + } +} + +// ---------------------------------------------------------------------- + +CanMoveResult CollisionWorld::canMoveOnFloor ( Object const * object, + Vector const & inPos, + float moveRadius, + Floor const * floor, + FloorLocator const & inGoalLoc, + bool checkY, + bool checkFlora, + bool checkFauna, + FloorLocator & outLoc, + int & outHitPortalId ) +{ + bool hasStartLoc = false; + FloorLocator startLoc; + + FloorLocator goalLoc = inGoalLoc; + + if(goalLoc.getFloor() != floor) + { + Vector goalPos_p = goalLoc.getPosition_p( floor->getCell() ); + + FloorLocator tempLoc; + + if(floor->dropTest( goalPos_p, tempLoc )) + { + goalLoc = tempLoc; + } + } + + // ---------- + // We have a starting position, but we don't know where the path first hits + // the floor, so try to find a starting point for the move. + + // If the start point is already above the floor, the floor move will begin + // at the floor point under the start point + + if(floor->dropTest(inPos,startLoc)) + { + Vector startPos_w = startLoc.getPosition_w(); + + UNREF(startPos_w); + + hasStartLoc = true; + } + else + { + // Otherwise we have to search the floor for the first boundary + // crossing and start there. + + Vector delta = goalLoc.getPosition_p() - inPos; + + FloorLocator tempLoc(inPos,moveRadius); + + if(floor->findEntryPoint(tempLoc,delta,false,startLoc)) + { + bool edgeCrossable = floor->getFloorMesh()->getFloorTri(startLoc.getId()).isCrossable(startLoc.getEdgeId()); + + if(!edgeCrossable) + { + return CMR_HitFloorEdge; + } + + hasStartLoc = true; + } + } + + // ---------- + // If we found a starting point for the floor move, run the floor move + + if(hasStartLoc) + { + startLoc.setRadius(moveRadius); + + return canMoveOnFloor(object,startLoc,floor,goalLoc,checkY,checkFlora,checkFauna,outLoc,outHitPortalId); + } + else + { + // Otherwise the move succeeds - failing to enter a floor is not a collision. + + return CMR_MoveOK; + } +} + +// ---------------------------------------------------------------------- +// Find all obstacles in a capsule containing the start and end extents, +// do a temporal collision test against each of them, return true if +// the creature hit one and a pointer to the first one hit. + +bool CollisionWorld::findFirstObstacle ( Object const * creature, float creatureRadius, Vector const & testPos, Vector const & goalPos, bool testStatics, bool testCreatures, Object const * & outObject ) +{ + UNREF(goalPos); + UNREF(creatureRadius); + + Vector startPos = creature->getPosition_p(); + + Vector delta = testPos - startPos; + + if(creature == NULL) return false; + + CollisionProperty const * collision = creature->getCollisionProperty(); + + if(collision == NULL) return false; + + BaseExtent const * baseExtent = collision->getExtent_p(); + + SimpleExtent const * simpleExtent = dynamic_cast(baseExtent); + + if(simpleExtent == NULL) return false; + + MultiShape const & shape = simpleExtent->getShape(); + + if(shape.getShapeType() != MultiShape::MST_Sphere) return false; + + // ---------- + // Test collision extents + + DetectResult minResult; + Object const * minObject = NULL; + + typedef std::vector ObjectVector; + static ObjectVector statics; + static ObjectVector creatures; + + statics.clear(); + creatures.clear(); + + if(ms_database->queryObjects(creature->getParentCell(), shape, delta, (testStatics ? &statics : NULL), (testCreatures ? &creatures : NULL))) + { + ObjectVector::size_type const nStatics = statics.size(); + ObjectVector::size_type i; + + for(i = 0; i < nStatics; i++) + { + Object const * obstacle = statics[i]; + + if(obstacle == NULL) continue; + + if(obstacle == creature) continue; + + if(Distance2d::Distance2(obstacle->getPosition_w(),goalPos) < 0.000001) continue; + + DetectResult result = CollisionDetect::testObjects(creature,delta,obstacle); + + if(result.collided && (result.collisionTime < minResult.collisionTime)) + { + minResult = result; + minObject = obstacle; + } + } + + ObjectVector::size_type const nCreatures = creatures.size(); + + for(i = 0; i < nCreatures; i++) + { + Object const * obstacle = creatures[i]; + + if(obstacle == NULL) continue; + + if(obstacle == creature) continue; + + if(Distance2d::Distance2(obstacle->getPosition_w(),goalPos) < 0.000001) continue; + + DetectResult result = CollisionDetect::testObjects(creature,delta,obstacle); + + if(result.collided && (result.collisionTime < minResult.collisionTime)) + { + minResult = result; + minObject = obstacle; + } + } + } + + //-- Check for floor collisions. + Footprint const *const footprint = collision->getFootprint(); + FloorLocator const *const floorLocator = (footprint ? footprint->getAnyContact() : NULL); + if (floorLocator) + { + Object const *floorCollisionObject = NULL; + Vector collisionLocation_w; + + // Do the floor check. + bool const floorCollisionOccurred = testFloorCollision(*floorLocator, creature->getTransform_p2w().rotate_l2p(delta), collisionLocation_w, floorCollisionObject); + if (floorCollisionOccurred && floorCollisionObject) + { + // We collided with a floor. Check if we collide with the floor sooner than anything else. + bool useFloorCollision; + + if(!minResult.collided) // don't return a floor as a collision object if nothing was collided with earlier + { + useFloorCollision = false; + } + else + { + // Figure out if collided object is closer than floor. Collided object looks like its + // extent is assumed to be in the same parent space as creature. That might be a bug. + float const minResultMagnitudeSquared = (creature->getPosition_p() - minResult.extentB->getCenter()).magnitudeSquared(); + float const floorMagnitudeSquared = (creature->getPosition_w() - collisionLocation_w).magnitudeSquared(); + + useFloorCollision = floorMagnitudeSquared < minResultMagnitudeSquared; + } + + if (useFloorCollision) + { + // We will use the floor object as the collision object. + outObject = floorCollisionObject; + return true; + } + } + } + + if(minResult.collided) + { + outObject = minObject; + + return true; + } + else + { + return false; + } +} + +// ---------------------------------------------------------------------- + +bool CollisionWorld::findFirstObstacle ( Sphere const & sphere, Vector const & goalPos, bool testStatics, bool testCreatures, Object const * & outObject ) +{ + Vector startPos = sphere.getCenter(); + + Vector delta = goalPos - startPos; + + MultiShape shape(sphere); + + // ---------- + // Test collision extents + + DetectResult minResult; + Object const * minObject = NULL; + + static std::vector statics; + static std::vector creatures; + + statics.clear(); + creatures.clear(); + + if(ms_database->queryObjects(CellProperty::getWorldCellProperty(), shape, delta, (testStatics ? &statics : NULL), (testCreatures ? &creatures : NULL))) + { + size_t const nStatics = statics.size(); + size_t i; + + for(i = 0; i < nStatics; i++) + { + Object const * obstacle = statics[i]; + + if(obstacle == NULL) continue; + + DetectResult result = CollisionDetect::testSphereObject(sphere,delta,obstacle); + + if(result.collided && (result.collisionTime < minResult.collisionTime)) + { + minResult = result; + minObject = obstacle; + } + } + + size_t const nCreatures = creatures.size(); + + for(i = 0; i < nCreatures; i++) + { + Object const * obstacle = creatures[i]; + + if(obstacle == NULL) continue; + + if(Distance2d::Distance2(obstacle->getPosition_w(),goalPos) < 0.000001) continue; + + DetectResult result = CollisionDetect::testSphereObject(sphere,delta,obstacle); + + if(result.collided && (result.collisionTime < minResult.collisionTime)) + { + minResult = result; + minObject = obstacle; + } + } + } + + if(minResult.collided) + { + outObject = minObject; + + return true; + } + else + { + return false; + } +} + +// ---------------------------------------------------------------------- +// Compute the radius of the largest sphere that can fit around the given point without hitting anything + +bool CollisionWorld::calcBubble ( CellProperty const * cell, Vector const & point_p, float maxDistance, float & outRadius ) +{ + return calcBubble(cell,point_p,NULL,maxDistance,outRadius); +} + +bool CollisionWorld::calcBubble ( CellProperty const * cell, Vector const & point_p, Object const * ignoreObject, float maxDistance, float & outRadius ) +{ + Vector point_w = CollisionUtils::transformToWorld(cell,point_p); + + float minDistance = maxDistance; + + { + static ObjectVec results; + + results.clear(); + + if(ms_database->queryCloseStatics(point_w,minDistance,&results)) + { + ObjectVec::size_type const count = results.size(); + + for(ObjectVec::size_type i = 0; i < count; i++) + { + Object const * object = results[i]; + + NOT_NULL(object); + + if(object == ignoreObject) continue; + + CollisionProperty const * collision = object->getCollisionProperty(); + + NOT_NULL(collision); + + float distance = REAL_MAX; + + if(collision->getDistance(point_w,minDistance,distance)) + { + if(distance < minDistance) minDistance = distance; + } + } + } + } + + // ---------- + + { + static FloorVec results; + + results.clear(); + + if(ms_database->queryCloseFloors(point_w,minDistance,&results)) + { + FloorVec::size_type const count = results.size(); + + for(FloorVec::size_type i = 0; i < count; i++) + { + Floor const * floor = results[i]; + + NOT_NULL(floor); + + if(floor->getOwner() == ignoreObject) continue; + + float distance = REAL_MAX; + + FloorEdgeId dummyId; + + if(floor->getDistanceUncrossable2d(point_w,minDistance,distance,dummyId)) + { + if(distance < minDistance) minDistance = distance; + } + } + } + } + + // ---------- + + if(cell && (cell != CellProperty::getWorldCellProperty())) + { + Floor const * floor = cell->getFloor(); + + if(floor) + { + float distance = REAL_MAX; + + FloorEdgeId dummyId; + + if(floor->getDistanceUncrossable2d(point_w,minDistance,distance,dummyId)) + { + if(distance < minDistance) minDistance = distance; + } + } + } + + // ---------- + + if(minDistance < maxDistance) + { + outRadius = minDistance; + return true; + } + else + { + return false; + } +} + +// ---------------------------------------------------------------------- + +bool CollisionWorld::testClear ( CellProperty const * cell, Sphere const & sphere_p ) +{ + bool hitStatic = ms_database->queryMaterial(cell,sphere_p,MT_Solid); + + if(hitStatic) return false; + + return true; +} + +// ---------------------------------------------------------------------- + +bool CollisionWorld::canMoveInSpace( Object const * object, Sphere const & sphere, Vector const & goalPos ) +{ + Vector delta = goalPos - sphere.getCenter(); + float collisionTime = 0.0f; + + if(ms_database->queryMaterial(CellProperty::getWorldCellProperty(),sphere,delta,MT_Solid,false,false,object,collisionTime)) + { + float epsilon = 0.05f / delta.magnitude(); + + if(collisionTime > -epsilon) + { + return false; + } + } + + return true; +} + +// ---------------------------------------------------------------------- + +bool CollisionWorld::findLocators ( CellProperty const * cell, Vector const & point, std::vector & outLocs ) +{ + outLocs.clear(); + + // ---------- + // If the point is in a cell, its only locator is the one on the cell floor + + if(cell && (cell != CellProperty::getWorldCellProperty())) + { + Floor const * floor = cell->getFloor(); + + FloorLocator tempLoc; + + if( floor && floor->dropTest(point,tempLoc) ) + { + outLocs.push_back(tempLoc); + + return true; + } + else + { + return false; + } + } + else + { + // ---------- + // Otherwise we need to find all object floors and see if the point falls on any of them + + if(ms_database) + { + FloorVec floors; + + Segment3d querySeg( point, point - (Vector::unitY * 10.0f) ); + + IGNORE_RETURN(ms_database->queryFloors(cell,querySeg,&floors)); + + for(uint i = 0; i < floors.size(); i++) + { + Floor * floor = floors[i]; + + FloorLocator tempLoc; + + if(floor && floor->dropTest(point,tempLoc)) + { + outLocs.push_back(tempLoc); + } + } + } + + return outLocs.size() != 0.0f; + } +} + +// ---------------------------------------------------------------------- +// This should work OK, but if the point drops onto multiple floors it will make a locator for only +// the topmost floor + +bool CollisionWorld::makeLocator ( CellProperty const * cell, Vector const & point, FloorLocator & outLoc ) +{ + static FloorLocatorVec tempLocs; + + tempLocs.clear(); + + if(findLocators(cell,point,tempLocs)) + { + float maxHeight = -REAL_MAX; + int highestLoc = -1; + + for(uint i = 0; i < tempLocs.size(); i++) + { + float height = tempLocs[i].getPosition_p().y; + + if(height > maxHeight) + { + maxHeight = height; + highestLoc = static_cast(i); + } + } + + if(highestLoc != -1) + { + outLoc = tempLocs[static_cast(highestLoc)]; + return true; + } + else + { + outLoc = FloorLocator(point, 0.0f); + return false; + } + } + else + { + outLoc = FloorLocator(point, 0.0f); + return false; + } +} + +// ---------------------------------------------------------------------- + +bool CollisionWorld::isServerSide ( void ) +{ + return ms_serverSide; +} + +void CollisionWorld::setServerSide ( bool serverSide ) +{ + ms_serverSide = serverSide; +} + +//---------------------------------------------------------------------- + +void CollisionWorld::handleSceneChange(std::string const & sceneId) +{ + ms_forceReattachContactsPostResolve = FootprintForceReattachManager::isSceneFootprintForceReattach(sceneId); +} + +//---------------------------------------------------------------------- + +/** @pre the object is "on" all floors in floorList + * @return the floor that is least below the player + */ +Floor const * CollisionWorld::getFloorStandingOn(Object const & object) +{ + CollisionProperty const * const objectCollisionProperty = object.getCollisionProperty(); + Footprint const * const objectFootprint = (objectCollisionProperty != NULL) ? objectCollisionProperty->getFootprint() : NULL; + + if (objectFootprint == NULL) + { + return NULL; + } + + MultiListHandle const & floorList = objectFootprint->getFloorList(); + float const objectHeight = object.getPosition_w().y; + Floor const * resultFloor = NULL; + float resultDistanceBelowObject = 0.0f; + float dropTestHeight = std::numeric_limits::min(); + + for(MultiListConstDataIterator it(floorList); it; ++it) + { + FloorContactShape const * const contact = *it; + FloorLocator const & loc = contact->m_contact; + Floor const * const f = loc.getFloor(); + + FloorLocator tempLoc; + if(f->dropTest(object.getPosition_p(),tempLoc)) + { + //if we might be standing on it (it's below us), give 1/2 meter buffer space + if((tempLoc.getPosition_w().y - 0.5) <= objectHeight) + { + float const distanceBelowObject = std::max(0.0f, objectHeight - tempLoc.getPosition_w().y); + + //if none found yet, use it + if(!resultFloor) + { + resultFloor = f; + resultDistanceBelowObject = distanceBelowObject; + dropTestHeight = tempLoc.getPosition_w().y; + } + else + { + //if it's less below us than the current, use it + if(distanceBelowObject < resultDistanceBelowObject) + { + resultFloor = f; + resultDistanceBelowObject = distanceBelowObject; + dropTestHeight = tempLoc.getPosition_w().y; + } + } + } + } + } + + if (dropTestHeight > std::numeric_limits::min()) + { + TerrainObject const * const terrainObject = TerrainObject::getConstInstance(); + + if (terrainObject != NULL) + { + CellProperty const * const parentCell = object.getParentCell(); + + if (parentCell->isWorldCell()) + { + float terrainHeight; + bool result = false; + + // This has to be split client/server because on the server forcing chunk + // creation is thread safe, but on the client it is not thread safe. + + if (isServerSide()) + { + result = terrainObject->getHeightForceChunkCreation(object.getPosition_w(), terrainHeight); + } + else + { + result = terrainObject->getHeight(object.getPosition_w(), terrainHeight); + } + + if (result) + { + if (dropTestHeight < terrainHeight) + { + resultFloor = NULL; + } + } + } + } + } + + return resultFloor; +} + +// ---------------------------------------------------------------------- diff --git a/engine/shared/library/sharedCollision/src/shared/core/CollisionWorld.h b/engine/shared/library/sharedCollision/src/shared/core/CollisionWorld.h new file mode 100644 index 00000000..db5bf1e3 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/CollisionWorld.h @@ -0,0 +1,176 @@ +// ====================================================================== +// +// CollisionWorld.h +// copyright (c) 2001 Sony Online Entertainment +// +// ---------------------------------------------------------------------- + +#ifndef INCLUDED_CollisionWorld_H +#define INCLUDED_CollisionWorld_H + +#include "sharedCollision/CollisionEnums.h" +#include "sharedCollision/CollisionNotification.h" +#include "sharedCollision/FloorContactShape.h" +#include "sharedCollision/MultiList.h" +#include "sharedMath/Vector.h" + +class Camera; +class CellProperty; +class Circle; +class CollisionProperty; +class Floor; +class FloorLocator; +class Footprint; +class Line3d; +class Object; +class Segment3d; +class SpatialDatabase; +class Sphere; +class MultiShape; + +typedef stdvector::fwd ObjectVec; +typedef stdvector::fwd ColliderList; +typedef stdvector::fwd FootprintList; + +// ====================================================================== + +class CollisionWorld +{ +public: + + typedef void (*WarpWarningCallback)(Object const &object, Vector const &oldPosition_w, Vector const &newPosition_w, int segmentCount); + + // collision callbacks that are used by non-Footprint objects that are in a 3D environment. + // these are mostly used by space objects such as ShipObject + typedef bool (*CanTestCollisionDetectionOnObjectThisFrameFunction)(Object * const object); + typedef void (*NoCollisionDetectionThisFrameFunction)(Object * const object); + typedef bool (*CollisionDetectionOnHitFunction)(Object * const object, Object * const wasHitByThisObject); + typedef bool (*DoCollisionWithTerrainFunction)(Object * const object); + + static void registerCanTestCollisionDetectionOnObjectThisFrame(CanTestCollisionDetectionOnObjectThisFrameFunction); + static void registerNoCollisionDetectionThisFrame(NoCollisionDetectionThisFrameFunction); + static void registerDoCollisionDetectionOnHit(CollisionDetectionOnHitFunction); + static void registerDoCollisionWithTerrain(DoCollisionWithTerrainFunction); + +public: + + // ---------- + + static void install ( bool serverSide ); + static void remove ( void ); + + static void update ( float time ); + + static bool isUpdating ( void ); + + static void reportCallback ( void ); + + static void setNearWarpWarningCallback(WarpWarningCallback callback); + static void setFarWarpWarningCallback(WarpWarningCallback callback); + + // ---------- + + static void addObject ( Object * object ); + static void removeObject ( Object * object ); + static void moveObject ( Object * object ); + static void cellChanged ( Object * object ); + static void appearanceChanged ( Object * object ); + + + // ---------- + + static void objectWarped ( Object * object ); + + static SpatialDatabase * getDatabase ( void ); + + // ---------- + // Queries + + static bool query ( Line3d const & line, ObjectVec * outList ); + + static bool query ( Sphere const & sphere, ObjectVec * outList ); + + static QueryInteractionResult queryInteraction ( CellProperty const * cellA, Vector const & pointA, + CellProperty const * cellB, Vector const & pointB, + Object const * ignoreObject, + bool useTerrainLos, + bool generateTerrainLos, + float terrainLosMinDistance, + float terrainLosMaxDistance, + float & outHitTime, Object const * & outHitObject ); + + // ---------- + // CanMove + + // checkY - if true, the canMove succeeds only if the object would end up at the correct X,Y, and Z coords. if false, only checks X and Z + + static CanMoveResult canMove ( Object const * object, Vector const & goal, float moveRadius, bool checkY, bool checkFlora, bool checkFauna ); + static CanMoveResult canMove ( CellProperty const * startCell, Vector const & startPos, Vector const & goal, float moveRadius, bool checkY, bool checkFlora, bool checkFauna ); + + static bool findFirstObstacle ( Object const * object, float radius, Vector const & testPos, Vector const & goalPos, bool testStatics, bool testCreatures, Object const * & outObject ); + static bool findFirstObstacle ( Sphere const & sphere, Vector const & goalPos, bool testStatics, bool testCreatures, Object const * & outObject ); + + static bool calcBubble ( CellProperty const * cell, Vector const & point_p, float maxDistance, float & outRadius ); + + static bool calcBubble ( CellProperty const * cell, Vector const & point_p, Object const * ignoreObject, float maxDistance, float & outRadius ); + + static bool testClear ( CellProperty const * cell, Sphere const & sphere_p ); + + static bool canMoveInSpace ( Object const * object, Sphere const & sphere, Vector const & goal ); + + // ---------- + + static bool findLocators ( CellProperty const * cell, Vector const & point, stdvector::fwd & outLocs ); + + static bool makeLocator ( CellProperty const * cell, Vector const & point, FloorLocator & outLoc); + + static void environmentChanged ( MultiShape const & shape ); + + static void shoveAway(CollisionProperty * const collisionProperty, ColliderList const & colliderList, float time); + + static bool isServerSide ( void ); + static void setServerSide ( bool serverSide ); + + static void handleSceneChange(std::string const & sceneId); + + static Floor const * getFloorStandingOn(Object const & object); + +protected: + // collision callbacks that are used by non-Footprint objects such as ShipObject + static bool canTestCollisionDetectionOnObjectThisFrame(Object * object); + static void noCollisionDetectionThisFrame(Object * object); + static bool doCollisionDetectionOnHit(Object * object, Object * wasHitByThisObject); + static bool doCollisionWithTerrain(Object * object); + + static void update ( CollisionProperty * collision, float time ); + static void updateSegment ( CollisionProperty * collision, float time ); + static ResolutionResult updateStep ( CollisionProperty * collision, float time ); + + static bool spatialSweepAndResolve(CollisionProperty * collider); + + // ---------- + + static CanMoveResult canMove ( Object const * object, CellProperty const * startCell, FloorLocator const & startLoc, CellProperty const * goalCell, FloorLocator const & goalLoc, bool checkY, bool checkFlora, bool checkFauna ); + static CanMoveResult canMove ( Object const * object, CellProperty const * startCell, FloorLocator const & startLoc, Vector const & goalPos, bool checkY, bool checkFlora, bool checkFauna, FloorLocator & endLoc ); + + static CanMoveResult canMoveOnFloor ( Object const * object, Vector const & startPos, float moveRadius, Floor const * floor, FloorLocator const & goalLoc, bool checkY, bool checkFlora, bool checkFauna, FloorLocator & outLoc, int & outHitPortalId ); + static CanMoveResult canMoveOnFloor ( Object const * object, FloorLocator const & startLoc, Floor const * floor, FloorLocator const & goalLoc, bool checkY, bool checkFlora, bool checkFauna, FloorLocator & outLoc, int & outHitPortalId ); + + static CanMoveResult canMoveInCell ( Object const * object, CellProperty const * startCell, FloorLocator const & startLoc, Vector const & goalPos, bool checkY, bool checkFlora, bool checkFauna, FloorLocator & outLoc, int & outHitPortalId ); + static CanMoveResult canMoveInCell ( Object const * object, CellProperty const * startCell, FloorLocator const & startLoc, FloorLocator const & goalLoc, bool checkY, bool checkFlora, bool checkFauna, FloorLocator & outLoc, int & outHitPortalId ); + + // ---------- + + static void updateReportString ( void ); + +private: + + CollisionWorld(); + CollisionWorld( CollisionWorld const & copy ); + CollisionWorld & operator = ( CollisionWorld const & copy ); +}; + +// ====================================================================== + +#endif // #ifndef INCLUDED_CollisionWorld_H + diff --git a/engine/shared/library/sharedCollision/src/shared/core/ConfigSharedCollision.cpp b/engine/shared/library/sharedCollision/src/shared/core/ConfigSharedCollision.cpp new file mode 100644 index 00000000..0b3d4164 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/ConfigSharedCollision.cpp @@ -0,0 +1,222 @@ +//=================================================================== +// +// ConfigSharedCollision.cpp +// copyright 2000, verant interactive +// +//=================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/ConfigSharedCollision.h" + +#include "sharedCollision/CollisionWorld.h" +#include "sharedCollision/SpatialDatabase.h" + +#include "sharedFoundation/ConfigFile.h" +#include "sharedFoundation/Production.h" +#include "sharedDebug/DebugFlags.h" + +//=================================================================== + +namespace ConfigSharedCollisionNamespace +{ + +bool ms_drawFloors = false; +bool ms_drawExtents = false; +bool ms_drawCollisionNormals = false; +bool ms_drawSphereTrees = false; +bool ms_drawPathNodes = false; +bool ms_drawBoxTrees = false; +bool ms_drawCMeshes = false; +bool ms_drawFootprints = false; +bool ms_drawCreatureLengths = false; + +bool ms_enableStaticCollision = true; +bool ms_enableFloraCollision = true; +bool ms_enableFloorCollision = true; +bool ms_enableCollisionResolve = true; +bool ms_enableCollisionFlags = false; +bool ms_enableCollisionValidate = false; +bool ms_enableRemoteCollision = false; +bool ms_enableTestCode = false; +bool ms_enableCreatureCollision = true; +bool ms_enableIntraFrameTests = true; +bool ms_enableMountRadii = false; +bool ms_buildBoxTrees = false; +bool ms_buildDebugData = false; + +bool ms_updateStatus = true; +bool ms_reportStatus = false; +bool ms_reportChanges = false; +bool ms_reportMessages = false; +#if PRODUCTION == 0 +bool ms_reportWarnings = true; +#else +bool ms_reportWarnings = false; +#endif +bool ms_reportEvents = false; +bool ms_reportFloating = false; +bool ms_logLongFrames = false; +bool ms_ignoreTerrainLos = true; +bool ms_generateTerrainLos = false; +bool ms_shoveEnabled = true; + +float ms_wallEpsilon = 0.01f; // the distance that we keep the footprints away from the walls +float ms_areaEpsilon = 0.000001f; // One square millimeter +float ms_hopHeight = 0.30f; // how high a floor can be off the terrain and still allow us to hop up onto it +float ms_terrainLOSMinDistance = 4.0f; +float ms_terrainLOSMaxDistance = 64.0f; +float ms_losUprightScale = 1.5f; +float ms_losProneScale = 1.0f; + +PlayEffectHook ms_playEffectHook = NULL; +IsPlayerHouseHook ms_isPlayerHouseHook = NULL; + + int ms_spatialSweepAndResolveDefaultMask = static_cast(SpatialDatabase::Q_Static); + + bool ms_spaceAiLoggingEnabled = false; + bool ms_useOriginalAvoidanceAlgorithm = false; +} + +using namespace ConfigSharedCollisionNamespace; + +// ---------------------------------------------------------------------- + +void ConfigSharedCollision::install ( void ) +{ +#if PRODUCTION == 0 + + DebugFlags::registerFlag( ms_drawFloors, "SharedCollision", "drawFloors"); + DebugFlags::registerFlag( ms_drawExtents, "SharedCollision", "drawExtents"); + DebugFlags::registerFlag( ms_drawCollisionNormals, "SharedCollision", "drawCollisionNormals"); + DebugFlags::registerFlag( ms_drawSphereTrees, "SharedCollision", "drawSphereTrees"); + DebugFlags::registerFlag( ms_drawPathNodes, "SharedCollision", "drawPathNodes"); + DebugFlags::registerFlag( ms_drawBoxTrees, "SharedCollision", "drawBoxTrees"); + DebugFlags::registerFlag( ms_drawCMeshes, "SharedCollision", "drawCMeshes"); + DebugFlags::registerFlag( ms_drawFootprints, "SharedCollision", "drawFootprints"); + DebugFlags::registerFlag( ms_drawCreatureLengths, "SharedCollision", "drawCreatureLengths"); + + + DebugFlags::registerFlag( ms_enableStaticCollision, "SharedCollision", "enableStaticCollision"); + DebugFlags::registerFlag( ms_enableFloraCollision, "SharedCollision", "enableFloraCollision"); + DebugFlags::registerFlag( ms_enableFloorCollision, "SharedCollision", "enableFloorCollision"); + DebugFlags::registerFlag( ms_enableCollisionResolve, "SharedCollision", "enableCollisionResolve"); + DebugFlags::registerFlag( ms_enableCollisionFlags, "SharedCollision", "enableCollisionFlags"); + DebugFlags::registerFlag( ms_enableCollisionValidate, "SharedCollision", "enableCollisionValidate"); + DebugFlags::registerFlag( ms_enableTestCode, "SharedCollision", "enableTestCode"); + DebugFlags::registerFlag( ms_enableCreatureCollision, "SharedCollision", "enableCreatureCollision"); + DebugFlags::registerFlag( ms_enableIntraFrameTests, "SharedCollision", "enableIntraFrameTests"); + DebugFlags::registerFlag( ms_enableMountRadii, "SharedCollision", "enableMountRadii"); + DebugFlags::registerFlag( ms_buildBoxTrees, "SharedCollision", "buildBoxTrees"); + DebugFlags::registerFlag( ms_buildDebugData, "SharedCollision", "buildDebugData"); + + DebugFlags::registerFlag( ms_updateStatus, "SharedCollision", "updateStatus", CollisionWorld::reportCallback); + DebugFlags::registerFlag( ms_reportStatus, "SharedCollision", "reportStatus"); + DebugFlags::registerFlag( ms_reportChanges, "SharedCollision", "reportChanges"); + DebugFlags::registerFlag( ms_reportMessages, "SharedCollision", "reportMessages"); + DebugFlags::registerFlag( ms_reportWarnings, "SharedCollision", "reportWarnings"); + DebugFlags::registerFlag( ms_reportEvents, "SharedCollision", "reportEvents"); + DebugFlags::registerFlag( ms_reportFloating, "SharedCollision", "reportFloating"); + DebugFlags::registerFlag( ms_logLongFrames, "SharedCollision", "logLongFrames"); + DebugFlags::registerFlag( ms_shoveEnabled, "SharedCollision", "0x4F474F67"); // hash of shoveEnabled + DebugFlags::registerFlag( ms_spaceAiLoggingEnabled, "SharedCollision", "spaceAiLoggingEnabled"); + DebugFlags::registerFlag( ms_useOriginalAvoidanceAlgorithm, "SharedCollision", "useOriginalAvoidanceAlgorithm"); + + // ---------- + +#endif + + // the following options should be read regardless of production mode + DebugFlags::registerFlag( ms_ignoreTerrainLos, "SharedCollision", "ignoreTerrainLos"); + DebugFlags::registerFlag( ms_generateTerrainLos, "SharedCollision", "generateTerrainLos"); + + ms_wallEpsilon = ConfigFile::getKeyFloat("SharedCollision", "wallEpsilon", ms_wallEpsilon); + ms_areaEpsilon = ConfigFile::getKeyFloat("SharedCollision", "areaEpsilon", ms_areaEpsilon); + ms_hopHeight = ConfigFile::getKeyFloat("SharedCollision", "hopHeight", ms_hopHeight); + ms_terrainLOSMinDistance = ConfigFile::getKeyFloat("SharedCollision", "terrainLOSMinDistance", ms_terrainLOSMinDistance); + ms_terrainLOSMaxDistance = ConfigFile::getKeyFloat("SharedCollision", "terrainLOSMaxDistance", ms_terrainLOSMaxDistance); +} + +// ---------------------------------------------------------------------- + +bool ConfigSharedCollision::getDrawFloors ( void ) { return ms_drawFloors; } +bool ConfigSharedCollision::getDrawExtents ( void ) { return ms_drawExtents; } +bool ConfigSharedCollision::getDrawCollisionNormals ( void ) { return ms_drawCollisionNormals; } +bool ConfigSharedCollision::getDrawSphereTrees ( void ) { return ms_drawSphereTrees; } +bool ConfigSharedCollision::getDrawPathNodes ( void ) { return ms_drawPathNodes; } +bool ConfigSharedCollision::getDrawBoxTrees ( void ) { return ms_drawBoxTrees; } +bool ConfigSharedCollision::getDrawCMeshes ( void ) { return ms_drawCMeshes; } +bool ConfigSharedCollision::getDrawFootprints ( void ) { return ms_drawFootprints; } +bool ConfigSharedCollision::getDrawCreatureLengths ( void ) { return ms_drawCreatureLengths; } + + +void ConfigSharedCollision::setDrawFloors ( bool draw ) { ms_drawFloors = draw; } +void ConfigSharedCollision::setDrawExtents ( bool draw ) { ms_drawExtents = draw; } +void ConfigSharedCollision::setDrawCollisionNormals ( bool draw ) { ms_drawCollisionNormals = draw; } +void ConfigSharedCollision::setDrawSphereTrees ( bool draw ) { ms_drawSphereTrees = draw; } +void ConfigSharedCollision::setDrawPathNodes ( bool draw ) { ms_drawPathNodes = draw; } +void ConfigSharedCollision::setDrawBoxTrees ( bool draw ) { ms_drawBoxTrees = draw; } +void ConfigSharedCollision::setDrawCMeshes ( bool draw ) { ms_drawCMeshes = draw; } +void ConfigSharedCollision::setDrawFootprints ( bool draw ) { ms_drawFootprints = draw; } +void ConfigSharedCollision::setDrawCreatureLengths ( bool draw ) { ms_drawCreatureLengths = draw; } + + +bool ConfigSharedCollision::getEnableStaticCollision ( void ) { return ms_enableStaticCollision; } +bool ConfigSharedCollision::getEnableFloraCollision ( void ) { return ms_enableFloraCollision; } +bool ConfigSharedCollision::getEnableFloorCollision ( void ) { return ms_enableFloorCollision; } +bool ConfigSharedCollision::getEnableCollisionResolve ( void ) { return ms_enableCollisionResolve; } +bool ConfigSharedCollision::getEnableCollisionFlags ( void ) { return ms_enableCollisionFlags; } +bool ConfigSharedCollision::getEnableCollisionValidate ( void ) { return ms_enableCollisionValidate; } +bool ConfigSharedCollision::getEnableRemoteCollision ( void ) { return ms_enableRemoteCollision; } +bool ConfigSharedCollision::getEnableTestCode ( void ) { return ms_enableTestCode; } +bool ConfigSharedCollision::getEnableCreatureCollision ( void ) { return ms_enableCreatureCollision; } +bool ConfigSharedCollision::getEnableIntraFrameTests ( void ) { return ms_enableIntraFrameTests; } +bool ConfigSharedCollision::getEnableMountRadii ( void ) { return ms_enableMountRadii; } +bool ConfigSharedCollision::getBuildBoxTrees ( void ) { return ms_buildBoxTrees; } +bool ConfigSharedCollision::getBuildDebugData ( void ) { return ms_buildDebugData; } +void ConfigSharedCollision::setBuildDebugData (bool val) +{ + ms_buildDebugData = val; +} + + +bool ConfigSharedCollision::getReportStatus ( void ) { return ms_reportStatus; } +bool ConfigSharedCollision::getReportChanges ( void ) { return ms_reportChanges; } +bool ConfigSharedCollision::getReportMessages ( void ) { return ms_reportMessages; } +bool ConfigSharedCollision::getReportWarnings ( void ) { return ms_reportWarnings; } +bool ConfigSharedCollision::getReportEvents ( void ) { return ms_reportEvents; } +bool ConfigSharedCollision::getReportFloating ( void ) { return ms_reportFloating; } +bool ConfigSharedCollision::getLogLongFrames ( void ) { return ms_logLongFrames; } +bool ConfigSharedCollision::getIgnoreTerrainLos ( void ) { return ms_ignoreTerrainLos; } +bool ConfigSharedCollision::getGenerateTerrainLos ( void ) { return ms_generateTerrainLos; } +bool ConfigSharedCollision::getShoveEnabled ( void ) { return ms_shoveEnabled; } + + +float ConfigSharedCollision::getWallEpsilon ( void ) { return ms_wallEpsilon; } +float ConfigSharedCollision::getAreaEpsilon ( void ) { return ms_areaEpsilon; } +float ConfigSharedCollision::getHopHeight ( void ) { return ms_hopHeight; } +float ConfigSharedCollision::getTerrainLOSMinDistance ( void ) { return ms_terrainLOSMinDistance; } +float ConfigSharedCollision::getTerrainLOSMaxDistance ( void ) { return ms_terrainLOSMaxDistance; } +float ConfigSharedCollision::getLosUprightScale ( void ) { return ms_losUprightScale; } +float ConfigSharedCollision::getLosProneScale ( void ) { return ms_losProneScale; } + +bool ConfigSharedCollision::isSpaceAiLoggingEnabled() { return ms_spaceAiLoggingEnabled; } +bool ConfigSharedCollision::useOriginalAvoidanceAlgorithm() { return ms_useOriginalAvoidanceAlgorithm; } + +// ---------- + +void ConfigSharedCollision::setPlayEffectHook ( PlayEffectHook hook ) { ms_playEffectHook = hook; } +PlayEffectHook ConfigSharedCollision::getPlayEffectHook ( void ) { return ms_playEffectHook; } + +void ConfigSharedCollision::setIsPlayerHouseHook ( IsPlayerHouseHook hook ) { ms_isPlayerHouseHook = hook; } +IsPlayerHouseHook ConfigSharedCollision::getIsPlayerHouseHook ( void) { return ms_isPlayerHouseHook; } + +int ConfigSharedCollision::getSpatialSweepAndResolveDefaultMask() +{ + return ms_spatialSweepAndResolveDefaultMask; +} + +void ConfigSharedCollision::setSpatialSweepAndResolveDefaultMask(int queryMask) +{ + ms_spatialSweepAndResolveDefaultMask = queryMask; +} + diff --git a/engine/shared/library/sharedCollision/src/shared/core/ConfigSharedCollision.h b/engine/shared/library/sharedCollision/src/shared/core/ConfigSharedCollision.h new file mode 100644 index 00000000..a9799ffa --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/ConfigSharedCollision.h @@ -0,0 +1,108 @@ +//=================================================================== +// +// ConfigSharedCollision.h +// copyright 2000, verant interactive +// +//=================================================================== + +#ifndef INCLUDED_ConfigSharedCollision_H +#define INCLUDED_ConfigSharedCollision_H + +class CrcLowerString; +class Object; + +typedef bool (*PlayEffectHook)(CrcLowerString const & effectName, Object * object, CrcLowerString const & hardpoint ); +typedef bool (*IsPlayerHouseHook)(Object const * object); + +//=================================================================== + +class ConfigSharedCollision +{ +public: + + static void install (); + + static bool getDrawFloors ( void ); + static bool getDrawExtents ( void ); + static bool getDrawCollisionNormals ( void ); + static bool getDrawSphereTrees ( void ); + static bool getDrawPathNodes ( void ); + static bool getDrawBoxTrees ( void ); + static bool getDrawCMeshes ( void ); + static bool getDrawFootprints ( void ); + static bool getDrawCreatureLengths ( void ); + + static void setDrawFloors ( bool draw ); + static void setDrawExtents ( bool draw ); + static void setDrawCollisionNormals ( bool draw ); + static void setDrawSphereTrees ( bool draw ); + static void setDrawPathNodes ( bool draw ); + static void setDrawBoxTrees ( bool draw ); + static void setDrawCMeshes ( bool draw ); + static void setDrawFootprints ( bool draw ); + static void setDrawCreatureLengths ( bool draw ); + + static bool getEnableStaticCollision ( void ); + static bool getEnableFloraCollision ( void ); + static bool getEnableFloorCollision ( void ); + static bool getEnableCollisionResolve ( void ); + static bool getEnableCollisionFlags ( void ); + static bool getEnableCollisionValidate ( void ); + static bool getEnableRemoteCollision ( void ); + static bool getEnableTestCode ( void ); + static bool getEnableCreatureCollision ( void ); // If enabled, the player will collide with mobs + static bool getEnableIntraFrameTests ( void ); + static bool getEnableMountRadii ( void ); + static bool getBuildBoxTrees ( void ); + static bool getBuildDebugData ( void ); + static void setBuildDebugData ( bool val ); + + static void setEnableCreatureCollision ( bool enable ); + + static bool getReportStatus ( void ); // Spam that goes to the debug window - # of objects in collision world, profiling + static bool getReportChanges ( void ); // Objects entering/leaving the collision world + static bool getReportMessages ( void ); // Debugging message spam + static bool getReportWarnings ( void ); // Possible errors in the collision system + static bool getReportEvents ( void ); // Game-relevant events - objects colliding + static bool getReportFloating ( void ); // whether objects are floating, when they start/stop floating + static bool getLogLongFrames ( void ); + static bool getIgnoreTerrainLos ( void ); + static bool getGenerateTerrainLos ( void ); + static bool getShoveEnabled ( void ); + + static float getWallEpsilon ( void ); + static float getAreaEpsilon ( void ); + static float getHopHeight ( void ); + static float getTerrainLOSMinDistance ( void ); + static float getTerrainLOSMaxDistance ( void ); + static float getLosUprightScale ( void ); + static float getLosProneScale ( void ); + + // ---------- + + static void setPlayEffectHook ( PlayEffectHook hook ); + static PlayEffectHook getPlayEffectHook ( void ); + + static void setIsPlayerHouseHook ( IsPlayerHouseHook hook ); + static IsPlayerHouseHook getIsPlayerHouseHook ( void ); + + // SpatialSweepAndResolveDefaultMask deals with SpatialDatabase::Query masks + // the default is SpatialDatabase::Q_Static + static int getSpatialSweepAndResolveDefaultMask(); + static void setSpatialSweepAndResolveDefaultMask(int queryMask); + + static bool isSpaceAiLoggingEnabled(); + static bool useOriginalAvoidanceAlgorithm(); + +private: + + ConfigSharedCollision (); + ConfigSharedCollision (const ConfigSharedCollision&); + ConfigSharedCollision& operator= (const ConfigSharedCollision&); +}; + +//=================================================================== + +#endif + + diff --git a/engine/shared/library/sharedCollision/src/shared/core/Contact3d.cpp b/engine/shared/library/sharedCollision/src/shared/core/Contact3d.cpp new file mode 100644 index 00000000..51fdfece --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Contact3d.cpp @@ -0,0 +1,355 @@ +// ====================================================================== +// +// Contact3d.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/Contact3d.h" + +#include "sharedCollision/Intersect3d.h" +#include "sharedCollision/Intersect2d.h" +#include "sharedCollision/Distance2d.h" +#include "sharedCollision/Distance3d.h" +#include "sharedCollision/Containment3d.h" +#include "sharedCollision/Overlap3d.h" + +#include "sharedMath/Circle.h" +#include "sharedMath/Cylinder.h" +#include "sharedMath/Sphere.h" +#include "sharedMath/AxialBox.h" +#include "sharedMath/YawedBox.h" +#include "sharedMath/OrientedBox.h" +#include "sharedMath/Line3d.h" +#include "sharedMath/Torus.h" +#include "sharedMath/ShapeUtils.h" +#include "sharedMath/OrientedCylinder.h" +#include "sharedMath/MultiShape.h" + +Contact Contact::noContact ( false, true, REAL_MAX, Vector::zero, Vector::zero ); +Contact Contact::cantResolve ( false, false, REAL_MAX, Vector::zero, Vector::zero ); + +template +void validateIntersection ( ShapeA const & A, Vector const & V, ShapeB const & B, Range hitTime ) +{ + ShapeA a = A; + ShapeA b = ShapeUtils::translate(A,V); + ShapeA c = ShapeUtils::translate(A,V * hitTime.getMin()); + ShapeA d = ShapeUtils::translate(A,V * hitTime.getMax()); + + ContainmentResult hitA = Containment3d::Test(a,B); + ContainmentResult hitB = Containment3d::Test(b,B); + ContainmentResult hitC = Containment3d::Test(c,B); + ContainmentResult hitD = Containment3d::Test(d,B); + + float distA = Distance3d::DistanceBetween(B,a); + float distB = Distance3d::DistanceBetween(B,b); + float distC = Distance3d::DistanceBetween(B,c); + float distD = Distance3d::DistanceBetween(B,d); + + // If the intersection test worked and the hit times are correct, distA should be positive or small, + // distB should be negative, and distC & distD should be small. + + UNREF(hitA); UNREF(hitB); UNREF(hitC); UNREF(hitD); + UNREF(distA); UNREF(distB); UNREF(distC); UNREF(distD); +} + +// ---------------------------------------------------------------------- + +namespace Contact3d +{ + +// ---------------------------------------------------------------------- + +Contact ContactCirclePoint ( Circle const & A, Vector const & V, Vector const & B ) +{ + Range hitTime = Intersect2d::IntersectPointCircle(B,-V,A); + + if((hitTime.getMin() < 1.0f) && (hitTime.getMax() > 0.0f)) + { + Vector N = (A.getCenter() + V * hitTime.getMin()) - B; + + N.y = 0.0f; + + IGNORE_RETURN( N.normalize() ); + + return Contact( true, true, hitTime.getMin(), B, N ); + } + else + { + return Contact::noContact; + } +} + +Contact ContactCircleCircle ( Circle const & A, Vector const & V, Circle const & B ) +{ + Range hitTime = Intersect2d::Intersect(A,V,B); + + if((hitTime.getMin() < 1.0f) && (hitTime.getMax() > 0.0f)) + { + Vector newCenter = A.getCenter() + V * hitTime.getMin(); + + Vector closestPoint = Distance2d::ClosestPointCircle(newCenter,B); + + Vector N = A.getCenter() - closestPoint; + + N.y = 0.0f; + + IGNORE_RETURN( N.normalize() ); + + return Contact( true, true, hitTime.getMin(), closestPoint, N ); + } + else + { + return Contact::noContact; + } +} + +Contact ContactCircleABox ( Circle const & A, Vector const & V, AxialBox const & B ) +{ + Range hitTime = Intersect2d::Intersect(A,V,B); + + if((hitTime.getMin() < 1.0f) && (hitTime.getMax() > 0.0f)) + { + Vector newCenter = A.getCenter() + V * hitTime.getMin(); + + Vector closestPoint = Distance2d::ClosestPointABox(newCenter,B); + + Vector N = A.getCenter() - closestPoint; + + N.y = 0.0f; + + IGNORE_RETURN( N.normalize() ); + + return Contact( true, true, hitTime.getMin(), closestPoint, N ); + } + else + { + return Contact::noContact; + } +} + +Contact ContactCircleYBox ( Circle const & A, Vector const & V, YawedBox const & B ) +{ + Range hitTime = Intersect2d::Intersect(A,V,B); + + if((hitTime.getMin() < 1.0f) && (hitTime.getMax() > 0.0f)) + { + Vector newCenter = A.getCenter() + V * hitTime.getMin(); + + Vector closestPoint = Distance2d::ClosestPointYBox(newCenter,B); + + Vector N = A.getCenter() - closestPoint; + + N.y = 0.0f; + + IGNORE_RETURN( N.normalize() ); + + return Contact( true, true, hitTime.getMin(), closestPoint, N ); + } + else + { + return Contact::noContact; + } +} + +// ---------------------------------------------------------------------- + +Contact ContactSphereSphere ( Sphere const & A, Vector const & V, Sphere const & B ) +{ + Range hitTime = Intersect3d::Intersect(A,V,B); + + if((hitTime.getMin() < 1.0f) && (hitTime.getMax() > 0.0f)) + { + Sphere newSphere( A.getCenter() + V * hitTime.getMin(), A.getRadius() ); + + Vector closestPoint = Distance3d::ClosestPointSphere(B.getCenter(),newSphere); + + Vector N = closestPoint - B.getCenter(); + + IGNORE_RETURN( N.normalize() ); + + return Contact( true, true, hitTime.getMin(), closestPoint, N ); + } + else + { + return Contact::noContact; + } +} + +// ---------- + +Contact ContactSphereCylinder ( Sphere const & A, Vector const & V, Cylinder const & B ) +{ + Range hitTime = Intersect3d::Intersect(A,V,B); + + if((hitTime.getMin() < 1.0f) && (hitTime.getMax() > 0.0f)) + { + Sphere newSphere = ShapeUtils::translate(A,V*hitTime.getMin()); + + Vector closestPoint = Distance3d::ClosestPointCylinder(newSphere.getCenter(),B); + + Vector N = newSphere.getCenter() - closestPoint; + + IGNORE_RETURN( N.normalize() ); + + return Contact( true, true, hitTime.getMin(), closestPoint, N ); + } + else + { + return Contact::noContact; + } +} + +// ---------- + +Contact ContactSphereOCylinder ( Sphere const & A, Vector const & V, OrientedCylinder const & B ) +{ + Vector localCenter = B.transformToLocal(A.getCenter()); + Vector localVelocity = B.rotateToLocal(V); + + Contact temp = ContactSphereCylinder( Sphere(localCenter,A.getRadius()), localVelocity, B.getLocalShape() ); + + temp.m_point = B.transformToWorld(temp.m_point); + temp.m_normal = B.rotateToWorld(temp.m_normal); + + return temp; +} + +// ---------- + +Contact ContactSphereABox ( Sphere const & A, Vector const & V, AxialBox const & B ) +{ + Range hitTime = Intersect3d::Intersect(A,V,B); + + if((hitTime.getMin() < 1.0f) && (hitTime.getMax() > 0.0f)) + { + Sphere newSphere = ShapeUtils::translate(A,V*hitTime.getMin()); + + Vector closestPoint = Distance3d::ClosestPointABox(newSphere.getCenter(),B); + + Vector N = newSphere.getCenter() - closestPoint; + + IGNORE_RETURN( N.normalize() ); + + return Contact( true, true, hitTime.getMin(), closestPoint, N ); + } + else + { + return Contact::noContact; + } +} + +Contact ContactSphereYBox ( Sphere const & A, Vector const & V, YawedBox const & B ) +{ + Range hitTime = Intersect3d::Intersect(A,V,B); + + if((hitTime.getMin() < 1.0f) && (hitTime.getMax() > 0.0f)) + { + Sphere newSphere = ShapeUtils::translate(A,V*hitTime.getMin()); + + Vector closestPoint = Distance3d::ClosestPointYBox(newSphere.getCenter(),B); + + Vector N = newSphere.getCenter() - closestPoint; + + IGNORE_RETURN( N.normalize() ); + + return Contact( true, true, hitTime.getMin(), closestPoint, N ); + } + else + { + return Contact::noContact; + } +} + + +Contact ContactSphereOBox ( Sphere const & A, Vector const & V, OrientedBox const & B ) +{ + Range hitTime = Intersect3d::Intersect(A,V,B); + + if((hitTime.getMin() < 1.0f) && (hitTime.getMax() > 0.0f)) + { + Sphere newSphere = ShapeUtils::translate(A,V*hitTime.getMin()); + + Vector closestPoint = Distance3d::ClosestPointOBox(newSphere.getCenter(),B); + + Vector N = newSphere.getCenter() - closestPoint; + + IGNORE_RETURN( N.normalize() ); + + return Contact( true, true, hitTime.getMin(), closestPoint, N ); + } + else + { + return Contact::noContact; + } +} + +// ---------------------------------------------------------------------- + +Contact ContactCylinderSphere ( Cylinder const & A, Vector const & V, Sphere const & B ) +{ + Range hitTime = Intersect3d::IntersectSphereCylinder(B,-V,A); + +#if DEBUG_LEVEL == DEBUG_LEVEL_DEBUG + validateIntersection(A,V,B,hitTime); +#endif + + // ---------- + + if((hitTime.getMin() < 1.0f) && (hitTime.getMax() > 0.0f)) + { + Cylinder newCylinder( A.getBase() + V * hitTime.getMin(), A.getRadius(), A.getHeight() ); + + Vector closestPoint = Distance3d::ClosestPointCylinder(B.getCenter(),newCylinder); + + Vector N = closestPoint - B.getCenter(); + + IGNORE_RETURN( N.normalize() ); + + return Contact( true, true, hitTime.getMin(), closestPoint, N ); + } + else + { + return Contact::noContact; + } +} + +// ---------------------------------------------------------------------- + +Contact ContactMultiShapes ( MultiShape const & A, Vector const & V, MultiShape const & B ) +{ + MultiShape::ShapeType typeA = A.getShapeType(); + MultiShape::ShapeType typeB = B.getShapeType(); + + // ---------- + + if(typeA == MultiShape::MST_Sphere) + { + Sphere sphereA = A.getSphere(); + + if (typeB == MultiShape::MST_Sphere) { return FindContact( sphereA, V, B.getSphere() ); } + else if(typeB == MultiShape::MST_Cylinder) { return FindContact( sphereA, V, B.getCylinder() ); } + else if(typeB == MultiShape::MST_OrientedCylinder) { return FindContact( sphereA, V, B.getOrientedCylinder() ); } + else if(typeB == MultiShape::MST_AxialBox) { return FindContact( sphereA, V, B.getAxialBox() ); } + else if(typeB == MultiShape::MST_YawedBox) { return FindContact( sphereA, V, B.getYawedBox() ); } + else if(typeB == MultiShape::MST_OrientedBox) { return FindContact( sphereA, V, B.getOrientedBox() ); } + else { return Contact::cantResolve; } + } + else if(typeA == MultiShape::MST_Cylinder) + { + Cylinder cylA = A.getCylinder(); + + if (typeB == MultiShape::MST_Sphere) { return FindContact( cylA, V, B.getSphere() ); } + else { return Contact::cantResolve; } + } + else + { + return Contact::cantResolve; + } +} + +// ---------------------------------------------------------------------- + +} // namespace Contact3d diff --git a/engine/shared/library/sharedCollision/src/shared/core/Contact3d.h b/engine/shared/library/sharedCollision/src/shared/core/Contact3d.h new file mode 100644 index 00000000..2d4d7236 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Contact3d.h @@ -0,0 +1,138 @@ +// ====================================================================== +// +// Contact3d.h +// copyright (c) 2001 Sony Online Entertainment +// +// Given two primitives and a motion step, compute their initial point +// of contact, the time of contact, and the contact normal +// +// ====================================================================== + +#ifndef INCLUDED_Contact3d_H +#define INCLUDED_Contact3d_H + +#include "sharedMath/Vector.h" + +class Vector; +class Triangle3d; +class Segment3d; +class Plane3d; +class Ribbon3d; +class Line3d; +class Ray3d; +class Range; +class AxialBox; +class Sphere; +class OrientedBox; +class Ring; +class Cylinder; +class OrientedCylinder; +class Circle; +class YawedBox; +class MultiShape; +class BaseExtent; +class CollisionSurface; +class CellProperty; + +// ---------- + +class Contact +{ +public: + + Contact( bool newExists, + bool newResolvable, + float newTime, + Vector newPoint, + Vector newNormal ) + : m_exists(newExists), + m_resolvable(newResolvable), + m_time(newTime), + m_point(newPoint), + m_normal(newNormal), + m_surfaceId1(-1), + m_surfaceId2(-1), + m_cell(NULL), + m_extent(NULL), + m_surface(NULL) + { + } + + Contact() + { + } + + static Contact noContact; + static Contact cantResolve; + + // ---------- + + bool m_exists; + bool m_resolvable; + + float m_time; + Vector m_point; + Vector m_normal; + + int m_surfaceId1; + int m_surfaceId2; + + CellProperty const * m_cell; + BaseExtent const * m_extent; + CollisionSurface const * m_surface; +}; + +// ---------- + +namespace Contact3d +{ + +// ---------------------------------------------------------------------- + +Contact ContactCirclePoint ( Circle const & A, Vector const & V, Vector const & B ); +Contact ContactCircleCircle ( Circle const & A, Vector const & V, Circle const & B ); +Contact ContactCircleABox ( Circle const & A, Vector const & V, AxialBox const & B ); +Contact ContactCircleYBox ( Circle const & A, Vector const & V, YawedBox const & B ); + +// ---------- + +Contact ContactSphereSphere ( Sphere const & A, Vector const & V, Sphere const & B ); +Contact ContactSphereCylinder ( Sphere const & A, Vector const & V, Cylinder const & B ); +Contact ContactSphereOCylinder ( Sphere const & A, Vector const & V, OrientedCylinder const & B ); +Contact ContactSphereABox ( Sphere const & A, Vector const & V, AxialBox const & B ); +Contact ContactSphereYBox ( Sphere const & A, Vector const & V, YawedBox const & B ); +Contact ContactSphereOBox ( Sphere const & A, Vector const & V, OrientedBox const & B ); + +// ---------- + +Contact ContactCylinderSphere ( Cylinder const & A, Vector const & V, Sphere const & B ); + +// ---------- + +Contact ContactMultiShapes ( MultiShape const & A, Vector const & V, MultiShape const & B ); + +// ---------- +// Generics + +inline Contact FindContact ( Circle const & A, Vector const & V, Vector const & B ) { return ContactCirclePoint(A,V,B); } +inline Contact FindContact ( Circle const & A, Vector const & V, Circle const & B ) { return ContactCircleCircle(A,V,B); } +inline Contact FindContact ( Circle const & A, Vector const & V, AxialBox const & B ) { return ContactCircleABox(A,V,B); } +inline Contact FindContact ( Circle const & A, Vector const & V, YawedBox const & B ) { return ContactCircleYBox(A,V,B); } + +inline Contact FindContact ( Sphere const & A, Vector const & V, Sphere const & B ) { return ContactSphereSphere(A,V,B); } +inline Contact FindContact ( Sphere const & A, Vector const & V, Cylinder const & B ) { return ContactSphereCylinder(A,V,B); } +inline Contact FindContact ( Sphere const & A, Vector const & V, OrientedCylinder const & B ) { return ContactSphereOCylinder(A,V,B); } +inline Contact FindContact ( Sphere const & A, Vector const & V, AxialBox const & B ) { return ContactSphereABox(A,V,B); } +inline Contact FindContact ( Sphere const & A, Vector const & V, YawedBox const & B ) { return ContactSphereYBox(A,V,B); } +inline Contact FindContact ( Sphere const & A, Vector const & V, OrientedBox const & B ) { return ContactSphereOBox(A,V,B); } + +inline Contact FindContact ( Cylinder const & A, Vector const & V, Sphere const & B ) { return ContactCylinderSphere(A,V,B); } + +inline Contact FindContact ( MultiShape const & A, Vector const & V, MultiShape const & B ) { return ContactMultiShapes(A,V,B); } + +// ---------------------------------------------------------------------- + +} // namespace Contact3d + +#endif // #ifndef INCLUDED_Contact3d_H + diff --git a/engine/shared/library/sharedCollision/src/shared/core/ContactPoint.cpp b/engine/shared/library/sharedCollision/src/shared/core/ContactPoint.cpp new file mode 100644 index 00000000..304a14b7 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/ContactPoint.cpp @@ -0,0 +1,133 @@ +// ====================================================================== +// +// ContactPoint.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/ContactPoint.h" + +#include "sharedFile/Iff.h" + +#include "sharedCollision/CollisionSurface.h" +#include "sharedCollision/CollisionUtils.h" + +#include "sharedMath/Transform.h" + +// ---------------------------------------------------------------------- + +ContactPoint::ContactPoint() +: m_pSurface( NULL ), + m_position( Vector::zero ), + m_offset( 0.0f ), + m_hitId( -1 ) +{ +} + +ContactPoint::ContactPoint( CollisionSurface const * surface, Vector const & point, int id, float offset ) +: m_pSurface(surface), + m_position(point), + m_offset(offset), + m_hitId(id) +{ +} + +ContactPoint::ContactPoint ( ContactPoint const & locCopy ) +: m_pSurface(locCopy.m_pSurface), //lint !e1554 + m_position(locCopy.m_position), + m_offset(locCopy.m_offset), + m_hitId(locCopy.m_hitId) +{ +} + +// ---------------------------------------------------------------------- + +bool ContactPoint::isAttached ( void ) const +{ + return (m_hitId != -1) && (m_pSurface != NULL); +} + +// ---------------------------------------------------------------------- + +Vector const & ContactPoint::getPosition_l ( void ) const +{ + return m_position; +} + +// ---------- + +void ContactPoint::setPosition_l ( Vector const & newPos ) +{ + NAN_CHECK(newPos); + + m_position = newPos; +} + +// ---------------------------------------------------------------------- + +real ContactPoint::getOffset ( void ) const +{ + return m_offset; +} + +void ContactPoint::setOffset ( float newOffset ) +{ + NAN_CHECK(newOffset); + + m_offset = newOffset; +} + +// ---------- + +Vector ContactPoint::getOffsetPosition_l ( void ) const +{ + return m_position + Vector(0.0f,m_offset,0.0f); +} + +// ---------- + +int ContactPoint::getId ( void ) const +{ + return m_hitId; +} + +void ContactPoint::setId ( int newId ) +{ + m_hitId = newId; +} + +// ---------- + +CollisionSurface const * ContactPoint::getSurface ( void ) const +{ + return m_pSurface; +} + +void ContactPoint::setSurface ( CollisionSurface const * pSurf ) +{ + m_pSurface = pSurf; +} + +// ---------------------------------------------------------------------- + +void ContactPoint::read_0000 ( Iff & iff ) +{ + m_position = iff.read_floatVector(); + m_offset = iff.read_float(); + m_pSurface = NULL; + m_hitId = iff.read_int32(); + + NAN_CHECK(m_position); + NAN_CHECK(m_offset); +} + +// ---------- + +void ContactPoint::write ( Iff & iff ) const +{ + iff.insertChunkData(m_position); + iff.insertChunkData(m_offset); + iff.insertChunkData(m_hitId); +} + diff --git a/engine/shared/library/sharedCollision/src/shared/core/ContactPoint.h b/engine/shared/library/sharedCollision/src/shared/core/ContactPoint.h new file mode 100644 index 00000000..7e0d7fef --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/ContactPoint.h @@ -0,0 +1,68 @@ +// ====================================================================== +// +// ContactPoint.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_ContactPoint_H +#define INCLUDED_ContactPoint_H + +#include "sharedMath/Vector.h" + +class CollisionSurface; +class Transform; +class Iff; + +// ====================================================================== + +class ContactPoint +{ +public: + + ContactPoint(); + + ContactPoint( CollisionSurface const * surface, Vector const & point, int id, float offset ); + + ContactPoint( ContactPoint const & locCopy ); + + // ---------- + + Vector const & getPosition_l ( void ) const; + void setPosition_l ( Vector const & newPosition ); + + real getOffset ( void ) const; + void setOffset ( float offset ); + + Vector getOffsetPosition_l ( void ) const; + + int getId ( void ) const; + void setId ( int newId ); + + CollisionSurface const * getSurface ( void ) const; + void setSurface ( CollisionSurface const * pFloor ); + + // ---------- + + bool isAttached ( void ) const; + + // ---------- + + void read_0000 ( Iff & iff ); + void write ( Iff & iff ) const; + +protected: + + CollisionSurface const * m_pSurface; // The collision surface this contact point is on + + Vector m_position; // Position of the contact point in surface space + real m_offset; // Surface offset (need more explanation) + int m_hitId; // Hit identifier, which indicates what part of the collision surface + // this contact point is on. For collision meshes, the hit identifier is + // the index of the hit triangle. +}; + +// ====================================================================== + +#endif // #ifndef INCLUDED ContactPoint_H + diff --git a/engine/shared/library/sharedCollision/src/shared/core/Containment1d.cpp b/engine/shared/library/sharedCollision/src/shared/core/Containment1d.cpp new file mode 100644 index 00000000..55c3ba9c --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Containment1d.cpp @@ -0,0 +1,165 @@ +// ====================================================================== +// +// Containment1d.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/Containment1d.h" + +#include "sharedMath/Range.h" + +#include + +namespace Containment1d +{ + +// ---------------------------------------------------------------------- + +ContainmentResult TestFloatLess ( float f, float v ) +{ + if(f < v) { return CR_Inside; } + else if(f == v) { return CR_Boundary; } //lint !e777 // testing floats for equality + else { return CR_Outside; } +} + +// ---------- + +ContainmentResult TestFloatGreater ( float f, float v ) +{ + if(f > v) { return CR_Inside; } + else if(f == v) { return CR_Boundary; } //lint !e777 // testing floats for equality + else { return CR_Outside; } +} + +// ---------- + +ContainmentResult TestFloatRange ( float f, Range const & R ) +{ + if(f < R.getMax()) + { + if(f > R.getMin()) { return CR_Inside; } + else if(f == R.getMin()) { return CR_Boundary; } + else { return CR_Outside; } + } + else if (f == R.getMax()) + { + return CR_Boundary; + } + else + { + return CR_Outside; + } +} + +// ---------------------------------------------------------------------- + +ContainmentResult TestRangeLess ( Range const & R, float v ) +{ + if(R.getMin() > v) + { + return CR_Outside; + } + else if(R.getMin() == v) + { + if(R.getMax() == v) { return CR_Boundary; } + else { return CR_TouchingOutside; } + } + else + { + if(R.getMax() > v) { return CR_Overlap; } + else if (R.getMax() == v) { return CR_TouchingInside; } + else { return CR_Inside; } + } +} + +// ---------- + +ContainmentResult TestRangeGreater ( Range const & R, float v ) +{ + if(R.getMax() < v) + { + return CR_Outside; + } + else if(R.getMax() == v) + { + if(R.getMin() == v) { return CR_Boundary; } + else { return CR_TouchingOutside; } + } + else + { + if(R.getMin() < v) { return CR_Overlap; } + else if (R.getMin() == v) { return CR_TouchingInside; } + else { return CR_Inside; } + } +} + +// ---------- + +ContainmentResult TestRangeRange ( Range const & A, Range const & B ) +{ + real const & aMin = A.getMin(); + real const & aMax = A.getMax(); + + real const & bMin = B.getMin(); + real const & bMax = B.getMax(); + + if(aMax < bMin) + { + return CR_Outside; + } + else if(aMax == bMin) + { + if(aMin < bMin) { return CR_TouchingOutside; } + else if(aMin == bMin) { return CR_Boundary; } + else { DEBUG_FATAL(true,("Containment3d::TestRangeRange - Range A is inverted\n")); return CR_Outside; } + } + else + { + if(aMax < bMax) + { + if(aMin < bMin) { return CR_Overlap; } + else if(aMin == bMin) { return CR_TouchingInside; } + else { return CR_Inside; } + } + else if(aMax == bMax) + { + if(aMin < bMin) { return CR_Overlap; } + else if(aMin == bMin) { return CR_TouchingInside; } + else { return CR_TouchingInside; } + } + else + { + if(aMin < bMax) { return CR_Overlap; } + else if(aMin == bMax) { return CR_TouchingOutside; } + else { return CR_Outside; } + } + } +} + +// ---------------------------------------------------------------------- + +Range EncloseRanges ( Range const & A, Range const & B ) +{ + return Range::enclose(A,B); +} + +// ---------- + +Range EncloseRanges ( Range const & A, Range const & B, Range const & C ) +{ + return Range::enclose(A,B,C); +} + +// ---------- + +Range EncloseRanges ( Range const & A, Range const & B, Range const & C, Range const & D ) +{ + return Range::enclose(A,B,C,D); +} + +// ---------------------------------------------------------------------- + +} // namespace Containment1d + diff --git a/engine/shared/library/sharedCollision/src/shared/core/Containment1d.h b/engine/shared/library/sharedCollision/src/shared/core/Containment1d.h new file mode 100644 index 00000000..d766ecaf --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Containment1d.h @@ -0,0 +1,41 @@ +// ====================================================================== +// +// Containment1d.h +// copyright (c) 2001 Sony Online Entertainment +// +// Containment tests, bounding volume enclosure functions +// +// ---------------------------------------------------------------------- + +#ifndef INCLUDED_Containment1d_H +#define INCLUDED_Containment1d_H + +#include "sharedCollision/CollisionEnums.h" + +class Range; + +namespace Containment1d +{ + +// ---------------------------------------------------------------------- + +ContainmentResult TestFloatLess ( float f, float v ); +ContainmentResult TestFloatGreater ( float f, float v ); +ContainmentResult TestFloatRange ( float f, Range const & R ); + +ContainmentResult TestRangeLess ( Range const & R, float v ); +ContainmentResult TestRangeGreater ( Range const & R, float v ); +ContainmentResult TestRangeRange ( Range const & A, Range const & B ); + +// ---------- + +Range EncloseRanges ( Range const & A, Range const & B ); +Range EncloseRanges ( Range const & A, Range const & B, Range const & C ); +Range EncloseRanges ( Range const & A, Range const & B, Range const & C, Range const & D ); + +// ---------------------------------------------------------------------- + +}; // namespace Containment1d + +#endif + diff --git a/engine/shared/library/sharedCollision/src/shared/core/Containment2d.cpp b/engine/shared/library/sharedCollision/src/shared/core/Containment2d.cpp new file mode 100644 index 00000000..08dc2738 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Containment2d.cpp @@ -0,0 +1,148 @@ +// ====================================================================== +// +// Containment2d.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/Containment2d.h" + +#include "sharedCollision/Collision2d.h" +#include "sharedCollision/Containment1d.h" +#include "sharedCollision/Distance2d.h" +#include "sharedCollision/Distance3d.h" + +#include "sharedMath/Range.h" +#include "sharedMath/AxialBox.h" +#include "sharedMath/YawedBox.h" +#include "sharedMath/Circle.h" +#include "sharedMath/Vector2d.h" +#include "sharedMath/Line3d.h" +#include "sharedMath/Segment3d.h" + +#include "sharedMath/Transform.h" + +using namespace Collision2d; + +namespace Containment2d +{ + +// ---------------------------------------------------------------------- +// 2d test - returns true if the test point is on the right side of the +// line + +ContainmentResult TestPointLine ( Vector2d const & P, Vector2d const & B, Vector2d const & D ) +{ + float d = ((P.x - B.x) * D.y) + ((P.y - B.y) * -D.x); + + return Containment1d::TestFloatGreater(d,0.0f); +} + +ContainmentResult TestPointLine ( Vector const & P, Line3d const & L ) +{ + Vector const & B = L.getPoint(); + Vector const & N = L.getNormal(); + + float d = ((P.x - B.x) * N.z) + ((P.z - B.z) * -N.x); + + return Containment1d::TestFloatGreater(d,0.0f); +} + +ContainmentResult TestPointSeg ( Vector const & P, Segment3d const & S ) +{ + Vector const & B = S.getBegin(); + Vector const & E = S.getEnd(); + + float d = ((P.x - B.x) * (E.z - B.z)) + ((P.z - B.z) * -(E.x - B.x)); + + return Containment1d::TestFloatGreater(d,0.0f); +} + +ContainmentResult TestPointSeg ( Vector const & P, Vector const & B, Vector const & E ) +{ + float d = ((P.x - B.x) * (E.z - B.z)) + ((P.z - B.z) * -(E.x - B.x)); + + return Containment1d::TestFloatGreater(d,0.0f); +} + +// ---------------------------------------------------------------------- + +ContainmentResult TestPointABox ( Vector const & V, AxialBox const & B ) +{ + ContainmentResult testX = Containment1d::TestFloatRange( V.x, B.getRangeX() ); + ContainmentResult testZ = Containment1d::TestFloatRange( V.z, B.getRangeZ() ); + + return Containment::ComposeAxisTests(testX,testZ); +} + +ContainmentResult TestPointCircle ( Vector const & V, Circle const & C ) +{ + real radiusSquared = C.getRadius() * C.getRadius(); + + real distanceSquared = Distance2d::Distance2PointPoint(V,C.getCenter()); + + return Containment1d::TestFloatLess(distanceSquared,radiusSquared); +} + +// ---------------------------------------------------------------------- + +ContainmentResult TestCircleABox ( Circle const & C, AxialBox const & B ) +{ + ContainmentResult test = TestPointABox( C.getCenter(), B ); + + if(test != CR_Outside) + { + // Center point is inside the box + + ContainmentResult testX = Containment1d::TestRangeRange( C.getRangeX(), B.getRangeX() ); + ContainmentResult testZ = Containment1d::TestRangeRange( C.getRangeZ(), B.getRangeZ() ); + + return Containment::ComposeAxisTests(testX,testZ); + } + else + { + // Center point is outside the box + + Vector V = Distance3d::ClosestPointABox(C.getCenter(),B); + + float dist = Distance2d::DistancePointPoint(V,C.getCenter()); + + ContainmentResult result = Containment1d::TestFloatLess( dist, C.getRadius() ); + + if(result == CR_Outside) return CR_Outside; + else if(result == CR_Boundary) return CR_TouchingOutside; + else return CR_Overlap; + } +} + +// ---------------------------------------------------------------------- +// To test a circle against a yawed box, transform the circle to box-space +// and test against the axial box + +ContainmentResult TestCircleYBox ( Circle const & C, YawedBox const & B ) +{ + Transform T = B.getTransform_l2p(); + + Vector localCenter = T.rotateTranslate_p2l(C.getCenter()); + + AxialBox localBox = B.getLocalShape(); + + Circle localCircle(localCenter,C.getRadius()); + + return TestCircleABox( localCircle, localBox ); +}; + +// ---------------------------------------------------------------------- + +ContainmentResult TestCircleCircle ( Circle const & A, Circle const & B ) +{ + real dist = Distance2d::DistancePointPoint(A.getCenter(),B.getCenter()); + + return Containment1d::TestRangeRange( A.getLocalRangeX() + dist, B.getLocalRangeX() ); +} + +// ---------------------------------------------------------------------- + +} // namespace Containment2d + diff --git a/engine/shared/library/sharedCollision/src/shared/core/Containment2d.h b/engine/shared/library/sharedCollision/src/shared/core/Containment2d.h new file mode 100644 index 00000000..f4d802eb --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Containment2d.h @@ -0,0 +1,58 @@ +// ====================================================================== +// +// Containment2d.h +// copyright (c) 2001 Sony Online Entertainment +// +// Containment tests, bounding volume enclosure functions +// +// ---------------------------------------------------------------------- + +#ifndef INCLUDED_Containment2d_H +#define INCLUDED_Containment2d_H + +class AxialBox; +class YawedBox; +class Sphere; +class Circle; +class Vector; +class Vector2d; +class Line3d; +class Segment3d; + +#include "sharedCollision/CollisionEnums.h" + +namespace Containment2d +{ + +// ---------------------------------------------------------------------- +// Point-vs-line + +// The current Line2d is not in point-normal form, so this takes a point and direction instead + +ContainmentResult TestPointLine ( Vector2d const & P, Vector2d const & B, Vector2d const & D ); + +ContainmentResult TestPointLine ( Vector const & P, Line3d const & L ); + +ContainmentResult TestPointSeg ( Vector const & P, Segment3d const & S ); + +ContainmentResult TestPointSeg ( Vector const & P, Vector const & B, Vector const & E ); + +// ---------- +// Point-vs-volume + +ContainmentResult TestPointABox ( Vector const & V, AxialBox const & B ); +ContainmentResult TestPointCircle ( Vector const & V, Circle const & C ); + +// ---------- +// Volume-vs-volume + +ContainmentResult TestCircleABox ( Circle const & C, AxialBox const & B ); +ContainmentResult TestCircleYBox ( Circle const & C, YawedBox const & B ); +ContainmentResult TestCircleCircle ( Circle const & A, Circle const & B ); + +// ---------------------------------------------------------------------- + +}; // namespace Containment2d + +#endif + diff --git a/engine/shared/library/sharedCollision/src/shared/core/Containment3d.cpp b/engine/shared/library/sharedCollision/src/shared/core/Containment3d.cpp new file mode 100644 index 00000000..70ea0f20 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Containment3d.cpp @@ -0,0 +1,818 @@ +// ====================================================================== +// +// Containment3d.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/Containment3d.h" + +#include "sharedCollision/Containment2d.h" +#include "sharedCollision/Distance3d.h" +#include "sharedCollision/Containment1d.h" + +#include "sharedMath/Range.h" +#include "sharedMath/Vector.h" +#include "sharedMath/Triangle3d.h" +#include "sharedMath/Plane3d.h" +#include "sharedMath/Circle.h" +#include "sharedMath/Sphere.h" +#include "sharedMath/Cylinder.h" +#include "sharedMath/OrientedCylinder.h" +#include "sharedMath/AxialBox.h" +#include "sharedMath/YawedBox.h" +#include "sharedMath/OrientedBox.h" +#include "sharedMath/Transform.h" +#include "sharedMath/Torus.h" +#include "sharedMath/MultiShape.h" +#include "sharedMath/Segment3d.h" + +#include + +namespace Containment3d +{ + +// ---------------------------------------------------------------------- +// Point-vs-planar + +ContainmentResult TestPointPlane ( Vector const & A, + Plane3d const & P ) +{ + Vector D = A - P.getPoint(); + + float t = D.dot(P.getNormal()); + + bool bInside = t < 0.0f; + + return bInside ? CR_Inside : CR_Outside; +} + +// ---------- + +ContainmentResult TestPointTri ( Vector const & P, Triangle3d const & T ) +{ + return TestPointPlane(P, T.getPlane() ); +} + +// ---------------------------------------------------------------------- +// Planar-vs-planar + +ContainmentResult TestTriTri ( Triangle3d const & A, + Triangle3d const & B ) +{ + ContainmentResult sideA = TestPointTri( A.getCornerA(), B ); + ContainmentResult sideB = TestPointTri( A.getCornerB(), B ); + ContainmentResult sideC = TestPointTri( A.getCornerC(), B ); + + if((sideA != sideB) || (sideB != sideC)) + { + return CR_Overlap; + } + + return sideA; +} + +// ---------------------------------------------------------------------- +// Point-vs-volume + +ContainmentResult TestPointSphere ( Vector const & V, + Sphere const & S ) +{ + real radiusSquared = S.getRadius() * S.getRadius(); + + real distanceSquared = (V-S.getCenter()).magnitudeSquared(); + + return Containment1d::TestFloatLess(distanceSquared,radiusSquared); +} + +// ---------- + +ContainmentResult TestPointCylinder ( Vector const & V, + Cylinder const & C ) +{ + Vector delta = V - C.getBase(); + delta.y = 0; + + real dist = delta.magnitude(); + real radius = C.getRadius(); + + ContainmentResult rTest = Containment1d::TestFloatLess(dist,radius); + ContainmentResult vTest = Containment1d::TestFloatRange(V.y, C.getRangeY()); + + // ---------- + + return Containment::ComposeAxisTests(rTest,vTest); +} + +// ---------- + +ContainmentResult TestPointABox ( Vector const & V, AxialBox const & B ) +{ + ContainmentResult testX = Containment1d::TestFloatRange( V.x, B.getRangeX() ); + ContainmentResult testY = Containment1d::TestFloatRange( V.y, B.getRangeY() ); + ContainmentResult testZ = Containment1d::TestFloatRange( V.z, B.getRangeZ() ); + + return Containment::ComposeAxisTests( testX, testY, testZ ); +} + +// ---------- + +ContainmentResult TestPointYBox ( Vector const & V, YawedBox const & B ) +{ + Vector localV = B.transformToLocal(V); + + return Test(localV,B.getLocalShape()); +} + +// ---------- + +ContainmentResult TestPointOBox ( Vector const & V, OrientedBox const & B ) +{ + Vector localV = B.transformToLocal(V); + + return Test(localV,B.getLocalShape()); +} + +// ---------- +//@todo - Is there a version of this that doesn't need a square root? + +ContainmentResult TestPointTorus ( Vector const & V, Torus const & T ) +{ + Vector L = T.transformToLocal(V); + + float r = sqrt( L.x * L.x + L.z * L.z ) - T.getMajorRadius(); + + float d = (r * r) + (L.y * L.y); + + float m = T.getMinorRadius() * T.getMinorRadius(); + + return Containment1d::TestFloatLess(d,m); +} + +// ---------- + +ContainmentResult TestPointShape ( Vector const & A, MultiShape const & B ) +{ + MultiShape::ShapeType typeB = B.getShapeType(); + + // ---------- + + if (typeB == MultiShape::MST_Sphere) { return Test( A, B.getSphere() ); } + else if(typeB == MultiShape::MST_Cylinder) { return Test( A, B.getCylinder() ); } + else if(typeB == MultiShape::MST_AxialBox) { return Test( A, B.getAxialBox() ); } + else if(typeB == MultiShape::MST_YawedBox) { return Test( A, B.getYawedBox() ); } + else if(typeB == MultiShape::MST_OrientedBox) { return Test( A, B.getOrientedBox() ); } + else { return CR_Outside; } +} + +// ---------------------------------------------------------------------- +// Volume-vs-volume, spheres + +ContainmentResult TestSphereSphere ( Sphere const & A, + Sphere const & B ) +{ + // ---------- + // do a quick outside test first + + Vector delta = B.getCenter() - A.getCenter(); + + real dist2 = delta.magnitudeSquared(); + + real sumRadius = A.getRadius() + B.getRadius(); + real sumRadius2 = sumRadius * sumRadius; + + if(dist2 > sumRadius2) + { + return CR_Outside; + } + else if(dist2 == sumRadius2) //lint !e777 // testing floats for equality + { + return CR_TouchingOutside; + } + else + { + // ---------- + // and if that fails do the full test + + real dist = sqrt(dist2); + + Range AD( dist - A.getRadius(), dist + A.getRadius() ); + Range BD( -B.getRadius(), B.getRadius() ); + + return Containment1d::TestRangeRange( AD, BD ); + } +} + +// ---------- + +ContainmentResult TestSphereCylinder ( Sphere const & S, + Cylinder const & C ) +{ + Vector delta = C.getBase() - S.getCenter(); + delta.y = 0; + + real dist = delta.magnitude(); + + Range SD( dist - S.getRadius(), dist + S.getRadius() ); + + Range CD( -C.getRadius(), C.getRadius() ); + + ContainmentResult hTest = Containment1d::TestRangeRange( SD, CD ); + ContainmentResult hTest2 = Containment1d::TestFloatRange( dist, CD ); + + ContainmentResult vTest = Containment1d::TestRangeRange( S.getRangeY(), C.getRangeY() ); + ContainmentResult cTest = Containment1d::TestFloatRange( S.getCenter().y, C.getRangeY() ); + + // ---------- + + if(hTest == CR_Outside) + { + // Sphere can't possibly touch the cylinder + + return CR_Outside; + } + else if((hTest == CR_TouchingOutside) || (hTest == CR_Boundary)) + { + // Sphere is touching the outside of the cylinder's tube if its + // center is inside the vertical range of the cylinder + + if((cTest == CR_Inside) || (cTest == CR_Boundary)) + { + return CR_TouchingOutside; + } + else + { + return CR_Outside; + } + } + else if((hTest == CR_Inside) || (hTest == CR_TouchingInside)) + { + // Sphere is in the tube of the cylinder. It touches the cylinder + // if its vertical range touches the vertical range of the cylinder + + return Containment::ComposeAxisTests(hTest,vTest); + } + else + { + // hTest == CR_Overlap + + if(vTest == CR_Outside) + { + return CR_Outside; + } + else if((vTest == CR_Inside) || (vTest == CR_TouchingInside)) + { + return CR_Overlap; + } + else if (vTest == CR_Boundary) + { + // This really shouldn't be happening + return CR_Boundary; + } + else if (vTest == CR_TouchingOutside) + { + if(hTest2 == CR_Inside) + { + // Sphere is touching a cap of the cylinder + return CR_TouchingOutside; + } + else if(hTest2 == CR_Boundary) + { + // Sphere is touching the edge of the cap of the cylinder + return CR_TouchingOutside; + } + else + { + // Sphere isn't touching the cap + return CR_Outside; + } + } + else + { + // vTest == CR_Overlap + + if((cTest == CR_Inside) || (cTest == CR_Boundary)) + { + // The ranges overlap vertically and horizontally, and the center of + // the sphere is inside the vertical range - the sphere overlaps the + // cylinder + + return CR_Overlap; + } + else + { + // The sphere is inside both ranges, but its center is outside both + // ranges. The sphere overlaps the cylinder if the closest point + // on the cylinder is inside the sphere. + + Vector closestPoint = Distance3d::ClosestPointCylinder(S.getCenter(),C); + + ContainmentResult result = TestPointSphere(closestPoint,S); + + if(result == CR_Outside) + { + return CR_Outside; + } + else if(result == CR_Boundary) + { + return CR_TouchingOutside; + } + else + { + return CR_Overlap; + } + } + } + } +} + +ContainmentResult TestSphereOCylinder ( Sphere const & A, OrientedCylinder const & B ) +{ + Vector localCenter = B.transformToLocal(A.getCenter()); + + Sphere localSphere(localCenter,A.getRadius()); + + return Test( localSphere, B.getLocalShape() ); +} + +//@todo - HACK - these need to be made correct + +ContainmentResult TestSphereABox ( Sphere const & S, AxialBox const & B ) +{ + if(Test(S.getCenter(),B) == CR_Inside) + { + return CR_Overlap; + } + else + { + Vector closest = Distance3d::ClosestPointABox( S.getCenter(), B ); + + if(TestPointSphere(closest,S) == CR_Inside) + { + return CR_Overlap; + } + else + { + return CR_Outside; + } + } +} + +ContainmentResult TestSphereYBox ( Sphere const & S, YawedBox const & B ) +{ + if(Test(S.getCenter(),B) == CR_Inside) + { + return CR_Overlap; + } + else + { + Vector closest = Distance3d::ClosestPointYBox( S.getCenter(), B ); + + if(TestPointSphere(closest,S) == CR_Inside) + { + return CR_Overlap; + } + else + { + return CR_Outside; + } + } +} + +ContainmentResult TestSphereOBox ( Sphere const & S, OrientedBox const & B ) +{ + if(Test(S.getCenter(),B) == CR_Inside) + { + return CR_Overlap; + } + else + { + Vector closest = Distance3d::ClosestPointOBox( S.getCenter(), B ); + + if(TestPointSphere(closest,S) == CR_Inside) + { + return CR_Overlap; + } + else + { + return CR_Outside; + } + } +} + +// ---------------------------------------------------------------------- +// Volume-vs-volume, cylinders + +ContainmentResult TestCylinderSphere ( Cylinder const & A, Sphere const & B ) +{ + return TestSphereCylinder(B,A); +} + +ContainmentResult TestCylinderCylinder ( Cylinder const & A, + Cylinder const & B ) +{ + Vector delta = B.getBase() - A.getBase(); + delta.y = 0; + + real dist = delta.magnitude(); + + Range AD( dist - A.getRadius(), dist + A.getRadius() ); + Range BD( -B.getRadius(), B.getRadius() ); + + ContainmentResult hTest = Containment1d::TestRangeRange( AD, BD ); + ContainmentResult vTest = Containment1d::TestRangeRange( A.getRangeY(), B.getRangeY() ); + + // ---------- + + return Containment::ComposeAxisTests(hTest,vTest); +} + +// ---------- + +ContainmentResult TestCylinderABox ( Cylinder const & C, + AxialBox const & B ) +{ + ContainmentResult test2d = Containment2d::TestCircleABox(C.getBaseCircle(),B); + + ContainmentResult testY = Containment1d::TestRangeRange( C.getRangeY(), B.getRangeY() ); + + return Containment::ComposeAxisTests( test2d, testY ); +} + +// ---------- + +ContainmentResult TestCylinderYBox ( Cylinder const & C, + YawedBox const & B ) +{ + ContainmentResult test2d = Containment2d::TestCircleYBox(C.getBaseCircle(),B); + + ContainmentResult testY = Containment1d::TestRangeRange( C.getRangeY(), B.getRangeY() ); + + return Containment::ComposeAxisTests( test2d, testY ); + +} + +// ---------------------------------------------------------------------- + +ContainmentResult TestABoxABox ( AxialBox const & A, + AxialBox const & B ) +{ + ContainmentResult testX = Containment1d::TestRangeRange( A.getRangeX(), B.getRangeX() ); + ContainmentResult testY = Containment1d::TestRangeRange( A.getRangeY(), B.getRangeY() ); + ContainmentResult testZ = Containment1d::TestRangeRange( A.getRangeZ(), B.getRangeZ() ); + + return Containment::ComposeAxisTests( testX, testY, testZ ); +} + +// ---------------------------------------------------------------------- + +ContainmentResult TestMultiShapes ( MultiShape const & A, + MultiShape const & B ) +{ + MultiShape::ShapeType typeA = A.getShapeType(); + MultiShape::ShapeType typeB = B.getShapeType(); + + // ---------- + + if(typeA == MultiShape::MST_Cylinder) + { + Cylinder cylA = A.getCylinder(); + + if (typeB == MultiShape::MST_Sphere) { return Test( cylA, B.getSphere() ); } + else if(typeB == MultiShape::MST_Cylinder) { return Test( cylA, B.getCylinder() ); } + else if(typeB == MultiShape::MST_YawedBox) { return Test( cylA, B.getYawedBox() ); } + else if(typeB == MultiShape::MST_AxialBox) { return Test( cylA, B.getAxialBox() ); } + else { return CR_Outside; } + } + else if(typeB == MultiShape::MST_Cylinder) + { + Cylinder cylB = B.getCylinder(); + + if (typeA == MultiShape::MST_Sphere) { return Test( A.getSphere(), cylB ); } + else if(typeA == MultiShape::MST_AxialBox) { return Test( A.getAxialBox(), cylB ); } + else if(typeA == MultiShape::MST_YawedBox) { return Test( A.getYawedBox(), cylB ); } + else { return CR_Outside; } + } + else if(typeA == MultiShape::MST_Sphere) + { + Sphere sphereA = A.getSphere(); + + if (typeB == MultiShape::MST_Sphere) { return Test( sphereA, B.getSphere() ); } + else if(typeB == MultiShape::MST_Cylinder) { return Test( sphereA, B.getCylinder() ); } + else if(typeB == MultiShape::MST_OrientedCylinder) { return Test( sphereA, B.getOrientedCylinder() ); } + else if(typeB == MultiShape::MST_AxialBox) { return Test( sphereA, B.getAxialBox() ); } + else if(typeB == MultiShape::MST_YawedBox) { return Test( sphereA, B.getYawedBox() ); } + else if(typeB == MultiShape::MST_OrientedBox) { return Test( sphereA, B.getOrientedBox() ); } + else { return CR_Outside; } + } + else if(typeB == MultiShape::MST_Sphere) + { + Sphere sphereB = B.getSphere(); + + if (typeB == MultiShape::MST_Sphere) { return Test( A.getSphere(), sphereB ); } + else if(typeB == MultiShape::MST_Cylinder) { return Test( A.getCylinder(), sphereB ); } + else if(typeB == MultiShape::MST_OrientedCylinder) { return Test( A.getOrientedCylinder(), sphereB ); } + else if(typeB == MultiShape::MST_AxialBox) { return Test( A.getAxialBox(), sphereB ); } + else if(typeB == MultiShape::MST_YawedBox) { return Test( A.getYawedBox(), sphereB ); } + else if(typeB == MultiShape::MST_OrientedBox) { return Test( A.getOrientedBox(), sphereB ); } + else { return CR_Outside; } + } + else + { + return CR_Outside; + } +} + + + + + + + + +// ---------------------------------------------------------------------- +// Point-vs-planar with epsilon + +ContainmentResult TestPointPlane ( Vector const & A, + Plane3d const & P, + real epsilon ) +{ + DEBUG_FATAL(!P.isNormalized(),("Containment3d::TestPointPlane (epsilon) - Got a non-normalized plane\n")); + + real dist = (A - P.getPoint()).dot(P.getNormal()); + + if(WithinEpsilonInclusive(dist,0.0f,epsilon)) return CR_Boundary; + + return (dist < 0) ? CR_Inside : CR_Outside; +} + +// ---------- + +ContainmentResult TestPointTri ( Vector const & P, + Triangle3d const & T, + real epsilon ) +{ + return TestPointPlane( P, T.getPlane(), epsilon ); +} + +// ---------------------------------------------------------------------- +// Planar-vs-planar with epsilon + +ContainmentResult TestTriPlane ( Triangle3d const & T, + Plane3d const & P, + real epsilon ) +{ + ContainmentResult testA = TestPointPlane( T.getCornerA(), P, epsilon ); + ContainmentResult testB = TestPointPlane( T.getCornerB(), P, epsilon ); + ContainmentResult testC = TestPointPlane( T.getCornerC(), P, epsilon ); + + // If all corners are in the same region, the triangle is in + // that region also. + + if((testA == testB) && (testB == testC)) + { + return testA; + } + + bool hasInside = (testA == CR_Inside) || (testB == CR_Inside) || (testC == CR_Inside); + bool hasOutside = (testA == CR_Outside) || (testB == CR_Outside) || (testC == CR_Outside); + bool hasBoundary = (testA == CR_Boundary) || (testB == CR_Boundary) || (testC == CR_Boundary); + + if(hasInside && hasOutside) return CR_Overlap; + if(hasInside && hasBoundary) return CR_TouchingInside; + if(hasOutside && hasBoundary) return CR_TouchingOutside; + + DEBUG_FATAL(true,("Containment3d::TestTriPlane (epsilon) - Should never get here\n")); + + return CR_Overlap; +} + +// ---------- + +ContainmentResult TestTriTri ( Triangle3d const & A, + Triangle3d const & B, + real epsilon ) +{ + return TestTriPlane( A, B.getPlane(), epsilon ); +} + + + + + + + + + + + +// ====================================================================== +// Enclosure functions + +AxialBox EncloseABox ( VectorVector const & points ) +{ + AxialBox temp; + + for(uint i = 0; i < points.size(); i++) + { + temp.add( points[i] ); + } + + return temp; +} + +AxialBox EncloseABox ( Sphere const & sphere ) +{ + float r = sphere.getRadius(); + Vector c = sphere.getCenter(); + + Vector d(r,r,r); + + return AxialBox( c + d, c - d ); +} + +AxialBox EncloseABox ( Segment3d const & S ) +{ + return AxialBox( S.getBegin(), S.getEnd() ); +} + +AxialBox EncloseABox ( Cylinder const & C ) +{ + Vector center = C.getCenter(); + + Vector extent( C.getExtentX(), C.getExtentY(), C.getExtentZ() ); + + return AxialBox(center-extent,center+extent); +} + +AxialBox EncloseABox ( AxialBox const & A, AxialBox const & B ) +{ + AxialBox temp = A; + + temp.add(B); + + return temp; +} + +// ---------------------------------------------------------------------- + +Sphere EncloseSphere ( Sphere const & shape ) +{ + return shape; +} + +Sphere EncloseSphere ( Cylinder const & shape ) +{ + float x = shape.getExtentX(); + float y = shape.getExtentY(); + + float radius = sqrt( x*x + y*y ); + + return Sphere( shape.getCenter(), radius ); +} + +Sphere EncloseSphere ( OrientedCylinder const & shape ) +{ + float x = shape.getExtentX(); + float y = shape.getExtentY(); + + float radius = sqrt( x*x + y*y ); + + return Sphere( shape.getCenter(), radius ); +} + +Sphere EncloseSphere ( AxialBox const & shape ) +{ + float x = shape.getExtentX(); + float y = shape.getExtentY(); + float z = shape.getExtentZ(); + + float radius = sqrt( x*x + y*y + z*z ); + + return Sphere( shape.getCenter(), radius ); +} + +Sphere EncloseSphere ( YawedBox const & shape ) +{ + float x = shape.getExtentX(); + float y = shape.getExtentY(); + float z = shape.getExtentZ(); + + float radius = sqrt( x*x + y*y + z*z ); + + return Sphere( shape.getCenter(), radius ); +} + +Sphere EncloseSphere ( OrientedBox const & shape ) +{ + float x = shape.getExtentX(); + float y = shape.getExtentY(); + float z = shape.getExtentZ(); + + float radius = sqrt( x*x + y*y + z*z ); + + return Sphere( shape.getCenter(), radius ); +} + +// ---------------------------------------------------------------------- + +Sphere EncloseSphere ( Vector const & A, Vector const & B ) +{ + Vector center = (B+A) / 2.0f; + + real radius = (B-A).magnitude() / 2.0f; + + return Sphere(center,radius); +} + +// ---------- + +Sphere EncloseSphere ( Sphere const & S, Vector const & V ) +{ + if( Containment::isContainment(TestPointSphere(V,S)) ) return S; + + Vector normal = V - S.getCenter(); + IGNORE_RETURN( normal.normalize() ); + + Vector pointS = S.getCenter() - normal * S.getRadius(); + + Vector center = Vector::midpoint(V,pointS); + + real radius = (V - pointS).magnitude() / 2.0f; + + return Sphere(center,radius); +} + +// ---------- + +Sphere EncloseSphere ( Sphere const & A, Sphere const & B ) +{ + if( Containment::isContainment(TestSphereSphere(A,B)) ) return B; + if( Containment::isContainment(TestSphereSphere(B,A)) ) return A; + + // ---------- + + Vector normal = B.getCenter() - A.getCenter(); + + IGNORE_RETURN( normal.normalize() ); + + Vector pointA = A.getCenter() - normal * A.getRadius(); + Vector pointB = B.getCenter() + normal * B.getRadius(); + + Vector center = Vector::midpoint(pointA,pointB); + + real radius = (pointB - pointA).magnitude() / 2.0f; + + return Sphere(center,radius); +} + +// ---------- + +Sphere EncloseSphere ( VectorVector const & points ) +{ + if(points.size() == 0) + { + return Sphere::zero; + } + else if(points.size() == 1) + { + return Sphere( points[0], 0.0f ); + } + + // ---------- + + Sphere sphere = EncloseSphere(points[0],points[1]); + + for(uint i = 2; i < points.size(); i++) + { + sphere = EncloseSphere(sphere,points[i]); + } + + return sphere; +} + +// ---------------------------------------------------------------------- + +bool ClipSphere ( Sphere const & A, Sphere const & B, Sphere & out ) +{ + float distance = A.getCenter().magnitudeBetween(B.getCenter()); + + float radius = B.getRadius() - distance; + + if(radius < 0.0) + { + return false; + } + else + { + out = Sphere(A.getCenter(),radius); + + return true; + } +} + +// ---------------------------------------------------------------------- + +} // namespace Containment3d + diff --git a/engine/shared/library/sharedCollision/src/shared/core/Containment3d.h b/engine/shared/library/sharedCollision/src/shared/core/Containment3d.h new file mode 100644 index 00000000..12fb0a38 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Containment3d.h @@ -0,0 +1,202 @@ +// ====================================================================== +// +// Containment3d.h +// copyright (c) 2001 Sony Online Entertainment +// +// Containment tests, bounding volume enclosure functions +// +// ---------------------------------------------------------------------- + +#ifndef INCLUDED_Containment3d_H +#define INCLUDED_Containment3d_H + +class Vector; +class Triangle3d; +class Plane3d; +class Segment3d; + +class Sphere; +class Cylinder; +class OrientedCylinder; +class AxialBox; +class YawedBox; +class OrientedBox; + +class Torus; +class MultiShape; + +#include "sharedCollision/CollisionEnums.h" + +typedef stdvector::fwd VectorVector; + +// ---------- +// These methods should always be of the form + +// ContainmentResult TestShapeBoundary ( Shape const & S, Boundary const & B ); +// ContainmentResult TestShapeBoundary ( Shape const & S, Boundary const & B, real epsilon ); + +namespace Containment3d +{ + +// ---------------------------------------------------------------------- +// Static non-epsilon containment tests - These are faster but may give +// inconsistent results when the test shape touches or lies on the boundary + +// ---------- +// Point-vs-planar - containment means the point lies "behind" the plane + +ContainmentResult TestPointPlane ( Vector const & V, Plane3d const & P ); +ContainmentResult TestPointTri ( Vector const & V, Triangle3d const & T ); + +// ---------- +// Planar-vs-planar - containment means the object lies "behind" the plane + +ContainmentResult TestTriTri ( Triangle3d const & A, Triangle3d const & B ); + +// ---------- +// Point-vs-volume + +ContainmentResult TestPointSphere ( Vector const & A, Sphere const & B ); +ContainmentResult TestPointCylinder ( Vector const & A, Cylinder const & B ); +ContainmentResult TestPointABox ( Vector const & A, AxialBox const & B ); +ContainmentResult TestPointYBox ( Vector const & A, YawedBox const & B ); +ContainmentResult TestPointOBox ( Vector const & A, OrientedBox const & B ); +ContainmentResult TestPointTorus ( Vector const & A, Torus const & B ); +ContainmentResult TestPointShape ( Vector const & A, MultiShape const & B ); + +// ---------- +// Volume-vs-volume + +ContainmentResult TestSphereSphere ( Sphere const & A, Sphere const & B ); +ContainmentResult TestSphereCylinder ( Sphere const & A, Cylinder const & B ); +ContainmentResult TestSphereOCylinder ( Sphere const & A, OrientedCylinder const & B ); +ContainmentResult TestSphereABox ( Sphere const & A, AxialBox const & B ); +ContainmentResult TestSphereYBox ( Sphere const & A, YawedBox const & B ); +ContainmentResult TestSphereOBox ( Sphere const & A, OrientedBox const & B ); + + + +ContainmentResult TestCylinderSphere ( Cylinder const & A, Sphere const & B ); +ContainmentResult TestCylinderCylinder ( Cylinder const & A, Cylinder const & B ); +ContainmentResult TestCylinderABox ( Cylinder const & A, AxialBox const & B); +ContainmentResult TestCylinderYBox ( Cylinder const & A, YawedBox const & B ); + +ContainmentResult TestABoxABox ( AxialBox const & A, AxialBox const & B ); + +ContainmentResult TestMultiShapes ( MultiShape const & A, MultiShape const & B ); + +// ---------- +// Aliases for convenience + +inline ContainmentResult Test ( Vector const & A, Plane3d const & B ) { return TestPointPlane(A,B); } +inline ContainmentResult Test ( Vector const & A, Triangle3d const & B ) { return TestPointTri(A,B); } + +inline ContainmentResult Test ( Triangle3d const & A, Triangle3d const & B ) { return TestTriTri(A,B); } + +// ---------- + +inline ContainmentResult Test ( Vector const & A, Sphere const & B ) { return TestPointSphere(A,B); } +inline ContainmentResult Test ( Vector const & A, Cylinder const & B ) { return TestPointCylinder(A,B); } +inline ContainmentResult Test ( Vector const & A, AxialBox const & B ) { return TestPointABox(A,B); } +inline ContainmentResult Test ( Vector const & A, YawedBox const & B ) { return TestPointYBox(A,B); } +inline ContainmentResult Test ( Vector const & A, OrientedBox const & B ) { return TestPointOBox(A,B); } +inline ContainmentResult Test ( Vector const & A, MultiShape const & B ) { return TestPointShape(A,B); } + +// ---------- + +inline ContainmentResult Test ( Sphere const & A, Sphere const & B ) { return TestSphereSphere(A,B); } +inline ContainmentResult Test ( Sphere const & A, Cylinder const & B ) { return TestSphereCylinder(A,B); } +inline ContainmentResult Test ( Sphere const & A, OrientedCylinder const & B ) { return TestSphereOCylinder(A,B); } +inline ContainmentResult Test ( Sphere const & A, AxialBox const & B ) { return TestSphereABox(A,B); } +inline ContainmentResult Test ( Sphere const & A, YawedBox const & B ) { return TestSphereYBox(A,B); } +inline ContainmentResult Test ( Sphere const & A, OrientedBox const & B ) { return TestSphereOBox(A,B); } + +// ---------- + +inline ContainmentResult Test ( Cylinder const & A, Sphere const & B ) { return TestCylinderSphere(A,B); } +inline ContainmentResult Test ( Cylinder const & A, Cylinder const & B ) { return TestCylinderCylinder(A,B); } +inline ContainmentResult Test ( Cylinder const & A, AxialBox const & B ) { return TestCylinderABox(A,B); } +inline ContainmentResult Test ( Cylinder const & A, YawedBox const & B ) { return TestCylinderYBox(A,B); } + +// ---------- + +inline ContainmentResult Test ( AxialBox const & A, AxialBox const & B ) { return TestABoxABox(A,B); } + +// ---------- + +inline ContainmentResult Test ( MultiShape const & A, MultiShape const & B ) { return TestMultiShapes(A,B); } + +// ---------- +// these we're fudging a bit by running the test in the opposite order +// (which isn't technically correct) + +inline ContainmentResult Test ( AxialBox const & A, Cylinder const & B ) { return Test(B,A); } +inline ContainmentResult Test ( YawedBox const & A, Cylinder const & B ) { return Test(B,A); } + +inline ContainmentResult Test ( AxialBox const & A, Sphere const & B ) { return Test(B,A); } +inline ContainmentResult Test ( YawedBox const & A, Sphere const & B ) { return Test(B,A); } +inline ContainmentResult Test ( OrientedBox const & A, Sphere const & B ) { return Test(B,A); } +inline ContainmentResult Test ( OrientedCylinder const & A, Sphere const & B ) { return Test(B,A); } + + +// ---------------------------------------------------------------------- +// Epsilon tests - These require more computation but handle boundary +// conditions correctly. Since we can't make assumptions about what +// a "good" epsilon would be, users must pass in their epsilon. + +ContainmentResult TestPointPlane ( Vector const & V, + Plane3d const & P, + real epsilon ); + +ContainmentResult TestPointTri ( Vector const & V, + Triangle3d const & T, + real epsilon ); + +ContainmentResult TestTriPlane ( Triangle3d const & T, + Plane3d const & P, + real epsilon ); + +ContainmentResult TestTriTri ( Triangle3d const & A, + Triangle3d const & B, + real epsilon ); + +// ---------------------------------------------------------------------- +// Enclosure functions + +// These should be of the form + +// Bounds EncloseBounds ( Primitive const & A, Primitive const & B, ... ) + +AxialBox EncloseABox ( VectorVector const & points ); +AxialBox EncloseABox ( Sphere const & S ); +AxialBox EncloseABox ( Segment3d const & S ); +AxialBox EncloseABox ( Cylinder const & S ); + +AxialBox EncloseABox ( AxialBox const & A, AxialBox const & B ); + +Sphere EncloseSphere ( Sphere const & shape ); +Sphere EncloseSphere ( Cylinder const & shape ); +Sphere EncloseSphere ( OrientedCylinder const & shape ); +Sphere EncloseSphere ( AxialBox const & shape ); +Sphere EncloseSphere ( YawedBox const & shape ); +Sphere EncloseSphere ( OrientedBox const & shape ); + +Sphere EncloseSphere ( Vector const & A, Vector const & B ); +Sphere EncloseSphere ( Sphere const & S, Vector const & V ); +Sphere EncloseSphere ( Sphere const & A, Sphere const & B ); +Sphere EncloseSphere ( VectorVector const & points ); + +// ---------------------------------------------------------------------- +// Clip functions - shrink/clip A to fit inside B, or return false if we can't + +// These should be of the form + +// bool ClipBounds ( Clippee const & A, Clipper const & B, Clippee & out ); + +bool ClipSphere ( Sphere const & A, Sphere const & B, Sphere & out ); + +// ---------------------------------------------------------------------- + +}; // namespace Containment3d + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/Distance2d.cpp b/engine/shared/library/sharedCollision/src/shared/core/Distance2d.cpp new file mode 100644 index 00000000..8174e7c4 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Distance2d.cpp @@ -0,0 +1,276 @@ +// ====================================================================== +// +// Distance2d.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/Distance2d.h" + +#include "sharedCollision/CollisionUtils.h" +#include "sharedCollision/Containment2d.h" +#include "sharedCollision/Collision2d.h" + +#include "sharedMath/Vector.h" +#include "sharedMath/AxialBox.h" +#include "sharedMath/YawedBox.h" +#include "sharedMath/Quadratic.h" +#include "sharedMath/Circle.h" +#include "sharedMath/Ring.h" +#include "sharedMath/Line3d.h" +#include "sharedMath/Ray3d.h" +#include "sharedMath/Segment3d.h" +#include "sharedMath/Range.h" +#include "sharedMath/Sphere.h" +#include "sharedMath/Cylinder.h" + +namespace Distance2d +{ + +// ---------------------------------------------------------------------- +// Static 2d distance + +float DistancePointPoint ( Vector const & A, Vector const & B ) +{ + Vector delta(B.x - A.x, 0.0f, B.z - A.z); + + return delta.magnitude(); +} + +float DistancePointLine ( Vector const & V, Line3d const & L ) +{ + Vector C = ClosestPointLine(V,L); + + return Distance2d::DistancePointPoint(V,C); +} + +float DistancePointRay ( Vector const & V, Ray3d const & R ) +{ + Vector C = ClosestPointRay(V,R); + + return Distance2d::DistancePointPoint(V,C); +} + +float DistancePointSeg ( Vector const & V, Segment3d const & S ) +{ + Vector C = ClosestPointSeg(V,S); + + return Distance2d::DistancePointPoint(V,C); +} + +float DistancePointSeg ( Vector const & V, Vector const & begin, Vector const & end ) +{ + return Distance2d::DistancePointSeg( V, Segment3d(begin,end) ); +} + +float DistancePointSphere ( Vector const & V, Sphere const & S ) +{ + float dist = Distance2d::DistancePointPoint(V,S.getCenter()); + + return dist - S.getRadius(); +} + +float DistancePointCylinder ( Vector const & V, Cylinder const & C ) +{ + float dist = Distance2d::DistancePointPoint(V,C.getBase()); + + return dist - C.getRadius(); +} + +// ---------- +// Static 2d distance squared + +float Distance2PointPoint ( Vector const & A, Vector const & B ) +{ + Vector delta(B.x - A.x, 0.0f, B.z - A.z); + + return delta.magnitudeSquared(); +} + +float Distance2PointLine ( Vector const & V, Line3d const & L ) +{ + Vector C = ClosestPointLine(V,L); + + return Distance2d::Distance2PointPoint(V,C); +} + +float Distance2PointRay ( Vector const & V, Ray3d const & R ) +{ + Vector C = ClosestPointRay(V,R); + + return Distance2d::Distance2PointPoint(V,C); +} + +float Distance2PointSeg ( Vector const & V, Segment3d const & S ) +{ + Vector C = ClosestPointSeg(V,S); + + return Distance2d::Distance2PointPoint(V,C); +} + +// ---------------------------------------------------------------------- + +bool ClosestParamLine ( Vector const & V, Line3d const & L, float & outParam ) +{ + Vector A = Collision2d::flatten(V); + Vector P = Collision2d::flatten(L.getPoint()); + Vector D = Collision2d::flatten(L.getNormal()); + + float mag2 = D.magnitudeSquared(); + + if(mag2 > 0.0f) + { + outParam = D.dot(A-P) / mag2; + + return true; + } + else + { + return false; + } +} + +Vector ClosestPointLine ( Vector const & V, Line3d const & L ) +{ + float t(0); + + if(ClosestParamLine(V,L,t)) + { + return L.atParam(t); + } + else + { + return L.getPoint(); + } +} + +Vector ClosestPointRay ( Vector const & V, Ray3d const & R ) +{ + float t(0); + + if(ClosestParamLine(V,R.getLine(),t)) + { + return R.atParam(Range::plusInf.clamp(t)); + } + else + { + return R.getPoint(); + } +} + +Vector ClosestPointSeg ( Vector const & V, Segment3d const & S ) +{ + float t(0); + + if(ClosestParamLine(V,S.getLine(),t)) + { + return S.atParam(Range::unit.clamp(t)); + } + else + { + return S.getBegin(); + } +} + +Vector ClosestPointABox ( Vector const & V, AxialBox const & B ) +{ + Vector temp; + + Vector min = B.getMin(); + Vector max = B.getMax(); + + temp.x = clamp( min.x, V.x, max.x ); + temp.y = 0.0f; + temp.z = clamp( min.z, V.z, max.z ); + + return temp; +} + +Vector ClosestPointYBox ( Vector const & V, YawedBox const & B ) +{ + Vector lV = B.transformToLocal(V); + + AxialBox lB = B.getLocalShape(); + + Vector lC = ClosestPointABox(lV,lB); + + Vector out = B.transformToWorld(lC); + + return out; +} + +Vector ClosestPointCircle ( Vector const & V, Circle const & C ) +{ + if(Containment2d::TestPointCircle(V,C) == CR_Inside) + { + return V; + } + + Vector D = V - C.getCenter(); + + D.y = 0.0f; + + IGNORE_RETURN( D.normalize() ); + + D *= C.getRadius(); + + D += C.getCenter(); + + return D; +} + +Vector ClosestPointRing ( Vector const & V, Ring const & R ) +{ + Vector D = V - R.getCenter(); + + D.y = 0.0f; + + IGNORE_RETURN( D.normalize() ); + + D *= R.getRadius(); + + D += R.getCenter(); + + return D; +} + +// ---------------------------------------------------------------------- + +Quadratic DistancePointPoint ( Vector const & A2, Vector const & V2, Vector const & B2 ) +{ + Vector A(A2.x,0.0f,A2.z); + Vector V(V2.x,0.0f,V2.z); + Vector B(B2.x,0.0f,B2.z); + + Vector D = (B-A); + + real a = V.magnitudeSquared(); + real b = -2.0f * D.dot(V); + real c = D.magnitudeSquared(); + + return Quadratic(a,b,c); +} + +// ---------------------------------------------------------------------- + +float ProjectAxis ( Line3d const & L, Vector const & V ) +{ + return Collision2d::ComponentAlong( V - L.getPoint(), L.getNormal() ); +} + +Range ProjectAxis ( Line3d const & L, Circle const & C ) +{ + Vector N = L.getNormal(); + N.y = 0.0f; + + real d = C.getRadius() / N.magnitude(); + + real c = ProjectAxis( L, C.getCenter() ); + + return Range( c - d, c + d ); +} + +// ---------------------------------------------------------------------- + +} // namespace Distance2d diff --git a/engine/shared/library/sharedCollision/src/shared/core/Distance2d.h b/engine/shared/library/sharedCollision/src/shared/core/Distance2d.h new file mode 100644 index 00000000..ef1b79fd --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Distance2d.h @@ -0,0 +1,89 @@ +// ====================================================================== +// +// Distance2d.h +// copyright (c) 2001 Sony Online Entertainment +// +// Containment tests, bounding volume enclosure functions +// +// ---------------------------------------------------------------------- + +#ifndef INCLUDED_Distance2d_H +#define INCLUDED_Distance2d_H + +#include "sharedCollision/CollisionEnums.h" + +class Vector; +class AxialBox; +class Quadratic; +class YawedBox; +class Circle; +class Line3d; +class Ray3d; +class Range; +class Segment3d; +class Sphere; +class Cylinder; + +// ---------------------------------------------------------------------- + +namespace Distance2d +{ + +// ---------- +// Static 2d distance + +float DistancePointPoint ( Vector const & A, Vector const & B ); +float DistancePointLine ( Vector const & V, Line3d const & L ); +float DistancePointRay ( Vector const & V, Ray3d const & R ); +float DistancePointSeg ( Vector const & V, Segment3d const & S ); + +float DistancePointSeg ( Vector const & V, Vector const & begin, Vector const & end ); + +float DistancePointSphere ( Vector const & V, Sphere const & S ); +float DistancePointCylinder ( Vector const & V, Cylinder const & C ); + +// Static 2d distance squared + +float Distance2PointPoint ( Vector const & V, Vector const & B ); +float Distance2PointLine ( Vector const & V, Line3d const & L ); +float Distance2PointRay ( Vector const & V, Ray3d const & R ); +float Distance2PointSeg ( Vector const & V, Segment3d const & S ); + +float Distance2PointSeg ( Vector const & V, Vector const & begin, Vector const & end ); + + +inline float Distance ( Vector const & A, Vector const & B ) { return DistancePointPoint(A,B); } + +inline float Distance2 ( Vector const & A, Vector const & B ) { return Distance2PointPoint(A,B); } + +// ---------- +// Dynamic distance (returns distance squared as a function of time) + +Quadratic DistancePointPoint ( Vector const & A, + Vector const & V, + Vector const & B ); + +// ---------- + +float ClosestParamLine ( Vector const & V, Line3d const & L ); + +Vector ClosestPointLine ( Vector const & V, Line3d const & L ); +Vector ClosestPointRay ( Vector const & V, Ray3d const & R ); +Vector ClosestPointSeg ( Vector const & V, Segment3d const & S ); + +Vector ClosestPointCircle ( Vector const & V, Circle const & C ); +Vector ClosestPointLine ( Vector const & V, Line3d const & L ); +Vector ClosestPointABox ( Vector const & V, AxialBox const & B ); +Vector ClosestPointYBox ( Vector const & V, YawedBox const & B ); + +float ProjectAxis ( Line3d const & L, Vector const & V ); +Range ProjectAxis ( Line3d const & L, Circle const & C ); + +// ---------- + +}; // namespace Distance2d + +// ---------------------------------------------------------------------- + +#endif // #ifndef INCLUDED_Distance2d_H + diff --git a/engine/shared/library/sharedCollision/src/shared/core/Distance3d.cpp b/engine/shared/library/sharedCollision/src/shared/core/Distance3d.cpp new file mode 100644 index 00000000..5a32dfe1 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Distance3d.cpp @@ -0,0 +1,821 @@ +// ====================================================================== +// +// Distance3d.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/Distance3d.h" + +#include "sharedCollision/Collision2d.h" +#include "sharedCollision/CollisionUtils.h" +#include "sharedCollision/Containment1d.h" +#include "sharedCollision/Containment3d.h" +#include "sharedCollision/Intersect2d.h" +#include "sharedCollision/Intersect3d.h" +#include "sharedCollision/Overlap3d.h" + +#include "sharedMath/AxialBox.h" +#include "sharedMath/Cylinder.h" +#include "sharedMath/Line3d.h" +#include "sharedMath/MultiShape.h" +#include "sharedMath/OrientedBox.h" +#include "sharedMath/OrientedCircle.h" +#include "sharedMath/OrientedCylinder.h" +#include "sharedMath/Plane3d.h" +#include "sharedMath/Quadratic.h" +#include "sharedMath/Ray3d.h" +#include "sharedMath/Segment3d.h" +#include "sharedMath/Sphere.h" +#include "sharedMath/Triangle3d.h" +#include "sharedMath/Vector.h" +#include "sharedMath/YawedBox.h" + +#include +#include + +using namespace Intersect3d; + +namespace Distance3d +{ + +// ---------- + +Vector selectCloser ( Vector const & P, Vector const & A, Vector const & B ) +{ + real dist1 = (P-A).magnitudeSquared(); + real dist2 = (P-B).magnitudeSquared(); + + if(dist1 < dist2) + { + return A; + } + else + { + return B; + } +} + +// ====================================================================== +// Closest distance computation + +real DistancePointPoint ( Vector const & A, Vector const & B ) +{ + return (B-A).magnitude(); +} + +// ---------------------------------------------------------------------- + +real DistancePointLine ( Vector const & V, Line3d const & L ) +{ + Vector closestPoint = ClosestPointLine(V,L); + + real dist = (closestPoint - V).magnitude(); + + return dist; +} + +// ---------- + +real DistancePointSeg ( Vector const & V, Segment3d const & S ) +{ + Vector closestPoint = ClosestPointSeg(V,S); + + real dist = (closestPoint - V).magnitude(); + + return dist; +} + +// ---------------------------------------------------------------------- + +real DistancePointPlane ( Vector const & V, Plane3d const & P ) +{ + return (V - P.getPoint()).dot(P.getNormal()); +} + +// ---------- + +real DistancePointTri ( Vector const & V, Triangle3d const & T ) +{ + Vector P = ClosestPointTri(V,T); + + return DistancePointPoint(P,V); +} + +// ---------- + +real DistancePointPoly ( Vector const & V, VertexList const & vertices ) +{ + Vector P = ClosestPointPoly(V,vertices); + + return DistancePointPoint(P,V); +} + +// ---------------------------------------------------------------------- + +float DistancePointSphere ( Vector const & V, Sphere const & S ) +{ + float dist = DistancePointPoint(V,S.getCenter()) - S.getRadius(); + + if(dist < 0.0f) dist = 0.0f; + + return dist; +} + +// ---------- + +real DistancePointCylinder ( Vector const & V, Cylinder const & C ) +{ + Vector close = ClosestPointCylinder(V,C); + + return DistancePointPoint(V,close); +} + +// ---------- + +real DistancePointABox ( Vector const & V, AxialBox const & B ) +{ + Vector close = ClosestPointABox(V,B); + + return DistancePointPoint(V,close); +} + +// ---------- + +real DistancePointYBox ( Vector const & V, YawedBox const & B ) +{ + Vector close = ClosestPointYBox(V,B); + + return DistancePointPoint(V,close); +} + +// ---------- + +real DistancePointOBox ( Vector const & V, OrientedBox const & B ) +{ + Vector close = ClosestPointOBox(V,B); + + return DistancePointPoint(V,close); +} + +// ---------------------------------------------------------------------- + +float DistancePointShape ( Vector const & V, MultiShape const & S ) +{ + MultiShape::ShapeType type = S.getShapeType(); + + // ---------- + + if (type == MultiShape::MST_Sphere) { return DistanceBetween( V, S.getSphere() ); } + else if(type == MultiShape::MST_Cylinder) { return DistanceBetween( V, S.getCylinder() ); } + else if(type == MultiShape::MST_AxialBox) { return DistanceBetween( V, S.getAxialBox() ); } + else if(type == MultiShape::MST_YawedBox) { return DistanceBetween( V, S.getYawedBox() ); } + else if(type == MultiShape::MST_OrientedBox) { return DistanceBetween( V, S.getOrientedBox() ); } + else { return REAL_MAX; } +} +// ---------------------------------------------------------------------- + +real DistanceLineLine ( Line3d const & A, Line3d const & B ) +{ + Vector D = B.getPoint() - A.getPoint(); + + Vector N = A.getNormal().cross(B.getNormal()); + + IGNORE_RETURN( N.normalize() ); + + float dist = static_cast(fabs( D.dot(N) )); + + return dist; +} + +// ---------------------------------------------------------------------- + +real DistanceSphereCylinder ( Sphere const & S, Cylinder const & C ) +{ + float dist = DistancePointCylinder(S.getCenter(),C); + + dist -= S.getRadius(); + + return dist; +} + +// ====================================================================== + +real Distance2PointPoint ( Vector const & A, Vector const & B ) +{ + return (B-A).magnitudeSquared(); +} + +// ---------- + +real Distance2PointSeg ( Vector const & V, Segment3d const & S ) +{ + Vector closestPoint = ClosestPointSeg(V,S); + + real dist2 = (closestPoint - V).magnitudeSquared(); + + return dist2; +} + +// ---------- + +real Distance2PointPoly ( Vector const & V, VertexList const & vertices ) +{ + Vector P = ClosestPointPoly(V,vertices); + + return Distance2PointPoint(P,V); +} + +// ---------- + +real Distance2PointABox ( Vector const & V, AxialBox const & B ) +{ + Vector close = ClosestPointABox(V,B); + + return Distance2PointPoint(V,close); +} + +// ====================================================================== +// returns the parametric coordinates for the closest pair on A and B + +// The derivation for this is a bit long - express the distance function +// F in terms of s and t, find the min by solving for where the partial +// derivatives of F are both 0. The math collapses down a lot. + +bool ClosestPairLineLine ( Line3d const & A, Line3d const & B, float & outA, float & outB ) +{ + Vector D = B.getPoint() - A.getPoint(); + + Vector AN = A.getNormal(); + Vector BN = B.getNormal(); + + float aa = AN.dot(AN); + float ab = AN.dot(BN); + float bb = BN.dot(BN); + + float ad = AN.dot(D); + float bd = BN.dot(D); + + Matrix2 M( aa, -ab, -ab, bb ); + + Vector2d O(0.0f,0.0f); + + if(M.solveFor(Vector2d(ad,-bd),O)) + { + outA = O.x; + outB = O.y; + + return true; + } + else + { + return false; + } +} + +// ---------------------------------------------------------------------- + +Vector ClosestPointLine ( Vector const & V, Line3d const & L ) +{ + Vector B = L.getPoint(); + Vector N = L.getNormal(); + + float n2 = N.magnitudeSquared(); + + Vector C = V - B; + + real t = C.dot(N) / n2; + + return B + N * t; +} + +// ---------- + +Vector ClosestPointRay ( Vector const & V, Ray3d const & R ) +{ + Vector B = R.getPoint(); + Vector N = R.getNormal(); + + float n2 = N.magnitudeSquared(); + + Vector C = V - B; + + float t = std::max( 0.0f, C.dot(N) / n2 ); + + return B + N * t; +} + +// ---------- + +Vector ClosestPointSeg ( Vector const & V, Segment3d const & S ) +{ + Vector B = S.getBegin(); + Vector N = S.getDelta(); + + float n2 = N.magnitudeSquared(); + + Vector C = V - B; + + float t = clamp( 0.0f, C.dot(N) / n2, 1.0f ); + + return B + N * t; +} + +// ---------- + +Vector ClosestPointPlane ( Vector const & V, Plane3d const & P ) +{ + Vector point; + real param; + + Line3d line(V,P.getNormal()); + + bool hit = IntersectLinePlaneUnsided( line, P, point, param ); + + if(!hit) + { + DEBUG_FATAL(true,("ClosestPointPlane - Can't intersect line with plane")); + + return P.getPoint(); + } + + return point; +} + +// ---------- + +Vector ClosestPointTri ( Vector const & V, Triangle3d const & T ) +{ + { + Line3d L(V,T.getNormal()); + + Vector point; + float param; + + if(IntersectLineTriUnsided(L,T,point,param)) + { + return point; + } + } + + Vector edgeA = ClosestPointSeg(V,T.getEdgeSegment0()); + Vector edgeB = ClosestPointSeg(V,T.getEdgeSegment1()); + Vector edgeC = ClosestPointSeg(V,T.getEdgeSegment2()); + + Vector out = edgeA; + + out = selectCloser(V,out,edgeB); + out = selectCloser(V,out,edgeC); + + return out; +} + +// ---------- + +Vector ClosestPointSphere ( Vector const & V, Sphere const & S ) +{ + Vector delta = V - S.getCenter(); + + IGNORE_RETURN( delta.normalize() ); + + delta *= S.getRadius(); + + Vector point = S.getCenter() + delta; + + return point; +} + +// ---------- + +Vector ClosestPointCylinder ( Vector const & V, Cylinder const & C ) +{ + Vector delta = V - C.getBase(); + + delta.y = 0.0f; + + real dist = delta.magnitude(); + + Vector point = V; + + // clamp the x-z coords of the point so they're inside the tube + + IGNORE_RETURN( delta.normalize() ); + delta *= std::min( C.getRadius(), dist ); + + point = C.getBase() + delta; + + // and clamp the y coord so it's inside also + + real min = C.getBase().y; + real max = min + C.getHeight(); + + point.y = clamp( min, V.y, max ); + + return point; +} + +// ---------- + +Vector ClosestPointABox ( Vector const & V, AxialBox const & B ) +{ + Vector temp; + + Vector min = B.getMin(); + Vector max = B.getMax(); + + temp.x = clamp( min.x, V.x, max.x ); + temp.y = clamp( min.y, V.y, max.y ); + temp.z = clamp( min.z, V.z, max.z ); + + return temp; +} + +Vector ClosestPointYBox ( Vector const & V, YawedBox const & B ) +{ + Vector lV = B.transformToLocal(V); + + AxialBox lB = B.getLocalShape(); + + Vector lC = ClosestPointABox(lV,lB); + + Vector out = B.transformToWorld(lC); + + return out; +} + +Vector ClosestPointOBox ( Vector const & V, OrientedBox const & B ) +{ + Vector lV = B.transformToLocal(V); + + AxialBox lB = B.getLocalShape(); + + Vector lC = ClosestPointABox(lV,lB); + + Vector out = B.transformToWorld(lC); + + return out; +} + +// ---------------------------------------------------------------------- + +Vector ClosestPointYLine ( Segment3d const & S, Vector const & V ) +{ + Line3d A = S.getLine(); + Line3d B(V,Vector::unitY); + + float paramA; + float paramB; + + ClosestPairLineLine(A,B,paramA,paramB); + + if(paramA < 0.0f) paramA = 0.0f; + if(paramA > 1.0f) paramA = 1.0f; + + return S.atParam(paramA); +} + +// ---------------------------------------------------------------------- + +// This is a really crappy way to find the closest point on a polygon, but it works. + +// Find the closest point on a polygon to a given point by tesselating the polygon +// into triangles and finding the closest point on any triangle. + +// This will fail for non-convex polygons. + +Vector ClosestPointPoly ( Vector const & V, VertexList const & vertices ) +{ + if(vertices.size() == 0) return V; + + if(vertices.size() == 1) return vertices[0]; + + if(vertices.size() == 2) return ClosestPointSeg( V, Segment3d(vertices[0],vertices[1]) ); + + // ---------- + + Vector closest = ClosestPointTri( V, Triangle3d(vertices[0],vertices[1],vertices[2]) ); + + int nTris = vertices.size() - 2; + + for(int i = 1; i < nTris; i++) + { + Triangle3d tri( vertices[0], vertices[i+1], vertices[i+2] ); + + Vector temp = ClosestPointTri(V,tri); + + closest = selectCloser(V,closest,temp); + } + + return closest; +} + +// ---------------------------------------------------------------------- +// Separating plane calculation + +// Given the results of two object-plane containment tests, determine if +// the objects are separated by the plane + +bool TestSeparated ( ContainmentResult A, ContainmentResult B ) +{ + VALIDATE_RANGE_INCLUSIVE_EXCLUSIVE( CR_Outside, A, CR_Invalid); + VALIDATE_RANGE_INCLUSIVE_EXCLUSIVE( CR_Outside, A, CR_Invalid); + + // If either object overlaps the plane, the plane is not a separating plane. + + if((A == CR_Overlap) || (B == CR_Overlap)) return false; + + // If either object lies in the plane, the plane is a separating plane + // unless both objects lie in the plane. + + if((A == CR_Boundary) || (B == CR_Boundary)) + { + return !((A == CR_Boundary) && (B == CR_Boundary)); + } + + // Otherwise, if the objects are not on the same side of the plane, + // the plane is a separating plane. + + bool aInside = ((A == CR_Inside) || (A == CR_TouchingInside)); + bool bInside = ((B == CR_Inside) || (B == CR_TouchingInside)); + + return aInside != bInside; //lint !e731 +} + +bool TestSeparated ( Triangle3d const & A, + Triangle3d const & B, + Plane3d const & P, + real epsilon ) +{ + ContainmentResult testA = Containment3d::TestTriPlane(A,P,epsilon); + ContainmentResult testB = Containment3d::TestTriPlane(B,P,epsilon); + + return TestSeparated(testA,testB); +} + +bool TestCoplanar( Triangle3d const & A, Triangle3d const & B, real epsilon ) +{ + int testA = Containment3d::TestTriPlane(A,B.getPlane(),epsilon); + int testB = Containment3d::TestTriPlane(B,A.getPlane(),epsilon); + + return (testA == CR_Boundary) && (testB == CR_Boundary); +} + +// ---------------------------------------------------------------------- +// Compute a separating plane for two coplanar triangles by testing +// planes perpendicular to the triangle edges + +bool SeparateTriTri_coplanar ( Triangle3d const & A, Triangle3d const & B, Plane3d & out, real epsilon ) +{ + if(!TestCoplanar(A,B,epsilon)) + { + return false; + } + + // ---------- + // Test if triangle B is outside any of A's edge-planes + + { + for(int i = 0; i < 3; i++) + { + Plane3d P = Plane3d( A.getCorner(i), A.getEdgeDir(i).cross( A.getNormal() ) ); + + ContainmentResult test = Containment3d::TestTriPlane( B, P, epsilon ); + + if( (test == CR_Outside) || (test == CR_TouchingOutside) || (test == CR_Boundary) ) + { + out = P; + return true; + } + } + } + + // ---------- + // Test if triangle A is outside any of B's edge-planes + + { + for(int i = 0; i < 3; i++) + { + Plane3d P = Plane3d( B.getCorner(i), B.getEdgeDir(i).cross( B.getNormal() ) ); + + ContainmentResult test = Containment3d::TestTriPlane( A, P, epsilon ); + + if( (test == CR_Outside) || (test == CR_TouchingOutside) || (test == CR_Boundary) ) + { + out = P; + return true; + } + } + } + + // ---------- + + return false; +} + +// ---------------------------------------------------------------------- + +bool Separate ( Triangle3d const & A, Triangle3d const & B, Plane3d & out, real epsilon ) +{ + // If the two triangles are intersecting, no separating plane exists + + //@todo - Is it more efficient to use SeparateTriTri to compute Overlap3d::TestTriTri via + // determining that no separating planes exist? + + if(Overlap3d::TestTriTri(A,B)) + { + return false; + } + + // If the triangles are coplanar we have to generate the separating plane + // differently + + if(TestCoplanar(A,B,epsilon)) + { + return SeparateTriTri_coplanar(A,B,out,epsilon); + } + + // ---------- + // Check to see if either triangle is a separating plane + + if(TestSeparated(A,B,A.getPlane(),epsilon)) + { + out = A.getPlane(); + return true; + } + + if(TestSeparated(A,B,B.getPlane(),epsilon)) + { + out = B.getPlane(); + return true; + } + + // ---------- + // Neither triangle is itself a separating plane, so try all edge-vert pairs + + { + // A's edges, B's vertices + + for(int i = 0; i < 3; i++) + { + Segment3d S = A.getEdgeSegment(i); + + for(int j = 0; j < 3; j++) + { + Vector V = B.getCorner(j); + + Plane3d P(V,S.getBegin(),S.getEnd()); + + if(TestSeparated(A,B,P,epsilon)) + { + out = P; + return true; + } + } + } + } + + { + // B's edges, A's vertices + + for(int i = 0; i < 3; i++) + { + Segment3d S = B.getEdgeSegment(i); + + for(int j = 0; j < 3; j++) + { + Vector V = A.getCorner(j); + + Plane3d P(V,S.getBegin(),S.getEnd()); + + if(TestSeparated(A,B,P,epsilon)) + { + out = P; + return true; + } + } + } + } + + // ---------- + // No separating plane found! + + return false; +} + +// ---------------------------------------------------------------------- +// Project a point onto an axis + +float ProjectAxis ( Line3d const & L, Vector const & V ) +{ + return Collision3d::ComponentAlong( V - L.getPoint(), L.getNormal() ); +} + +// ---------- + +Range ProjectAxis ( Line3d const & L, AxialBox const & B ) +{ + Vector const & N = L.getNormal(); + Vector extent = B.getDelta(); + + real x = abs( Collision3d::ComponentAlong( Vector::unitX, N ) ) * extent.x; + real y = abs( Collision3d::ComponentAlong( Vector::unitY, N ) ) * extent.y; + real z = abs( Collision3d::ComponentAlong( Vector::unitZ, N ) ) * extent.z; + + real d = x + y + z; + + real c = ProjectAxis ( L, B.getCenter() ); + + return Range( c - d, c + d ); +} + +// ---------- + +Range ProjectAxis ( Line3d const & L, OrientedBox const & B ) +{ + Vector const & N = L.getNormal(); + + real x = abs( Collision3d::ComponentAlong( B.getAxisX(), N ) ) * B.getExtentX(); + real y = abs( Collision3d::ComponentAlong( B.getAxisY(), N ) ) * B.getExtentY(); + real z = abs( Collision3d::ComponentAlong( B.getAxisZ(), N ) ) * B.getExtentZ(); + + real d = x + y + z; + + real c = ProjectAxis( L, B.getCenter() ); + + return Range( c - d, c + d ); +} + +// ---------- + +Range ProjectAxis ( Line3d const & L, Sphere const & S ) +{ + real d = S.getRadius() / L.getNormal().magnitude(); + + real c = ProjectAxis( L, S.getCenter() ); + + return Range( c - d, c + d ); +} + +// ---------- + +Range ProjectAxis ( Line3d const & L, OrientedCircle const & S ) +{ + Vector const & N = L.getNormal(); + + Vector cross = S.getAxis().cross(N); + + float sine = cross.magnitude() / N.magnitude(); + + real d = sine * S.getRadius(); + + real c = ProjectAxis( L, S.getCenter() ); + + return Range( c - d, c + d ); +} + +// ---------- + +Range ProjectAxis ( Line3d const & L, Segment3d const & C ) +{ + real a = ProjectAxis(L,C.getBegin()); + real b = ProjectAxis(L,C.getEnd()); + + if(a < b) std::swap(a,b); + + return Range(a,b); +} + +// ---------- + +Range ProjectAxis ( Line3d const & L, OrientedCylinder const & C ) +{ + Vector const & N = L.getNormal(); + + Vector cross = C.getAxis().cross(N); + + float sine = cross.magnitude() / N.magnitude(); + + real d = sine * C.getRadius(); + + Range c = ProjectAxis( L, C.getAxisSegment() ); + + return Range( c.getMin() - d, c.getMax() + d ); +} + +// ---------------------------------------------------------------------- + +Quadratic DistancePointPoint ( Vector const & A, Vector const & V, Vector const & B ) +{ + Vector D = (B-A); + + real a = V.magnitudeSquared(); + real b = -2.0f * D.dot(V); + real c = D.magnitudeSquared(); + + return Quadratic(a,b,c); +} + +// ---------------------------------------------------------------------- + +} // namespace Collision3d diff --git a/engine/shared/library/sharedCollision/src/shared/core/Distance3d.h b/engine/shared/library/sharedCollision/src/shared/core/Distance3d.h new file mode 100644 index 00000000..2309a92d --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Distance3d.h @@ -0,0 +1,191 @@ +// ====================================================================== +// +// Distance3d.h +// copyright (c) 2001 Sony Online Entertainment +// +// Distance between two primitives, closest points, separating planes +// +// ---------------------------------------------------------------------- + +#ifndef INCLUDED_Distance3d_H +#define INCLUDED_Distance3d_H + +class Vector; +class Triangle3d; +class Plane3d; +class Segment3d; +class Line3d; +class Cylinder; +class OrientedCylinder; +class AxialBox; +class YawedBox; +class OrientedBox; +class OrientedCircle; +class Quadratic; +class Range; +class Sphere; +class MultiShape; + +typedef stdvector::fwd VertexList; + +#include "sharedCollision/CollisionEnums.h" +#include "sharedMath/Vector.h" + +// ---------------------------------------------------------------------- +// 3d operations + +namespace Distance3d +{ + +// ---------------------------------------------------------------------- +// Closest distance computation + +float DistancePointPoint ( Vector const & A, Vector const & B ); + +float DistancePointLine ( Vector const & V, Line3d const & L ); +float DistancePointSeg ( Vector const & V, Segment3d const & S ); + +float DistancePointPlane ( Vector const & V, Plane3d const & P ); // this is signed distance - negative is behind the plane +float DistancePointTri ( Vector const & V, Triangle3d const & T ); +float DistancePointPoly ( Vector const & V, VertexList const & vertices ); + +float DistancePointSphere ( Vector const & V, Sphere const & S ); +float DistancePointCylinder ( Vector const & V, Cylinder const & C ); +float DistancePointABox ( Vector const & V, AxialBox const & B ); +float DistancePointYBox ( Vector const & V, YawedBox const & B ); +float DistancePointOBox ( Vector const & V, OrientedBox const & B ); + +float DistancePointShape ( Vector const & V, MultiShape const & S ); + +float DistanceLineLine ( Line3d const & A, Line3d const & B ); + +float DistanceSphereCylinder ( Sphere const & S, Cylinder const & C ); + +// ---------- + +inline float DistanceBetween ( Vector const & V, Sphere const & S ) { return DistancePointSphere(V,S); } +inline float DistanceBetween ( Vector const & V, Cylinder const & C ) { return DistancePointCylinder(V,C); } +inline float DistanceBetween ( Vector const & V, AxialBox const & B ) { return DistancePointABox(V,B); } +inline float DistanceBetween ( Vector const & V, YawedBox const & B ) { return DistancePointYBox(V,B); } +inline float DistanceBetween ( Vector const & V, OrientedBox const & B ) { return DistancePointOBox(V,B); } +inline float DistanceBetween ( Vector const & V, MultiShape const & S ) { return DistancePointShape(V,S); } + +inline float DistanceBetween ( Sphere const & S, Cylinder const & C ) { return DistanceSphereCylinder(S,C); } + +inline float DistanceBetween ( Cylinder const & C, Sphere const & S ) { return DistanceSphereCylinder(S,C); } + +// ---------------------------------------------------------------------- +// Closest distance squared computation + +float Distance2PointPoint ( Vector const & A, Vector const & B ); + +float Distance2PointPoly ( Vector const & V, VertexList const & vertices ); +float Distance2PointABox ( Vector const & V, AxialBox const & B ); + +// ---------------------------------------------------------------------- +// Farthest distance computation + +float FarDistancePointSphere ( Vector const & V, Sphere const & S ); + +// ---------------------------------------------------------------------- +// Closest point calculation + +// These always return the point on (the boundary of) A that's closest to B. + +Vector ClosestPointLine ( Vector const & V, Line3d const & L ); +Vector ClosestPointSeg ( Vector const & V, Segment3d const & S ); + +Vector ClosestPointPlane ( Vector const & V, Plane3d const & P ); + +Vector ClosestPointTri ( Vector const & V, Triangle3d const & T ); +Vector ClosestPointPoly ( Vector const & V, VertexList const & vertices ); + +// ---------- + +Vector ClosestPointSphere ( Vector const & V, Sphere const & S ); +Vector ClosestPointCylinder ( Vector const & V, Cylinder const & C ); +Vector ClosestPointABox ( Vector const & V, AxialBox const & B ); +Vector ClosestPointYBox ( Vector const & V, YawedBox const & B ); +Vector ClosestPointOBox ( Vector const & V, OrientedBox const & B ); + +// closest point on S to a y-axis-aligned line passing through V + +Vector ClosestPointYLine ( Segment3d const & S, Vector const & V ); + +// ---------- + +inline Vector ClosestPoint ( Vector const & V, Sphere const & S ) { return ClosestPointSphere(V,S); } +inline Vector ClosestPoint ( Vector const & V, Cylinder const & S ) { return ClosestPointCylinder(V,S); } +inline Vector ClosestPoint ( Vector const & V, AxialBox const & S ) { return ClosestPointABox(V,S); } +inline Vector ClosestPoint ( Vector const & V, YawedBox const & S ) { return ClosestPointYBox(V,S); } +inline Vector ClosestPoint ( Vector const & V, OrientedBox const & S ) { return ClosestPointOBox(V,S); } + +// ---------------------------------------------------------------------- +// Farthest point calculation + +Vector FarthestPoint ( Vector const & V, Sphere const & S ); +Vector FarthestPoint ( Vector const & V, Cylinder const & S ); +Vector FarthestPoint ( Vector const & V, AxialBox const & S ); +Vector FarthestPoint ( Vector const & V, YawedBox const & S ); +Vector FarthestPoint ( Vector const & V, OrientedBox const & S ); + +// ---------------------------------------------------------------------- +// Closest point calculation, parametric + +bool ClosestPairLineLine ( Line3d const & A, + Line3d const & B, + float & outA, + float & outB ); + +// ---------------------------------------------------------------------- +// Separating plane test (returns true if the plane separates the two objects) + +bool TestSeparate ( Plane3d & P, + Triangle3d const & A, + Triangle3d const & B, + float epsilon ); + +// ---------------------------------------------------------------------- +// Separating plane calculation + +bool Separate ( Triangle3d const & A, + Triangle3d const & B, + Plane3d & out, + float epsilon ); + +// ---------------------------------------------------------------------- +// Determine if two objects are separated by + +bool TestSeparated ( ContainmentResult A, ContainmentResult B ); + +bool TestSeparated ( Triangle3d const & A, Triangle3d const & B, Plane3d const & P, float epsilon ); + +bool TestCoplanar ( Triangle3d const & A, Triangle3d const & B, float epsilon ); + +// ---------------------------------------------------------------------- +// Axial projections - project a primitive onto a line to produce a 1d +// range in line-parametric space + +float ProjectAxis ( Line3d const & L, Vector const & V ); + +Range ProjectAxis ( Line3d const & L, AxialBox const & B ); +Range ProjectAxis ( Line3d const & L, OrientedBox const & B ); +Range ProjectAxis ( Line3d const & L, Sphere const & S ); +Range ProjectAxis ( Line3d const & L, OrientedCircle const & S ); +Range ProjectAxis ( Line3d const & L, Segment3d const & C ); + +// ---------------------------------------------------------------------- +// Moving-point-vs-point distance - returns squared distance +// as a function of time. + +Quadratic DistancePointPoint ( Vector const & A, + Vector const & V, + Vector const & B ); + +// ---------------------------------------------------------------------- + +}; // namespace Collision3d + +// ---------------------------------------------------------------------- + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/DoorHelper.cpp b/engine/shared/library/sharedCollision/src/shared/core/DoorHelper.cpp new file mode 100644 index 00000000..9acbac91 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/DoorHelper.cpp @@ -0,0 +1,135 @@ +// ====================================================================== +// +// DoorHelper.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/DoorHelper.h" + +#include "sharedCollision/DoorInfo.h" + +#include "sharedObject/World.h" + +// ====================================================================== + +DoorHelper::DoorHelper( DoorInfo const & info ) +: m_pos ( 0.0f ), + m_openTime ( info.m_openTime ), + m_closeTime ( info.m_closeTime ), + m_hitFrame ( -100 ), + m_opening ( false ), + m_open ( false ), + m_event ( DHE_invalid ) +{ +} + +void DoorHelper::update ( float time ) +{ + UNREF(time); + + // @todo change this to time-based hitting instead of frame-based hitting + int const framesSinceHit = World::getFrameNumber() - m_hitFrame; + if ((framesSinceHit >= - 1) && (framesSinceHit <= 1)) + { + m_opening = true; + } + else + { + m_opening = false; + } +} + +void DoorHelper::alter ( float time ) +{ + float startPos = m_pos; + + if(m_opening) + { + m_pos += time * (1.0f / m_openTime); + } + else + { + m_pos -= time * (1.0f / m_closeTime); + } + + float endPos = m_pos; + + // ---------- + + if((startPos == 0.0f) && (endPos > 0.0f)) + { + m_event = DHE_openBegin; + } + else if((startPos < 1.0f) && (endPos >= 1.0f)) + { + m_event = DHE_openEnd; + } + else if((startPos == 1.0f) && (endPos < 1.0f)) + { + m_event = DHE_closeBegin; + } + else if((startPos > 0.0f) && (endPos <= 0.0f)) + { + m_event = DHE_closeEnd; + } + else + { + m_event = DHE_invalid; + } + + // ---------- + + if(m_pos > 1.0f) + { + m_pos = 1.0f; + } + + if(m_pos < 0.0f) + { + m_open = false; + m_pos = 0.0f; + } +} + +// ---------- + +void DoorHelper::hit ( void ) +{ + m_open = true; + m_hitFrame = World::getFrameNumber(); +} + +// ---------- + +float DoorHelper::getPos ( void ) const +{ + return m_pos; +} + +bool DoorHelper::isOpen ( void ) const +{ + return m_open; +} + +bool DoorHelper::isClosed ( void ) const +{ + return !m_open; +} + +bool DoorHelper::isOpening ( void ) const +{ + return m_open && m_opening && (m_pos < 1.0f); +} + +bool DoorHelper::isClosing ( void ) const +{ + return m_open && (!m_opening) && (m_pos > 0.0f); +} + +DoorHelper::Event DoorHelper::getEvent ( void ) const +{ + return m_event; +} + diff --git a/engine/shared/library/sharedCollision/src/shared/core/DoorHelper.h b/engine/shared/library/sharedCollision/src/shared/core/DoorHelper.h new file mode 100644 index 00000000..b6b9c219 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/DoorHelper.h @@ -0,0 +1,60 @@ +// ====================================================================== +// +// DoorHelper.h +// Copyright 2002 Sony Online Entertainment +// All Rights Reserved. +// +// ====================================================================== + +#ifndef INCLUDED_DoorHelper_H +#define INCLUDED_DoorHelper_H + +class DoorInfo; + +// ---------------------------------------------------------------------- + +class DoorHelper +{ +public: + + DoorHelper( DoorInfo const & info ); + + // ---------- + + void update ( float time ); + void alter ( float time ); + + void hit ( void ); + + float getPos ( void ) const; + + bool isOpen ( void ) const; + bool isClosed ( void ) const; + bool isOpening ( void ) const; + bool isClosing ( void ) const; + + enum Event + { + DHE_openBegin = 0, + DHE_openEnd = 1, + DHE_closeBegin = 2, + DHE_closeEnd = 3, + DHE_invalid = -1, + }; + + Event getEvent ( void ) const; + +protected: + + float m_pos; + float m_openTime; + float m_closeTime; + int m_hitFrame; + bool m_opening; + bool m_open; + Event m_event; +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/DoorInfo.h b/engine/shared/library/sharedCollision/src/shared/core/DoorInfo.h new file mode 100644 index 00000000..e41a2f40 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/DoorInfo.h @@ -0,0 +1,48 @@ +// ====================================================================== +// +// DoorInfo.h +// Copyright 2002 Sony Online Entertainment +// All Rights Reserved. +// +// ====================================================================== + +#ifndef INCLUDED_DoorInfo_H +#define INCLUDED_DoorInfo_H + +#include "sharedMath/Vector.h" + +class IndexedTriangleList; + +// ---------------------------------------------------------------------- + +class DoorInfo +{ +public: + + const char * m_frameAppearance; + const char * m_doorAppearance; + const char * m_doorAppearance2; + + bool m_doorFlip2; + Vector m_delta; + float m_openTime; + float m_closeTime; + float m_spring; + float m_smoothness; + float m_triggerRadius; + bool m_forceField; + + const char * m_openBeginEffect; + const char * m_openEndEffect; + const char * m_closeBeginEffect; + const char * m_closeEndEffect; + + IndexedTriangleList const * m_portalGeometry; + + bool m_alwaysOpen; +}; + +// ====================================================================== + +#endif + diff --git a/engine/shared/library/sharedCollision/src/shared/core/DoorObject.cpp b/engine/shared/library/sharedCollision/src/shared/core/DoorObject.cpp new file mode 100644 index 00000000..98cf5297 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/DoorObject.cpp @@ -0,0 +1,612 @@ + +// ====================================================================== +// +// DoorObject.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/DoorObject.h" + +#include "sharedCollision/BarrierObject.h" +#include "sharedCollision/CollisionProperty.h" +#include "sharedCollision/CollisionUtils.h" +#include "sharedCollision/ConfigSharedCollision.h" +#include "sharedCollision/Containment3d.h" +#include "sharedCollision/DoorInfo.h" +#include "sharedCollision/Extent.h" +#include "sharedCollision/NeighborObject.h" +#include "sharedCollision/SimpleExtent.h" + +#include "sharedFoundation/ConfigFile.h" +#include "sharedFoundation/CrcLowerString.h" + +#include "sharedMath/DebugShapeRenderer.h" + +#include "sharedObject/AlterResult.h" +#include "sharedObject/AlterScheduler.h" +#include "sharedObject/Appearance.h" +#include "sharedObject/AppearanceTemplateList.h" +#include "sharedObject/CellProperty.h" +#include "sharedObject/Portal.h" +#include "sharedObject/Tweakable.h" + +static VectorArgb gs_forceFieldColor(0.6f,0.5f,0.5f,1.0f); + +// ====================================================================== + +namespace DoorObjectNamespace +{ + int const cs_maxHitByObjectsToTrack = 25; + float const cs_triggerRadiusFudgeFactor = 1.5f; +} + +using namespace DoorObjectNamespace; + +// ---------------------------------------------------------------------- + +class DoorCollisionProperty : public CollisionProperty +{ +public: + + DoorCollisionProperty( DoorObject & owner ) + : CollisionProperty(owner) + { + } + + virtual bool canCollideWith ( CollisionProperty const * otherCollision ) const + { + UNREF(otherCollision); + + return true; + } + + virtual bool blocksInteraction ( InteractionType interaction ) const + { + UNREF(interaction); + + return false; + } + + virtual bool blocksMovement () const + { + return false; + } + + virtual void hitBy ( CollisionProperty * dynamicCollider ) + { + DoorObject & door = static_cast(getOwner()); + + door.hitBy(dynamicCollider); + } + +protected: + +private: + + DoorCollisionProperty(); + DoorCollisionProperty(const DoorCollisionProperty&); + DoorCollisionProperty& operator= (const DoorCollisionProperty&); +}; + +// ---------------------------------------------------------------------- + +DoorObject::DoorObject ( DoorInfo const & info, Portal * portal ) +: m_doorHelper ( info ), + m_doorHelper2 ( info ), + m_delta ( info.m_delta ), + m_portal ( portal ), + m_neighbor ( NULL ), + m_spring ( info.m_spring ), + m_smoothness ( info.m_smoothness ), + m_draw ( true ), + m_barrier ( NULL ), + m_oldDoorPos ( Vector::maxXYZ ), + m_wasOpen(false), + m_isForceField( false ), + m_alwaysOpen ( info.m_alwaysOpen ), + m_triggerRadiusSquared ( sqr(info.m_triggerRadius * cs_triggerRadiusFudgeFactor) ), + m_hitByObjects () +{ + m_effects[0] = info.m_openBeginEffect; + m_effects[1] = info.m_openEndEffect; + m_effects[2] = info.m_closeBeginEffect; + m_effects[3] = info.m_closeEndEffect; + + setDebugName("Door object"); + + for (int i = 0; i < MAX_DRAWN_DOORS; ++i) + m_drawnDoor[i] = NULL; + + createAppearance(info); + createTrigger(info); + createBarrier(info); +} + +// ---------------------------------------------------------------------- + +DoorObject::~DoorObject() +{ + m_hitByObjects.clear(); + m_portal = NULL; +} + +// ---------------------------------------------------------------------- + +Vector const DoorObject::getCurrentPos ( void ) const +{ + return tween( getOpenParam() ) * m_delta; +} + +// ---------------------------------------------------------------------- + +Portal * DoorObject::getPortal ( void ) +{ + return m_portal; +} + +// ---------------------------------------------------------------------- + +Portal const * DoorObject::getPortal ( void ) const +{ + return m_portal; +} + +// ---------------------------------------------------------------------- + +void DoorObject::setPortal ( Portal * newPortal ) +{ + m_portal = newPortal; +} + +// ---------------------------------------------------------------------- + +DoorObject * DoorObject::getNeighbor ( void ) +{ + return m_neighbor; +} + +// ---------------------------------------------------------------------- + +DoorObject const * DoorObject::getNeighbor ( void ) const +{ + return m_neighbor; +} + +// ---------------------------------------------------------------------- + +int DoorObject::getNumberOfDrawnDoors ( void ) +{ + for (int i = 0; i < MAX_DRAWN_DOORS; ++i) + if (m_drawnDoor[i] == NULL) + return i; + + return MAX_DRAWN_DOORS; +} + +// ---------------------------------------------------------------------- + +Object * DoorObject::getDrawnDoor ( int index ) +{ + DEBUG_FATAL(index < 0 || index >= getNumberOfDrawnDoors(), ("door index out of range 0/%d/%d", index, getNumberOfDrawnDoors())); + return m_drawnDoor[index]; +} + +// ---------------------------------------------------------------------- + +Object * DoorObject::getBarrier ( void ) +{ + return m_barrier; +} + +// ---------------------------------------------------------------------- + +void DoorObject::createAppearance ( DoorInfo const & info ) +{ + if (info.m_frameAppearance && info.m_frameAppearance[0]) + { + setAppearance(AppearanceTemplateList::createAppearance(info.m_frameAppearance)); + } + + m_drawnDoor[0] = new Object(); + m_drawnDoor[0]->attachToObject_p(this,true); + + if(info.m_forceField) + { + // Create the portal polygon and translate it + // to the origin so that it matches the door + // hardpoint + + m_isForceField = true; + + VertexList verts; + getPortal()->getLocalVerts(verts); + Collision3d::MovePolyOnto(verts,Vector::zero); + Appearance * appearance = CellProperty::createForceField(verts,gs_forceFieldColor); + + if(appearance != NULL) + { + m_drawnDoor[0]->setAppearance( appearance ); + Extent * appearanceExtent = new Extent( Containment3d::EncloseSphere(verts) ); + appearanceExtent->incrementReference(); + appearance->setExtent( appearanceExtent ); + } + } + else + { + if (info.m_doorAppearance && info.m_doorAppearance[0]) + { + m_drawnDoor[0]->setAppearance(AppearanceTemplateList::createAppearance(info.m_doorAppearance)); + } + + if (info.m_doorAppearance2 && info.m_doorAppearance2[0]) + { + m_drawnDoor[1] = new Object(); + m_drawnDoor[1]->attachToObject_p(this,true); + m_drawnDoor[1]->setAppearance(AppearanceTemplateList::createAppearance(info.m_doorAppearance2)); + if (info.m_doorFlip2) + m_drawnDoor[1]->yaw_o(PI); + } + } +} + +// ---------------------------------------------------------------------- + +void DoorObject::createTrigger ( DoorInfo const & info ) +{ + DoorCollisionProperty * collision = new DoorCollisionProperty(*this); + + Sphere collisionSphere( Vector(0.0f,1.0f,0.0f), info.m_triggerRadius ); + + collision->attachSourceExtent( new SimpleExtent( MultiShape(collisionSphere) ) ); + + addProperty(*collision); +} + +// ---------------------------------------------------------------------- + +void DoorObject::createBarrier( DoorInfo const & info ) +{ + UNREF(info); + + // Set up the barrier we use to keep players from going through doors they're + // not allowed through + + // Force fields don't need appearances for their barriers + + if (!info.m_alwaysOpen) + { + m_barrier = new BarrierObject(this, !info.m_forceField); + } +} + +// ---------------------------------------------------------------------- + +float DoorObject::alter ( float time ) +{ + time = AlterScheduler::getTimeSinceLastFrame(); + + m_doorHelper.update(time); + m_doorHelper.alter(time); + m_doorHelper2.update(time); + m_doorHelper2.alter(time); + + // ---------- + + bool open = m_doorHelper.getPos() > 0.0f; + + if (m_portal) + { + if (m_alwaysOpen) + m_portal->setClosed(false); + else + if (!m_isForceField) + m_portal->setClosed(!open); + } + + // ---------- + // play client effects via the hook + + PlayEffectHook playEffectHook = ConfigSharedCollision::getPlayEffectHook(); + + DoorHelper::Event event = m_doorHelper.getEvent(); + + if((event != DoorHelper::DHE_invalid) && playEffectHook && m_drawnDoor[0] && m_drawnDoor[0]->getAppearance()) + { + const char *effect = m_effects[event]; + + if(effect && effect[0]) + { + bool playEffect = true; + + if ((event == DoorHelper::DHE_openBegin) && (!isPassageAllowed())) + { + playEffect = false; + } + + if (playEffect) + { + // yuck, have to do an allocation to make a CrcLowerString + + const CrcLowerString c(effect); + playEffectHook(c, m_drawnDoor[0], CrcLowerString::empty); + } + } + } + + // ---------- + + if(!m_isForceField) + { + if (m_drawnDoor[0]) + { + Vector newPos = getCurrentPos(); + + if(m_oldDoorPos != newPos) + { + m_drawnDoor[0]->setPosition_p( newPos ); + m_oldDoorPos = newPos; + + if (m_drawnDoor[1]) + m_drawnDoor[1]->setPosition_p( -newPos ); + } + } + } + + m_draw = true; + + // ---------- + // apply tweaks + + float tweak0 = tween(m_doorHelper.getPos()); + float tweak1 = tween(m_doorHelper2.getPos()); + + if(getAppearance()) + { + Tweakable * tweak = dynamic_cast(getAppearance()); + + if(tweak) + { + tweak->setTweakValue(0,tweak0); + tweak->setTweakValue(1,tweak1); + } + } + + if(m_drawnDoor[0]) + { + Tweakable * tweak = dynamic_cast(m_drawnDoor[0]->getAppearance()); + + if(tweak) + { + tweak->setTweakValue(0,tweak0); + tweak->setTweakValue(1,tweak1); + } + } + + if(m_drawnDoor[1]) + { + Tweakable * tweak = dynamic_cast(m_drawnDoor[1]->getAppearance()); + + if(tweak) + { + tweak->setTweakValue(0,tweak0); + tweak->setTweakValue(1,tweak1); + } + } + + if(m_barrier) + { + Tweakable * tweak = dynamic_cast(m_barrier->getAppearance()); + + if(tweak) + { + tweak->setTweakValue(0,tweak0); + tweak->setTweakValue(1,tweak1); + } + } + + // ---------- + // Check if there are any hit-by objects that are still within the door's trigger range. + // If so, the door helper should be triggered as 'hit', this door object should still be + // altered and the door will remain open. + + maintainHitByObjectVector(); + if (!m_hitByObjects.empty()) + { + if (isPassageAllowed()) + m_doorHelper.hit(); + + if (m_neighbor && m_neighbor->isPassageAllowed()) + m_neighbor->m_doorHelper.hit(); + } + + // ---------- + // Figure out if we need to alter next frame. + float result = (m_hitByObjects.empty() && isClosed()) ? AlterResult::cms_keepNoAlter : AlterResult::cms_alterNextFrame; + + // Alter the base class. + AlterResult::incorporateAlterResult(result, Object::alter(time)); + + return result; +} + +// ---------------------------------------------------------------------- + +float DoorObject::tween ( float t ) const +{ + // skew t using the spring factor to make the door seem to spring open faster + + float skew = 1.0f / m_spring; + + t = pow(t,skew); + + // and smooth it out with a cosine so it's not quite so jarring + + float smoothed = ( 1.0f - cos( t * PI ) ) / 2.0f; + + return (smoothed * m_smoothness) + ( t * (1.0f - m_smoothness) ); +} + +// ---------------------------------------------------------------------- + +void DoorObject::hitBy( CollisionProperty const * collision ) +{ + if(collision == NULL) return; + + CellProperty const * doorCell = getParentCell(); + CellProperty const * doorNeighborCell = m_neighbor ? m_neighbor->getParentCell() : doorCell; + CellProperty const * colliderCurrentCell = collision->getCell(); + CellProperty const * colliderLastCell = collision->getLastCell(); + + // ignore things not in or moving into or out of the cell of the door or the door's neighbor. + if ((colliderCurrentCell != doorCell) && (colliderLastCell != doorCell) && (colliderCurrentCell != doorNeighborCell) && (colliderLastCell != doorNeighborCell)) + { + return; + } + + scheduleForAlter(); + + if(collision->isPlayer() && !isPassageAllowed()) + { + // Even though we won't open the door here, we want to add this object to the + // hitByObject list. If the state of the door changes so that it is passable, + // we want it to open (via alter()) without requiring the player to move. + trackHitByObject(collision->getOwner()); + + m_doorHelper2.hit(); + + if(m_neighbor) + { + m_neighbor->m_doorHelper2.hit(); + } + } + else + { + // Track this object as a hit-by object. The door will remain 'hit' if passable (i.e. open on the client) + // until all hit by objects either are deleted or are farther away than the door trigger distance. + trackHitByObject(collision->getOwner()); + + if (isPassageAllowed()) + m_doorHelper.hit(); + + if(m_neighbor && m_neighbor->isPassageAllowed()) + { + m_neighbor->m_doorHelper.hit(); + } + } +} + +// ---------------------------------------------------------------------- +// Only draw one of the pair of neighboring doors + +void DoorObject::setNeighbor ( DoorObject * newNeighbor ) +{ + DEBUG_FATAL(newNeighbor == this,("DoorObject::setNeighbor - Trying to make the door a neighbor of itself\n")); + m_neighbor = newNeighbor; + if(m_barrier) + { + if (newNeighbor) + m_barrier->setNeighbor( newNeighbor->m_barrier ); + else + m_barrier->setNeighbor( NULL ); + } +} + +// ---------------------------------------------------------------------- + +bool DoorObject::isPassageAllowed ( void ) const +{ + if(m_portal) + { + return m_portal->isPassageAllowed(); + } + else + { + return false; + } +} + +// ---------------------------------------------------------------------- + +bool DoorObject::isAllPassageAllowed ( void ) const +{ + if(!isPassageAllowed()) + { + return false; + } + + if(m_neighbor && (!m_neighbor->isPassageAllowed())) + { + return false; + } + + return true; +} + +// ---------------------------------------------------------------------- + +void DoorObject::scheduleForAlter() +{ + NOT_NULL(getAttachedTo()); + getAttachedTo()->scheduleForAlter(); +} + + +// ====================================================================== +// class DoorObject: PRIVATE +// ====================================================================== + +void DoorObject::trackHitByObject(Object const &hitByObject) +{ + // Don't let this collection of tracked-by objects get too long. Limit this amount. + if (static_cast(m_hitByObjects.size()) >= cs_maxHitByObjectsToTrack) + return; + + // NOTE: This can't be a set because the key's value can change when a watcher's value changes from non-NULL to NULL. + // Check if the object already exists in the list. + if (std::find(m_hitByObjects.begin(), m_hitByObjects.end(), ConstWatcher(&hitByObject)) != m_hitByObjects.end()) + return; + + m_hitByObjects.push_back(ConstWatcher(&hitByObject)); +} + +// ---------------------------------------------------------------------- + +void DoorObject::maintainHitByObjectVector() +{ + if (m_hitByObjects.empty()) + return; + + Vector const doorPosition_w = getPosition_w(); + + // Remove any objects that no longer exist or no longer are within the trigger radius. + for (WatcherObjectVector::iterator it = m_hitByObjects.begin(); it != m_hitByObjects.end(); ) + { + if (it->getPointer() == NULL) + { + // This hit-by object has been deleted so clear it out of the tracking list. + it = m_hitByObjects.erase(it); + continue; + } + + //-- Remove the tracking object if it is outside the trigger range of the door. + + // Get distance between track object and door. + Vector const hitByObject_w = it->getPointer()->getPosition_w(); + float const distanceBetweenSquared = doorPosition_w.magnitudeBetweenSquared(hitByObject_w); + + if (distanceBetweenSquared > m_triggerRadiusSquared) + { + // Expel from list since hit by object has moved too far away. + it = m_hitByObjects.erase(it); + } + else + { + // Process next item in list. + ++it; + } + } +} + +// ---------------------------------------------------------------------- diff --git a/engine/shared/library/sharedCollision/src/shared/core/DoorObject.h b/engine/shared/library/sharedCollision/src/shared/core/DoorObject.h new file mode 100644 index 00000000..55384ade --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/DoorObject.h @@ -0,0 +1,173 @@ +// ====================================================================== +// +// DoorObject.h +// Copyright 2002 Sony Online Entertainment +// All Rights Reserved. +// +// ====================================================================== + +#ifndef INCLUDED_DoorObject_H +#define INCLUDED_DoorObject_H + +#include "sharedObject/Object.h" + +#include "sharedCollision/DoorHelper.h" + +#include + +class Portal; +class BarrierObject; +class DoorObject; +class NeighborObject; +class CollisionProperty; + +typedef stdvector::fwd VertexList; + +// ---------------------------------------------------------------------- + +class DoorObject : public Object +{ +public: + + DoorObject( DoorInfo const & info, Portal * portal ); + virtual ~DoorObject(); + + // ---------- + + Vector const getCurrentPos ( void ) const; + + // ---------- + // Delegated to the door helper + + float getOpenParam ( void ) const; + + bool isOpen ( void ) const; + bool isClosed ( void ) const; + bool isOpening ( void ) const; + bool isClosing ( void ) const; + + // ---------- + + virtual bool isPassageAllowed ( void ) const; // true if I can go through the door + virtual bool isAllPassageAllowed ( void ) const; // true if I can go through the door and come back + + // ---------- + + virtual float alter ( float time ); + + virtual float tween ( float t ) const; + + virtual void hitBy ( CollisionProperty const * collision ); + + // ---------- + + virtual Portal * getPortal ( void ); + virtual Portal const * getPortal ( void ) const; + virtual void setPortal ( Portal * newPortal ); + + // ---------- + + virtual DoorObject * getNeighbor ( void ); + virtual DoorObject const * getNeighbor ( void ) const; + virtual void setNeighbor ( DoorObject * newNeighbor ); + + virtual int getNumberOfDrawnDoors ( void ); + virtual Object * getDrawnDoor ( int index ); + virtual Object * getBarrier ( void ); + + virtual void scheduleForAlter(); + +protected: + + void createAppearance ( DoorInfo const & info ); + void createTrigger ( DoorInfo const & info ); + void createBarrier ( DoorInfo const & info ); + + // ---------- + + DoorHelper m_doorHelper; + DoorHelper m_doorHelper2; + + Vector m_delta; + + Portal * m_portal; + DoorObject * m_neighbor; + + float m_spring; + float m_smoothness; + + bool m_draw; + + enum { MAX_DRAWN_DOORS = 2 }; + Object * m_drawnDoor[MAX_DRAWN_DOORS]; + + BarrierObject * m_barrier; + + Vector m_oldDoorPos; + + bool m_wasOpen; + + const char * m_effects[4]; + + bool m_isForceField; + + bool const m_alwaysOpen; + +private: + + typedef std::vector > WatcherObjectVector; + +private: + + void trackHitByObject(Object const &hitByObject); + void maintainHitByObjectVector(); + + // Disabled. + DoorObject( DoorObject const & copy ); + DoorObject & operator = ( DoorObject const & copy ); + +private: + + float const m_triggerRadiusSquared; + WatcherObjectVector m_hitByObjects; + +}; + +// ====================================================================== + +inline float DoorObject::getOpenParam ( void ) const +{ + return m_doorHelper.getPos(); +} + +// ---------------------------------------------------------------------- + +inline bool DoorObject::isOpen ( void ) const +{ + return m_doorHelper.isOpen(); +} + +// ---------------------------------------------------------------------- + +inline bool DoorObject::isClosed ( void ) const +{ + return m_doorHelper.isClosed(); +} + +// ---------------------------------------------------------------------- + +inline bool DoorObject::isOpening ( void ) const +{ + return m_doorHelper.isOpening(); +} + +// ---------------------------------------------------------------------- + +inline bool DoorObject::isClosing ( void ) const +{ + return m_doorHelper.isClosing(); +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/FirstSharedCollision.h b/engine/shared/library/sharedCollision/src/shared/core/FirstSharedCollision.h new file mode 100644 index 00000000..0190c335 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/FirstSharedCollision.h @@ -0,0 +1,18 @@ +// ====================================================================== +// +// FirstSharedCollision.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_FirstSharedCollision_H +#define INCLUDED_FirstSharedCollision_H + +// ====================================================================== + +#include "sharedFoundation/FirstSharedFoundation.h" + +// ====================================================================== + +#endif + diff --git a/engine/shared/library/sharedCollision/src/shared/core/Floor.cpp b/engine/shared/library/sharedCollision/src/shared/core/Floor.cpp new file mode 100644 index 00000000..4d08d7df --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Floor.cpp @@ -0,0 +1,663 @@ +// ====================================================================== +// +// Floor.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/Floor.h" + +#include "sharedCollision/CollisionUtils.h" +#include "sharedCollision/ConfigSharedCollision.h" +#include "sharedCollision/Extent.h" +#include "sharedCollision/FloorLocator.h" +#include "sharedCollision/FloorManager.h" +#include "sharedCollision/FloorMesh.h" +#include "sharedCollision/FloorTri.h" +#include "sharedCollision/Footprint.h" +#include "sharedCollision/Intersect3d.h" +#include "sharedMath/DebugShapeRenderer.h" +#include "sharedMath/Line3d.h" +#include "sharedMath/Plane3d.h" +#include "sharedMath/Ray3d.h" +#include "sharedMath/Segment3d.h" +#include "sharedMath/ShapeUtils.h" +#include "sharedMath/Sphere.h" +#include "sharedMath/Transform.h" +#include "sharedMath/Triangle3d.h" +#include "sharedMath/Vector.h" +#include "sharedObject/Appearance.h" +#include "sharedObject/CellProperty.h" +#include "sharedObject/Object.h" + +#include +#include + +// Need to disable this warning to make MultiListHandle work + +#pragma warning(disable : 4355) // 'this' used in base member initializer list + +// ====================================================================== + +Floor::Floor( FloorMesh const * pMesh, Object const * owner, Appearance const * appearance, bool objectFloor ) +: BaseClass(), + CollisionSurface(), + m_owner(owner), + m_appearance(appearance), + m_objectFloor(objectFloor), + m_mesh(pMesh), + m_footList(this,1), + m_databaseList(this,1), + m_spatialSubdivisionHandle(NULL), + m_extent(NULL), + m_extentDirty(true), + m_sphere_l(), + m_sphere_w() +{ + NOT_NULL(pMesh); + + const_cast(m_mesh)->addReference(); + + const_cast(m_mesh)->setObjectFloor(objectFloor); + + const_cast(m_mesh)->setAppearance(appearance); + + // Only build box trees for object floors + + if( ConfigSharedCollision::getBuildBoxTrees() && m_objectFloor && !m_mesh->hasBoxTree() ) + { + const_cast(m_mesh)->buildBoxTree(); + } +} + +// ---------- + +Floor::~Floor() +{ + if(isCellFloor()) + { + if(!m_footList.isEmpty()) + { + + DEBUG_WARNING(true,("Floor::~Floor - Deleting a cell floor that still has footprints on it - this is probably an error, and is probably caused by the parent building getting deleted without being removed from the world\n")); + + // gotta do this to prevent erasing-while-iterating bug + + static std::vector tempList; + + tempList.clear(); + + for(MultiListIterator it(m_footList); it; ++it) + { + tempList.push_back(*it); + } + + int footCount = tempList.size(); + + for(int i = 0; i < footCount; i++) + { + tempList[i]->snapToObject(); + } + + } + } + + const_cast(m_mesh)->releaseReference(); + m_mesh = NULL; + + m_owner = NULL; + m_appearance = NULL; + + m_spatialSubdivisionHandle = NULL; + + delete m_extent; + m_extent = NULL; +} + +// ---------------------------------------------------------------------- + +Transform const & Floor::getTransform_o2p ( void ) const +{ + if(isCellFloor()) + { + return Transform::identity; + } + else + { + Object const * pOwner = getOwner(); + + if(pOwner) + { + return pOwner->getTransform_o2p(); + } + else + { + return Transform::identity; + } + } +} + +// ---------- + +Transform const & Floor::getTransform_o2w ( void ) const +{ + Object const * pOwner = getOwner(); + + if(pOwner) + { + return pOwner->getTransform_o2w(); + } + else + { + return Transform::identity; + } +} + +// ---------- + +float Floor::getScale ( void ) const +{ + if(isCellFloor()) + { + return 1.0f; + } + else + { + Object const * pOwner = getOwner(); + + if(pOwner) + { + return pOwner->getScale().x; + } + else + { + return 1.0f; + } + } +} + +// ---------------------------------------------------------------------- + +BaseExtent const * Floor::getExtent_l ( void ) const +{ + return getFloorMesh()->getExtent_l(); +} + +// ---------- + +BaseExtent const * Floor::getExtent_p ( void ) const +{ + if(getExtentDirty()) + { + updateExtent(); + } + + return m_extent; +} + +// ---------- + +bool Floor::getExtentDirty ( void ) const +{ + return m_extentDirty; +} + +// ---------- + +void Floor::updateExtent ( void ) const +{ + BaseExtent const * localExtent = getExtent_l(); + + if(!m_extent) + { + if(localExtent) + { + m_extent = localExtent->clone(); + } + } + + if(m_extent && localExtent) + { + m_extent->transform( localExtent, getTransform_o2p(), getScale() ); + + m_extentDirty = false; + } + + // ---------- + + m_sphere_l = getExtent_l()->getBoundingSphere(); + + Vector worldCenter = getTransform_o2w().rotateTranslate_l2p(m_sphere_l.getCenter()); + + m_sphere_w = Sphere(worldCenter,m_sphere_l.getRadius()); +} + +// ---------------------------------------------------------------------- + +bool Floor::dropTestBounds ( Vector const & position_p ) const +{ + Line3d line_l( transform_p2l(position_p), -Vector::unitY ); + + return getFloorMesh()->testBounds(line_l); +} + +// ---------------------------------------------------------------------- + +bool Floor::segTestBounds ( Vector const & begin, Vector const & end ) const +{ + Segment3d seg(transform_p2l(begin), transform_p2l(end)); + + return getFloorMesh()->testBounds(seg); +} + +// ---------------------------------------------------------------------- + +Object const * Floor::getOwner ( void ) const +{ + if(m_owner != NULL) + { + return m_owner; + } + else if(m_appearance) + { + return m_appearance->getOwner(); + } + else + { + return NULL; + } +} + +// ---------- + +Appearance const * Floor::getAppearance ( void ) const +{ + if(m_appearance != NULL) + { + return m_appearance; + } + else if(m_owner) + { + return m_owner->getAppearance(); + } + else + { + return NULL; + } +} + +// ---------- + +CellProperty const * Floor::getCell ( void ) const +{ + if(m_owner) + { + CellProperty const * cell = m_owner->getCellProperty(); + if (cell) + return cell; + else + return m_owner->getParentCell(); + } + else + { + return NULL; + } +} + +// ---------------------------------------------------------------------- + +bool Floor::findFloorTri ( Vector const & position_p, bool bAllowJump, FloorLocator & outLoc ) const +{ + Vector position_l = transform_p2l(position_p); + + FloorLocator testLoc(this, position_l); + + bool found = getFloorMesh()->findFloorTri(testLoc,bAllowJump,outLoc); + + outLoc.setFloor(this); + + return found; +} + +// ---------------------------------------------------------------------- + +bool Floor::findFloorTri ( Vector const & position_p, float heightTolerance, bool bAllowJump, FloorLocator & outLoc ) const +{ + Vector position_l = transform_p2l(position_p); + + FloorLocator testLoc( this, position_l ); + + bool found = getFloorMesh()->findFloorTri(testLoc,heightTolerance,bAllowJump,outLoc); + + outLoc.setFloor(this); + + return found; +} + +// ---------------------------------------------------------------------- + +bool Floor::dropTest ( Vector const & position_p, FloorLocator & outLoc ) const +{ + return dropTest(position_p,ConfigSharedCollision::getHopHeight(),outLoc); +} + +// ---------- + +bool Floor::dropTest ( Vector const & position_p, float hopHeight, FloorLocator & outLoc ) const +{ + Vector position_l = transform_p2l(position_p); + + FloorLocator testLoc( this, position_l ); + + bool dropOk = getFloorMesh()->dropTest(testLoc,hopHeight,outLoc); + + if(dropOk) outLoc.setFloor(this); + + return dropOk; +} + +// ---------- + +bool Floor::dropTest ( Vector const & position_p, int triId, FloorLocator & outLoc ) const +{ + Vector position_l = transform_p2l(position_p); + + FloorLocator testLoc( this, position_l ); + + bool dropOk = getFloorMesh()->dropTest(testLoc,triId,outLoc); + + if(dropOk) outLoc.setFloor(this); + + return dropOk; +} + +// ---------------------------------------------------------------------- +//@todo - Add scale support + +Vector Floor::transform_p2l ( Vector const & point ) const +{ + return getTransform_o2p().rotateTranslate_p2l(point); +} + +Vector Floor::transform_l2p ( Vector const & point ) const +{ + return getTransform_o2p().rotateTranslate_l2p(point); +} + +Vector Floor::rotate_p2l ( Vector const & dir ) const +{ + return getTransform_o2p().rotate_p2l(dir); +} + +Vector Floor::rotate_l2p ( Vector const & dir ) const +{ + return getTransform_o2p().rotate_l2p(dir); +} + +// ---------------------------------------------------------------------- + +PathWalkResult Floor::canMove ( FloorLocator const & testLoc, + Vector const & worldDelta, + int ignoreTriId, + int ignoreEdge, + FloorLocator & result ) const +{ + Vector delta = rotate_p2l(worldDelta); + + PathWalkResult walkResult = getFloorMesh()->pathWalkCircle(testLoc,delta,ignoreTriId,ignoreEdge,result); + + result.setFloor(this); + + result.setContactNormal( rotate_l2p(result.getContactNormal()) ); + + return walkResult; +} + +// ---------------------------------------------------------------------- + +PathWalkResult Floor::moveLocator ( FloorLocator const & startLoc, Vector const & worldGoal, int ignoreId, int ignoreEdge, FloorLocator & outLoc ) const +{ + UNREF(ignoreId); + UNREF(ignoreEdge); + + Vector goal = transform_p2l(worldGoal); + + Vector startPoint = startLoc.getPosition_l(); + + Vector delta = goal - startPoint; + + delta.y = 0.0f; + + int hitTriId = -1; + int hitEdge = -1; + float hitTime = 0.0f; + + PathWalkResult result = getFloorMesh()->pathWalkPoint(startLoc,delta,hitTriId,hitEdge,hitTime); + + if((result == PWR_WalkOk) || (result == PWR_InContact)) + { + outLoc = FloorLocator(this,startPoint+delta,hitTriId,0.0f,startLoc.getRadius()); + } + else + { + outLoc = FloorLocator(this,startPoint+delta,-1,0.0f,startLoc.getRadius()); + } + + if(outLoc.getId() != -1) + { + Plane3d plane = getFloorMesh()->getTriangle( outLoc.getId() ).getPlane(); + + Vector snapPoint; + float dummy; + + if( Intersect3d::IntersectLinePlaneUnsided( Line3d(outLoc.getPosition_l(),-Vector::unitY), plane, snapPoint, dummy ) ) + { + outLoc.setPosition_l(snapPoint); + } + } + + { + Floor const * floor = outLoc.getFloor(); + + FloorLocator temp; + + if((outLoc.getId() != -1) && floor && !floor->dropTest(outLoc.getPosition_p(),outLoc.getId(),temp)) + { + outLoc.detach(); + } + } + + + return result; +} + +// ---------------------------------------------------------------------- + +bool Floor::findClosestLocation ( Vector const & position_p, FloorLocator & outLoc ) const +{ + Vector position_l = transform_p2l(position_p); + + FloorLocator testLoc(this,position_l); + + bool findOK = getFloorMesh()->findClosestLocation(testLoc,outLoc); + + outLoc.setFloor(this); + + return findOK; +} + +// ---------------------------------------------------------------------- + +bool Floor::findElevatorNeighbors( FloorLocator const & testLoc, + FloorLocator & outClosestAbove, + FloorLocator & outClosestBelow ) const +{ + if(testLoc.getFloor() != this) + { + FLOOR_WARNING("Floor::findElevatorNeighbors - Test location isn't on this floor\n"); + } + + Line3d line(testLoc.getPosition_l(),-Vector::unitY); + int ignoreId = testLoc.getId(); + + bool found = getFloorMesh()->findClosestPair(line,ignoreId,outClosestBelow,outClosestAbove); + + outClosestAbove.setFloor(this); + outClosestBelow.setFloor(this); + + return found; +} + +// ---------------------------------------------------------------------- + +void Floor::drawDebugShapes ( DebugShapeRenderer * renderer, bool drawExtent ) const +{ + UNREF(renderer); + UNREF(drawExtent); +#ifdef _DEBUG + + getFloorMesh()->drawDebugShapes(renderer,drawExtent); + +#endif +} + +// ---------------------------------------------------------------------- + +Sphere Floor::getBoundingSphere_l ( void ) const +{ + if(getExtentDirty()) updateExtent(); + +#ifdef _DEBUG + + if(ConfigSharedCollision::getEnableCollisionValidate()) + { + Sphere temp = getExtent_l()->getBoundingSphere(); + + if(!CollisionUtils::epsilonEqual(temp,m_sphere_l,0.001f)) + { + DEBUG_WARNING(true,("Floor::getBoundingSphere_l - Current sphere != cached sphere")); + } + } + +#endif + + return m_sphere_l; +} + +// ---------- + +Sphere Floor::getBoundingSphere_w ( void ) const +{ + if(getExtentDirty()) updateExtent(); + +#ifdef _DEBUG + + if(ConfigSharedCollision::getEnableCollisionValidate()) + { + Vector worldCenter = getTransform_o2w().rotateTranslate_l2p(m_sphere_l.getCenter()); + + Sphere temp = Sphere(worldCenter,m_sphere_l.getRadius()); + + if(!CollisionUtils::epsilonEqual(temp,m_sphere_w,0.001f)) + { + DEBUG_WARNING(true,("Floor::getBoundingSphere_w - Current sphere != cached sphere")); + } + } + +#endif + + return m_sphere_w; +} + +// ---------------------------------------------------------------------- + +bool Floor::findEntryPoint ( FloorLocator const & inStartLoc, Vector const & delta, bool useRadius, FloorLocator & outLoc ) const +{ + FloorLocator startLoc = inStartLoc; + + startLoc.setFloor(this); + + Vector localDelta = rotate_p2l(delta); + + bool result = getFloorMesh()->findEntrance(startLoc,localDelta,useRadius,outLoc); + + outLoc.setFloor(this); + + return result; +} + +// ---------------------------------------------------------------------- + +bool Floor::intersectClosest ( Ray3d const & ray, FloorLocator & outLoc ) const +{ + Vector localPoint = transform_p2l(ray.getPoint()); + Vector localNormal = rotate_p2l(ray.getNormal()); + + bool result = getFloorMesh()->intersectClosest( Ray3d(localPoint,localNormal), outLoc ); + + outLoc.setFloor(this); + + return result; +} + +bool Floor::intersectClosest ( Segment3d const & seg, FloorLocator & outLoc ) const +{ + Vector localBegin = transform_p2l(seg.getBegin()); + Vector localEnd = transform_p2l(seg.getEnd()); + + bool result = getFloorMesh()->intersectClosest( Segment3d(localBegin,localEnd), outLoc ); + + outLoc.setFloor(this); + + return result; +} + +bool Floor::intersectClosest ( Line3d const & line, FloorLocator & outLoc ) const +{ + Vector localPoint = transform_p2l(line.getPoint()); + Vector localNormal = rotate_p2l(line.getNormal()); + + bool result = getFloorMesh()->intersectClosest( Line3d(localPoint,localNormal), outLoc ); + + outLoc.setFloor(this); + + return result; +} + +// ---------------------------------------------------------------------- + +bool Floor::getGoodLocation ( float radius, Vector & outLoc ) const +{ + if(getFloorMesh()->getGoodLocation(radius,outLoc)) + { + outLoc = transform_l2p(outLoc); + + return true; + } + else + { + return false; + } +} + +// ---------------------------------------------------------------------- + +bool Floor::getDistanceUncrossable2d ( Vector const & position_w, float maxDistance, float & outDistance, FloorEdgeId & outEdgeId ) const +{ + Vector position_p = CollisionUtils::transformFromWorld(position_w,getCell()); + Vector position_l = transform_p2l(position_p); + + return getFloorMesh()->getDistanceUncrossable2d(position_l,maxDistance,outDistance,outEdgeId); +} + +// ---------------------------------------------------------------------- + +NetworkId const & Floor::getId() const +{ + CellProperty const * const cellProperty = getCell(); + + return (cellProperty != NULL) ? cellProperty->getOwner().getNetworkId() : NetworkId::cms_invalid; +} + +// ====================================================================== diff --git a/engine/shared/library/sharedCollision/src/shared/core/Floor.h b/engine/shared/library/sharedCollision/src/shared/core/Floor.h new file mode 100644 index 00000000..f6352c4d --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Floor.h @@ -0,0 +1,184 @@ +// ====================================================================== +// +// Floor.h +// copyright (c) 2001 Sony Online Entertainment +// +// ---------- +// +// ====================================================================== + +#ifndef INCLUDED_Floor_H +#define INCLUDED_Floor_H + +#include "sharedCollision/BaseClass.h" +#include "sharedCollision/CollisionSurface.h" +#include "sharedCollision/MultiList.h" +#include "sharedCollision/CollisionEnums.h" + +#include "sharedMath/Sphere.h" + +class Appearance; +class AxialBox; +class CellProperty; +class DebugShapeRenderer; +class FloorEdgeId; +class FloorLocator; +class FloorMesh; +class IndexedTriangleList; +class NetworkId; +class Object; +class Ray3d; +class SpatialSubdivisionHandle; +class Sphere; +class Transform; +class Vector; + +// ====================================================================== + +class Floor : public BaseClass, public CollisionSurface +{ +public: + + Floor( FloorMesh const * pMesh, Object const * owner, Appearance const * appearance, bool objectFloor ); + virtual ~Floor(); + + // ---------- + // Interface inherited from CollisionSurface + + virtual Transform const & getTransform_o2p ( void ) const; + virtual Transform const & getTransform_o2w ( void ) const; + virtual float getScale ( void ) const; + virtual BaseExtent const * getExtent_l ( void ) const; + virtual BaseExtent const * getExtent_p ( void ) const; + + // ---------- + + FloorMesh const * getFloorMesh ( void ) const; + Object const * getOwner ( void ) const; + Appearance const * getAppearance ( void ) const; + CellProperty const * getCell ( void ) const; + NetworkId const & getId() const; + + // ---------- + // Other tests + + PathWalkResult canMove ( FloorLocator const & worldLoc, Vector const & worldDelta, int ignoreTriId, int ignoreEdge, FloorLocator & result ) const; + PathWalkResult moveLocator ( FloorLocator const & startLoc, Vector const & worldGoal, int ignoreTriId, int ignoreEdge, FloorLocator & outLoc ) const; + + bool findClosestLocation ( Vector const & position_p, FloorLocator & outLoc ) const; + + bool findFloorTri ( Vector const & position_p, float heightTolerance, bool bAllowJump, FloorLocator & outLoc ) const; + bool findFloorTri ( Vector const & position_p, bool bAllowJump, FloorLocator & outLoc ) const; + + bool dropTest ( Vector const & position_p, FloorLocator & outLoc ) const; + bool dropTest ( Vector const & position_p, float hopHeight, FloorLocator & outLoc ) const; + bool dropTest ( Vector const & position_p, int triID, FloorLocator & outLoc ) const; + bool dropTestBounds ( Vector const & position_p ) const; + bool segTestBounds ( Vector const & begin, Vector const & end ) const; + + void drawDebugShapes ( DebugShapeRenderer * renderer, bool drawExtent ) const; + + bool findElevatorNeighbors ( FloorLocator const & testLoc, FloorLocator & outClosestAbove, FloorLocator & outClosestBelow ) const; + + bool findEntryPoint ( FloorLocator const & startLoc, Vector const & delta, bool useRadius, FloorLocator & outLoc ) const; + + bool intersectClosest ( Ray3d const & ray, FloorLocator & outLoc ) const; + bool intersectClosest ( Segment3d const & seg, FloorLocator & outLoc ) const; + bool intersectClosest ( Line3d const & line, FloorLocator & outLoc ) const; + + // ---------- + + MultiListHandle & getFootList ( void ); + MultiListHandle const & getFootList ( void ) const; + + SpatialSubdivisionHandle * getSpatialSubdivisionHandle ( void ); + void setSpatialSubdivisionHandle ( SpatialSubdivisionHandle * newHandle ); + + // ---------- + + Sphere getBoundingSphere_l ( void ) const; + Sphere getBoundingSphere_w ( void ) const; + + bool isCellFloor ( void ) const; + + virtual bool getExtentDirty ( void ) const; + virtual void updateExtent ( void ) const; + + bool getGoodLocation ( float radius, Vector & outLoc ) const; + + Vector transform_p2l ( Vector const & point ) const; + Vector transform_l2p ( Vector const & point ) const; + + Vector rotate_p2l ( Vector const & dir ) const; + Vector rotate_l2p ( Vector const & dir ) const; + + bool getDistanceUncrossable2d ( Vector const & position_w, float maxDistance, float & outDistance, FloorEdgeId & outEdgeId ) const; + +private: + + friend class FloorManager; + + // ---------- + // Data + + Object const * m_owner; + Appearance const * m_appearance; + bool m_objectFloor; + FloorMesh const * m_mesh; //! The collision mesh for this floor + MultiListHandle m_footList; //! List of footprints on this floor + MultiListHandle m_databaseList; //! List of spatial databases this floor is in + SpatialSubdivisionHandle * m_spatialSubdivisionHandle; + + mutable BaseExtent * m_extent; + mutable bool m_extentDirty; + mutable Sphere m_sphere_l; + mutable Sphere m_sphere_w; + + // ---------- + // disable copying + + Floor( Floor const & copy ); + Floor & operator = ( Floor const & copy ); +}; + +// ---------------------------------------------------------------------- + +inline FloorMesh const * Floor::getFloorMesh ( void ) const +{ + return m_mesh; +} + +// ---------------------------------------------------------------------- + +inline SpatialSubdivisionHandle * Floor::getSpatialSubdivisionHandle ( void ) +{ + return m_spatialSubdivisionHandle; +} + +inline void Floor::setSpatialSubdivisionHandle ( SpatialSubdivisionHandle * handle ) +{ + m_spatialSubdivisionHandle = handle; +} + +// ---------------------------------------------------------------------- + +inline MultiListHandle & Floor::getFootList ( void ) +{ + return m_footList; +} + +inline MultiListHandle const & Floor::getFootList ( void ) const +{ + return m_footList; +} + +// ---------------------------------------------------------------------- + +inline bool Floor::isCellFloor ( void ) const +{ + return !m_objectFloor; +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/FloorContactShape.cpp b/engine/shared/library/sharedCollision/src/shared/core/FloorContactShape.cpp new file mode 100644 index 00000000..4c639c96 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/FloorContactShape.cpp @@ -0,0 +1,9 @@ +// ====================================================================== +// +// FloorContactShape.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/FloorContactShape.h" diff --git a/engine/shared/library/sharedCollision/src/shared/core/FloorContactShape.h b/engine/shared/library/sharedCollision/src/shared/core/FloorContactShape.h new file mode 100644 index 00000000..adb6060b --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/FloorContactShape.h @@ -0,0 +1,34 @@ +// ====================================================================== +// +// FloorContactShape.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_FloorContactShape_H +#define INCLUDED_FloorContactShape_H + +#include "sharedCollision/FloorLocator.h" +#include "sharedCollision/BaseClass.h" + +class DebugShapeRenderer; + +// ====================================================================== + +class FloorContactShape : public BaseClass +{ +public: + + FloorLocator m_contact; + FloorLocator m_result; + +#ifdef _DEBUG + FloorLocator m_backupContact; +#endif + +}; + +// ---------------------------------------------------------------------- + +#endif // #ifndef INCLUDED FloorContactShape_H + diff --git a/engine/shared/library/sharedCollision/src/shared/core/FloorLocator.cpp b/engine/shared/library/sharedCollision/src/shared/core/FloorLocator.cpp new file mode 100644 index 00000000..f595384a --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/FloorLocator.cpp @@ -0,0 +1,457 @@ +// ====================================================================== +// +// FloorLocator.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/FloorLocator.h" + +#include "sharedFile/Iff.h" + +#include "sharedMath/Transform.h" +#include "sharedMath/Line3d.h" +#include "sharedMath/Plane3d.h" +#include "sharedMath/Triangle3d.h" + +#include "sharedCollision/Floor.h" +#include "sharedCollision/FloorMesh.h" // so we can get the floor tri from the contact point +#include "sharedCollision/FloorTri.h" +#include "sharedCollision/Intersect3d.h" +#include "sharedCollision/CollisionUtils.h" + +#include "sharedCollision/ConfigSharedCollision.h" + +#include "sharedObject/CellProperty.h" +#include "sharedTerrain/TerrainObject.h" + +FloorLocator FloorLocator::invalid; +FloorTri gs_dummyFloorTri; + +// ---------------------------------------------------------------------- + +FloorLocator::FloorLocator() +: ContactPoint(), + m_valid(false), + m_floor(NULL), + m_radius(0.0f), + m_time(0.0f), + m_edgeId(-1), + m_hitTriId(-1), + m_hitEdgeId(-1), + m_contactNormal(Vector::zero) +{ +} + +// ---------- + +FloorLocator::FloorLocator( Floor const * floor, Vector const & localPoint, int triId, float dist, float radius ) +: ContactPoint((floor ? floor->getFloorMesh() : NULL),localPoint,triId,dist), + m_valid(true), + m_floor(floor), + m_radius(radius), + m_time(0.0f), + m_edgeId(-1), + m_hitTriId(-1), + m_hitEdgeId(-1), + m_contactNormal(Vector::zero) +{ +} + +// ---------- + +FloorLocator::FloorLocator( CollisionSurface const * mesh, Vector const & point, int triId, float dist, float radius ) +: ContactPoint(mesh,point,triId,dist), + m_valid(true), + m_floor(NULL), + m_radius(radius), + m_time(0.0f), + m_edgeId(-1), + m_hitTriId(-1), + m_hitEdgeId(-1), + m_contactNormal(Vector::zero) +{ + NOT_NULL(dynamic_cast(mesh)); +} + +// ---------- + +FloorLocator::FloorLocator( Floor const * floor, Vector const & localPoint ) +: ContactPoint((floor ? floor->getFloorMesh() : NULL),localPoint,-1,0.0f), + m_valid(true), + m_floor(floor), + m_radius(0.0f), + m_time(0.0f), + m_edgeId(-1), + m_hitTriId(-1), + m_hitEdgeId(-1), + m_contactNormal(Vector::zero) +{ +} + +// ---------- + +FloorLocator::FloorLocator( CollisionSurface const * mesh, Vector const & point ) +: ContactPoint(mesh,point,-1,0.0f), + m_valid(true), + m_floor(NULL), + m_radius(0.0f), + m_time(0.0f), + m_edgeId(-1), + m_hitTriId(-1), + m_hitEdgeId(-1), + m_contactNormal(Vector::zero) +{ + NOT_NULL(dynamic_cast(mesh)); +} + +// ---------- + +FloorLocator::FloorLocator( Vector const & point, float radius ) +: ContactPoint(NULL,point,-1,0.0f), + m_valid(true), + m_floor(NULL), + m_radius(radius), + m_time(0.0f), + m_edgeId(-1), + m_hitTriId(-1), + m_hitEdgeId(-1), + m_contactNormal(Vector::zero) +{ +} + +// ---------- + +FloorLocator::FloorLocator ( FloorLocator const & locCopy ) +: ContactPoint(locCopy), + m_valid(locCopy.m_valid), + m_floor(locCopy.m_floor), + m_radius(locCopy.m_radius), + m_time(locCopy.m_time), + m_edgeId(locCopy.m_edgeId), + m_hitTriId(locCopy.m_hitTriId), + m_hitEdgeId(locCopy.m_hitEdgeId), + m_contactNormal(locCopy.m_contactNormal) +{ +} + +// ---------- + +FloorLocator::~FloorLocator() +{ +} + +// ---------- + +FloorLocator & FloorLocator::operator = ( ContactPoint const & contact ) +{ + NOT_NULL(dynamic_cast(contact.getSurface())); + + m_pSurface = contact.getSurface(); + m_position = contact.getPosition_l(); + m_offset = contact.getOffset(); + m_hitId = contact.getId(); + + m_valid = true; + + return *this; +} + +// ---------------------------------------------------------------------- + +Vector FloorLocator::getPosition_p ( void ) const +{ + if(m_floor) + { + return m_floor->getTransform_o2p().rotateTranslate_l2p(getPosition_l()); + } + else + { + return getPosition_l(); + } +} + +// ---------- + +Vector FloorLocator::getPosition_p ( CellProperty const * relativeCell ) const +{ + return CollisionUtils::transformToCell(getCell(),getPosition_p(),relativeCell); +} + +// ---------- + +void FloorLocator::setPosition_p ( Vector const & newPos ) +{ + NAN_CHECK(newPos); + + if(m_floor) + { + setPosition_l(m_floor->getTransform_o2p().rotateTranslate_p2l(newPos)); + } + else + { + setPosition_l(newPos); + } +} + +// ---------- + +Vector FloorLocator::getPosition_w ( void ) const +{ + return CollisionUtils::transformToWorld(getCell(),getPosition_p()); +} + +// ---------------------------------------------------------------------- + +Floor const * FloorLocator::getFloor ( void ) const +{ + return m_floor; +} + +// ---------- + +void FloorLocator::setFloor ( Floor const * newFloor ) +{ + if(newFloor) + { + if(m_floor) + { + if(m_floor != newFloor) + { + Vector oldPos = getPosition_p(); + + setPosition_l( newFloor->getTransform_o2p().rotateTranslate_p2l(oldPos) ); + } + } + else + { + if(getFloorMesh() == NULL) + { + Vector oldPos = getPosition_p(); + + setPosition_l( newFloor->getTransform_o2p().rotateTranslate_p2l(oldPos) ); + } + } + + m_floor = newFloor; + setFloorMesh(newFloor->getFloorMesh()); + } + else + { + m_floor = NULL; + } +} + +// ---------------------------------------------------------------------- + +FloorMesh const * FloorLocator::getFloorMesh ( void ) const +{ + return safe_cast(getSurface()); +} + +// ---------- + +void FloorLocator::setFloorMesh ( FloorMesh const * mesh ) +{ + setSurface(mesh); +} + +// ---------------------------------------------------------------------- + +FloorTri const & FloorLocator::getFloorTri ( void ) const +{ + FloorMesh const * mesh = getFloorMesh(); + + if(mesh && (m_hitId != -1)) + { + return mesh->getFloorTri(static_cast(m_hitId)); + } + else + { + DEBUG_FATAL(true, ("FloorLocator::getFloorTri - Tried to get a floor tri for a locator that's not attached to a floor mesh\n")); + + return gs_dummyFloorTri; + } +} + +// ---------------------------------------------------------------------- + +CellProperty const * FloorLocator::getCell ( void ) const +{ + Floor const * floor = getFloor(); + + if (floor) + { + return floor->getCell(); + } + else + { + if(getFloorMesh()) + { + return NULL; + } + else + { + return CellProperty::getWorldCellProperty(); + } + } +} + +// ---------- + +CellProperty * FloorLocator::getCell ( void ) +{ + Floor const * floor = getFloor(); + + if (floor) + { + return const_cast(floor->getCell()); + } + else + { + if(getFloorMesh()) + { + return NULL; + } + else + { + return CellProperty::getWorldCellProperty(); + } + } +} + +// ---------------------------------------------------------------------- + +Vector FloorLocator::getSurfaceNormal_l ( void ) const +{ + if(isAttached()) + { + return getFloorTri().getNormal(); + } + else + { + return Vector::unitY; + } +} + +// ---------- + +Vector FloorLocator::getSurfaceNormal_p ( void ) const +{ + Floor const * floor = getFloor(); + + if(floor) + { + return floor->getTransform_o2p().rotate_l2p(getSurfaceNormal_l()); + } + else + { + return getSurfaceNormal_l(); + } +} + +// ---------- + +Vector FloorLocator::getSurfaceNormal_w ( void ) const +{ + Floor const * floor = getFloor(); + + if(floor) + { + return floor->getTransform_o2w().rotate_l2p(getSurfaceNormal_l()); + } + else + { + return getSurfaceNormal_l(); + } +} + +// ---------------------------------------------------------------------- + +bool FloorLocator::isFallthrough ( void ) const +{ + if(isAttached() && getFloorTri().isFallthrough()) + { + return true; + } + + return false; +} + +// ---------------------------------------------------------------------- + +bool FloorLocator::isAttachedTo( Floor const * pFloor ) const +{ + return getFloor() == pFloor; +} + +// ---------------------------------------------------------------------- + +void FloorLocator::read_0000 ( Iff & iff ) +{ + ContactPoint::read_0000(iff); + + m_radius = iff.read_float(); + + Vector down = iff.read_floatVector(); + UNREF(down); + + bool portalAdjacent = iff.read_bool8(); + + UNREF(portalAdjacent); +} + +// ---------- + +void FloorLocator::write ( Iff & iff ) const +{ + ContactPoint::write(iff); + + iff.insertChunkData(m_radius); + iff.insertChunkData(-Vector::unitY); + iff.insertChunkData(false); +} + +// ---------------------------------------------------------------------- + +void FloorLocator::detach ( void ) +{ + setFloor(NULL); + setId(-1); +} + +// ---------------------------------------------------------------------- + +void FloorLocator::reattach ( void ) +{ + Floor const * floor = getFloor(); + + if(floor) + { + FloorLocator temp; + + if(floor->dropTest(getPosition_p(),2.0f,temp)) + { + temp.setRadius(getRadius()); + + *this = temp; + } + } +} + +// ---------------------------------------------------------------------- + +void FloorLocator::snapToFloor ( void ) +{ + if(!isAttached()) return; + + float floorHeight = getFloorTri().getHeightAt(m_position.x,m_position.z); + + float newOffset = m_offset + m_position.y - floorHeight; + + Vector newPos(m_position.x,floorHeight,m_position.z); + + setPosition_l(newPos); + setOffset(newOffset); +} diff --git a/engine/shared/library/sharedCollision/src/shared/core/FloorLocator.h b/engine/shared/library/sharedCollision/src/shared/core/FloorLocator.h new file mode 100644 index 00000000..af69149e --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/FloorLocator.h @@ -0,0 +1,232 @@ +// ====================================================================== +// +// FloorLocator.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_FloorLocator_H +#define INCLUDED_FloorLocator_H + +#include "sharedMath/Vector.h" +#include "sharedCollision/ContactPoint.h" +#include "sharedCollision/CollisionEnums.h" +#include "sharedCollision/CollisionUtils.h" + +class CellProperty; +class CollisionSurface; +class Floor; +class FloorMesh; +class FloorTri; +class Iff; +class Transform; + +// ====================================================================== + +class FloorLocator : public ContactPoint +{ +public: + + FloorLocator(); + + FloorLocator( Floor const * floor, Vector const & localPoint, int triId, float offset, float radius ); + FloorLocator( CollisionSurface const * mesh, Vector const & point, int triId, float offset, float radius ); + + FloorLocator( Floor const * floor, Vector const & localPoint ); + + FloorLocator( CollisionSurface const * mesh, Vector const & point ); + + FloorLocator( Vector const & point, float radius ); + + FloorLocator( FloorLocator const & copy ); + + ~FloorLocator(); + + FloorLocator & operator = ( ContactPoint const & contact ); + + static FloorLocator invalid; + + // ---------- + + Vector getPosition_p ( void ) const; + Vector getPosition_p ( CellProperty const * relativeCell ) const; + void setPosition_p ( Vector const & newPos ); + + Vector getPosition_w ( void ) const; + + bool isValid ( void ) const; + void setValid ( bool valid ); + + Floor const * getFloor ( void ) const; + void setFloor ( Floor const * newFloor ); + + FloorMesh const * getFloorMesh ( void ) const; + void setFloorMesh ( FloorMesh const * newFloorMesh ); + + FloorTri const & getFloorTri ( void ) const; + + CellProperty * getCell ( void ); + CellProperty const * getCell ( void ) const; + + float getRadius ( void ) const; + void setRadius ( float newRadius ); + + float getTime ( void ) const; + void setTime ( float newTime ); + + int getTriId ( void ) const; + void setTriId ( int triId ); + + int getEdgeId ( void ) const; + void setEdgeId ( int edgeId ); + + int getHitTriId ( void ) const; + void setHitTriId ( int hitTriId ); + + int getHitEdgeId ( void ) const; + void setHitEdgeId ( int hitEdgeId ); + + Vector const & getContactNormal ( void ) const; + void setContactNormal ( Vector const & normal ); + + Vector getSurfaceNormal_l ( void ) const; + Vector getSurfaceNormal_p ( void ) const; + Vector getSurfaceNormal_w ( void ) const; + + void attachTo ( FloorMesh const * floorMesh, int triId ); + void attachTo ( FloorMesh const * floorMesh, int triId, int edgeId ); + + // ---------- + + bool isFallthrough ( void ) const; + bool isAttachedTo ( Floor const * pFloor ) const; + + // ---------- + + void read_0000 ( Iff & iff ); + void write ( Iff & iff ) const; + + void detach ( void ); + void reattach ( void ); + + void snapToFloor ( void ); + + +private: + + bool m_valid; + Floor const * m_floor; + float m_radius; + float m_time; + int m_edgeId; + int m_hitTriId; + int m_hitEdgeId; + Vector m_contactNormal; +}; + +// ---------------------------------------------------------------------- + +inline bool FloorLocator::isValid ( void ) const +{ + return m_valid; +} + +inline void FloorLocator::setValid ( bool valid ) +{ + m_valid = valid; +} + +// ---------- + +inline float FloorLocator::getRadius ( void ) const +{ + return m_radius; +} + +inline void FloorLocator::setRadius ( float newRadius ) +{ + NAN_CHECK(newRadius); + + m_radius = newRadius; +} + +// ---------- + +inline float FloorLocator::getTime ( void ) const +{ + return m_time; +} + +inline void FloorLocator::setTime ( float newTime ) +{ + NAN_CHECK(newTime); + + m_time = newTime; +} + +// ---------- + +inline int FloorLocator::getTriId ( void ) const +{ + return getId(); +} + +inline void FloorLocator::setTriId ( int newId ) +{ + setId(newId); +} + +// ---------- + +inline int FloorLocator::getEdgeId ( void ) const +{ + return m_edgeId; +} + +inline void FloorLocator::setEdgeId ( int newId ) +{ + m_edgeId = newId; +} + +// ---------- + +inline int FloorLocator::getHitTriId ( void ) const +{ + return m_hitTriId; +} + +inline void FloorLocator::setHitTriId ( int hitTriId ) +{ + m_hitTriId = hitTriId; +} + +// ---------- + +inline int FloorLocator::getHitEdgeId ( void ) const +{ + return m_hitEdgeId; +} + +inline void FloorLocator::setHitEdgeId ( int hitEdgeId ) +{ + m_hitEdgeId = hitEdgeId; +} + +// ---------- + +inline Vector const & FloorLocator::getContactNormal ( void ) const +{ + return m_contactNormal; +} + +inline void FloorLocator::setContactNormal ( Vector const & contactNormal ) +{ + NAN_CHECK(contactNormal); + + m_contactNormal = contactNormal; +} + +// ---------------------------------------------------------------------- + +#endif + diff --git a/engine/shared/library/sharedCollision/src/shared/core/FloorManager.cpp b/engine/shared/library/sharedCollision/src/shared/core/FloorManager.cpp new file mode 100644 index 00000000..677849e7 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/FloorManager.cpp @@ -0,0 +1,88 @@ +// ====================================================================== +// +// FloorManager.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/FloorManager.h" + +#include "sharedCollision/Floor.h" +#include "sharedCollision/FloorMesh.h" + +#include "sharedObject/Object.h" + +#include "sharedFoundation/DataResourceList.h" + + +namespace FloorManagerNamespace +{ + +ObjectFactory ms_pathGraphFactory = NULL; +ObjectWriter ms_pathGraphWriter = NULL; +ObjectRenderer ms_pathGraphRenderer = NULL; + +}; + +using namespace FloorManagerNamespace; + +// ---------------------------------------------------------------------- + +void FloorManager::setPathGraphFactory ( ObjectFactory factory ) +{ + ms_pathGraphFactory = factory; +} + +ObjectFactory FloorManager::getPathGraphFactory ( void ) +{ + return ms_pathGraphFactory; +} + +// ---------- + +void FloorManager::setPathGraphWriter ( ObjectWriter writer ) +{ + ms_pathGraphWriter = writer; +} + +ObjectWriter FloorManager::getPathGraphWriter ( void ) +{ + return ms_pathGraphWriter; +} + +// ---------- + +void FloorManager::setPathGraphRenderer( ObjectRenderer renderer ) +{ + ms_pathGraphRenderer = renderer; +} + +ObjectRenderer FloorManager::getPathGraphRenderer ( void ) +{ + return ms_pathGraphRenderer; +} + +// ---------------------------------------------------------------------- + +Floor * FloorManager::createFloor ( const char * floorMeshFilename, Object const * owner, Appearance const * appearance, bool objectFloor ) +{ + FloorMesh * pMesh = const_cast(FloorMeshList::fetch(floorMeshFilename)); + + if(!pMesh) + { + WARNING(true, ("Could not find floor mesh %s", floorMeshFilename)); + return NULL; + } + + // ---------- + + Floor * pFloor = new Floor( pMesh, owner, appearance, objectFloor ); + + pMesh->releaseReference(); + + return pFloor; +} + +// ---------------------------------------------------------------------- + diff --git a/engine/shared/library/sharedCollision/src/shared/core/FloorManager.h b/engine/shared/library/sharedCollision/src/shared/core/FloorManager.h new file mode 100644 index 00000000..92341e23 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/FloorManager.h @@ -0,0 +1,52 @@ +// ====================================================================== +// +// FloorManager.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_FloorManager_H +#define INCLUDED_FloorManager_H + +// ====================================================================== + +class Appearance; +class BaseClass; +class DebugShapeRenderer; +class Floor; +class Iff; +class Object; + +typedef BaseClass * (*ObjectFactory) ( Iff & iff ); +typedef void (*ObjectWriter) ( BaseClass const * object, Iff & iff ); +typedef void (*ObjectRenderer) ( BaseClass const * object, DebugShapeRenderer * renderer ); + +// ---------------------------------------------------------------------- + +class FloorManager +{ +public: + + // ---------- + + static void setPathGraphFactory ( ObjectFactory factory ); + static ObjectFactory getPathGraphFactory ( void ); + + static void setPathGraphWriter ( ObjectWriter writer ); + static ObjectWriter getPathGraphWriter ( void ); + + static void setPathGraphRenderer ( ObjectRenderer renderer ); + static ObjectRenderer getPathGraphRenderer ( void ); + + // ---------- + + //! Load the floor mesh with the given filename and create a new floor + // that uses the mesh + + static Floor * createFloor ( const char * floorMeshFilename, Object const * owner, Appearance const * appearance, bool objectFloor ); +}; + +// ====================================================================== + +#endif // #ifndef INCLUDED_FloorManager_H + diff --git a/engine/shared/library/sharedCollision/src/shared/core/FloorMesh.cpp b/engine/shared/library/sharedCollision/src/shared/core/FloorMesh.cpp new file mode 100644 index 00000000..01ec2350 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/FloorMesh.cpp @@ -0,0 +1,3985 @@ +// ====================================================================== +// +// FloorMesh.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/FloorMesh.h" + +#include "sharedCollision/BaseClass.h" +#include "sharedCollision/ConfigSharedCollision.h" +#include "sharedCollision/FloorLocator.h" +#include "sharedCollision/FloorTri.h" +#include "sharedCollision/CollisionUtils.h" +#include "sharedCollision/FloorManager.h" + +#include "sharedCollision/CollisionWorld.h" +#include "sharedCollision/Overlap2d.h" +#include "sharedCollision/Overlap3d.h" +#include "sharedCollision/Distance3d.h" +#include "sharedCollision/Intersect3d.h" +#include "sharedCollision/Intersect2d.h" +#include "sharedCollision/Containment2d.h" +#include "sharedCollision/Distance2d.h" +#include "sharedCollision/CollisionUtils.h" +#include "sharedCollision/SimpleExtent.h" +#include "sharedCollision/BoxTree.h" +#include "sharedCollision/Extent.h" +#include "sharedCollision/BoxExtent.h" + +#include "sharedDebug/DataLint.h" + +#include "sharedMath/Triangle3d.h" +#include "sharedMath/Line3d.h" +#include "sharedMath/Segment3d.h" +#include "sharedMath/Plane3d.h" +#include "sharedMath/Ribbon3d.h" +#include "sharedMath/AxialBox.h" +#include "sharedMath/Circle.h" +#include "sharedMath/DebugShapeRenderer.h" +#include "sharedMath/PackedArgb.h" +#include "sharedMath/Sphere.h" + +#include "sharedFoundation/Tag.h" +#include "sharedFoundation/DataResourceList.h" + +#include "sharedFile/Iff.h" + +#include "sharedMath/IndexedTriangleList.h" +#include "sharedMath/Transform.h" + +#include "sharedObject/Appearance.h" + +#include "sharedRandom/Random.h" + +#include +#include +#include +#include +#include + +typedef std::deque FloorTriIdQueue; +typedef std::stack > FloorTriStack; + +float wallEpsilon = 0.01f; + +float gs_hopTolerance = 0.3f; +float gs_clearTolerance = 0.001f; +float gs_hitPastTolerance = 0.2f; // how far (meters) in the past a hit can occur and still be considered a hit + +const Tag TAG_VERT = TAG(V,E,R,T); +const Tag TAG_TRIS = TAG(T,R,I,S); +const Tag TAG_FLOR = TAG(F,L,O,R); +const Tag TAG_PNOD = TAG(P,N,O,D); +const Tag TAG_PEDG = TAG(P,E,D,G); +const Tag TAG_BTRE = TAG(B,T,R,E); +const Tag TAG_BEDG = TAG(B,E,D,G); +const Tag TAG_PGRF = TAG(P,G,R,F); + +template <> FloorMeshList::CreateDataResourceMap *FloorMeshList::ms_bindings = NULL; +template <> FloorMeshList::LoadedDataResourceMap *FloorMeshList::ms_loaded = NULL; + +// ---------------------------------------------------------------------- + +FloorMesh::FloorMesh(const std::string & filename) +: CollisionMesh(), + DataResource(filename.c_str ()), + m_vertices( new VectorVector() ), + m_floorTris( new FloorTriVec() ), + m_crossableEdges( new FloorEdgeIdVec() ), + m_uncrossableEdges( new FloorEdgeIdVec() ), + m_wallBaseEdges( new FloorEdgeIdVec() ), + m_wallTopEdges( new FloorEdgeIdVec() ), + m_pathGraph(NULL), + m_appearance(NULL), + m_triMarkCounter(1000), // just some random number + m_objectFloor(false) +{ +#ifdef _DEBUG + + m_crossableLines = NULL; + m_uncrossableLines = NULL; + m_interiorLines = NULL; + m_portalLines = NULL; + m_rampLines = NULL; + m_fallthroughTriLines = NULL; + m_solidTriLines = NULL; + +#endif +} + +// ---------- + +FloorMesh::FloorMesh( VectorVector const & vertices, IntVector const & indices ) +: CollisionMesh(), + DataResource(""), + m_vertices( new VectorVector() ), + m_floorTris( new FloorTriVec() ), + m_crossableEdges( new FloorEdgeIdVec() ), + m_uncrossableEdges( new FloorEdgeIdVec() ), + m_wallBaseEdges( new FloorEdgeIdVec() ), + m_wallTopEdges( new FloorEdgeIdVec() ), + m_pathGraph(NULL), + m_appearance(NULL), + m_triMarkCounter(1000), + m_objectFloor(false) +{ + build(vertices,indices); + +#ifdef _DEBUG + + m_crossableLines = NULL; + m_uncrossableLines = NULL; + m_interiorLines = NULL; + m_portalLines = NULL; + m_rampLines = NULL; + m_fallthroughTriLines = NULL; + m_solidTriLines = NULL; + +#endif +} + +// ---------- + +FloorMesh::~FloorMesh() +{ + delete m_vertices; + m_vertices = NULL; + + delete m_floorTris; + m_floorTris = NULL; + + delete m_crossableEdges; + m_crossableEdges = NULL; + + delete m_uncrossableEdges; + m_crossableEdges = NULL; + + delete m_wallBaseEdges; + m_wallBaseEdges = NULL; + + delete m_wallTopEdges; + m_wallTopEdges = NULL; + + delete m_pathGraph; + m_pathGraph = NULL; + + m_appearance = NULL; + +#ifdef _DEBUG + + delete m_crossableLines; + m_crossableLines = NULL; + + delete m_uncrossableLines; + m_uncrossableLines = NULL; + + delete m_interiorLines; + m_interiorLines = NULL; + + delete m_portalLines; + m_portalLines = NULL; + + delete m_rampLines; + m_rampLines = NULL; + + delete m_fallthroughTriLines; + m_fallthroughTriLines = NULL; + + delete m_solidTriLines; + m_solidTriLines = NULL; + +#endif +} + +// ---------------------------------------------------------------------- + +void FloorMesh::calcBounds ( void ) const +{ + AxialBox bounds; + + for(int i = 0; i < getVertexCount(); i++) + { + bounds.add( getVertex(i) ); + } + + // ---------- + // floor collision will catch when a player is over/under a floor + // mesh + + bounds.setMax( bounds.getMax() + Vector(0.0f,2.0f,0.0f) ); + bounds.setMin( bounds.getMin() - Vector(0.0f,1.0f,0.0f) ); + + // ---------- + + if(m_appearance) + { + Extent const * extent = m_appearance->getExtent(); + + BoxExtent const * boxExtent = dynamic_cast(extent); + + AxialBox const & box = boxExtent->getBox(); + + if(boxExtent) + { + bounds.add(box.getCorner(0)); + bounds.add(box.getCorner(1)); + bounds.add(box.getCorner(2)); + bounds.add(box.getCorner(3)); + } + } + + // ---------- + + m_extent->setShape( MultiShape(bounds) ); + + setBoundsDirty(false); +} + +// ====================================================================== +// Basic interface inherited from CollisionMesh - accessors + +int FloorMesh::getVertexCount ( void ) const +{ + return m_vertices->size(); +} + +Vector const & FloorMesh::getVertex ( int whichVertex ) const +{ + return m_vertices->at(whichVertex); +} + +void FloorMesh::setVertex ( int whichVertex, Vector const & newPoint ) +{ + m_vertices->at(whichVertex) = newPoint; +} + +// ---------- + +int FloorMesh::getTriCount ( void ) const +{ + return m_floorTris->size(); +} + +IndexedTri const & FloorMesh::getIndexedTri ( int whichTri ) const +{ + return m_floorTris->at(whichTri); +} + +void FloorMesh::setIndexedTri ( int whichTri, IndexedTri const & newTri ) +{ + IndexedTri & I = m_floorTris->at(whichTri); + + I = newTri; +} + +// ---------- + +Triangle3d FloorMesh::getTriangle ( int whichTri ) const +{ + FloorTri const & F = getFloorTri(whichTri); + + Vector const & A = getVertex( F.getCornerIndex(0) ); + Vector const & B = getVertex( F.getCornerIndex(1) ); + Vector const & C = getVertex( F.getCornerIndex(2) ); + + return Triangle3d(A,B,C); +} + +// ---------------------------------------------------------------------- + +Transform const & FloorMesh::getTransform_o2p ( void ) const +{ + return Transform::identity; +} + +// ---------------------------------------------------------------------- + +void FloorMesh::addTriangle ( Triangle3d const & t ) +{ + int index = static_cast( m_vertices->size() ); + + m_vertices->push_back(t.getCornerA()); + m_vertices->push_back(t.getCornerB()); + m_vertices->push_back(t.getCornerC()); + + addTriangle( index + 0, index + 1, index + 2 ); +} + +// ---------- + +void FloorMesh::addTriangle ( int iA, int iB, int iC ) +{ + FloorTri F; + + F.setCornerIndex( 0, iA ); + F.setCornerIndex( 1, iB ); + F.setCornerIndex( 2, iC ); + + F.setNeighborIndex( 0, -1 ); + F.setNeighborIndex( 1, -1 ); + F.setNeighborIndex( 2, -1 ); + + Vector A = getVertex( iA ); + Vector B = getVertex( iB ); + Vector C = getVertex( iC ); + + Vector temp = (C-A).cross(B-A); + IGNORE_RETURN( temp.normalize() ); + + F.setNormal( temp ); + + F.setIndex( static_cast( getTriCount() ) ); + + m_floorTris->push_back(F); +} + +// ---------------------------------------------------------------------- + +void FloorMesh::deleteVertex ( int whichVertex ) +{ + UNREF(whichVertex); + + FATAL(true,("can't delete floor vertices yet...")); +} + +void FloorMesh::deleteVertices ( IntVector const & vertIndices ) +{ + UNREF(vertIndices); + + FATAL(true,("can't delete floor vertices yet...")); +} + +// ---------------------------------------------------------------------- + +void FloorMesh::deleteTri ( int whichTriangle ) +{ + IGNORE_RETURN( m_floorTris->erase( m_floorTris->begin() + whichTriangle ) ); + + //@todo - This really doesn't need to be called after every erase, + // it ought to be called after all the erasing is done. + + assignIndices(); +} + +// ---------- +// Mark-n-sweep delete + +void FloorMesh::deleteTris ( IntVector const & triIndices ) +{ + if(triIndices.empty()) return; + + // ---------- + + clearMarks(0); + + // ---------- + + int indexCount = triIndices.size(); + + for(int iBad = 0; iBad < indexCount; iBad++) + { + getFloorTri( triIndices[iBad] ).setMark(1); + } + + // ---------- + + FloorTriVec * pGoodTris = new FloorTriVec; + + for(int iTri = 0; iTri < getTriCount(); iTri++) + { + FloorTri const & F = getFloorTri(iTri); + + if(F.getMark() != 1) + { + pGoodTris->push_back(F); + } + } + + // ---------- + + FloorTriVec * pOldTris = m_floorTris; + + m_floorTris = pGoodTris; + + delete pOldTris; + + assignIndices(); +} + +// ---------------------------------------------------------------------- + +void FloorMesh::clear ( void ) +{ + CollisionMesh::clear(); + + m_floorTris->clear(); + m_vertices->clear(); + + m_crossableEdges->clear(); + m_uncrossableEdges->clear(); + m_wallBaseEdges->clear(); + m_wallTopEdges->clear(); +} + +// ---------------------------------------------------------------------- + +void FloorMesh::assignIndices ( void ) +{ + for(int i = 0; i < getTriCount(); i++) + { + getFloorTri(i).setIndex( static_cast(i) ); + } +} + +// ---------------------------------------------------------------------- + +void FloorMesh::clearMarks ( int clearMarkValue ) +{ + for(int i = 0; i < getTriCount(); i++) + { + FloorTri & F = getFloorTri(i); + + F.setMark(clearMarkValue); + F.setEdgeMark(0,clearMarkValue); + F.setEdgeMark(1,clearMarkValue); + F.setEdgeMark(2,clearMarkValue); + } +} + +// ---------------------------------------------------------------------- + +void FloorMesh::clearPortalEdges ( void ) +{ + for(int i = 0; i < getTriCount(); i++) + { + FloorTri & F = getFloorTri(i); + + F.setPortalId(0,-1); + F.setPortalId(1,-1); + F.setPortalId(2,-1); + } +} + +// ---------------------------------------------------------------------- + +void FloorMesh::setAppearance ( Appearance const * appearance ) +{ + m_appearance = appearance; + + if(m_appearance && m_extent) + { + Extent const * extent = appearance->getExtent(); + + BoxExtent const * boxExtent = dynamic_cast(extent); + + if(boxExtent) + { + AxialBox bounds = m_extent->getShape().getAxialBox(); + + AxialBox box = boxExtent->getBox(); + + bounds.add(box.getCorner(0)); + bounds.add(box.getCorner(1)); + bounds.add(box.getCorner(2)); + bounds.add(box.getCorner(3)); + + m_extent->setShape( MultiShape(bounds) ); + } + } +} + +// ---------------------------------------------------------------------- + +void FloorMesh::build ( VectorVector const & verts, IntVector const & indices ) +{ + (*m_vertices) = verts; + m_floorTris->clear(); + + // ---------- + + int nTris = indices.size() / 3; + + for(int i = 0; i < nTris; i++) + { + int A = indices[i * 3 + 0]; + int B = indices[i * 3 + 1]; + int C = indices[i * 3 + 2]; + + addTriangle(A,B,C); + } +} + +// ---------------------------------------------------------------------- +// Flag all edges of all floor tris referenced by edgePairs as crossable + +//@todo - Uses brute-force search, may be slow for big meshes. + +void FloorMesh::addCrossableEdges ( IntVector const & edgePairs ) +{ + for(int iTri = 0; iTri < getTriCount(); iTri++) + { + FloorTri & F = getFloorTri(iTri); + + // ---------- + + int nPairs = edgePairs.size() / 2; + + for(int iPair = 0; iPair < nPairs; iPair++) + { + int A = edgePairs[iPair * 2 + 0]; + int B = edgePairs[iPair * 2 + 1]; + + int whichEdge = FindEdgeUndirected(F,A,B); + + if(whichEdge != -1) + { + F.setEdgeType(whichEdge,FET_Crossable); + } + } + } +} + +// ---------------------------------------------------------------------- +// Flag all edges of all floor tris referenced by edgePairs as crossable + +//@todo - Uses brute-force search, may be slow for big meshes. + +void FloorMesh::addRampEdges ( IntVector const & edgePairs ) +{ + for(int iTri = 0; iTri < getTriCount(); iTri++) + { + FloorTri & F = getFloorTri(iTri); + + // ---------- + + int nPairs = edgePairs.size() / 2; + + for(int iPair = 0; iPair < nPairs; iPair++) + { + int A = edgePairs[iPair * 2 + 0]; + int B = edgePairs[iPair * 2 + 1]; + + int whichEdge = FindEdgeUndirected(F,A,B); + + if(whichEdge != -1) + { + F.setEdgeType(whichEdge,FET_WallBase); + } + } + } +} + +// ---------------------------------------------------------------------- +// Flag the tris in the floor we want to be able to fall through as +// fall-through-able + +void FloorMesh::addFallthroughTris ( IntVector const & triIDs ) +{ + int idCount = triIDs.size(); + + for(int iTri = 0; iTri < idCount; iTri++) + { + int index = triIDs[iTri]; + + FloorTri & F = getFloorTri( index ); + + F.setFallthrough(true); + } +} + +// ---------------------------------------------------------------------- +// All edges of all floor tris start out unlinked and non-crossable. +// Once we've linked all the tris up, go through and flag any edges that +// now have neighbors as crossable. + +void FloorMesh::flagCrossableEdges ( void ) +{ + for(int iTri = 0; iTri < getTriCount(); iTri++) + { + FloorTri & F = getFloorTri(iTri); + + // ---------- + + for(int iEdge = 0; iEdge < 3; iEdge++) + { + if(F.hasNeighbor(iEdge)) + { + F.setEdgeType(iEdge,FET_Crossable); + } + } + } +} + +// ---------------------------------------------------------------------- + +FloorTri & FloorMesh::getFloorTri ( int whichTri ) +{ + FATAL(whichTri < 0, ("FloorMesh::getFloorTri - tried to get an invalid tri")); + FATAL(whichTri >= getTriCount(), ("FloorMesh::getFloorTri - tried to get an invalid tri")); + + return m_floorTris->at( whichTri ); +} + +FloorTri const & FloorMesh::getFloorTri ( int whichTri ) const +{ + FATAL(whichTri < 0, ("FloorMesh::getFloorTri - tried to get an invalid tri")); + FATAL(whichTri >= getTriCount(), ("FloorMesh::getFloorTri - tried to get an invalid tri")); + + return m_floorTris->at( whichTri ); +} + +// ---------- + +FloorEdgeIdVec const & FloorMesh::getEdgeList ( FloorEdgeType edgeType ) const +{ + switch(edgeType) + { + case FET_Crossable: return *m_crossableEdges; + case FET_Uncrossable: return *m_uncrossableEdges; + case FET_WallBase: return *m_wallBaseEdges; + case FET_WallTop: return *m_wallTopEdges; + default: { + DEBUG_FATAL(true,("FloorMesh::getEdgeList - requesting invalid list $d\n",edgeType)); + static FloorEdgeIdVec dummy; + return dummy; + } + } +} + +// ---------------------------------------------------------------------- + +BaseClass * FloorMesh::getPathGraph ( void ) +{ + return m_pathGraph; +} + +BaseClass const * FloorMesh::getPathGraph ( void ) const +{ + return m_pathGraph; +} + +void FloorMesh::attachPathGraph ( BaseClass * newGraph ) +{ + if(m_pathGraph != newGraph) + { + delete m_pathGraph; + m_pathGraph = newGraph; + } +} + +// ---------------------------------------------------------------------- +// Clean out any unused vertices and update the vertex indices in the +// floor tris to match + +void FloorMesh::sweep ( void ) +{ + int nVerts = getVertexCount(); + int nTris = getTriCount(); + + // ---------- + // Build our vertex reference count table + + IntVector vertRefcount(nVerts,0); + + for(int iTri = 0; iTri < nTris; iTri++) + { + IndexedTri const & tri = getIndexedTri(iTri); + + int A = tri.getCornerIndex(0); + int B = tri.getCornerIndex(1); + int C = tri.getCornerIndex(2); + + vertRefcount[A]++; + vertRefcount[B]++; + vertRefcount[C]++; + } + + // ---------- + // Use the refcount table to build a remap table that will pack + // all the used vertices at the front of the array + + IntVector vertRemap(nVerts); + + int nUsedVerts = 0; + int cursor = 0; + + for(int iRemap = 0; iRemap < nVerts; iRemap++) + { + if(vertRefcount[iRemap] != 0) + { + nUsedVerts++; + + vertRemap[iRemap] = cursor; + + cursor++; + } + else + { + vertRemap[iRemap] = -1; + } + } + + // ---------- + // Use the remap table to pack all vertex data at the beginning of the array + + for(int iVert = 0; iVert < nVerts; iVert++) + { + int newIndex = vertRemap[iVert]; + + // Skip unused vertices & vertices that aren't moving + + if(newIndex == -1) continue; + if(newIndex == static_cast(iVert)) continue; + + // Move the used vertices to their new place in the array + + setVertex( newIndex, getVertex(iVert) ); + } + + // ---------- + // Use the remap table to update the floor tri's vertex indices + + for(int iTri2 = 0; iTri2 < nTris; iTri2++) + { + IndexedTri temp = getIndexedTri(iTri2); + + int iA = vertRemap[ temp.getCornerIndex(0) ]; + int iB = vertRemap[ temp.getCornerIndex(1) ]; + int iC = vertRemap[ temp.getCornerIndex(2) ]; + + temp.setCornerIndex( 0, iA ); + temp.setCornerIndex( 1, iB ); + temp.setCornerIndex( 2, iC ); + + setIndexedTri( iTri2, temp ); + } + + // ---------- + // Data is repacked, shrink the array to hold only the used verts + + m_vertices->resize(nUsedVerts); +} + +// ---------------------------------------------------------------------- +// Brute-force, ON^2 adjacency calc - try and link every tri with every +// other tri + +void FloorMesh::link ( void ) +{ + int nTris = getTriCount(); + + for(int A = 0; A < nTris-1; A++) + { + for(int B = A+1; B < nTris; B++) + { + LinkTris( getFloorTri(A), getFloorTri(B) ); + } + } +} + +// ---------------------------------------------------------------------- + +void FloorMesh::compile ( void ) +{ + IGNORE_RETURN( scrub() ); // get rid of any degenerate triangles + merge(); // merge the vertices for the triangles + sweep(); // sweep out any unused vertices + link(); // build adjacency information for the triangles + flagCrossableEdges(); // use the adjacency information to mark internal floor edges as crossable + calcBounds(); // build the bounding box for the floor + setPartTags(); + buildBoundaryEdgeList(); +} + +// ---------------------------------------------------------------------- + +void FloorMesh::write ( Iff & iff ) +{ + iff.insertForm(TAG_FLOR); + iff.insertForm(TAG_0006); + + // ---------- + + iff.insertChunk(TAG_VERT); + { + int vertCount = getVertexCount(); + + iff.insertChunkData(vertCount); + + for (int i = 0; i < vertCount; i++) + { + iff.insertChunkFloatVector( getVertex(i) ); + } + } + iff.exitChunk(TAG_VERT); + + // ---------- + + iff.insertChunk(TAG_TRIS); + { + int triCount = getTriCount(); + + iff.insertChunkData(triCount); + + for (int i = 0; i < triCount; i++) + { + getFloorTri(i).write(iff); + } + } + iff.exitChunk(TAG_TRIS); + + // ---------- + + if(m_boxTree != NULL) + { + m_boxTree->write(iff); + } + + // ---------- + + if(!m_crossableEdges->empty() || !m_uncrossableEdges->empty() || !m_wallBaseEdges->empty()) + { + iff.insertChunk(TAG_BEDG); + { + int totalEdgeCount = m_crossableEdges->size() + m_uncrossableEdges->size() + m_wallBaseEdges->size(); + + iff.insertChunkData(totalEdgeCount); + + { + int edgeCount = m_crossableEdges->size(); + + for(int i = 0; i < edgeCount; i++) + { + FloorEdgeId & id = m_crossableEdges->at(i); + + bool crossable = getFloorTri(id.triId).isCrossable(id.edgeId); + + iff.insertChunkData(id.triId); + iff.insertChunkData(id.edgeId); + iff.insertChunkData(crossable); + } + } + + { + int edgeCount = m_uncrossableEdges->size(); + + for(int i = 0; i < edgeCount; i++) + { + FloorEdgeId & id = m_uncrossableEdges->at(i); + + bool crossable = getFloorTri(id.triId).isCrossable(id.edgeId); + + iff.insertChunkData(id.triId); + iff.insertChunkData(id.edgeId); + iff.insertChunkData(crossable); + } + } + + { + int edgeCount = m_wallBaseEdges->size(); + + for(int i = 0; i < edgeCount; i++) + { + FloorEdgeId & id = m_wallBaseEdges->at(i); + + bool crossable = getFloorTri(id.triId).isCrossable(id.edgeId); + + iff.insertChunkData(id.triId); + iff.insertChunkData(id.edgeId); + iff.insertChunkData(crossable); + } + } + } + iff.exitChunk(TAG_BEDG); + } + + // ---------- + + ObjectWriter graphWriter = FloorManager::getPathGraphWriter(); + + if(m_pathGraph && graphWriter) + { + graphWriter(m_pathGraph,iff); + } + + // ---------- + + iff.exitForm(TAG_0006); + iff.exitForm(TAG_FLOR); +} + +// ---------------------------------------------------------------------- + +void FloorMesh::read ( Iff & iff ) +{ + clear(); + + // ---------- + + int version = -1; + + iff.enterForm(TAG_FLOR); + + switch (iff.getCurrentName()) + { + case TAG_0000: + version = 0; + read_0000(iff); + break; + + case TAG_0001: + version = 1; + read_0001(iff); + break; + + case TAG_0002: + version = 2; + read_0002(iff); + break; + + case TAG_0003: + version = 3; + read_0003(iff); + break; + + case TAG_0004: + version = 4; + read_0004(iff); + break; + + case TAG_0005: + version = 5; + read_0005(iff); + break; + + case TAG_0006: + version = 6; + read_0006(iff); + break; + + default: + version = -1; + FATAL(true,("FloorMesh::Invalid version")); + break; + } + + iff.exitForm(TAG_FLOR); + + // ---------- + // Post-load stuff + + // floor versions 6 and earlier calculate their floor tri normals upside down. fix that here. + + if(version <= 6) + { + int triCount = getTriCount(); + + for(int i = 0; i < triCount; i++) + { + FloorTri & T = getFloorTri(i); + + T.setNormal( -T.getNormal() ); + } + } + + // Check for inverted floor tris + + if(DataLint::isInstalled()) + { + int triCount = getTriCount(); + + for(int i = 0; i < triCount; i++) + { + FloorTri & T = getFloorTri(i); + + if(T.getNormal().y <= 0.0f) + { + DEBUG_WARNING(true,("FloorMesh::read - Floor %s contains a triangle that's not facing up (art error) [%1.2f, %1.2f, %1.2f]",iff.getFileName(), T.getNormal().x, T.getNormal().y, T.getNormal().z)); + break; + } + } + } + + calcBounds(); + + calcHeightFuncs(); + + if(version < 5) buildBoundaryEdgeList(); + +#ifdef _DEBUG + + if(ConfigSharedCollision::getBuildDebugData()) + { + buildDebugData(); + } + +#endif +} + +// ---------------------------------------------------------------------- + +void FloorMesh::read_0000 ( Iff & iff ) +{ + iff.enterForm(TAG_0000); + + // ---------- + + iff.enterChunk(TAG_VERT); + { + while(iff.getChunkLengthLeft()) + { + Vector V = iff.read_floatVector(); + + m_vertices->push_back(V); + } + } + iff.exitChunk(TAG_VERT); + + // ---------- + + iff.enterChunk(TAG_TRIS); + { + while(iff.getChunkLengthLeft()) + { + FloorTri F; + + F.setIndex( iff.read_int32() ); + + F.setCornerIndex( 0, iff.read_int32() ); + F.setCornerIndex( 1, iff.read_int32() ); + F.setCornerIndex( 2, iff.read_int32() ); + + F.setNeighborIndex( 0, iff.read_int32() ); + F.setNeighborIndex( 1, iff.read_int32() ); + F.setNeighborIndex( 2, iff.read_int32() ); + + F.setNormal( iff.read_floatVector() ); + + F.setEdgeType( 0, FET_Uncrossable ); + F.setEdgeType( 1, FET_Uncrossable ); + F.setEdgeType( 2, FET_Uncrossable ); + + F.setFallthrough( false ); + + m_floorTris->push_back(F); + } + } + iff.exitChunk(TAG_TRIS); + + // ---------- + + iff.exitForm(TAG_0000); + + // Gotta fix up marking edges as crossable if this is an old floormesh + + flagCrossableEdges(); +} + +// ---------------------------------------------------------------------- + +void FloorMesh::read_0001 ( Iff & iff ) +{ + iff.enterForm(TAG_0001); + + // ---------- + + iff.enterChunk(TAG_VERT); + { + while(iff.getChunkLengthLeft()) + { + Vector V = iff.read_floatVector(); + + m_vertices->push_back(V); + } + } + iff.exitChunk(TAG_VERT); + + // ---------- + + iff.enterChunk(TAG_TRIS); + { + while(iff.getChunkLengthLeft()) + { + FloorTri F; + + F.setIndex( iff.read_int32() ); + + F.setCornerIndex( 0, iff.read_int32() ); + F.setCornerIndex( 1, iff.read_int32() ); + F.setCornerIndex( 2, iff.read_int32() ); + + F.setNeighborIndex( 0, iff.read_int32() ); + F.setNeighborIndex( 1, iff.read_int32() ); + F.setNeighborIndex( 2, iff.read_int32() ); + + F.setNormal( iff.read_floatVector() ); + + F.setCrossable( 0, iff.read_bool8() ); + F.setCrossable( 1, iff.read_bool8() ); + F.setCrossable( 2, iff.read_bool8() ); + + F.setFallthrough( iff.read_bool8() ); + + m_floorTris->push_back(F); + } + } + iff.exitChunk(TAG_TRIS); + + // ---------- + + iff.exitForm(TAG_0001); +} + +// ---------------------------------------------------------------------- + +void FloorMesh::read_0002 ( Iff & iff ) +{ + iff.enterForm(TAG_0002); + + // ---------- + + iff.enterChunk(TAG_VERT); + { + while(iff.getChunkLengthLeft()) + { + Vector V = iff.read_floatVector(); + + m_vertices->push_back(V); + } + } + iff.exitChunk(TAG_VERT); + + // ---------- + + iff.enterChunk(TAG_TRIS); + { + while(iff.getChunkLengthLeft()) + { + FloorTri F; + + F.read_0000(iff); + + m_floorTris->push_back(F); + } + } + iff.exitChunk(TAG_TRIS); + + // ---------- + + iff.exitForm(TAG_0002); +} + +// ---------------------------------------------------------------------- + +void FloorMesh::read_0003 ( Iff & iff ) +{ + iff.enterForm(TAG_0003); + + // ---------- + + iff.enterChunk(TAG_VERT); + { + while(iff.getChunkLengthLeft()) + { + Vector V = iff.read_floatVector(); + + m_vertices->push_back(V); + } + } + iff.exitChunk(TAG_VERT); + + // ---------- + + iff.enterChunk(TAG_TRIS); + { + FloorTri F; + + while(iff.getChunkLengthLeft()) + { + F.read_0001(iff); + + m_floorTris->push_back(F); + } + } + iff.exitChunk(TAG_TRIS); + + // ---------- + + iff.exitForm(TAG_0003); +} + +// ---------------------------------------------------------------------- + +void FloorMesh::read_0004 ( Iff & iff ) +{ + iff.enterForm(TAG_0004); + + // ---------- + + iff.enterChunk(TAG_VERT); + { + while(iff.getChunkLengthLeft()) + { + Vector V = iff.read_floatVector(); + + m_vertices->push_back(V); + } + } + iff.exitChunk(TAG_VERT); + + // ---------- + + iff.enterChunk(TAG_TRIS); + { + FloorTri F; + + while(iff.getChunkLengthLeft()) + { + F.read_0001(iff); + + m_floorTris->push_back(F); + } + } + iff.exitChunk(TAG_TRIS); + + // ---------- + // Version 4 floor meshes had two chunks of pathfinding data, one for nodes + // and one for edges, but they weren't in a containing form. + + ObjectFactory pathGraphFactory = FloorManager::getPathGraphFactory(); + + if(pathGraphFactory) + { + // The factory knows how to build a path graph from the old + // data format. + + BaseClass * pathGraph = pathGraphFactory(iff); + + attachPathGraph(pathGraph); + } + else + { + // We have a path graph to load but no factory to load it + // with, so skip the form + + iff.enterChunk(TAG_PNOD); + iff.exitChunk(TAG_PNOD,true); + + iff.enterChunk(TAG_PEDG); + iff.exitChunk(TAG_PEDG,true); + } + + // ---------- + + iff.exitForm(TAG_0004); +} + +// ---------------------------------------------------------------------- + +void FloorMesh::read_0005 ( Iff & iff ) +{ + iff.enterForm(TAG_0005); + + // ---------- + + iff.enterChunk(TAG_VERT); + { + int vertexCount = iff.getChunkLengthTotal() / sizeof(Vector); + + m_vertices->clear(); + m_vertices->reserve(vertexCount); + + while(iff.getChunkLengthLeft()) + { + Vector V = iff.read_floatVector(); + + m_vertices->push_back(V); + } + } + iff.exitChunk(TAG_VERT); + + // ---------- + + iff.enterChunk(TAG_TRIS); + { + int triCount = iff.getChunkLengthTotal() / FloorTri::getOnDiskSize_0001(); + + m_floorTris->clear(); + m_floorTris->reserve(triCount); + + FloorTri F; + + while(iff.getChunkLengthLeft()) + { + F.read_0001(iff); + + m_floorTris->push_back(F); + } + } + iff.exitChunk(TAG_TRIS); + + // ---------- + + if(iff.getCurrentName() == TAG_BTRE) + { + m_boxTree = new BoxTree(); + + m_boxTree->read(iff); + } + + // ---------- + + if(iff.getCurrentName() == TAG_BEDG) + { + iff.enterChunk(TAG_BEDG); + { + int edgeCount = iff.read_int32(); + + for(int i = 0; i < edgeCount; i++) + { + int triId = iff.read_int32(); + int edgeId = iff.read_int32(); + + FloorEdgeId id(triId,edgeId); + + bool crossable = iff.read_bool8(); + + UNREF(crossable); // redundant now that we've got an edge type + + switch(getFloorTri(id.triId).getEdgeType(id.edgeId)) + { + case FET_Crossable: { m_crossableEdges->push_back(id); break; } + case FET_Uncrossable: { m_uncrossableEdges->push_back(id); break; } + case FET_WallBase: { m_wallBaseEdges->push_back(id); break; } + default: { break; } + } + } + } + iff.exitChunk(TAG_BEDG); + } + + // ---------- + + if (!iff.atEndOfForm() && iff.getCurrentName() == TAG_PGRF) + { + ObjectFactory pathGraphFactory = FloorManager::getPathGraphFactory(); + + if(pathGraphFactory && CollisionWorld::isServerSide()) + { + BaseClass * pathGraph = pathGraphFactory(iff); + + attachPathGraph(pathGraph); + } + else + { + // We have a path graph to load but no factory to load it + // with, so skip the form + + iff.enterForm(); + iff.exitForm(true); + } + } + + // ---------- + + iff.exitForm(TAG_0005); +} + +// ---------------------------------------------------------------------- + +void FloorMesh::read_0006 ( Iff & iff ) +{ + iff.enterForm(TAG_0006); + + // ---------- + + iff.enterChunk(TAG_VERT); + { + int vertexCount = iff.read_int32(); + + m_vertices->clear(); + m_vertices->reserve(vertexCount); + + for(int i = 0; i < vertexCount; i++) + { + Vector V = iff.read_floatVector(); + + m_vertices->push_back(V); + } + } + iff.exitChunk(TAG_VERT); + + // ---------- + + iff.enterChunk(TAG_TRIS); + { + int triCount = iff.read_int32(); + + m_floorTris->clear(); + m_floorTris->reserve(triCount); + + FloorTri F; + + for(int i = 0; i < triCount; i++) + { + F.read_0002(iff); + + m_floorTris->push_back(F); + } + } + iff.exitChunk(TAG_TRIS); + + // ---------- + + if(iff.getCurrentName() == TAG_BTRE) + { + m_boxTree = new BoxTree(); + + m_boxTree->read(iff); + } + + // ---------- + + if(iff.getCurrentName() == TAG_BEDG) + { + iff.enterChunk(TAG_BEDG); + { + int edgeCount = iff.read_int32(); + + for(int i = 0; i < edgeCount; i++) + { + int triId = iff.read_int32(); + int edgeId = iff.read_int32(); + + FloorEdgeId id(triId,edgeId); + + bool crossable = iff.read_bool8(); + + UNREF(crossable); // redundant now that we've got an edge type + + switch(getFloorTri(id.triId).getEdgeType(id.edgeId)) + { + case FET_Crossable: { m_crossableEdges->push_back(id); break; } + case FET_Uncrossable: { m_uncrossableEdges->push_back(id); break; } + case FET_WallBase: { m_wallBaseEdges->push_back(id); break; } + default: { break; } + } + } + } + iff.exitChunk(TAG_BEDG); + } + + // ---------- + + if (!iff.atEndOfForm() && iff.getCurrentName() == TAG_PGRF) + { + ObjectFactory pathGraphFactory = FloorManager::getPathGraphFactory(); + + if(pathGraphFactory && CollisionWorld::isServerSide()) + { + BaseClass * pathGraph = pathGraphFactory(iff); + + attachPathGraph(pathGraph); + } + else + { + // We have a path graph to load but no factory to load it + // with, so skip the form + + iff.enterForm(); + iff.exitForm(true); + } + } + + // ---------- + + iff.exitForm(TAG_0006); +} + +// ====================================================================== +// DataResource implementation + +void FloorMesh::load ( Iff & iff ) +{ + read(iff); +} + +// ---------- + +Tag FloorMesh::getId ( void ) const +{ + return TAG_FLOR; +} + + +// ---------------------------------------------------------------------- + +FloorMesh * FloorMesh::create ( const std::string & filename ) +{ + return new FloorMesh(filename); +} + +// ---------- + +void FloorMesh::release ( void ) const +{ + FloorMeshList::release(*this); +} + + +// ---------------------------------------------------------------------- + +void FloorMesh::install ( void ) +{ + FloorMeshList::registerTemplate(TAG_FLOR,FloorMesh::create); +} + +void FloorMesh::remove ( void ) +{ + FloorMeshList::remove(); +} + +// ---------------------------------------------------------------------- + +bool FloorMesh::findFloorTri ( FloorLocator const & testLoc, + float heightTolerance, + bool bAllowJump, + FloorLocator & outLoc ) const +{ + Vector testPoint = testLoc.getPosition_l(); + + // ---------- + // If the line through the test point doesn't intersect the floor's bounding box, + // the point can't land on the floor + + Line3d line(testPoint,-Vector::unitY); + + bool bHitsBounds = Overlap3d::TestLineABox( line, getBoundingABox() ); + + if(!bHitsBounds) + { + outLoc = FloorLocator::invalid; + return false; + } + + // ---------- + // Search the triangles for one that is below the test point + + FloorLocator closest; + + bool bFound = intersectClosest(line,closest); + + // ---------- + + if(!bFound || (closest.getId() == -1)) + { + outLoc = FloorLocator::invalid; + return false; + } + + // ---------- + + float dist = closest.getOffset(); + + if(dist > heightTolerance) + { + // Floor tri is too far below the test point + + outLoc = FloorLocator::invalid; + return false; + } + + if(dist > 0.0f) + { + // Floor tri is below the test point, we don't need a jump + // or hop to get on it + + outLoc = closest; + return true; + } + + // ---------- + + if(bAllowJump) + { + // Floor tri is above the test point, but we dont' care + // since we're allowing jumps + + outLoc = closest; + return true; + } + + // ---------- + + if(abs(dist) < gs_hopTolerance) + { + // Floor tri is above the test point but within the hop + // tolerance and we're allowing hops - we can hop up onto it. + + DEBUG_REPORT_LOG_PRINT(ConfigSharedCollision::getReportMessages(),("FloorMesh::findFloorTri - Found triangle we can hop onto\n")); + outLoc = closest; + return true; + } + + // ---------- + + if(getFloorTri( closest.getId() ).isFallthrough()) + { + // Floor tri is above the test point, out of hop range, and we're not allowing jumps + // BUT, since it's marked as fallthrough we can be on it anyway. + + DEBUG_REPORT_LOG_PRINT(ConfigSharedCollision::getReportMessages(),("FloorMesh::findFloorTri - Found fallthrough triangle we can jump onto\n")); + outLoc = closest; + return true; + } + + // ---------- + // No valid tri found + + outLoc = FloorLocator::invalid; + return false; +} + +// ---------- + +bool FloorMesh::findFloorTri ( FloorLocator const & testLoc, + bool bAllowJump, + FloorLocator & outLoc ) const +{ + return findFloorTri(testLoc,REAL_MAX,bAllowJump,outLoc); +} + +// ---------------------------------------------------------------------- + +void FloorMesh::makeHitResult ( Vector const & begin, Vector const & delta, int hitTriId, int hitEdgeId, float hitTime, FloorLocator & result ) const +{ + Vector endPoint = begin + delta * hitTime; + + Vector contactNormal = getTriangle(hitTriId).getEdgeDir(hitEdgeId).cross(-Vector::unitY); + + contactNormal.y = 0.0f; + + IGNORE_RETURN(contactNormal.normalize()); + + // ---------- + + result = FloorLocator(this,endPoint); + + result.setTriId(hitTriId); + result.setEdgeId(hitEdgeId); + + result.setHitTriId(hitTriId); + result.setHitEdgeId(hitEdgeId); + + result.setTime(hitTime); + result.setContactNormal(contactNormal); + + result.snapToFloor(); +} + +// ---------- + +void FloorMesh::makeHitResult2 ( Vector const & begin, Vector const & delta, int centerTriId, int hitTriId, int hitEdgeId, float hitTime, FloorLocator & result ) const +{ + Segment3d edge = getTriangle(hitTriId).getEdgeSegment(hitEdgeId); + + Vector endPoint = begin + delta * hitTime; + + Vector contactPoint = Distance2d::ClosestPointSeg(endPoint,edge); + Vector contactNormal = endPoint - contactPoint; + + contactNormal.y = 0.0f; + + IGNORE_RETURN(contactNormal.normalize()); + + // ---------- + + result = FloorLocator(this,endPoint); + + result.setTriId(centerTriId); + result.setEdgeId(-1); + + result.setHitTriId(hitTriId); + result.setHitEdgeId(hitEdgeId); + + result.setTime(hitTime); + result.setContactNormal(contactNormal); + + result.snapToFloor(); +} + +// ---------- + +void FloorMesh::makeExitResult ( Vector const & begin, Vector const & delta, int hitTriId, int hitEdgeId, float hitTime, FloorLocator & result ) const +{ + Segment3d edge = getTriangle(hitTriId).getEdgeSegment(hitEdgeId); + + Vector endPoint = begin + delta * hitTime; + + Vector contactPoint = Distance2d::ClosestPointSeg(endPoint,edge); + Vector contactNormal = endPoint - contactPoint; + + contactNormal.y = 0.0f; + + IGNORE_RETURN(contactNormal.normalize()); + + // ---------- + + result = FloorLocator(this,endPoint); + + result.setTriId(hitTriId); + result.setEdgeId(hitEdgeId); + + result.setHitTriId(hitTriId); + result.setHitEdgeId(hitEdgeId); + + result.setTime(hitTime); + result.setContactNormal(contactNormal); + + result.snapToFloor(); +} + +// ---------- + +void FloorMesh::makeSuccessResult ( FloorLocator const & loc, FloorLocator & result ) const +{ + result = loc; + + result.setEdgeId(-1); + result.setTime(1.0f); +} + +// ---------- + +void FloorMesh::makeFailureResult ( FloorLocator & result ) const +{ + result = FloorLocator::invalid; +} + +// ---------------------------------------------------------------------- +// Triangles facing down cannot be entered + +// Crossable edges of fallthrough tris can be entered from anywhere +// uncrossable edges cannot be entered + +// Crossable edges of non-fallthrough tris can be entered from above +// Ramp edges can be entered from above + +bool FloorMesh::canEnterEdge ( FloorLocator const & enterLoc ) const +{ + if(enterLoc.getEdgeId() == -1) return false; + if(enterLoc.getTriId() == -1) return false; + if(enterLoc.getFloorMesh() == NULL) return false; + + // Can't enter the tri if it's facing down + + if(getTriangle(enterLoc.getTriId()).getNormal().y < 0.0f) + { + return false; + } + + // ---------- + + FloorTri const & F = enterLoc.getFloorTri(); + + FloorEdgeType edgeType = F.getEdgeType(enterLoc.getEdgeId()); + + if(edgeType == FET_Uncrossable) + { + return false; + } + else if(edgeType == FET_Crossable) + { + if(F.isFallthrough()) + { + return true; + } + else + { + return enterLoc.getOffset() >= -1.0f; + } + } + else if(edgeType == FET_WallBase) + { + return enterLoc.getOffset() >= 0.0f; + } + else + { + DEBUG_WARNING(true,("FloorMesh::canEnterEdge - bad edge type")); + return false; + } +} + +bool FloorMesh::canEnterEdge ( FloorLocator const & startLoc, Vector const & delta, FloorEdgeId const & id, bool useRadius ) const +{ + FloorLocator entryLoc; + + if(intersectEdge(startLoc,delta,id,useRadius,false,entryLoc)) + { + return canEnterEdge(entryLoc); + } + else + { + return false; + } +} + +// ---------------------------------------------------------------------- +// Crossable edges can be exited from anywhere +// uncrossable edges cannot be exited + +// Ramp edges can be exited only if doing so causes a valid reentrance + +bool FloorMesh::canExitEdge ( FloorLocator const & exitLoc, Vector const & delta, bool useRadius ) const +{ + if(exitLoc.getTriId() == -1) return false; + if(exitLoc.getEdgeId() == -1) return false; + if(exitLoc.getFloorMesh() == NULL) return false; + + FloorEdgeType edgeType = exitLoc.getFloorTri().getEdgeType(exitLoc.getEdgeId()); + + Vector exitPos = exitLoc.getPosition_p(); + + UNREF(exitPos); + + if(edgeType == FET_Uncrossable) + { + return false; + } + else if(edgeType == FET_Crossable) + { + return true; + } + else if(edgeType == FET_WallBase) + { + // Can only enter through a non-fallthrough tri if the path crosses over + // the boundary + + FloorLocator entryLoc; + + // this is a bit messy - we want to see if there's a crossable edge immediately after + // the ramp edge that the locator can enter through, so we chop the move delta down + // to 10 cm long and use that for the test. + + Vector testDelta = delta; + + if(testDelta.magnitudeSquared() > 0.01f) + { + testDelta.normalize(); + + testDelta *= 0.1f; + } + + if(findEntrance(exitLoc,testDelta,useRadius,entryLoc)) + { + Vector enterPos = entryLoc.getPosition_p(); + + UNREF(enterPos); + + return true; + } + else + { + // if we're moving away from the edge, we consider it to be exitable + // (needed to do this to prevent a bug, but what was the bug?) + + Vector edgeDir = getTriangle(exitLoc.getTriId()).getEdgeDir(exitLoc.getEdgeId()); + + Vector edgeNormal(edgeDir.z,0.0f,-edgeDir.x); + + if(delta.dot(edgeNormal) > 0.0f) + { + return true; + } + else + { + return false; + } + } + } + else + { + DEBUG_WARNING(true,("FloorMesh::canExitEdge - bad edge type")); + + return false; + } +} + +// ---------- + +bool FloorMesh::canExitEdge ( FloorLocator const & startLoc, Vector const & delta, FloorEdgeId const & id, bool useRadius ) const +{ + if(id.triId == -1) return false; + if(id.edgeId == -1) return false; + + FloorEdgeType edgeType = getFloorTri(id.triId).getEdgeType(id.edgeId); + + if(edgeType == FET_Uncrossable) + { + return false; + } + else if(edgeType == FET_Crossable) + { + return true; + } + else + { + FloorLocator exitLoc; + + if(intersectEdge(startLoc,delta,id,useRadius,true,exitLoc)) + { + Vector newDelta = delta - (delta * exitLoc.getTime()); + + return canExitEdge(exitLoc,newDelta,useRadius); + } + else + { + return false; + } + } +} + +// ---------------------------------------------------------------------- + +PathWalkResult FloorMesh::findStartingTri ( FloorLocator const & startLoc, Vector const & delta, bool useRadius, int & outTriId ) const +{ + FloorLocator entryLoc; + + if(findEntrance(startLoc,delta,useRadius,entryLoc)) + { + outTriId = entryLoc.getId(); + + return PWR_WalkOk; + } + else + { + return PWR_DoesntEnter; + } +} + +// ---------------------------------------------------------------------- + +bool FloorMesh::findClosestLocation ( FloorLocator const & testLoc, FloorLocator & result ) const +{ + Vector point = testLoc.getPosition_l(); + + result = FloorLocator::invalid; + + real minDist = REAL_MAX; + Vector minPoint = Vector::zero; + int minId = -1; + + for(int i = 0; i < getTriCount(); i++) + { + Triangle3d T = getTriangle(i); + + Vector triPoint = Distance3d::ClosestPointTri(point,T); + + real dist = (point - triPoint).magnitudeSquared(); + + if(dist < minDist) + { + if(T.getNormal().y < 0.0f) + { + // Triangle's facing down, skip it. + continue; + } + + minDist = dist; + minPoint = triPoint; + minId = i; + } + } + + if(minId == -1) + { + result = FloorLocator::invalid; + + return false; + } + + // ---------- + + Vector scooted = Collision3d::MoveIntoTriangle(minPoint,getTriangle(minId),ConfigSharedCollision::getWallEpsilon()); + + result = FloorLocator(this,scooted,minId,0.0f,0.0f); + + return true; +} + +// ---------------------------------------------------------------------- + +bool FloorMesh::calcHitTime ( Segment3d const & moveSeg, int triId, int edgeId, float & outHitTime ) const +{ + Line3d moveLine = moveSeg.getLine(); + + Segment3d edge = getTriangle(triId).getEdgeSegment(edgeId); + + float hitTime; + + float param; + + bool hit = Intersect2d::IntersectLineSeg( moveLine, edge, hitTime, param ); + + if(hit) + { + outHitTime = hitTime; + + return true; + } + else + { + return false; + } +} + +// ---------------------------------------------------------------------- + +bool FloorMesh::attach ( FloorLocator & loc ) const +{ + if(loc.getFloorMesh() != this) + { + loc.setFloorMesh(this); + + FloorLocator tempLoc; + + bool dropOK = dropTest(loc,tempLoc); + + if(dropOK) loc = tempLoc; + + return dropOK; + } + else + { + return true; + } +} + +// ---------------------------------------------------------------------- + +PathWalkResult FloorMesh::pathWalkPoint ( FloorLocator const & startLoc, Vector const & delta, + int & outHitTriId, int & outHitEdge, float & outHitTime ) const +{ + Vector begin = startLoc.getPosition_l(); + Vector end = begin + delta; + + Segment3d pathSegment(begin,end); + Ribbon3d pathRibbon(pathSegment,-Vector::unitY); + + outHitTriId = -1; + outHitEdge = -1; + outHitTime = REAL_MAX; + + // ---------- + // Figure out the starting tri if we don't know it already + + int startTriId = startLoc.getId(); + + if(startTriId == -1) + { + PathWalkResult entryResult = findStartingTri(startLoc,delta,false,startTriId); + + if(entryResult == PWR_DoesntEnter) + { + return PWR_DoesntEnter; + } + else if(entryResult == PWR_CantEnter) + { + return PWR_CantEnter; + } + } + else + { + Triangle3d startTri = getTriangle(startTriId); + + if( !Overlap2d::TestPointTri(begin,startTri) ) + { + int enterTriEdge = Collision3d::TestEntranceTri(pathRibbon,startTri); + + if(enterTriEdge == -1) + { + return PWR_DoesntEnter; + } + else + { + FloorEdgeId id(startTriId,enterTriEdge); + + if(!canEnterEdge(startLoc,delta,id,false)) + { + return PWR_CantEnter; + } + } + } + } + + // ---------- + + int currentTriId = startTriId; + + // trap for an infinite loop - fan + int numIterations = 0; + + const int maxIterations = 100; + + while(currentTriId != -1 && numIterations < maxIterations) + { + Triangle3d currentTri = getTriangle(currentTriId); + + if( Overlap2d::TestPointTri(end,currentTri) ) + { + outHitTriId = currentTriId; + outHitEdge = -1; + outHitTime = REAL_MAX; + + return PWR_WalkOk; + } + + // ---------- + + int hitEdgeId = Collision3d::TestExitTri(pathRibbon, currentTri); + + if(hitEdgeId == -1) + { + outHitTriId = currentTriId; + outHitEdge = -1; + outHitTime = REAL_MAX; + + return PWR_WalkFailed; + } + + float tempHitTime = 0.0f; + + bool calcHitOK = calcHitTime( pathSegment, currentTriId, hitEdgeId, tempHitTime ); + + if(!calcHitOK) + { + FLOOR_WARNING("FloorMesh::pathWalkPoint - Couldn't calc exit time"); + + return PWR_WalkFailed; + } + else + { + FloorEdgeId id(currentTriId,hitEdgeId); + + if(!canExitEdge(startLoc,delta,id,false)) + { + Vector contactDelta = delta * tempHitTime; + + float contactDist = contactDelta.magnitude(); + + if(contactDist <= wallEpsilon) + { + // The starting point is extremely close to the contact point, close enough that + // the starting point can be considered to be in contact with its hit edge + + outHitTriId = currentTriId; + outHitEdge = hitEdgeId; + outHitTime = tempHitTime; + + return PWR_InContact; + } + else + { + outHitTriId = currentTriId; + outHitEdge = hitEdgeId; + outHitTime = tempHitTime; + + return PWR_HitEdge; + } + } + } + + // ---------- + + int neighborTriId = getFloorTri(currentTriId).getNeighborIndex(hitEdgeId); + + if(neighborTriId == -1) + { + float tempHitTime(0); + + bool calcHitOK = calcHitTime( pathSegment, currentTriId, hitEdgeId, tempHitTime ); + + if(!calcHitOK) + { + FLOOR_WARNING("FloorMesh::pathWalkPoint - Couldn't calc exit time"); + + return PWR_WalkFailed; + } + else + { + FloorTri const & floorTri = getFloorTri(currentTriId); + + int portalId = floorTri.getPortalId(hitEdgeId); + + if(portalId == -1) + { + outHitTriId = currentTriId; + outHitEdge = hitEdgeId; + outHitTime = tempHitTime; + + return PWR_ExitedMesh; + } + else + { + outHitTriId = currentTriId; + outHitEdge = hitEdgeId; + outHitTime = tempHitTime; + + return PWR_HitPortalEdge; + } + } + } + + if(neighborTriId == currentTriId) + { + outHitTriId = currentTriId; + outHitEdge = hitEdgeId; + outHitTime = REAL_MAX; + + return PWR_WalkFailed; + } + + + // ---------- + // Path didn't clip against this tri, step to the adjacent tri + + currentTriId = neighborTriId; + ++numIterations; + } + + // Should never get here, but... + if(numIterations >= maxIterations) + FLOOR_WARNING("FloorMesh::pathWalkPoint - numInterations exceeded maxInterations"); + else + FLOOR_WARNING("FloorMesh::pathWalkPoint - couldn't find a neighbortTriId"); + + outHitTriId = -1; + outHitEdge = -1; + outHitTime = REAL_MAX; + + return PWR_WalkFailed; //lint !e527 +} + +// ---------------------------------------------------------------------- + +void FloorMesh::setPartTags ( void ) +{ + FloorTriStack unprocessed; + + for(int i = 0; i < getTriCount(); i++) + { + FloorTri * tri = &getFloorTri(i); + + tri->setPartTag(-1); + + unprocessed.push(tri); + } + + FloorTriStack processing; + + int currentTag = 0; + + while(!unprocessed.empty()) + { + FloorTri * currentTri = unprocessed.top(); + unprocessed.pop(); + + if(currentTri->getPartTag() != -1) continue; + + processing.push(currentTri); + + while(!processing.empty()) + { + FloorTri * tri = processing.top(); + processing.pop(); + + if(tri->getPartTag() != -1) continue; + + tri->setPartTag(currentTag); + + int neighborA = tri->getNeighborIndex(0); + int neighborB = tri->getNeighborIndex(1); + int neighborC = tri->getNeighborIndex(2); + + if(neighborA != -1) processing.push(&getFloorTri( neighborA) ); + if(neighborB != -1) processing.push(&getFloorTri( neighborB) ); + if(neighborC != -1) processing.push(&getFloorTri( neighborC) ); + } + + currentTag++; + } +} + +// ---------- + +int FloorMesh::getPartCount ( void ) const +{ + int temp = 0; + + for(int i = 0; i < getTriCount(); i++) + { + int tag = getFloorTri(i).getPartTag(); + + if(tag > temp) temp = tag; + } + + return temp + 1; +} + +// ---------------------------------------------------------------------- +// compute the height functions for each tri in the floor + +void FloorMesh::calcHeightFuncs ( void ) +{ + int triCount = getTriCount(); + + for(int i = 0; i < triCount; i++) + { + FloorTri & T = getFloorTri(i); + + Vector A = getVertex(T.getCornerIndex(0)); + + Vector N = T.getNormal(); + + if(N.y == 0.0f) continue; + + float da = A.dot(N); + + float dydx = -(N.x/N.y); + float dydz = -(N.z/N.y); + float c = (da/N.y); + + Vector heightFunc(dydx,c,dydz); + + T.setHeightFunc(heightFunc); + } +} + +// ---------------------------------------------------------------------- +// Given an array of points representing a portal, flag edges of the +// floor mesh adjacent to the portal as being connected to it. Returns +// true if the portal was matched to any edge of the floor + +bool FloorMesh::matchSegmentToPoly ( Vector const & a, Vector const & b, VectorVector const & polyVerts ) +{ + Plane3d polyPlane( polyVerts[0], polyVerts[1], polyVerts[2] ); + + // ---------- + // Test 1 - Match the segment to the poly if it projects onto the interior + // of the poly and is less than 10 centimeters away from the poly's plane. + + //ClosestPointPlane can't handle a zero length normal vector + if(polyPlane.getNormal().magnitudeSquared() > Vector::NORMALIZE_THRESHOLD) + { + Vector a2 = Distance3d::ClosestPointPlane(a,polyPlane); + Vector b2 = Distance3d::ClosestPointPlane(b,polyPlane); + + if(a2.inPolygon(polyVerts) && b2.inPolygon(polyVerts)) + { + real distA = (a2 - a).magnitude(); + real distB = (b2 - b).magnitude(); + + if((distA < 0.1f) && (distB < 0.1f)) return true; + } + } + + // ---------- + // Test 2 - Match the segment to the poly if the vertices match a portal edge + // to within 5 centimeters + + int vertCount = polyVerts.size(); + + for(int i = 0; i < vertCount; i++) + { + Vector pa = polyVerts[i + 0]; + Vector pb = polyVerts[(i + 1) % vertCount]; + + Segment3d S(pa,pb); + + Vector a2 = Distance3d::ClosestPointSeg(a,S); + Vector b2 = Distance3d::ClosestPointSeg(b,S); + + // See if the distance between the verts is less than 5 centimeters for + // both ends + + real distA = (a2 - a).magnitude(); + real distB = (b2 - b).magnitude(); + + if((distA < 0.05f) && (distB < 0.05f)) return true; + } + + // ---------- + // Test 3 - Match the segment to the poly if the vertices are very very close + // to the poly + + { + Vector a2 = Distance3d::ClosestPointPoly(a,polyVerts); + Vector b2 = Distance3d::ClosestPointPoly(b,polyVerts); + + real distA = (a2 - a).magnitude(); + real distB = (b2 - b).magnitude(); + + if((distA < 0.01f) && (distB < 0.01f)) return true; + } + + return false; +} + +// ---------------------------------------------------------------------- + +void FloorMesh::findAdjacentBoundaryEdges ( VectorVector const & polyVerts, EdgeIdVec & outIds ) const +{ + if(polyVerts.size() < 3) return; + + for(int currentTri = 0; currentTri < getTriCount(); currentTri++ ) + { + FloorTri const & F = getFloorTri(currentTri); + Triangle3d T = getTriangle(currentTri); + + for(int currentEdge = 0; currentEdge < 3; currentEdge++ ) + { + // don't try and match internal edges + + if(F.getNeighborIndex(currentEdge) != -1) + { + continue; + } + + Vector a = T.getCorner(currentEdge); + Vector b = T.getCorner(currentEdge + 1); + + if(matchSegmentToPoly(a,b,polyVerts)) + { + outIds.push_back( EdgeId(currentTri,currentEdge) ); + } + } + } +} + +// ---------------------------------------------------------------------- + +bool FloorMesh::flagPortalEdges( VectorVector const & portalVerts, int portalId ) +{ + EdgeIdVec tempIds; + + findAdjacentBoundaryEdges(portalVerts,tempIds); + + int idCount = tempIds.size(); + + for(int i = 0; i < idCount; i++) + { + EdgeId const & id = tempIds[i]; + + getFloorTri(id.first).setPortalId( id.second, portalId ); + } + + return !tempIds.empty(); +} + +// ---------------------------------------------------------------------- + +bool FloorMesh::validate ( FloorLocator const & loc ) const +{ + if(loc.getId() == -1) + { + return true; + } + + FloorLocator temp; + + if( !testIntersect( Line3d(loc.getPosition_l(),-Vector::unitY), loc.getId(), temp) ) + { + FLOOR_WARNING("FloorMesh::validate - Validate locator failed"); + return false; + } + else + { + return true; + } +} + +// ---------------------------------------------------------------------- + +PathWalkResult FloorMesh::pathWalkCircle ( FloorLocator const & startLoc, + Vector const & delta, + int ignoreTriId, + int ignoreEdge, + FloorLocator & result ) const +{ + float radius = startLoc.getRadius(); + + // ---------- + // run the path walk + + Vector begin = startLoc.getPosition_l(); + + float hitTime = REAL_MAX; + int hitId = -1; + int hitEdgeId = -1; + int centerTriId = -1; + + PathWalkResult walkResult = pathWalkCircleGetIds( startLoc, delta, ignoreTriId, ignoreEdge, hitTime, hitId, hitEdgeId, centerTriId ); + + // ---------- + // use the results to build a FloorLocator + + if(walkResult == PWR_DoesntEnter) + { + FloorLocator endLoc(this,begin+delta,-1,0.0f,radius); + + makeSuccessResult(endLoc,result); + + return walkResult; + } + else if(walkResult == PWR_HitBeforeEnter) + { + makeHitResult2( begin, delta, centerTriId, hitId, hitEdgeId, hitTime, result ); + + result.setRadius(radius); + + return walkResult; + } + else if((walkResult == PWR_MissedStartTri) || (walkResult == PWR_CantEnter) || (walkResult == PWR_StartLocInvalid) || (walkResult == PWR_WalkFailed) || (walkResult == PWR_HitPast)) + { + makeFailureResult(result); + + result.setRadius(radius); + + return walkResult; + } + else if((walkResult == PWR_HitEdge) || (walkResult == PWR_InContact)) + { + makeHitResult2( begin, delta, centerTriId, hitId, hitEdgeId, hitTime, result ); + + result.setRadius(radius); + + return walkResult; + } + else if((walkResult == PWR_CenterHitEdge) || (walkResult == PWR_CenterInContact)) + { + makeHitResult( begin, delta, hitId, hitEdgeId, hitTime, result ); + + result.setRadius(radius); + + return walkResult; + } + else if(walkResult == PWR_ExitedMesh) + { + makeExitResult( begin, delta, hitId, hitEdgeId, hitTime, result ); + + result.setRadius(radius); + + return walkResult; + } + else if(walkResult == PWR_HitPortalEdge) + { + makeExitResult( begin, delta, hitId, hitEdgeId, hitTime, result ); + + result.setRadius(radius); + + return walkResult; + } + else if(walkResult == PWR_WalkOk) + { + Line3d endLine(begin+delta,-Vector::unitY); + FloorLocator dropLoc; + + if(centerTriId != -1) + { + if(!testIntersect(endLine, centerTriId, dropLoc)) + { + FLOOR_WARNING("FloorMesh::pathWalkCircle - Walk was OK, but end point isn't in the end triangle"); + + makeFailureResult(result); + + result.setRadius(radius); + + return PWR_WalkFailed; + } + } + + dropLoc.setRadius(radius); + + makeSuccessResult(dropLoc,result); + + return walkResult; + } + else + { + FLOOR_WARNING("FloorMesh::pathWalkCircle - Got an invalid return value from pathWalkCircle"); + + makeFailureResult(result); + + result.setRadius(radius); + + return PWR_WalkFailed; + } +} + +// ---------------------------------------------------------------------- +// Sweep a circle along the floor and return the first time of collision +// of the circle with any non-crossable edge in the floor. + +// This method is a little tricky - when we're sliding the circle across +// the floor, the circle can hit multiple edge of each triangle it touches. +// We have to keep track of the neighbor triangles of each edge it touches +// amd check all their edges also, but we don't want to check any triangle's +// edges twice. So, we keep a deque of the triangles the circle has hit and +// use mark values to keep from visiting triangles twice. This method ends +// up being quite speedy. + +// Returns true if something was hit. + +struct EdgeQueueEntry +{ + EdgeQueueEntry( int id, float time ) + : triId(id), hitTime(time) + { + } + + // This compare returns A.hitTime > B.hitTime so that the queue + // will be ordered earliest-to-latest + + static bool compare ( EdgeQueueEntry const & A, EdgeQueueEntry const & B ) + { + return A.hitTime > B.hitTime; + } + + bool operator < ( EdgeQueueEntry const & A ) const + { + return hitTime > A.hitTime; + } + + int triId; + float hitTime; +}; + +typedef std::priority_queue< EdgeQueueEntry > FloorEdgeQueue; +typedef std::stack FloorTriIdStack; + +// ---------- + +PathWalkResult FloorMesh::pathWalkCircleGetIds ( FloorLocator const & inStartLoc, Vector const & delta, + int ignoreTriId, int ignoreEdge, + float & outHitTime, int & outHitTriId, int & outHitEdge, int & outCenterTriId ) const +{ + FloorLocator startLoc = inStartLoc; + + Vector begin = startLoc.getPosition_l(); + Vector end = begin + delta; + Line3d beginLine(begin,-Vector::unitY); + Ribbon3d pathRibbon(begin,end,-Vector::unitY); + + Circle circle( begin, startLoc.getRadius() ); + + outHitTime = REAL_MAX; + outHitTriId = -1; + outHitEdge = -1; + outCenterTriId = -1; + + // ---------- + // Figure out the starting tri if we don't know it already + + int startTriId = startLoc.getId(); + + if(startTriId == -1) + { + PathWalkResult entryResult = findStartingTri(startLoc,delta,true,startTriId); + + if(entryResult == PWR_DoesntEnter) + { + return PWR_DoesntEnter; + } + else if(entryResult == PWR_CantEnter) + { + return PWR_CantEnter; + } + + startLoc.setId(startTriId); + } + else + { + Triangle3d startTri = getTriangle(startTriId); + + if( !Overlap3d::TestLineTriSided(beginLine,startTri) ) + { + int enterTriEdge = Collision3d::TestEntranceTri(pathRibbon,startTri); + + if(enterTriEdge == -1) + { + return PWR_DoesntEnter; + } + else + { + FloorEdgeId id(startTriId,enterTriEdge); + + if(!canEnterEdge(startLoc,delta,id,true)) + { + return PWR_CantEnter; + } + } + } + } + + // ---------- + // First things first - Find out if and when the center of the circle will + // exit the floor during this timestep + + PathWalkResult centerWalkResult; + float centerHitTime = REAL_MAX; + int centerHitTriId = -1; + int centerHitEdge = -1; + + { + centerWalkResult = pathWalkPoint( startLoc, delta, centerHitTriId, centerHitEdge, centerHitTime ); + + if( (centerWalkResult == PWR_MissedStartTri) + || (centerWalkResult == PWR_CantEnter) + || (centerWalkResult == PWR_WalkFailed) + || (centerWalkResult == PWR_StartLocInvalid) ) + { + return centerWalkResult; + } + else if( centerWalkResult == PWR_InContact ) + { + // The center of the circle is in contact with an edge and will stay in contact during this timestep. + + outHitTime = centerHitTime; + outHitTriId = centerHitTriId; + outHitEdge = centerHitEdge; + outCenterTriId = centerHitTriId; + + return PWR_CenterInContact; + } + else if( (centerWalkResult == PWR_ExitedMesh) || (centerWalkResult == PWR_HitEdge) || (centerWalkResult == PWR_HitPortalEdge) ) + { + // The center of the circle will hit an edge during this timestep + } + else + { + } + } + + // ---------- + + bool hitAnything = false; + float circleHitTime = 1.0f; + int circleHitTriId = -1; + int circleHitEdge = -1; + + int markValue = getTriMarkValue(); + + FloorEdgeQueue unprocessed; + + unprocessed.push( EdgeQueueEntry(startTriId,0.0f) ); + + while(!unprocessed.empty()) + { + int currentTriId = unprocessed.top().triId; + unprocessed.pop(); + + FloorTri const & currentTri = getFloorTri( currentTriId ); + + if(currentTri.getMark() == markValue) continue; + + currentTri.setMark(markValue); + + // ---------- + // Test for hits with each edge of the tri + + for(int i = 0; i < 3; i++) + { + // See if it's even possible to hit this edge + + if((ignoreTriId == currentTriId) && (ignoreEdge == i)) + { + continue; + } + + Segment3d edge = getTriangle(currentTriId).getEdgeSegment(i); + + Vector edgeDir = edge.getDelta(); + + Vector edgeNormal(edgeDir.z,0.0f,-edgeDir.x); + + Vector V(delta.x,0.0f,delta.z); + + if(edgeNormal.dot(V) > 0.0f) + { + // circle is moving away from the edge + + // ramp edges can only be hit from the front + + FloorEdgeType edgeType = getFloorTri(currentTriId).getEdgeType(i); + + if(edgeType == FET_WallBase) + { + continue; + } + } + + // Compute the time of intersection between the circle and the edge + + Range hitRange = Intersect2d::IntersectCircleSeg(circle,V,edge); + + // Ignore the hit if it ended in the past, starts further in the future than our + // earliest hit, or starts after the circle has already left the floor + + if( hitRange.isEmpty() ) continue; + if( hitRange.getMax() < 0.0f ) continue; + if( hitRange.getMin() > circleHitTime ) continue; + + if( (centerWalkResult == PWR_ExitedMesh) || (centerWalkResult == PWR_HitPortalEdge) || (centerWalkResult == PWR_HitEdge) ) + { + if(hitRange.getMin() > centerHitTime) continue; + } + + // This hit needs processing. + + FloorEdgeId id(currentTriId,i); + + if(canExitEdge(startLoc,delta,id,true)) + { + // Crossable edge hit - push the adjacent triangle onto the stack + + int neighbor = currentTri.getNeighborIndex(i); + + if(neighbor != -1) + { + unprocessed.push( EdgeQueueEntry(neighbor,hitRange.getMin()) ); + } + } + else + { + // If the circle is moving away from its contact point with the segment, there's no contact + + Vector endPoint = begin + V * hitRange.getMin(); + + Vector contactPoint = Distance2d::ClosestPointSeg(endPoint,edge); + Vector contactNormal = endPoint - contactPoint; + + contactNormal.y = 0.0f; + + if(contactNormal.dot(V) >= 0.0f) + { + continue; + } + + if(hitRange.getMin() == hitRange.getMax()) + { + // Degenerate hit - circle grazes one end of the segment. Ignore the hit. + + continue; + } + else if(hitRange.getMin() < 0.0f) + { + float stepDistance = delta.magnitude(); + + float contactDistance = stepDistance * -hitRange.getMin(); + + if(contactDistance < wallEpsilon) + { + circleHitTime = hitRange.getMin(); + circleHitTriId = currentTriId; + circleHitEdge = i; + hitAnything = true; + } + else + { + continue; + } + } + else + { + // Non-crossable edge hit - record the hit + + circleHitTime = hitRange.getMin(); + circleHitTriId = currentTriId; + circleHitEdge = i; + hitAnything = true; + } + } + } + } + + // ---------- + + if(hitAnything) + { + float stepDistance = delta.magnitude(); + + float contactDist = abs(stepDistance * circleHitTime); + + if((circleHitTime < 0) && (contactDist > wallEpsilon)) + { + // The circle hit an edge some time in the past. + + outHitTime = circleHitTime; + outHitTriId = circleHitTriId; + outHitEdge = circleHitEdge; + outCenterTriId = startTriId; + + return PWR_HitPast; + } + else + { + if(contactDist <= wallEpsilon) + { + // The circle is currently in contact with an edge + + outHitTime = circleHitTime; + outHitTriId = circleHitTriId; + outHitEdge = circleHitEdge; + outCenterTriId = startTriId; + + return PWR_InContact; + } + else + { + // The circle hits an edge during this timestep. + // Walk the center of the circle to where it will be at the contact time + + Vector newDelta = delta * circleHitTime; + + int newCenterTriId = -1; + int newCenterHitEdge = -1; + float newCenterHitTime = REAL_MAX; + + PathWalkResult result = pathWalkPoint( startLoc, newDelta, newCenterTriId, newCenterHitEdge, newCenterHitTime ); + + if(result == PWR_HitEdge) + { + outHitTime = newCenterHitTime; + outHitTriId = newCenterTriId; + outHitEdge = newCenterHitEdge; + outCenterTriId = newCenterTriId; + + return PWR_CenterHitEdge; + } + else if(result == PWR_InContact) + { + outHitTime = newCenterHitTime; + outHitTriId = newCenterTriId; + outHitEdge = newCenterHitEdge; + outCenterTriId = newCenterTriId; + + return PWR_CenterInContact; + } + else if(result == PWR_DoesntEnter) + { + outHitTime = circleHitTime; + outHitTriId = circleHitTriId; + outHitEdge = circleHitEdge; + outCenterTriId = -1; + + return PWR_HitBeforeEnter; + } + else if(result != PWR_WalkOk) + { + FLOOR_WARNING("FloorMeshpathWalkCircleGetIds - Walking the center to the time of the circle's contact failed"); + return PWR_WalkFailed; + } + + outHitTime = circleHitTime; + outHitTriId = circleHitTriId; + outHitEdge = circleHitEdge; + outCenterTriId = newCenterTriId; + + return PWR_HitEdge; + } + } + } + else + { + if(centerWalkResult == PWR_HitEdge) + { + outHitTime = centerHitTime; + outHitTriId = centerHitTriId; + outHitEdge = centerHitEdge; + outCenterTriId = centerHitTriId; + + return PWR_CenterHitEdge; + } + else if(centerWalkResult == PWR_InContact) + { + outHitTime = centerHitTime; + outHitTriId = centerHitTriId; + outHitEdge = centerHitEdge; + outCenterTriId = centerHitTriId; + + return PWR_CenterInContact; + } + else if(centerWalkResult == PWR_ExitedMesh) + { + outHitTime = centerHitTime; + outHitTriId = centerHitTriId; + outHitEdge = centerHitEdge; + outCenterTriId = centerHitTriId; + + return PWR_ExitedMesh; + } + else if(centerWalkResult == PWR_HitPortalEdge) + { + outHitTime = centerHitTime; + outHitTriId = centerHitTriId; + outHitEdge = centerHitEdge; + outCenterTriId = centerHitTriId; + + return PWR_HitPortalEdge; + } + else + { + // The circle doesn't hit anything, and the center doesn't exit the floor + + outHitTime = REAL_MAX; + outHitTriId = -1; + outHitEdge = -1; + outCenterTriId = centerHitTriId; + + return PWR_WalkOk; + } + } + + WARNING_STRICT_FATAL(true, ("pathWalkCircleGetIds failed to compute a result. Return PWR_WalkOk")); + outHitTime = REAL_MAX; + outHitTriId = -1; + outHitEdge = -1; + outCenterTriId = centerHitTriId; + + return PWR_WalkOk; +} + +// ---------------------------------------------------------------------- + +bool FloorMesh::testAboveCrossables ( FloorLocator const & testLoc ) const +{ + Vector offset(0.0f,gs_hopTolerance,0.0f); + + Circle circle( testLoc.getOffsetPosition_l() + offset, testLoc.getRadius() - gs_clearTolerance ); + + int edgeCount = m_crossableEdges->size(); + + for(int i = 0; i < edgeCount; i++) + { + FloorEdgeId id = m_crossableEdges->at(i); + + if(getFloorTri(id.triId).isFallthrough()) continue; + + Segment3d edge = getTriangle(id.triId).getEdgeSegment(id.edgeId); + + if( Overlap3d::TestSegCircle_Below(edge,circle) ) + { + return false; + } + } + + return true; +} + +// ---------------------------------------------------------------------- + +bool FloorMesh::testAboveCrossables ( FloorLocator const & testLoc, Vector const & inDelta ) const +{ + DEBUG_FATAL(inDelta.y != 0.0f,("FloorMesh::testCrossables - can't handle Y movement yet\n")); + + Vector delta(inDelta.x,0.0f,inDelta.z); + + Vector offset(0.0f,gs_hopTolerance,0.0f); + + Circle circle( testLoc.getOffsetPosition_l() + offset, testLoc.getRadius() - gs_clearTolerance ); + + int edgeCount = m_crossableEdges->size(); + + for(int i = 0; i < edgeCount; i++) + { + FloorEdgeId id = m_crossableEdges->at(i); + + if(getFloorTri(id.triId).isFallthrough()) continue; + + Segment3d edge = getTriangle(id.triId).getEdgeSegment(id.edgeId); + + Range hitRange = Intersect3d::IntersectCircleSeg_Below(circle,delta,edge); + + if(hitRange.isEmpty()) continue; + + if((hitRange.getMin() <= 1.0f) && (hitRange.getMax() >= 0.0f)) return false; + } + + return true; +} + +// ---------------------------------------------------------------------- + +bool FloorMesh::testClear ( FloorLocator const & inTestLoc ) const +{ + FloorLocator testLoc = inTestLoc; + + attach(testLoc); + + int startTriId = testLoc.getId(); + + if(startTriId == -1) + { + return true; + } + + Circle testCircle( testLoc.getOffsetPosition_l(), testLoc.getRadius() ); + + // ---------- + + int markValue = getTriMarkValue(); + + static FloorTriIdQueue unprocessed; + + unprocessed.clear(); + unprocessed.push_back( startTriId ); + + bool testedAboveCrossables = false; + + while(!unprocessed.empty()) + { + int currentTriId = unprocessed.front(); + unprocessed.pop_front(); + + FloorTri const & currentTri = getFloorTri( currentTriId ); + + if(currentTri.getMark() == markValue) continue; + + currentTri.setMark(markValue); + + // ---------- + + for(int i = 0; i < 3; i++) + { + Segment3d edge = getTriangle(currentTriId).getEdgeSegment(i); + + if(!Overlap2d::TestSegCircle(edge,testCircle)) continue; + + // ---------- + + FloorEdgeType type = currentTri.getEdgeType(i); + + if(type == FET_Crossable) + { + // circle overlaps a crossable edge, push the neighbor onto the stack + + int neighbor = currentTri.getNeighborIndex(i); + + if(neighbor != -1) + { + unprocessed.push_back( neighbor ); + } + + continue; + } + else if(type == FET_Uncrossable) + { + // circle overlaps an uncrossable edge, circle is not clear + + return false; + } + else if(type == FET_WallBase) + { + // circle overlaps a wall base. circle is not clear if it's under a crossable + // edge of a solid tri. we only need to run this test once. + + if(testedAboveCrossables) continue; + + if(!testAboveCrossables(testLoc)) + { + return false; + } + + testedAboveCrossables = true; + } + else + { + continue; + } + } + } + + return true; +} + +// ---------------------------------------------------------------------- + +bool FloorMesh::getClosestCollidableEdge ( FloorLocator const & loc, int & outTriId, int & outEdgeId, float & outDist ) const +{ + FloorLocator testLoc = loc; + + attach(testLoc); + + int startTriId = testLoc.getId(); + + if(startTriId == -1) + { + return false; + } + + Circle testCircle( testLoc.getPosition_l(), testLoc.getRadius() ); + + int minTriId = -1; + int minEdgeId = -1; + + // ---------- + + int markValue = getTriMarkValue(); + + static FloorTriIdQueue unprocessed; + + unprocessed.clear(); + unprocessed.push_back( startTriId ); + + bool testedAboveCrossables = false; + bool isAboveCrossables = false; + + while(!unprocessed.empty()) + { + int currentTriId = unprocessed.front(); + unprocessed.pop_front(); + + FloorTri const & currentTri = getFloorTri( currentTriId ); + + if(currentTri.getMark() == markValue) continue; + + currentTri.setMark(markValue); + + // ---------- + + for(int i = 0; i < 3; i++) + { + Segment3d edge = getTriangle(currentTriId).getEdgeSegment(i); + + float dist = Distance2d::DistancePointSeg( testCircle.getCenter(), edge ); + + if(dist >= testCircle.getRadius()) continue; + + // ---------- + + FloorEdgeType type = currentTri.getEdgeType(i); + + if(type == FET_Crossable) + { + int neighbor = currentTri.getNeighborIndex(i); + + if(neighbor != -1) + { + unprocessed.push_back( neighbor ); + } + } + else if(type == FET_Uncrossable) + { + testCircle.setRadius(dist); + + minTriId = currentTriId; + minEdgeId = i; + } + else if(type == FET_WallBase) + { + // circle overlaps a wall base. circle is not clear if it's under a crossable + // edge of a solid tri. we only need to run this test once. + + if(testedAboveCrossables) continue; + + isAboveCrossables = testAboveCrossables(testLoc); + testedAboveCrossables = true; + + if(!isAboveCrossables) + { + testCircle.setRadius(dist); + + minTriId = currentTriId; + minEdgeId = i; + } + } + else + { + continue; + } + } + } + + if(minTriId != -1) + { + outTriId = minTriId; + outEdgeId = minEdgeId; + outDist = testCircle.getRadius(); + + return true; + } + else + { + return false; + } +} + +// ---------------------------------------------------------------------- + +bool FloorMesh::testConnectable ( FloorLocator const & locA, FloorLocator const & locB ) const +{ + Vector posA = locA.getPosition_l(); + Vector posB = locB.getPosition_l(); + + Vector deltaA = posB - posA; + Vector deltaB = posA - posB; + + FloorLocator moveResultA; + FloorLocator moveResultB; + + PathWalkResult resultA = pathWalkCircle(locA,deltaA,-1,-1,moveResultA); + PathWalkResult resultB = pathWalkCircle(locB,deltaB,-1,-1,moveResultB); + + if((resultA == PWR_ExitedMesh) || (resultA == PWR_HitPortalEdge)) + { + // If the move from A didn't exit the floor at B, we can't connect the locators + + if(moveResultA.getTriId() != locB.getTriId()) + { + return false; + } + } + + if((resultB == PWR_ExitedMesh) || (resultB == PWR_HitPortalEdge)) + { + // If the move from B didn't exit the floor at A, we can't connect the locators + + if(moveResultB.getTriId() != locA.getTriId()) + { + return false; + } + } + + if((resultA != PWR_WalkOk) && (resultA != PWR_ExitedMesh) && (resultA != PWR_HitPortalEdge)) return false; + if((resultB != PWR_WalkOk) && (resultB != PWR_ExitedMesh) && (resultB != PWR_HitPortalEdge)) return false; + + if(resultA == PWR_WalkOk) + { + if(moveResultA.getId() != locB.getId()) + { + return false; + } + } + + if(resultB == PWR_WalkOk) + { + if(moveResultB.getId() != locA.getId()) + { + return false; + } + } + + return true; +} + +// ---------------------------------------------------------------------- +// The points returned by getGoodLocation won't be evenly distributed over +// the floor, but I don't think that's a problem. + +bool FloorMesh::getGoodLocation ( float radius, Vector & outLoc ) const +{ + int triCount = getTriCount(); + + int attempts = 10; + + while(attempts) + { + // Pick a random triangle in the floor + + int triIndex = Random::random(triCount-1); + + Triangle3d const & tri = getTriangle(triIndex); + + // Pick a random barycentric coordinate + + float baryX = Random::randomReal(0.0f,1.0f); + float baryY = Random::randomReal(0.0f,1.0f); + + if((baryX + baryY) > 1.0f) + { + baryX = 1.0f - baryX; + baryY = 1.0f - baryY; + } + + // See if the point on the triangle at that + // barycentric coordinate is clear + + Vector const & A = tri.getCornerA(); + Vector const & B = tri.getCornerB(); + Vector const & C = tri.getCornerC(); + + Vector point = A + (B-A) * baryX + (C-A) * baryY; + + FloorLocator testLoc(this,point,-1,0.0f,radius); + + if(testClear(testLoc)) + { + outLoc = point; + + return true; + } + + // ---------- + + attempts--; + } + + return false; +} + +// ---------------------------------------------------------------------- + +int FloorMesh::getTriMarkValue ( void ) const +{ + return m_triMarkCounter++; +} + +// ---------------------------------------------------------------------- + +void FloorMesh::buildBoundaryEdgeList ( void ) +{ + m_crossableEdges->clear(); + m_uncrossableEdges->clear(); + m_wallBaseEdges->clear(); + m_wallTopEdges->clear(); + + for(int i = 0; i < getTriCount(); i++) + { + FloorTri const & F = getFloorTri(i); + + for(int j = 0; j < 3; j++) + { + if(F.getNeighborIndex(j) == -1) + { + FloorEdgeId id(i,j); + + switch(F.getEdgeType(j)) + { + case FET_Crossable: { m_crossableEdges->push_back(id); break; } + case FET_Uncrossable: { m_uncrossableEdges->push_back(id); break; } + case FET_WallBase: { m_wallBaseEdges->push_back(id); break; } + default: { break; } + } + } + } + } +} + +// ---------------------------------------------------------------------- + +bool FloorMesh::intersectEdge ( FloorLocator const & startLoc, Vector const & delta, FloorEdgeId const & id, bool useRadius, bool testFront, FloorLocator & outLoc ) const +{ + Vector point = startLoc.getPosition_l(); + + Segment3d triSeg = getTriangle(id.triId).getEdgeSegment(id.edgeId); + Segment3d edge(triSeg.getBegin(), triSeg.getEnd()); + + float hitTime; + + if(useRadius) + { + Circle circle(point,startLoc.getRadius()); + + Range hitRange = Intersect2d::IntersectCircleSeg(circle,delta,edge); + + if( hitRange.isEmpty() ) return false; + if( hitRange.getMax() < 0.0f ) return false; + if( hitRange.getMin() > 1.0f ) return false; + + hitTime = hitRange.getMin(); + } + else + { + float param; + + Line3d moveLine(point,delta); + + bool hitSeg = Intersect2d::IntersectLineSeg( moveLine, edge, hitTime, param ); + + if(!hitSeg) return false; + if(hitTime > 1.0f) return false; + + if(hitTime < 0.0f) + { + float dist = -delta.magnitude() * hitTime; + + if(dist > 0.2f) + { + return false; + } + else + { + int blah = 2; + + blah += (int)hitTime; + + UNREF(blah); + } + } + } + + Vector cross = delta.cross(edge.getDelta()); + + if(testFront) + { + if(cross.y < 0.0f) return false; + } + else + { + if(cross.y > 0.0f) return false; + } + + Vector centerHitPos = point + delta * hitTime; + Vector crossPoint = Distance3d::ClosestPointYLine(edge,centerHitPos); + + float offset = centerHitPos.y - crossPoint.y + startLoc.getOffset(); + + Vector newPos(centerHitPos.x, crossPoint.y, centerHitPos.z); + + outLoc = startLoc; + + outLoc.setTriId(id.triId); + outLoc.setEdgeId(id.edgeId); + outLoc.setPosition_l(newPos); + outLoc.setOffset(offset); + outLoc.setTime(hitTime); + + return true; +} + +// ---------------------------------------------------------------------- + +bool FloorMesh::intersectBoundary ( FloorLocator const & startLoc, Vector const & delta, bool useRadius, FloorLocatorVec & results ) const +{ + results.clear(); + + bool result = false; + + result |= intersectBoundary( m_crossableEdges, startLoc, delta, useRadius, results ); + result |= intersectBoundary( m_wallBaseEdges, startLoc, delta, useRadius, results ); + + return result; +} + +// ---------- + +bool FloorMesh::intersectBoundary ( FloorEdgeIdVec * edgeList, FloorLocator const & startLoc, Vector const & delta, bool useRadius, FloorLocatorVec & results ) const +{ + bool hit = false; + + int edgeCount = edgeList->size(); + + for( int i = 0; i < edgeCount; i++) + { + FloorEdgeId & id = edgeList->at(i); + + Vector V(delta.x,0.0f,delta.z); + + FloorLocator hitLoc; + + bool hitEdgeId = intersectEdge(startLoc,V,id,useRadius,false,hitLoc); + + if(hitEdgeId) + { + hit = true; + + results.push_back(hitLoc); + } + } + + return hit; +} + + +// ---------------------------------------------------------------------- + +bool FloorMesh::dropTest ( FloorLocator const & testLoc, FloorLocator & outLoc ) const +{ + return dropTest(testLoc,ConfigSharedCollision::getHopHeight(),outLoc); +} + +// ---------- + +bool FloorMesh::dropTest ( FloorLocator const & testLoc, float hopHeight, FloorLocator & outLoc ) const +{ + Vector point = testLoc.getPosition_l(); + + Line3d line(point,-Vector::unitY); + + FloorLocator closestAbove; + FloorLocator closestBelow; + + float fallHeight = m_objectFloor ? REAL_MAX : 3.0f; + + // Our drop dir points down, so the closestBelow locator is in 'front' of the line, + // and the closestAbove locator is 'behind' it. + + if( findClosestPair(line,-1,closestBelow,closestAbove) ) + { + float distBelow = abs(closestBelow.getOffset()); + float distAbove = abs(closestAbove.getOffset()); + + closestAbove.setSurface(this); + closestBelow.setSurface(this); + + if(closestAbove.isAttached() && closestBelow.isAttached()) + { + // Handle test points on or slightly below the floor correctly + + if((distAbove < distBelow) && (distAbove < 1.0f)) + { + outLoc = closestAbove; + } + else + { + outLoc = closestBelow; + } + } + else if(closestBelow.isAttached()) + { + if(distBelow <= fallHeight) + { + outLoc = closestBelow; + } + else + { + if(closestBelow.getFloorTri().isFallthrough()) + { + outLoc = closestBelow; + } + else + { + outLoc = FloorLocator::invalid; + } + } + } + else if(closestAbove.isAttached()) + { + if(distAbove <= hopHeight) + { + outLoc = closestAbove; + } + else + { + if(closestAbove.getFloorTri().isFallthrough()) + { + outLoc = closestAbove; + } + else + { + outLoc = FloorLocator::invalid; + } + } + } + else + { + outLoc = FloorLocator::invalid; + } + } + else + { + outLoc = FloorLocator::invalid; + } + + // ---------- + + return outLoc.isAttached(); +} + +// ---------------------------------------------------------------------- + +bool FloorMesh::dropTest ( FloorLocator const & testLoc, int triID, FloorLocator & outLoc ) const +{ + Vector point = testLoc.getPosition_l(); + + Line3d line(point,-Vector::unitY); + + FloorLocator tempLoc; + + if(testIntersect(line,triID,tempLoc)) + { + float absDist = abs(tempLoc.getOffset()); + + if(absDist <= ConfigSharedCollision::getHopHeight()) + { + outLoc = tempLoc; + return true; + } + } + + return false; +} + +// ---------------------------------------------------------------------- + +bool FloorMesh::findEntrance ( FloorLocator const & startLoc, Vector const & delta, bool useRadius, FloorLocator & outLoc ) const +{ + static FloorLocatorVec results; + + results.clear(); + + bool intersected = intersectBoundary(startLoc,delta,useRadius,results); + + if(!intersected) return false; + + // ---------- + + float minDistA = REAL_MAX; // distance from the point on the boundary to the start of the segment + float minDistB = REAL_MAX; // distance from the point on the boundary to the X-Z cross point + + int minIndex = -1; + + int resultCount = results.size(); + + for (int i = 0; i < resultCount; i++) + { + FloorLocator const & entryLoc = results[i]; + + float hitTime = entryLoc.getTime(); + + Vector startOffsetPoint = startLoc.getOffsetPosition_l(); + Vector hitPoint = entryLoc.getPosition_l(); + + Vector crossOffsetPoint = startOffsetPoint + delta * hitTime; + + Vector temp = hitPoint - startOffsetPoint; + temp.y = 0.0f; + + float distA = temp.magnitude(); + float distB = hitPoint.magnitudeBetween(crossOffsetPoint); + + // For a crossing to be considered an entrance, it has to be within half a meter + // (fudge factor) of the earliest crossing, and it has to be closer to the move + // segment than the earliest crossing. + + if(distA >= (minDistA + 0.5f)) continue; + if(distB >= minDistB) continue; + + float offset = entryLoc.getOffset(); + + float hopHeight = -ConfigSharedCollision::getHopHeight(); + float fallHeight = m_objectFloor ? REAL_MAX : 3.0f; + + bool fallthrough = entryLoc.getFloorTri().isFallthrough(); + + if((offset < hopHeight) && (!fallthrough)) + { + // crosses too far below solid tri + + continue; + } + + if((offset > fallHeight) && (!fallthrough)) + { + // crosses too far above solid tri + + continue; + } + + if(canEnterEdge(entryLoc)) + { + minDistA = distA; + minDistB = distB; + minIndex = i; + } + } + + if(minIndex != -1) + { + outLoc = results[minIndex]; + + return true; + } + else + { + return false; + } +} + +// ---------------------------------------------------------------------- + +void FloorMesh::drawDebugShapes ( DebugShapeRenderer * renderer, bool drawExtent ) const +{ + UNREF(renderer); + UNREF(drawExtent); + +#ifdef _DEBUG + + if(renderer == NULL) return; + + if(ConfigSharedCollision::getDrawFloors()) + { + if( m_crossableLines ) renderer->drawLineList( *m_crossableLines, VectorArgb::solidGreen ); + if( m_uncrossableLines ) renderer->drawLineList( *m_uncrossableLines, VectorArgb::solidRed ); + if( m_interiorLines ) renderer->drawLineList( *m_interiorLines, VectorArgb::solidWhite); + if( m_portalLines ) renderer->drawLineList( *m_portalLines, PackedArgb(255,220,255,0) ); + if( m_rampLines ) renderer->drawLineList( *m_rampLines, PackedArgb(255,255,175,0) ); + if( m_fallthroughTriLines ) renderer->drawLineList( *m_fallthroughTriLines, VectorArgb::solidRed ); + if( m_solidTriLines ) renderer->drawLineList( *m_solidTriLines, VectorArgb::solidWhite ); + + if(drawExtent) + { + getExtent_l()->drawDebugShapes(renderer); + } + } + + if(ConfigSharedCollision::getDrawPathNodes()) + { + ObjectRenderer pathRenderer = FloorManager::getPathGraphRenderer(); + + if(m_pathGraph && pathRenderer) + { + pathRenderer(m_pathGraph,renderer); + } + } + + if(m_boxTree && ConfigSharedCollision::getDrawBoxTrees()) + { + m_boxTree->drawDebugShapes(renderer); + } + +#endif +} + +// ---------------------------------------------------------------------- + +#ifdef _DEBUG + +void FloorMesh::buildDebugData ( void ) +{ + Vector scootUp(0.0f,0.04f,0.0f); + + // ---------- + // Create our line arrays for the debug info + + delete m_crossableLines; + delete m_uncrossableLines; + delete m_interiorLines; + delete m_portalLines; + delete m_rampLines; + delete m_fallthroughTriLines; + delete m_solidTriLines; + + m_crossableLines = new VectorVector(); + m_uncrossableLines = new VectorVector(); + m_interiorLines = new VectorVector(); + m_portalLines = new VectorVector(); + m_rampLines = new VectorVector(); + m_fallthroughTriLines = new VectorVector(); + m_solidTriLines = new VectorVector(); + + // ---------- + + for(int i = 0; i < getTriCount(); i++) + { + FloorTri const & F = getFloorTri(i); + + // Create border edges for the tri + + for(int j = 0; j < 3; j++) + { + Vector a = getVertex(F.getCornerIndex(j)) + scootUp; + Vector b = getVertex(F.getCornerIndex(j+1)) + scootUp; + + if(F.getNeighborIndex(j) != -1) + { + m_interiorLines->push_back(a); + m_interiorLines->push_back(b); + + continue; + } + + // Pick which container the edge should go in. + + FloorEdgeType edgeType = F.getEdgeType(j); + + VectorVector * container = m_uncrossableLines; + + if(edgeType == FET_Crossable) container = m_crossableLines; + + if(edgeType == FET_WallBase) container = m_rampLines; + + if(F.getPortalId(j) != -1) container = m_portalLines; + + // Edge indicators are duplicated 3 times to make them more visible + + for(int i = 0; i < 3; i++) + { + container->push_back( a + scootUp * float(i) ); + container->push_back( b + scootUp * float(i) ); + } + } + + // Create the 'y's that are used to indicate fallthrough + // tris + + VectorVector * container = F.isFallthrough() ? m_fallthroughTriLines : m_solidTriLines; + + { + float lerp = 0.3f; + + Vector a = getVertex(F.getCornerIndex(0)) + scootUp; + Vector b = getVertex(F.getCornerIndex(1)) + scootUp; + Vector c = getVertex(F.getCornerIndex(2)) + scootUp; + + Vector center = (a + b + c) / 3.0f; + + a = a * lerp + center * (1.0f - lerp); + b = b * lerp + center * (1.0f - lerp); + c = c * lerp + center * (1.0f - lerp); + + container->push_back(a); container->push_back(center); + container->push_back(b); container->push_back(center); + container->push_back(c); container->push_back(center); + } + } +} + +#endif + +// ---------------------------------------------------------------------- + +bool FloorMesh::getDistanceUncrossable2d ( Vector const & V, float maxDistance, float & outDistance, FloorEdgeId & outEdgeId ) const +{ + if(!m_uncrossableEdges) return false; + + // ---------- + + int count = m_uncrossableEdges->size(); + + float minDistance = maxDistance; + + FloorEdgeId minId(-1,-1); + + for(int i = 0; i < count; i++) + { + FloorEdgeId const & id = m_uncrossableEdges->at(i); + + Segment3d edge = getTriangle(id.triId).getEdgeSegment(id.edgeId); + + float dist = Distance2d::DistancePointSeg(V,edge); + + if(dist < minDistance) + { + minDistance = dist; + minId = id; + } + } + + if(minDistance != maxDistance) + { + outDistance = minDistance; + outEdgeId = minId; + + return true; + } + else + { + return false; + } +} + +// ====================================================================== diff --git a/engine/shared/library/sharedCollision/src/shared/core/FloorMesh.h b/engine/shared/library/sharedCollision/src/shared/core/FloorMesh.h new file mode 100644 index 00000000..d48f177d --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/FloorMesh.h @@ -0,0 +1,349 @@ +// ====================================================================== +// +// FloorMesh.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_FloorMesh_H +#define INCLUDED_FloorMesh_H + +// ====================================================================== + +#include "sharedCollision/CollisionMesh.h" +#include "sharedFoundation/Tag.h" +#include "sharedFoundation/DataResource.h" +#include "sharedCollision/CollisionEnums.h" + +class FloorTri; +class ConnectedTri; +class Iff; +class FloorMesh; +class FloorLocator; +class Circle; +class Ribbon3d; +class DebugShapeRenderer; +class BaseClass; +class Appearance; + +#define FLOOR_LOG(A) DEBUG_REPORT_LOG_PRINT(ConfigSharedCollision::getReportMessages(),(A)) +#define FLOOR_WARNING(A) DEBUG_WARNING(ConfigSharedCollision::getReportWarnings(),(A)) + +template +class DataResourceList; +typedef DataResourceList FloorMeshList; +typedef stdvector::fwd FloorTriVec; +typedef std::pair IntPair; + +class FloorEdgeId +{ +public: + + FloorEdgeId () : triId(-1), edgeId(-1) {} + + FloorEdgeId ( int triangle, int edge ) : triId(triangle), edgeId(edge) {} + + int triId; + int edgeId; +}; + +typedef stdvector::fwd FloorEdgeIdVec; + +typedef stdvector::fwd FloorLocatorVec; + +// ====================================================================== + +class FloorMesh : public CollisionMesh, public DataResource +{ +public: + + explicit FloorMesh(const std::string & filename); + FloorMesh( VectorVector const & vertices, IntVector const & indices ); + + virtual ~FloorMesh(); + + // ---------- + + static void install ( void ); + static void remove ( void ); + + static FloorMesh * create ( const std::string & filename ); + + virtual void calcBounds ( void ) const; + + // ---------- + // Basic interface inherited from CollisionMesh - accessors/modifiers + + // ----- + + virtual int getVertexCount ( void ) const; + virtual Vector const & getVertex ( int whichVertex ) const; + virtual void setVertex ( int whichVertex, Vector const & newPoint ); + + virtual int getTriCount ( void ) const; + virtual IndexedTri const & getIndexedTri ( int whichTri ) const; + virtual void setIndexedTri ( int whichTri, IndexedTri const & newTri ); + + virtual Triangle3d getTriangle ( int whichTri ) const; + + virtual void deleteVertex ( int whichVertex ); + virtual void deleteVertices ( IntVector const & vertIndices ); + + virtual void deleteTri ( int whichTri ); + virtual void deleteTris ( IntVector const & triIndices ); + + // ---------- + + void clear(); + + // ---------- + + void clearMarks ( int clearMarkValue ); + + void setObjectFloor ( bool objectFloor ); + + void setAppearance ( Appearance const * appearance ); + + // ---------- + + void addTriangle ( Triangle3d const & newTri ); + void addTriangle ( int iA, int iB, int iC ); + + void build ( VectorVector const & verts, IntVector const & indices ); + + // ---------- + + FloorTri & getFloorTri ( int whichTri ); + FloorTri const & getFloorTri ( int whichTri ) const; + + FloorEdgeIdVec const & getEdgeList ( FloorEdgeType edgeType ) const; + + // ---------- + + BaseClass * getPathGraph ( void ); + BaseClass const * getPathGraph ( void ) const; + void attachPathGraph ( BaseClass * newGraph ); + + // ---------- + + void addCrossableEdges ( IntVector const & edgePairs ); + void addRampEdges ( IntVector const & edgePairs ); + void addFallthroughTris ( IntVector const & triIDs ); + bool flagPortalEdges ( VectorVector const & portalVerts, int portalId ); + void clearPortalEdges ( void ); + + // ---------- + + void write ( Iff & iff ); + void read ( Iff & iff ); + void read_0000 ( Iff & iff ); + void read_0001 ( Iff & iff ); + void read_0002 ( Iff & iff ); + void read_0003 ( Iff & iff ); + void read_0004 ( Iff & iff ); + void read_0005 ( Iff & iff ); + void read_0006 ( Iff & iff ); + + // ---------- + + void compile ( void ); //! Do this once all data has been added to the floor mesh + + // ---------- + // Interface inherited from CollisionSurface + + virtual Transform const & getTransform_o2p ( void ) const; + + // ---------- + // Floor-specific methods + + bool findFloorTri ( FloorLocator const & testLoc, + float heightTolerance, + bool bAllowJump, + FloorLocator & outLoc ) const; + + bool findFloorTri ( FloorLocator const & testLoc, + bool bAllowJump, + FloorLocator & out ) const; + + PathWalkResult pathWalkPoint( FloorLocator const & startLoc, Vector const & delta, + int & outHitTriId, int & outHitEdge, float & outHitTime ) const; + + PathWalkResult pathWalkCircle(FloorLocator const & startLoc, Vector const & delta, + int ignoreTriId, int ignoreEdge, + FloorLocator & result ) const; + + PathWalkResult pathWalkCircleGetIds(FloorLocator const & startLoc, Vector const & delta, + int ignoreTriId, int ignoreEdge, + float & outHitTime, int & outHitTriId, int & outHitEdge, int & outCenterTriId ) const; + + void makeHitResult ( Vector const & begin, + Vector const & delta, + int hitTriId, + int hitEdge, + float hitTime, + FloorLocator & result ) const; + + void makeHitResult2 ( Vector const & begin, + Vector const & delta, + int centerTriId, + int hitTriID, + int hitEdge, + float hitTime, + FloorLocator & result ) const; + + void makeExitResult ( Vector const & begin, + Vector const & delta, + int hitTriID, + int hitEdge, + float hitTime, + FloorLocator & result ) const; + + void makeHitPortalResult ( Vector const & begin, + Vector const & delta, + int hitTriID, + int hitEdge, + float hitTime, + FloorLocator & result ) const; + + bool calcHitTime ( Segment3d const & seg, + int hitTri, + int hitEdge, + float & outHitTime ) const; + + bool attach ( FloorLocator & loc ) const; + + void makeSuccessResult ( FloorLocator const & loc, FloorLocator & result ) const; + + void makeFailureResult ( FloorLocator & result ) const; + + bool intersectBoundary ( FloorLocator const & startLoc, Vector const & delta, bool useRadius, FloorLocatorVec & results ) const; + + bool intersectBoundary ( FloorEdgeIdVec * edgeList, FloorLocator const & startLoc, Vector const & delta, bool useRadius, FloorLocatorVec & results ) const; + + bool intersectEdge ( FloorLocator const & startLoc, Vector const & delta, FloorEdgeId const & id, bool useRadius, bool testFront, FloorLocator & outLoc ) const; + + bool findEntrance ( FloorLocator const & loc, Vector const & delta, bool useRadius, FloorLocator & outLoc ) const; + + PathWalkResult findStartingTri ( FloorLocator const & startLoc, Vector const & delta, bool useRadius, int & outTriId ) const; + + bool findClosestLocation ( FloorLocator const & testLoc, FloorLocator & outLoc ) const; + + bool dropTest ( FloorLocator const & testLoc, FloorLocator & outLoc ) const; + bool dropTest ( FloorLocator const & testLoc, float hopHeight, FloorLocator & outLoc ) const; + bool dropTest ( FloorLocator const & testLoc, int triId, FloorLocator & outLoc ) const; + + void drawDebugShapes ( DebugShapeRenderer * renderer, bool drawExtent ) const; + + bool validate ( FloorLocator const & loc ) const; + + // Determine if the locator can fit on the floor without hitting a non-crossable edge + + bool testClear ( FloorLocator const & testLoc ) const; + bool testAboveCrossables ( FloorLocator const & testLoc ) const; + bool testAboveCrossables ( FloorLocator const & testLoc, Vector const & delta ) const; + bool getClosestCollidableEdge ( FloorLocator const & testLoc, int & outTriId, int & outEdgeId, float & outDist ) const; + + bool testConnectable ( FloorLocator const & A, FloorLocator const & B ) const; + + void loadFromIff ( Iff & file ); + + bool getGoodLocation ( float radius, Vector & outLoc ) const; + + bool canEnterEdge ( FloorLocator const & enterLoc ) const; + bool canExitEdge ( FloorLocator const & exitLoc, Vector const & delta, bool useRadius) const; + + bool canEnterEdge ( FloorLocator const & startLoc, Vector const & delta, FloorEdgeId const & id, bool useRadius ) const; + bool canExitEdge ( FloorLocator const & startLoc, Vector const & delta, FloorEdgeId const & id, bool useRadius ) const; + + bool getDistanceUncrossable2d ( Vector const & position_l, float maxDistance, float & outDistance, FloorEdgeId & outEdgeId ) const; + + int getPartCount ( void ) const; + +protected: + + // ---------- + // Interface inherited from DataResource + + virtual void load ( Iff &file ); + virtual Tag getId ( void ) const; + virtual void release ( void ) const; + + // ---------- + + void sweep ( void ); //! Do a mark-sweep to remove any unused vertices + void link ( void ); //! Build connectivity info for the floor tris + void flagCrossableEdges ( void ); //! Mark all edges with neighbors as crossable + void assignIndices ( void ); //! Reassign indices to all the tris in the mesh + void setPartTags ( void ); //! Calculate part tags for each floor tri + void calcHeightFuncs ( void ); + + int getTriMarkValue ( void ) const; + + typedef std::pair EdgeId; + + typedef stdvector::fwd EdgeIdVec; + + void findAdjacentBoundaryEdges ( VectorVector const & polyVerts, EdgeIdVec & outIds ) const; + + static bool matchSegmentToPoly ( Vector const & a, Vector const & b, VectorVector const & polyVerts ); + + void buildBoundaryEdgeList ( void ); + void buildCrossableEdgeList ( void ); + +#ifdef _DEBUG + + virtual void buildDebugData ( void ); + + VectorVector * m_crossableLines; + VectorVector * m_uncrossableLines; + VectorVector * m_interiorLines; + VectorVector * m_portalLines; + VectorVector * m_rampLines; + VectorVector * m_fallthroughTriLines; + VectorVector * m_solidTriLines; + VectorVector * m_pathLines; + +#endif + +private: + + // Disable copying + + FloorMesh(const FloorMesh &source); + FloorMesh & operator =(const FloorMesh &source); + + // ---------- + // Data + + VectorVector * m_vertices; + FloorTriVec * m_floorTris; + + FloorEdgeIdVec * m_crossableEdges; + FloorEdgeIdVec * m_uncrossableEdges; + FloorEdgeIdVec * m_wallBaseEdges; + FloorEdgeIdVec * m_wallTopEdges; + + BaseClass * m_pathGraph; // blind pointer to path graph data + + Appearance const * m_appearance; + + mutable int m_triMarkCounter; + + mutable bool m_objectFloor; +}; + +// ---------------------------------------------------------------------- + +inline void FloorMesh::loadFromIff(Iff &file) +{ + load(file); +} + +inline void FloorMesh::setObjectFloor ( bool objectFloor ) +{ + m_objectFloor = objectFloor; +} + +// ---------------------------------------------------------------------- + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/FloorTri.cpp b/engine/shared/library/sharedCollision/src/shared/core/FloorTri.cpp new file mode 100644 index 00000000..db4b562a --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/FloorTri.cpp @@ -0,0 +1,198 @@ +// ====================================================================== +// +// Footprint.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/FloorTri.h" + +#include "sharedFile/Iff.h" + +IndexedTri::IndexedTri() +{ + m_corners[0] = -1; + m_corners[1] = -1; + m_corners[2] = -1; +} + +// ---------- + +void IndexedTri::read_0000 ( Iff & iff ) +{ + setCornerIndex( 0, iff.read_int32() ); + setCornerIndex( 1, iff.read_int32() ); + setCornerIndex( 2, iff.read_int32() ); +} + +void IndexedTri::write ( Iff & iff ) const +{ + iff.insertChunkData( getCornerIndex(0) ); + iff.insertChunkData( getCornerIndex(1) ); + iff.insertChunkData( getCornerIndex(2) ); +} + +// ---------------------------------------------------------------------- + +ConnectedTri::ConnectedTri() +: IndexedTri(), + m_index(-1) +{ + m_neighbors[0] = -1; + m_neighbors[1] = -1; + m_neighbors[2] = -1; +} + +void ConnectedTri::read_0000 ( Iff & iff ) +{ + IndexedTri::read_0000(iff); + + setIndex( iff.read_int32() ); + + setNeighborIndex( 0, iff.read_int32() ); + setNeighborIndex( 1, iff.read_int32() ); + setNeighborIndex( 2, iff.read_int32() ); +} + +void ConnectedTri::write ( Iff & iff ) const +{ + IndexedTri::write(iff); + + iff.insertChunkData( getIndex() ); + + iff.insertChunkData( getNeighborIndex(0) ); + iff.insertChunkData( getNeighborIndex(1) ); + iff.insertChunkData( getNeighborIndex(2) ); +} + +// ---------------------------------------------------------------------- + +FloorTri::FloorTri() +: ConnectedTri(), + m_normal( Vector::zero ), + m_fallthrough( false ), + m_partTag( -1 ), + m_mark( 0 ) +{ + m_edgeTypes[0] = FET_Uncrossable; + m_edgeTypes[1] = FET_Uncrossable; + m_edgeTypes[2] = FET_Uncrossable; + + m_edgeMarks[0] = 0; + m_edgeMarks[1] = 0; + m_edgeMarks[2] = 0; + + m_portalIds[0] = -1; + m_portalIds[1] = -1; + m_portalIds[2] = -1; +} + +FloorTri::~FloorTri() +{ +} + +// ---------------------------------------------------------------------- + +void FloorTri::read_0000 ( Iff & iff ) +{ + ConnectedTri::read_0000(iff); + + setNormal( iff.read_floatVector() ); + + setCrossable( 0, iff.read_bool8() ); + setCrossable( 1, iff.read_bool8() ); + setCrossable( 2, iff.read_bool8() ); + + setFallthrough( iff.read_bool8() ); +} + +// ---------- + +void FloorTri::read_0001 ( Iff & iff ) +{ + ConnectedTri::read_0000(iff); + + setNormal( iff.read_floatVector() ); + + setCrossable( 0, iff.read_bool8() ); + setCrossable( 1, iff.read_bool8() ); + setCrossable( 2, iff.read_bool8() ); + + setFallthrough( iff.read_bool8() ); + + setPartTag( iff.read_int32() ); + + setPortalId( 0, iff.read_int32() ); + setPortalId( 1, iff.read_int32() ); + setPortalId( 2, iff.read_int32() ); +} + +// ---------- + +void FloorTri::read_0002 ( Iff & iff ) +{ + ConnectedTri::read_0000(iff); + + setNormal( iff.read_floatVector() ); + + setEdgeType( 0, static_cast(iff.read_uint8()) ); + setEdgeType( 1, static_cast(iff.read_uint8()) ); + setEdgeType( 2, static_cast(iff.read_uint8()) ); + + setFallthrough( iff.read_bool8() ); + + setPartTag( iff.read_int32() ); + + setPortalId( 0, iff.read_int32() ); + setPortalId( 1, iff.read_int32() ); + setPortalId( 2, iff.read_int32() ); +} + +// ---------------------------------------------------------------------- + +void FloorTri::write ( Iff & iff ) const +{ + ConnectedTri::write(iff); + + iff.insertChunkFloatVector( getNormal() ); + + iff.insertChunkData( static_cast(getEdgeType(0)) ); + iff.insertChunkData( static_cast(getEdgeType(1)) ); + iff.insertChunkData( static_cast(getEdgeType(2)) ); + + iff.insertChunkData( isFallthrough() ); + + iff.insertChunkData( getPartTag() ); + + iff.insertChunkData( getPortalId(0) ); + iff.insertChunkData( getPortalId(1) ); + iff.insertChunkData( getPortalId(2) ); +} + +// ---------------------------------------------------------------------- + +void FloorTri::setHeightFunc ( Vector const & heightFunc ) +{ + m_heightFunc = heightFunc; +} + +float FloorTri::getHeightAt ( float x, float z ) const +{ + return (x * m_heightFunc.x) + m_heightFunc.y + (z * m_heightFunc.z); +} + +// ---------------------------------------------------------------------- +// Old versions of FloorMesh didn't explicitly write the number of +// floor tris in the mesh. We can determine how many tris are in the +// IFF chunk if we know how much data was written for each floor tri. + +int FloorTri::getOnDiskSize_0001 ( void ) +{ + // Version 0001 has 12 bytes for ConnectedTri, 16 bytes for IndexedTri, + // and 32 bytes for FloorTri == 60 bytes total. + + return 60; +} + +// ---------------------------------------------------------------------- diff --git a/engine/shared/library/sharedCollision/src/shared/core/FloorTri.h b/engine/shared/library/sharedCollision/src/shared/core/FloorTri.h new file mode 100644 index 00000000..28b423d9 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/FloorTri.h @@ -0,0 +1,285 @@ +// ====================================================================== +// +// FloorTri.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_FloorTri_H +#define INCLUDED_FloorTri_H + +// ====================================================================== + +#include "sharedMath/Vector.h" +#include "sharedCollision/CollisionEnums.h" + +class Iff; + +// ====================================================================== +// Dumb Data + +class IndexedTri +{ +public: + + IndexedTri(); + + // ---------- + + int getCornerIndex ( int whichCorner ) const; + void setCornerIndex ( int whichCorner, int newIndex ); + + void read_0000 ( Iff & iff ); + void write ( Iff & iff ) const; + + // ---------- + +protected: + + int m_corners [3]; //! Vertex indices +}; + +// ---------------------------------------------------------------------- + +inline int IndexedTri::getCornerIndex ( int whichCorner ) const +{ + return m_corners[whichCorner % 3]; +} + +inline void IndexedTri::setCornerIndex ( int whichCorner, int newIndex ) +{ + m_corners[whichCorner % 3] = newIndex; +} + +// ====================================================================== + +class ConnectedTri : public IndexedTri +{ +public: + + ConnectedTri(); + + int getIndex ( void ) const; + void setIndex ( int newIndex ); + + bool hasNeighbor ( int whichEdge ) const; + int getNeighborIndex ( int whichEdge ) const; + void setNeighborIndex ( int whichEdge, int newIndex ); + + void read_0000 ( Iff & iff ); + void write ( Iff & iff ) const; + + // ---------- + +protected: + + int m_index; //! This triangle's index + int m_neighbors[3]; //! Neighbor triangle indices +}; + +// ---------- + +inline int ConnectedTri::getIndex ( void ) const +{ + return m_index; +} + +inline void ConnectedTri::setIndex ( int newIndex ) +{ + m_index = newIndex; +} + +inline bool ConnectedTri::hasNeighbor ( int whichEdge ) const +{ + return getNeighborIndex(whichEdge) != -1; +} + +inline int ConnectedTri::getNeighborIndex ( int whichEdge ) const +{ + return m_neighbors[whichEdge % 3]; +} + +inline void ConnectedTri::setNeighborIndex ( int whichEdge, int newIndex ) +{ + m_neighbors[whichEdge % 3] = newIndex; +} + +// ---------------------------------------------------------------------- + +class FloorTri : public ConnectedTri +{ +public: + + FloorTri(); + ~FloorTri(); + + // ---------- + + Vector const & getNormal ( void ) const; + void setNormal ( Vector const & newNormal ); + + bool isFallthrough ( void ) const; + void setFallthrough ( bool bFallthrough ); + + bool isCrossable ( int whichEdge ) const; + void setCrossable ( int whichEdge, bool bCrossable ); + + FloorEdgeType getEdgeType ( int whichEdge ) const; + void setEdgeType ( int whichEdge, FloorEdgeType newType ); + + int getPartTag ( void ) const; + void setPartTag ( int newTag ); + + int getPortalId ( int whichEdge ) const; + void setPortalId ( int whichEdge, int newId ); + + void read_0000 ( Iff & iff ); + void read_0001 ( Iff & iff ); + void read_0002 ( Iff & iff ); + void write ( Iff & iff ) const; + + void setHeightFunc ( Vector const & func ); + float getHeightAt ( float x, float z ) const; + + // ---------- + + int getMark ( void ) const; + void setMark ( int newMark ) const; + + int getEdgeMark ( int whichEdge ) const; + void setEdgeMark ( int whichEdge, int newMark ) const; + + // ---------- + + static int getOnDiskSize_0001 ( void ); + static int getOnDiskSize_0002 ( void ); + +protected: + + Vector m_normal; //! This triangle's normal + FloorEdgeType m_edgeTypes[3]; + bool m_fallthrough; //! If this is true, you'll fall onto the terrain under the floor instead of standing on the floor + int m_partTag; //! Each disjoint part of a floor has a different part tag + + mutable int m_mark; //! Mark value, used in various algorithms to turn them from O(N^2) to O(N). + mutable int m_edgeMarks[3]; //! Mark value for each edge of this floor tri. + + int m_portalIds[3]; //! Adjacent portal IDs for the edges of the tri. + //! This probably is wasteful since most of the time they'll be -1, but... + + Vector m_heightFunc; //! y(x,z) = m_heightFunc.x * x + m_heightFunc.y + m_heightFunc.z * z +}; + +// ---------- + +inline Vector const & FloorTri::getNormal ( void ) const +{ + return m_normal; +} + +inline void FloorTri::setNormal ( Vector const & newNormal ) +{ + m_normal = newNormal; +} + +// ---------- + +inline int FloorTri::getMark ( void ) const +{ + return m_mark; +} + +inline void FloorTri::setMark ( int newMark ) const +{ + m_mark = newMark; +} + +// ---------- + +inline int FloorTri::getEdgeMark ( int whichEdge ) const +{ + return m_edgeMarks[whichEdge]; +} + +inline void FloorTri::setEdgeMark ( int whichEdge, int newMark ) const +{ + m_edgeMarks[whichEdge] = newMark; +} +// ---------- + +inline bool FloorTri::isFallthrough ( void ) const +{ + return m_fallthrough; +} + +inline void FloorTri::setFallthrough ( bool bFallthrough ) +{ + m_fallthrough = bFallthrough; +} + +// ---------- + +inline bool FloorTri::isCrossable ( int whichEdge ) const +{ + if(m_portalIds[whichEdge % 3] == -1) + { + return m_edgeTypes[whichEdge % 3] == FET_Crossable; + } + else + { + return true; + } +} + +inline void FloorTri::setCrossable ( int whichEdge, bool bCrossable ) +{ + m_edgeTypes[whichEdge % 3] = bCrossable ? FET_Crossable : FET_Uncrossable; +} + +// ---------- + +inline int FloorTri::getPartTag ( void ) const +{ + return m_partTag; +} + +inline void FloorTri::setPartTag ( int newTag ) +{ + m_partTag = newTag; +} + +// ---------- + +inline FloorEdgeType FloorTri::getEdgeType ( int whichEdge ) const +{ + if(m_portalIds[whichEdge % 3] == -1) + { + return m_edgeTypes[whichEdge % 3]; + } + else + { + return FET_Crossable; + } +} + +inline void FloorTri::setEdgeType ( int whichEdge, FloorEdgeType newType ) +{ + m_edgeTypes[whichEdge % 3] = newType; +} + +// ---------- + +inline int FloorTri::getPortalId ( int whichEdge ) const +{ + return m_portalIds[whichEdge]; +} + +inline void FloorTri::setPortalId ( int whichEdge, int newId ) +{ + m_portalIds[whichEdge] = newId; +} + +// ====================================================================== + +#endif + diff --git a/engine/shared/library/sharedCollision/src/shared/core/Footprint.cpp b/engine/shared/library/sharedCollision/src/shared/core/Footprint.cpp new file mode 100644 index 00000000..00bec3b3 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Footprint.cpp @@ -0,0 +1,1692 @@ +// ====================================================================== +// +// Footprint.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/Footprint.h" + +#include "sharedCollision/CollisionDetect.h" +#include "sharedCollision/CollisionEnums.h" +#include "sharedCollision/CollisionProperty.h" +#include "sharedCollision/CollisionResolve.h" +#include "sharedCollision/CollisionUtils.h" +#include "sharedCollision/CollisionWorld.h" +#include "sharedCollision/ConfigSharedCollision.h" +#include "sharedCollision/Containment3d.h" +#include "sharedCollision/Distance3d.h" +#include "sharedCollision/Floor.h" +#include "sharedCollision/FloorContactShape.h" +#include "sharedCollision/FloorMesh.h" +#include "sharedCollision/FloorTri.h" +#include "sharedCollision/Intersect1d.h" +#include "sharedCollision/Intersect3d.h" +#include "sharedCollision/SimpleExtent.h" +#include "sharedCollision/SpatialDatabase.h" + +#include "sharedFoundation/Fatal.h" +#include "sharedFoundation/Clock.h" +#include "sharedFoundation/ConfigFile.h" + +#include "sharedDebug/Report.h" +#include "sharedDebug/PerformanceTimer.h" + +#include "sharedMath/AxialBox.h" +#include "sharedMath/DebugShapeRenderer.h" +#include "sharedMath/Circle.h" +#include "sharedMath/Cylinder.h" +#include "sharedMath/DebugShapeRenderer.h" +#include "sharedMath/Line3d.h" +#include "sharedMath/Ray3d.h" +#include "sharedMath/Segment3d.h" +#include "sharedMath/Sphere.h" +#include "sharedMath/Plane3d.h" +#include "sharedMath/Triangle3d.h" + +#include "sharedObject/CellProperty.h" +#include "sharedObject/Appearance.h" +#include "sharedObject/AppearanceTemplate.h" + +#include "sharedRandom/Random.h" + +#include "sharedTerrain/TerrainObject.h" + +#include + +#pragma warning(disable : 4355) // 'this' used in base member initializer list + +// ---------- + +typedef MultiListDataIterator ContactIterator; +typedef MultiListConstDataIterator ConstContactIterator; + +// ---------------------------------------------------------------------- + +namespace FootprintNamespace +{ +} + +using namespace FootprintNamespace; + +extern real terrainHeightTime; +extern real canMoveTime; +extern real canMoveTime2; + + +// ---------------------------------------------------------------------- + +static bool epsilonEqual( float value, float target, float epsilon ) +{ + float delta = value - target; + + if(delta > epsilon) return false; + if(delta < -epsilon) return false; + + return true; +} + +// ====================================================================== + +Footprint::Footprint ( Vector const & position, float radius, CollisionProperty * parent, const float swimHeight ) +: BaseClass(), + m_parent(parent), + m_cellObject(NULL), + m_position_p(Vector::zero), + m_position_w(Vector::zero), + m_radius(radius), + m_floorList( this, 0 ), + m_swimHeight (swimHeight), + m_hasTerrainHeight(false), + m_hasFloorHeight(false), + m_hasGroundHeight(false), + m_terrainHeight(0.0f), + m_floorHeight(0.0f), + m_groundHeight(0.0f), + m_groundNormal(Vector::unitY), + m_addToWorldTime(-1), + m_addToWorldPos(Vector::zero), + m_floating(false), + m_floatingTime(20) +#ifdef _DEBUG + , + m_backupPosition_p(position), + m_backupCell(NULL), + m_backupObjectPosition_p(position), + m_backupObjectCell(NULL), + m_lineHitTime( -1.0f ), + m_lineOrigin( Vector(0.0f,1.5f,0.0f) ), + m_lineDelta( Vector(0.0f,0.0f,10.0f) ), + m_canMove( true ), + m_lineHitPos( Vector::zero ), + m_bubbleSize(0.0f), + m_bubbleEmpty(false) +#endif +{ + NOT_NULL(parent); + + setPosition(getObjectCell(),position); + + updateHeights(); +} + +// ---------- + +Footprint::~Footprint() +{ + detach(); + + m_parent = NULL; + m_cellObject = NULL; +} + +// ====================================================================== + +void Footprint::detach ( void ) +{ + FLOOR_LOG("Footprint::detach - Detaching all contact points\n"); + + getFloorList().clear(); +} + +// ---------------------------------------------------------------------- + +void Footprint::reattachContacts ( void ) +{ + // move detached contacts to the footprint + + ContactIterator it; + + for(it = getFloorList(); it; ++it) + { + FloorContactShape * contact = *it; + + if(!contact) continue; + + FloorLocator & loc = contact->m_contact; + + if(!loc.isAttached()) + { + Vector footPos = getPosition_p(loc.getCell()); + + loc.setPosition_p(footPos); + } + } + + // If all the foot's contacts have been detached, try and reattach them + // before sweeping to catch walking-off-ledge cases + + for(it = getFloorList(); it; ++it) + { + FloorContactShape * contact = *it; + + if(!contact) continue; + + if(contact->m_contact.getId() != -1) + { + return; + } + } + + for(it = getFloorList(); it; ++it) + { + FloorContactShape * contact = *it; + + if(!contact) continue; + + contact->m_contact.reattach(); + } +} + +// ---------- + +void Footprint::forceReattachContacts ( void ) +{ + for(ContactIterator it(getFloorList()); it; ++it) + { + FloorContactShape * contact = *it; + + if(!contact) continue; + + if(contact->m_contact.getId() == -1) + { + contact->m_contact.reattach(); + } + } +} + +// ---------- + +void Footprint::snapContacts ( void ) +{ + for(ContactIterator it(getFloorList()); it; ++it) + { + (*it)->m_contact.snapToFloor(); + } +} + +// ---------------------------------------------------------------------- +// Remove any contact points from the footprint that have been detached +// from their surfaces + +void Footprint::sweepContacts ( void ) +{ + ContactIterator it(getFloorList()); + + CellProperty const * objectCell = getObjectCell(); + CellProperty const * worldCell = CellProperty::getWorldCellProperty(); + + while(it) + { + FloorContactShape * contact = *it; + + MultiListNode * node = it.getNode(); + + ++it; + + FloorLocator & loc = contact->m_contact; + + FloorLocator temp; + + Floor const * floor = loc.getFloor(); + + if((loc.getId() != -1) && floor && !floor->dropTest(loc.getPosition_p(),loc.getId(),temp)) + { + loc.detach(); + } + + if(!loc.isAttached()) + { + Floor const * floor = loc.getFloor(); + + if(floor) + { + CellProperty const * contactCell = loc.getCell(); + + if(contactCell == worldCell) + { + if(objectCell != worldCell) + { + // detached world cell contacts are erased immediately if the object isn't in the world cell + } + else + { + // otherwise the contact sticks around until it's more than 2 meters away from the floor's bounding box + + SimpleExtent const * extent = safe_cast(floor->getFloorMesh()->getExtent_l()); + AxialBox box = extent->getShape().getAxialBox(); + + float dist2 = Distance3d::Distance2PointABox(loc.getPosition_l(),box); + + if(dist2 < 4.0f) continue; + } + } + else + { + // Remove all cell contacts as soon as they're detached. + // This may cause some thrashing on portal transitions, but leaving detached cell contacts + // hanging around causes bugs (and is also slower due to the entrance tests) + } + } + + FLOOR_LOG("Footprint::sweepContacts - Removing detached contact\n"); + + getFloorList().erase(node); + } + else + { + // If we're in a cell, remove any world cell contacts. Otherwise we might run into invisible edges + // from things in the world (ex. bunker uberlair has fallthrough floor all along the interior; if + // you don't detach the world contact you hit an edge of the corral floor it when you're down inside + // the bunker building) + + CellProperty const * contactCell = loc.getCell(); + + if(objectCell != worldCell) + { + if(contactCell == worldCell) + { + getFloorList().erase(node); + } + } + } + } +} + +// ---------------------------------------------------------------------- + +Object const * Footprint::getOwner ( void ) const +{ + NOT_NULL(m_parent); + + return &(m_parent->getOwner()); +} + +Object * Footprint::getOwner ( void ) +{ + NOT_NULL(m_parent); + + return &(m_parent->getOwner()); +} + +// ---------------------------------------------------------------------- + +void Footprint::addContact ( FloorLocator const & loc ) +{ + if(loc.getFloor() == NULL) + { + FLOOR_LOG("Footprint::addContact - loc isn't attached to a floor\n"); + } + + FLOOR_LOG("Footprint::addContact - Adding contact point\n"); + + // ---------- + + FloorContactShape * contact = new FloorContactShape(); + + contact->m_contact = loc; + contact->m_contact.setRadius(getRadius()); + + // ---------- + + Floor const * pcFloor = loc.getFloor(); + + Floor * floor = const_cast(pcFloor); + + getFloorList().connectTo( floor->getFootList(), contact ); + +} //lint !e429 // custodial pointer 'contact' has not been freed or returned - it's attached to the floor list + +// ---------------------------------------------------------------------- +// move the footprint over its contact points + +//@todo - This should probably check to see if the contacts are aligned first. + +bool Footprint::syncToContacts ( void ) +{ + if(!getFloorList().isEmpty()) + { + FloorContactShape * contact = safe_cast(getFloorList().getHead()->getData()); + + FloorLocator & loc = contact->m_contact; + + Vector point = loc.getPosition_p(getCell()); + + Vector foot = getPosition_p(); + + foot.x = point.x; + foot.z = point.z; + + setPosition_p(foot); + } + + IGNORE_RETURN( updateHeights() ); + + return true; +} + +// ---------------------------------------------------------------------- + +bool Footprint::isOnSolidFloor ( void ) const +{ + for(ConstContactIterator it(getFloorList()); it; ++it) + { + FloorContactShape const * contact = *it; + + if(!contact) continue; + + if(contact->m_contact.isFallthrough()) continue; + + if(contact->m_contact.isAttached()) return true; + } + + return false; +} + +// ---------------------------------------------------------------------- +// The footprint is out of sync if the object it is attached to is not at +// the same X-Z coordinates (within a small tolerance). + +bool Footprint::isObjectInSync ( void ) const +{ + Vector objectPos = getObjectPosition_p(); + + Vector footPos = getPosition_p(getObjectCell()); + + if(!epsilonEqual(objectPos.x,footPos.x,0.00001f)) return false; + + if(!epsilonEqual(objectPos.z,footPos.z,0.00001f)) return false; + + return true; +} + +// ---------------------------------------------------------------------- +/** + * Set the swim height for this footprint. + * + * This is necessary because swim height varies per creature per scale, + * and scale is not known until after end baselines. Meanwhile, this + * Footprint is created prior to knowledge of the scale. CreatureObject's + * endBaselines sets this value appropriately once scale factor is known. + * + * @param swimHeight the depth of the water at which point the creature + * starts swimming. + */ + +void Footprint::setSwimHeight ( float swimHeight ) +{ + NAN_CHECK(swimHeight); + + m_swimHeight = swimHeight; +} + +// ---------------------------------------------------------------------- + +Vector const & Footprint::getPosition_p ( void ) const +{ + if(m_cellObject.getPointer() == NULL) + { + WARNING(true,("Footprint::getPosition_p - Footprint's parent cell has disappeared")); + + Footprint * self = const_cast(this); + + self->m_position_p = self->m_position_w; + + self->m_cellObject = &(CellProperty::getWorldCellProperty()->getOwner()); + } + + return m_position_p; +} + +// ---------- + +Vector Footprint::getPosition_p ( CellProperty const * relativeCell ) const +{ + return CollisionUtils::transformToCell( getCell(), getPosition_p(), relativeCell ); +} + +// ---------- + +Vector const & Footprint::getPosition_w ( void ) const +{ + return m_position_w; +} + +// ---------- + +void Footprint::setPosition_p ( Vector const & pos ) +{ + NAN_CHECK(pos); + + m_position_p = pos; + m_position_w = CollisionUtils::transformToWorld(getCell(),pos); +} + +// ---------- + +void Footprint::setPosition_w ( Vector const & position_w ) +{ + NAN_CHECK(position_w); + + m_position_p = CollisionUtils::transformFromWorld(position_w,getCell()); + m_position_w = position_w; +} + +// ---------- + +CellProperty * Footprint::getCell ( void ) +{ + if(m_cellObject.getPointer()) + { + return m_cellObject->getCellProperty(); + } + else + { + return CellProperty::getWorldCellProperty(); + } +} + +CellProperty const * Footprint::getCell ( void ) const +{ + if(m_cellObject.getPointer()) + { + return m_cellObject->getCellProperty(); + } + else + { + return CellProperty::getWorldCellProperty(); + } +} + +// ---------- + +void Footprint::setPosition ( CellProperty * pNewCell, Vector const & pos ) +{ + NAN_CHECK(pos); + + if(pNewCell == NULL) pNewCell = CellProperty::getWorldCellProperty(); + + m_cellObject = &(pNewCell->getOwner()); + m_position_p = pos; + m_position_w = CollisionUtils::transformToWorld(pNewCell,pos); +} + +// ---------------------------------------------------------------------- +// Teleport the footprint (and attached object) to a different point on the same floor. + +bool Footprint::teleport ( FloorLocator const & loc ) +{ + detach(); + + addContact(loc); + + syncToContacts(); + + setObjectPosition_p( getPosition_p() ); + + m_parent->setIdle(false); + + return true; +} + +// ---------------------------------------------------------------------- + +bool Footprint::isAttached ( void ) const +{ + for(ConstContactIterator it(getFloorList()); it; ++it) + { + FloorContactShape const * contact = *it; + + if(!contact) continue; + + if(contact->m_contact.isAttached()) return true; + } + + return false; +} + +// ---------------------------------------------------------------------- +// Force attach of footprint to cell floor +// Force attach of footprint to nearby cells + +void Footprint::updatePreResolve ( float time ) +{ + UNREF(time); + + CellProperty * objectCell = getObjectCell(); + + Vector newPosition_p = getPosition_p(objectCell); + + setPosition(objectCell,newPosition_p); + + IGNORE_RETURN(attachTo(objectCell->getFloor(),false)); + + // This requires some explanation - When creatures are moving around really close to portals, + // the system in place now can occasionally miss moves that should really be portal crossings. + + // To detect these and make the collision system handle them, we create contact points for all + // cells "near" the creature and let them sort themselves out. + + // #AMA 11-13-03 - this test is too expensive right now, and we can live without it (the + // fixup code will take care of things). floor corners where a portal bisects the corner (like in + // the tutorial) may act funky, but players shouldn't be able to get off the floor. + + /* + static std::vector hitCells; + + hitCells.clear(); + + Vector objectPos = getObjectPosition_p(); + Vector footPos = getPosition_p(); + + Vector goalPos(objectPos.x,footPos.y,objectPos.z); + + Sphere testSphere = Containment3d::EncloseSphere(footPos,goalPos); + + if(testSphere.getRadius() > 5.0f) + { + Vector center = testSphere.getCenter(); + float radius = testSphere.getRadius(); + + WARNING(true,("Footprint::updatePreResolve - Query sphere is too big - %f at (%f,%f,%f)\n",radius,center.x,center.y,center.z)); + + testSphere = Sphere(goalPos,5.0f); + } + + testSphere.setRadius( testSphere.getRadius() + getRadius() ); + + if(getCell()->getDestinationCells(testSphere,hitCells)) + { + int hitCount = hitCells.size(); + + for(int i = 0; i < hitCount; i++) + { + IGNORE_RETURN( attachTo(hitCells[i]->getFloor(),false) ); + } + } + */ + +#ifdef WIN32 + // This code absolutely kills the game server. rdelashmit says this doesn't need to be + // done for the game server, but since there's no #ifdef for a game server build, I'm + // using WIN32, which means it will sill get built for the WIN32 game server, but that + // won't hurt; we just don't want this code in the the live game server - hnguyen + + // If we're crossing a portal, make sure we attach to the floor on the other side of it. + float t = 0.f; + CellProperty * const c = getCell()->getDestinationCell(getPosition_p(), getObjectPosition_p(), t, true); + if (c) + IGNORE_RETURN(attachTo(c->getFloor(), false)); +#endif + + // Shove the object away from any walls it's embedded in. (but only on the client) + + if(!m_parent->isServerSide() && ConfigSharedCollision::getShoveEnabled()) + { + if(!testClear()) + { + shoveAway(time); + } + } +} + +// ---------------------------------------------------------------------- + +bool Footprint::testClear ( void ) const +{ +// Vector const & objectPos_p = getObjectPosition_p(); + + for(ConstContactIterator it(getFloorList()); it; ++it) + { + FloorContactShape const * contact = *it; + + if(!contact) continue; + + FloorLocator const & loc = contact->m_contact; + + if(!loc.isAttached()) continue; + + FloorMesh const * mesh = loc.getFloorMesh(); + + if(!mesh) continue; + + // ---------- + + if (!isFloorWithinThreshold(*contact)) + continue; + + if(!mesh->testClear(loc)) return false; + } + + return true; +} + +// ---------- + +void Footprint::shoveAway ( float time ) +{ + UNREF(time); + + Vector scootAccum = Vector::zero; + + for(ConstContactIterator it(getFloorList()); it; ++it) + { + FloorContactShape const * contact = *it; + + if(!contact) continue; + + FloorLocator const & loc = contact->m_contact; + + if(!loc.isAttached()) continue; + + Floor const * floor = loc.getFloor(); + + if(!floor) continue; + + FloorMesh const * mesh = floor->getFloorMesh(); + + if(!mesh) continue; + + // ---------- + + int closeTriId = -1; + int closeEdgeId = -1; + float dist = 0.0f; + + if(mesh->getClosestCollidableEdge(loc,closeTriId,closeEdgeId,dist)) + { + float scootDist = getRadius() - dist; + + if(scootDist > 0.0f) + { + Vector contactNormal_l = mesh->getTriangle(closeTriId).getEdgeDir(closeEdgeId).cross( -Vector::unitY ); + + if(contactNormal_l.normalize()) + { + Vector contactNormal_p = floor->rotate_l2p(contactNormal_l); + + scootAccum += contactNormal_p * scootDist; + } + } + } + } + + // ---------- + + if(scootAccum != Vector::zero) + { + Vector objectPos_p = getObjectPosition_p(); + + // scale the scoot distance up just a bit so the player isn't exactly on top of the wall + + scootAccum *= 1.05f; + + objectPos_p += scootAccum; + + setObjectPosition_p(objectPos_p); + + } +} + +// ---------------------------------------------------------------------- + +void Footprint::updatePostResolve ( float time ) +{ + UNREF(time); + + setPosition(getObjectCell(),getObjectPosition_p()); + + reattachContacts(); + + sweepContacts(); + + updateHeights(); + + updateOffsets(); + +#ifdef _DEBUG + + runDebugTests(); + +#endif +} + +// ---------------------------------------------------------------------- + +#ifdef _DEBUG + +void Footprint::runDebugTests ( void ) +{ + //@todo - HACK - Testing code for collision world line query + + if(ConfigSharedCollision::getEnableTestCode()) + { + Vector delta = getOwner()->rotate_o2p(m_lineDelta); + + m_lineHitTime = -1.0f; + + Vector objPos = getOwner()->getPosition_p(); + CellProperty const * objCell = getOwner()->getParentCell(); + + /* + FloorLocator objLoc; + CollisionWorld::makeLocator(objCell,objPos,objLoc); + + CanMoveResult result = CollisionWorld::canMove(getOwner(),objPos+delta,0.0f,false,true); + + m_canMove = (result == CMR_MoveOK); + + if(!m_canMove) + { + m_lineHitPos = m_lineOrigin + m_lineDelta; + m_lineHitTime = 1.0f; + } + + m_bubbleSize = 20.0f; + + IGNORE_RETURN(CollisionWorld::calcBubble(getCell(),getPosition_p(),20.0f,m_bubbleSize)); + */ + + Object const * hitObject = NULL; + + QueryInteractionResult result = CollisionWorld::queryInteraction(objCell, + objPos + m_lineOrigin, + objCell, + objPos + m_lineOrigin + delta, + NULL, + !ConfigSharedCollision::getIgnoreTerrainLos(), + ConfigSharedCollision::getGenerateTerrainLos(), + ConfigSharedCollision::getTerrainLOSMinDistance(), + ConfigSharedCollision::getTerrainLOSMaxDistance(), + m_lineHitTime, + hitObject); + + m_canMove = (result == QIR_None); + + m_lineHitPos = m_lineOrigin + m_lineDelta * m_lineHitTime; + } +} +#endif + + +// ---------------------------------------------------------------------- + +FloorLocator const * Footprint::getAnyContact ( void ) const +{ + for(ConstContactIterator it(getFloorList()); it; ++it) + { + FloorContactShape const * contact = *it; + + if(!contact) continue; + + if(contact->m_contact.isAttached()) return &contact->m_contact; + } + + return NULL; +} + +// ---------- + +FloorLocator const * Footprint::getSolidContact ( void ) const +{ + FloorLocator const * temp = NULL; + + float maxHeight = -REAL_MAX; + + for(ConstContactIterator it(getFloorList()); it; ++it) + { + FloorContactShape const * contact = *it; + + if(!contact) continue; + + if(contact->m_contact.isFallthrough()) continue; + + if(!contact->m_contact.isAttached()) continue; + + Vector contactPos = contact->m_contact.getPosition_p(); + + if(contactPos.y > maxHeight) + { + maxHeight = contactPos.y; + + temp = &contact->m_contact; + } + } + + return temp; +} + +// ---------------------------------------------------------------------- + +Object const * Footprint::getStandingOn ( void ) const +{ + FloorLocator const * contact = getSolidContact(); + + if(contact == NULL) return NULL; + + Floor const * floor = contact->getFloor(); + + if(floor == NULL) return NULL; + + return floor->getOwner(); +} + +// ---------------------------------------------------------------------- + +bool Footprint::snapToCellFloor ( void ) +{ + Floor * floor = getCell()->getFloor(); + + if(floor) + { + Vector objectPos = getObjectPosition_p(); + + FloorLocator closestLoc = FloorLocator::invalid; + + bool found = floor->findClosestLocation(objectPos,closestLoc); + + if(found) + { + return teleport(closestLoc); + } + } + + // ---------- + + return false; +} + +// ---------------------------------------------------------------------- + +bool Footprint::isAttachedTo ( Floor const * floor ) const +{ + if(floor == NULL) return false; + + return getFloorList().find( floor->getFootList() ) != NULL; +} + +// ---------------------------------------------------------------------- + +bool Footprint::updateHeights ( void ) +{ + // ---------- + // Update terrain height + + float logicalTerrainHeight = -REAL_MAX; + Vector terrainNormal = Vector::unitY; + + if (isInCell () || !TerrainObject::getConstInstance ()) + { + m_hasTerrainHeight = false; + m_terrainHeight = -REAL_MAX; + } + else + { + //-- Capture both the logical and real terrain height. + m_hasTerrainHeight = TerrainObject::getConstInstance ()->getLogicalHeight(getPosition_w(), m_swimHeight, logicalTerrainHeight, m_terrainHeight, terrainNormal); + } + + // ---------- + // Update floor height + + m_hasFloorHeight = false; + m_floorHeight = -REAL_MAX; + + Vector floorNormal = Vector::unitY; + + for(ContactIterator it(getFloorList()); it; ++it) + { + FloorContactShape * contact = *it; + + if(!contact->m_contact.isAttached()) continue; + if(contact->m_contact.isFallthrough()) continue; + + FloorLocator & loc = contact->m_contact; + + Vector localContact = loc.getPosition_p(getCell()); + + real contactHeight = localContact.y; + + if(contactHeight > m_floorHeight) + { + m_floorHeight = contactHeight; + floorNormal = loc.getSurfaceNormal_p(); + m_hasFloorHeight = true; + } + } + + // ---------- + // Update ground height + + if(m_hasFloorHeight || m_hasTerrainHeight) + { + m_hasGroundHeight = true; + + if(m_floorHeight > logicalTerrainHeight) + { + m_groundHeight = m_floorHeight; + m_groundNormal = floorNormal; + } + else + { + m_groundHeight = logicalTerrainHeight; + m_groundNormal = terrainNormal; + } + } + else + { + m_hasGroundHeight = false; + m_groundHeight = getObjectPosition_p().y; + m_groundNormal = Vector::unitY; + } + + // ---------- + + snapToGround(); + + return m_hasGroundHeight; +} + +// ---------------------------------------------------------------------- + +void Footprint::snapToGround ( void ) +{ + NAN_CHECK(m_groundHeight); + + Vector pos = getPosition_p(); + + pos.y = m_groundHeight; + + setPosition_p(pos); +} + +// ---------------------------------------------------------------------- + +bool Footprint::isFloating ( void ) const +{ + return m_floating; +} + +// ---------------------------------------------------------------------- +// Snap the footprint underneath the object + +bool Footprint::snapToObject ( void ) +{ + Vector lastPos = getObjectPosition_p(); + CellProperty * lastCell = getObjectCell(); + + DEBUG_REPORT_LOG_PRINT(ConfigSharedCollision::getReportMessages(),("Footprint::snapToObject - Syncing to object position (%f,%f,%f)\n",lastPos.x,lastPos.y,lastPos.z)); + + detach(); + + setPosition(lastCell,lastPos); + + updateHeights(); + + m_parent->storePosition(); + + return true; +} + +// ---------- + +bool Footprint::snapObjectToTerrain() +{ + if(!isOnSolidFloor() && !isInCell()) + { + Vector position = getObjectPosition_p(); + + float newLogicalHeight; + float newRealHeight; + + if(TerrainObject::getConstInstance ()->getLogicalHeight (position, m_swimHeight, newLogicalHeight, newRealHeight)) + { + if(newLogicalHeight != position.y) + { + setObjectPosition_p(position); + + return true; + } + } + } + + return false; +} + +// ---------- + +bool Footprint::snapObjectToGround ( void ) +{ + if(m_hasGroundHeight) + { + Vector newPos = getObjectPosition_p(); + + newPos.y = m_groundHeight; + + setObjectPosition_p(newPos); + } + + return true; +} + +// ---------------------------------------------------------------------- + +void Footprint::updateOffsets ( void ) +{ + for(ContactIterator it(getFloorList()); it; ++it) + { + FloorLocator & contact = (*it)->m_contact; + + contact.snapToFloor(); + + Vector const & contactPos_p = contact.getPosition_p(); + + Vector objectPos_p = getObjectPosition_p(contact.getCell()); + + float offset = objectPos_p.y - contactPos_p.y; + + contact.setOffset(offset); + } +} + +// ---------------------------------------------------------------------- + +bool Footprint::attachTo ( Floor const * floor, bool fromObject ) +{ + if(floor == NULL) return false; + + if(isAttachedTo(floor)) return false; + + // ---------- + + Vector localPos; + + if(fromObject) + { + localPos = getObjectPosition_p(floor->getCell()); + } + else + { + localPos = getPosition_p(floor->getCell()); + } + + Vector floorPos = floor->transform_p2l(localPos); + + FloorLocator newContact( floor, floorPos, -1, 0.0f, getRadius() ); + + addContact(newContact); + + return true; +} + +// ---------------------------------------------------------------------- + +bool Footprint::isInCell ( void ) const +{ + return getCell() != CellProperty::getWorldCellProperty(); +} + +// ---------------------------------------------------------------------- + +#ifdef _DEBUG + +void Footprint::backup ( void ) +{ + m_backupObjectPosition_p = getObjectPosition_p(); + m_backupObjectCell = getObjectCell(); + m_backupPosition_p = getPosition_p(); + m_backupCell = getCell(); + + for(ContactIterator it(getFloorList()); it; ++it) + { + FloorContactShape * contact = (*it); + + contact->m_backupContact = contact->m_contact; + } +} + +void Footprint::revert ( void ) +{ + setPosition(m_backupCell,m_backupPosition_p); + + updateHeights(); + + for(ContactIterator it(getFloorList()); it; ++it) + { + FloorContactShape * contact = (*it); + + contact->m_contact = contact->m_backupContact; + } + + CellProperty::setPortalTransitionsEnabled(false); + + // can't set the cell from shared code + + getOwner()->setPosition_p(m_backupPosition_p); + + CellProperty::setPortalTransitionsEnabled(true); +} + +#endif + +// ---------------------------------------------------------------------- + +Vector Footprint::getObjectPosition_p ( void ) const +{ + return getOwner()->getPosition_p(); +} + +// ---------- + +Vector Footprint::getObjectPosition_p ( CellProperty const * relativeCell ) const +{ + return CollisionUtils::transformToCell ( getObjectCell(), getObjectPosition_p(), relativeCell ); +} + +// ---------- + +void Footprint::setObjectPosition_p ( Vector const & newPos ) +{ + NAN_CHECK(newPos); + + if(getObjectPosition_p() == newPos) return; + + getOwner()->setPosition_p(newPos); +} + +// ---------------------------------------------------------------------- + +CellProperty const * Footprint::getObjectCell ( void ) const +{ + return getOwner()->getParentCell(); +} + +CellProperty * Footprint::getObjectCell ( void ) +{ + return getOwner()->getParentCell(); +} + +// ---------------------------------------------------------------------- +// returns the # of floors actually moved + +int Footprint::elevatorMove ( int nFloors, Transform & outTransform ) +{ + if(!isInCell()) return 0; + + bool bDown = nFloors < 0; + int absDist = bDown ? -nFloors : nFloors; + + // ---------- + // Find the contact that's on the cell's floor + + FloorContactShape const * cellContact = NULL; + + for(ConstContactIterator it(getFloorList()); it; ++it) + { + FloorContactShape const * contact = (*it); + + if(!contact->m_contact.isAttached()) continue; + + FloorLocator const & loc = contact->m_contact; + + Floor const * floor = loc.getFloor(); + + // ---------- + + if(floor && floor->isCellFloor()) + { + cellContact = contact; + break; + } + } + + if(cellContact == NULL) + { + return 0; + } + + Floor const * floor = cellContact->m_contact.getFloor(); + + if(!floor) + { + return 0; + } + + // ---------- + + int iFloor = 0; + + FloorLocator contactPoint = cellContact->m_contact; + + for(; iFloor < absDist; iFloor++) + { + FloorLocator nextUp = FloorLocator::invalid; + FloorLocator nextDown = FloorLocator::invalid;; + + if(!floor) + { + FLOOR_WARNING("Footprint::elevatorMove - In a cell, but no cell floor to do elevator moving on"); + return 0; + } + + IGNORE_RETURN( floor->findElevatorNeighbors( contactPoint, nextUp, nextDown ) ); + + if(bDown && nextDown.isAttached()) + { + contactPoint = nextDown; + } + else if(!bDown && nextUp.isAttached()) + { + contactPoint = nextUp; + } + else + { + break; + } + } + + if(iFloor == 0) + { + return 0; + } + else + { + outTransform = getOwner()->getTransform_o2p(); + + outTransform.setPosition_p( contactPoint.getPosition_p() + Vector(0.0f,0.01f,0.0f) ); + + return iFloor; + } +} + +// ---------------------------------------------------------------------- + +void Footprint::drawDebugShapes ( DebugShapeRenderer * renderer ) const +{ + UNREF(renderer); + +#ifdef _DEBUG + + if(renderer == NULL) return; + + if(ConfigSharedCollision::getDrawFootprints()) + { + renderer->setWorldSpace(true); + + // ---------- + //@todo HACK for testing line query + + Vector localEnd = m_lineOrigin + m_lineDelta; + Vector localA = m_lineOrigin + Vector(0.04f,0.0f,0.0f); + Vector localB = m_lineOrigin - Vector(0.04f,0.0f,0.0f); + + Vector worldEnd = getOwner()->rotateTranslate_o2w(localEnd); + Vector worldA = getOwner()->rotateTranslate_o2w(localA); + Vector worldB = getOwner()->rotateTranslate_o2w(localB); + Vector worldHit = getOwner()->rotateTranslate_o2w(m_lineHitPos); + + if(ConfigSharedCollision::getEnableTestCode()) + { + // These lines MUST be drawn in world space, otherwise they'll inherit the + // player's scaling factor. + + if((!m_canMove) || (m_lineHitTime >= 0.0f)) + { + VectorArgb lineColor = VectorArgb::solidRed; + + if(!m_canMove) lineColor = VectorArgb::solidMagenta; + + renderer->setColor(lineColor); + renderer->drawLine(worldA,worldHit); + renderer->drawLine(worldB,worldHit); + + renderer->setColor(VectorArgb::solidYellow); + renderer->drawCylinder( Cylinder(worldHit,0.5f,0.2f), 16 ); + } + else + { + VectorArgb lineColor = VectorArgb::solidGreen; + + renderer->setColor(lineColor); + renderer->drawLine(worldA, worldEnd); + renderer->drawLine(worldB, worldEnd); + } + + // ---------- + // bubbles + + renderer->setColor(VectorArgb::solidCyan); + } + + // ---------- + // draw the contacts + + float radiusTweak = 0.0f; + + for(ConstContactIterator it(getFloorList()); it; ++it) + { + FloorLocator const & loc = (*it)->m_contact; + + if(loc.isAttached()) + { + renderer->setColor( VectorArgb::solidYellow ); + } + else + { + renderer->setColor( VectorArgb::solidRed ); + } + + Vector position_w = loc.getPosition_w(); + + float cylRadius = loc.getRadius() + radiusTweak; + float cylHeight = loc.getOffset() + 0.02f; + + Cylinder C(position_w,cylRadius,cylHeight); + + renderer->drawCylinder(C,16); + + radiusTweak += 0.1f; + } + + // draw the footprint + { + renderer->setColor( VectorArgb::solidCyan ); + + Cylinder C(getPosition_w(),0.25,0.06f); + + renderer->drawCylinder(C,16); + } + + // ---------- + + renderer->setWorldSpace(false); + } + +#endif +} + +// ---------------------------------------------------------------------- + +void Footprint::updateFloating ( void ) +{ + bool reportFloating = ConfigSharedCollision::getReportFloating(); + + m_parent->setIdle(false); + + CollisionWorld::getDatabase()->updateFloorCollision(m_parent,true); + + forceReattachContacts(); + snapContacts(); + + updateHeights(); + + int id = static_cast(m_parent->getOwner().getNetworkId().getValue()); + + if(!m_hasGroundHeight) + { + TerrainObject const * terrain = TerrainObject::getInstance(); + + if(terrain && !terrain->isReferenceObject(&m_parent->getOwner())) + { + m_floating = false; + } + else + { + DEBUG_REPORT_LOG(reportFloating && !m_floating,("Floating: Object %d at height %f starting to float due to no ground height\n",id,m_addToWorldPos.y)); + m_floating = true; + } + } + else + { + int elapsed = Clock::timeMs() - m_addToWorldTime; + bool timeout = elapsed > (m_floatingTime * 1000); + + // Things newly added to the world are floating if the Y value of + // their object is significantly (> 1 meter) above the ground + + if(m_addToWorldPos.y > (m_groundHeight + 0.5f)) + { + if(timeout) + { + WARNING(reportFloating && m_floating,("Floating: Object %d at (%f,%f,%f) stopped floating due to timeout - ground at %f",id,m_addToWorldPos.x,m_addToWorldPos.y,m_addToWorldPos.z,m_groundHeight)); + m_floating = false; + } + else + { + DEBUG_REPORT_LOG(reportFloating && !m_floating,("Floating: Object %d starting to float due to being too high - object at %f, ground at %f\n",id,m_addToWorldPos.y,m_groundHeight)); + m_floating = true; + } + } + else + { + if(m_hasFloorHeight && (m_floorHeight == m_groundHeight)) + { + DEBUG_REPORT_LOG(reportFloating && m_floating,("Floating: Object %d stopped floating after %d msecs due to a floor appearing underneath it - object at %f, ground at %f\n",id,elapsed,m_addToWorldPos.y,m_groundHeight)); + } + else + { + DEBUG_REPORT_LOG(reportFloating && m_floating,("Floating: Object %d stopped floating after %d msecs due to terrain appearing underneath it - object at %f, ground at %f\n",id,elapsed,m_addToWorldPos.y,m_groundHeight)); + } + + m_floating = false; + } + } +} + +// ---------------------------------------------------------------------- + +void Footprint::stopFloating ( void ) +{ + m_floating = false; +} + +// ---------------------------------------------------------------------- + +void Footprint::setFloatingTime ( int seconds ) +{ + m_floatingTime = seconds; +} + +// ---------------------------------------------------------------------- + +void Footprint::alignToGroundNoFloat ( void ) +{ + //-- Stop floating in the event that we are already marked as floating. + stopFloating(); + + //-- Lookup ground height. This has the desired side effect of snapping us to the ground. + bool const hasGroundHeight = updateHeights(); + UNREF(hasGroundHeight); +#ifdef _DEBUG + if (!hasGroundHeight) + { + Object const *const owner = getOwner(); + if (owner) + DEBUG_WARNING(true, ("Footprint::alignToGroundNoFloat(): called on object id=[%s],template=[%s] when no ground could be found.", owner->getNetworkId().getValueString().c_str(), owner->getObjectTemplateName())); + else + DEBUG_WARNING(true, ("Footprint::alignToGroundNoFloat(): called on object id=[],template=[] when no ground could be found.")); + } +#endif +} + +// ---------------------------------------------------------------------- + +void Footprint::addToWorld ( void ) +{ + IGNORE_RETURN( snapToObject() ); + + if(isInCell() && !isAttached()) + { + IGNORE_RETURN( snapToCellFloor() ); + } + + m_addToWorldTime = static_cast(Clock::timeMs()); + m_addToWorldPos = getObjectPosition_p(); + + updateFloating(); +} + +// ---------------------------------------------------------------------- + +float Footprint::getRadius ( void ) const +{ + // On the client, remote objects have a vry small radius + + if(m_parent && !m_parent->isServerSide() && !m_parent->isPlayer()) + { + return 0.05f; + } + + // On the server, AIs have a very small radius + + if(m_parent && m_parent->isServerSide() && !m_parent->isPlayerControlled()) + { + return 0.05f; + } + + // On both, players have a radius of 0.5 + + if(m_parent && (m_parent->isPlayer() || m_parent->isPlayerControlled())) + { + // unless they're a mount and we have mount radii enabled, in which case we use the actual radius + + if(ConfigSharedCollision::getEnableMountRadii() && m_parent->isMount()) + { + return m_radius; + } + else + { + return 0.5f; + } + } + + return m_radius; +} + +// ---------------------------------------------------------------------- + +void Footprint::setRadius ( float newRadius ) +{ + NAN_CHECK(newRadius); + + m_radius = newRadius; + + updateContactRadii(); +} + +// ---------------------------------------------------------------------- + +void Footprint::updateContactRadii( void ) +{ + float radius = getRadius(); + + for(ContactIterator it(getFloorList()); it; ++it) + { + FloorContactShape * contact = *it; + + if(!contact) continue; + + contact->m_contact.setRadius(radius); + } +} + +// ---------------------------------------------------------------------- + +void Footprint::cellChanged ( void ) +{ + CellProperty const * objectCell = getObjectCell(); + + if(m_parent->isServerSide()) + { + snapToObject(); + } + + if(objectCell == CellProperty::getWorldCellProperty()) + { + static FloorVec results; + + results.clear(); + + Capsule queryCapsule_w = m_parent->getQueryCapsule_w(); + + CollisionWorld::getDatabase()->queryFloors(queryCapsule_w,&results); + + for(uint i = 0; i < results.size(); i++) + { + Floor * floor = results.at(i); + + attachTo(floor,false); + } + } + else + { + if(m_parent->isServerSide()) + { + snapToCellFloor(); + } + } +} + +// ---------------------------------------------------------------------- + +void Footprint::objectWarped ( void ) +{ + addToWorld(); +} + +// ---------------------------------------------------------------------- + +Vector Footprint::getGroundNormal_p ( void ) const +{ + return m_groundNormal; +} + +// ---------------------------------------------------------------------- + +Vector Footprint::getGroundNormal_w ( void ) const +{ + return CollisionUtils::rotateToCell(getCell(),getGroundNormal_p(),CellProperty::getWorldCellProperty()); +} + +//---------------------------------------------------------------------- + +bool Footprint::isFloorWithinThreshold(FloorContactShape const & floorContactShape) const +{ + FloorLocator const & loc = floorContactShape.m_contact; + + Vector const & localContact = loc.getPosition_p(getCell()); + + real const contactHeight = localContact.y; + + float floorHeight = 0.0f; + if (getFloorHeight(floorHeight)) + { + if(contactHeight < (floorHeight - 0.5f)) + return false; + } + + return true; +} + +// ====================================================================== diff --git a/engine/shared/library/sharedCollision/src/shared/core/Footprint.h b/engine/shared/library/sharedCollision/src/shared/core/Footprint.h new file mode 100644 index 00000000..e7de02e7 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Footprint.h @@ -0,0 +1,295 @@ +// ====================================================================== +// +// Footprint.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_Footprint_H +#define INCLUDED_Footprint_H + +// ====================================================================== + +#include "sharedCollision/BaseClass.h" +#include "sharedCollision/FloorLocator.h" +#include "sharedCollision/MultiList.h" + +#include "sharedFoundation/Watcher.h" + +class Floor; +class FloorContactShape; +class FloorTri; +class Object; +class CellProperty; +class DebugShapeRenderer; +class CollisionProperty; + +// ====================================================================== + +class Footprint : public BaseClass +{ +public: + + Footprint( Vector const & position, float radius, CollisionProperty * parent, float swimHeight ); + virtual ~Footprint(); + + // ---------- + + void updatePreResolve ( float time ); + void updatePostResolve ( float time ); + + int elevatorMove ( int nFloors, Transform & outTransform ); + void drawDebugShapes ( DebugShapeRenderer * R ) const; + void addToWorld ( void ); + void cellChanged ( void ); + void objectWarped ( void ); + + bool isInCell ( void ) const; + bool isOnSolidFloor ( void ) const; + bool isAttached ( void ) const; + bool isObjectInSync ( void ) const; // returns true if the object is over the footprint + bool hasContacts ( void ) const; + + void setSwimHeight ( float swimHeight ); + + bool getTerrainHeight ( float & outHeight ) const; + bool getFloorHeight ( float & outHeight ) const; + bool getGroundHeight ( float & outHeight ) const; + + Vector getGroundNormal_p ( void ) const; + Vector getGroundNormal_w ( void ) const; + + float getRadius ( void ) const; + void setRadius ( float newRadius ); + void updateContactRadii ( void ); + + FloorLocator const * getAnyContact ( void ) const; + FloorLocator const * getSolidContact ( void ) const; + + Object const * getStandingOn ( void ) const; + + // ---------- + + void addContact ( FloorLocator const & loc ); + + MultiListHandle & getFloorList ( void ); + MultiListHandle const & getFloorList ( void ) const; + + void detach ( void ); + + bool isFloating ( void ) const; + void stopFloating ( void ); + void setFloatingTime ( int seconds ); + + // ---------- + + void alignToGroundNoFloat ( void ); + + bool isFloorWithinThreshold(FloorContactShape const & floorContactShape) const; + +protected: + + friend class FloorManager; + friend class Floor; + friend class SpatialDatabase; + friend class CollisionWorld; + + // These priorities represent how important it is that we get the footprint + // attached to a floor + + enum ForceAttachPriority + { + FAP_None, // No force. If the footprint doesn't fall onto the floor, it's not attached to the floor + FAP_AllowJump, // The footprint should be attached to the floor even if doing so would require a jump + FAP_IfInsideBounds, // The footprint should be attached to the floor if it is inside the floor's bounding box + FAP_AtAllCosts, // The footprint must be attached to the floor + }; + + // ---------- + // accessors + + Object * getOwner ( void ); + Object const * getOwner ( void ) const; + + Vector const & getPosition_p ( void ) const; + Vector getPosition_p ( CellProperty const * relativeCell ) const; + Vector const & getPosition_w ( void ) const; + + void setPosition_p ( Vector const & position_p ); + void setPosition_w ( Vector const & position_w ); + + CellProperty * getCell ( void ); + CellProperty const * getCell ( void ) const; + + void setPosition ( CellProperty * cell, Vector const & position_p ); + + // ---------- + // accessors + + Vector getObjectPosition_p ( void ) const; + Vector getObjectPosition_p ( CellProperty const * relativeCell ) const; + void setObjectPosition_p ( Vector const & newPos ); + + CellProperty const * getObjectCell ( void ) const; + CellProperty * getObjectCell ( void ) ; + + // ---------- + // synchronization + + bool snapToObject ( void ); + bool snapObjectToTerrain ( void ); + bool snapObjectToGround ( void ); + + bool syncToContacts ( void ); + + + // ---------- + // misc + + void updateOffsets ( void ); + + bool updateHeights ( void ); + void snapToGround ( void ); + bool teleport ( FloorLocator const & loc ); + + void runDebugTests ( void ); + + void updateFloating ( void ); + + bool testClear ( void ) const; + + void shoveAway ( float time ); + + + // these are just for debugging + +#ifdef _DEBUG + + void backup ( void ); + void revert ( void ); + +#endif + + // ---------- + // attach/detach code + + bool attachTo ( Floor const * pFloor, bool fromObject ); + bool snapToCellFloor ( void ); + bool isAttachedTo ( Floor const * pFloor ) const; + + void reattachContacts ( void ); + void forceReattachContacts ( void ); + void sweepContacts ( void ); + void snapContacts ( void ); + + // ---------------------------------------------------------------------- + +protected: + + CollisionProperty * m_parent; + + typedef Watcher CellWatcher; + + CellWatcher m_cellObject; + Vector m_position_p; + Vector m_position_w; + + + float m_radius; + + MultiListHandle m_floorList; //! List of floors this footprint is on + + float m_swimHeight; + + bool m_hasTerrainHeight; + bool m_hasFloorHeight; + bool m_hasGroundHeight; + + float m_terrainHeight; + float m_floorHeight; + float m_groundHeight; + + Vector m_groundNormal; + + int m_addToWorldTime; // time in seconds + Vector m_addToWorldPos; + bool m_floating; + int m_floatingTime; + + // ---------- + // Data used for debugging + +#ifdef _DEBUG + + Vector m_backupPosition_p; + CellProperty * m_backupCell; + + Vector m_backupObjectPosition_p; + CellProperty * m_backupObjectCell; + + float m_lineHitTime; // HACK for testing line query + Vector m_lineOrigin; + Vector m_lineDelta; + bool m_canMove; + Vector m_lineHitPos; + float m_bubbleSize; + bool m_bubbleEmpty; + +#endif + + // ---------- + +private: + + Footprint( Footprint const & copy ); + Footprint & operator = (Footprint const & copy ); +}; + +// ---------------------------------------------------------------------- + +inline bool Footprint::hasContacts ( void ) const +{ + return m_floorList.isConnected(); +} + +// ---------------------------------------------------------------------- + +inline bool Footprint::getTerrainHeight ( float & outHeight ) const +{ + outHeight = m_terrainHeight; + + return m_hasTerrainHeight; +} + +inline bool Footprint::getFloorHeight ( float & outHeight ) const +{ + outHeight = m_floorHeight; + + return m_hasFloorHeight; +} + +inline bool Footprint::getGroundHeight ( float & outHeight ) const +{ + outHeight = m_groundHeight; + + return m_hasGroundHeight; +} + +// ---------------------------------------------------------------------- + +inline MultiListHandle & Footprint::getFloorList ( void ) +{ + return m_floorList; +} + +// ---------- + +inline MultiListHandle const & Footprint::getFloorList ( void ) const +{ + return m_floorList; +} + +// ====================================================================== + +#endif + diff --git a/engine/shared/library/sharedCollision/src/shared/core/FootprintForceReattachManager.cpp b/engine/shared/library/sharedCollision/src/shared/core/FootprintForceReattachManager.cpp new file mode 100644 index 00000000..d0671160 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/FootprintForceReattachManager.cpp @@ -0,0 +1,69 @@ +// ====================================================================== +// +// CollisionWorld.cpp +// copyright (c) 2002 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/FootprintForceReattachManager.h" + +#include "sharedUtility/DataTable.h" +#include "sharedUtility/DataTableManager.h" + +#include +#include + +namespace FootprintForceReattachManagerNamespace +{ + typedef stdvector::fwd StringVector; + + StringVector ms_sceneIds; + + const char * const ms_datatableName = "datatables/planet/footprint_force_reattach.iff"; + + bool s_installed = false; + void install() + { + s_installed = true; + + ms_sceneIds.clear(); + + DataTable const * const dt = DataTableManager::getTable(ms_datatableName, true); + + if (NULL == dt) + WARNING(true, ("FootprintForceReattachManager unable to find [%s]", ms_datatableName)); + else + { + int const numRows = dt->getNumRows(); + + ms_sceneIds.reserve(numRows); + + { + for (int row = 0; row < numRows; ++row) + { + std::string const & planetName = dt->getStringValue(0, row); + ms_sceneIds.push_back(planetName); + } + } + + //-- sort to allow for binary search + std::sort(ms_sceneIds.begin(), ms_sceneIds.end()); + } + } +} + +using namespace FootprintForceReattachManagerNamespace; + +//---------------------------------------------------------------------- + +bool FootprintForceReattachManager::isSceneFootprintForceReattach(std::string const & sceneId) +{ + if (!s_installed) + install(); + + bool const found = std::binary_search(ms_sceneIds.begin(), ms_sceneIds.end(), sceneId); + return found; +} + +//---------------------------------------------------------------------- diff --git a/engine/shared/library/sharedCollision/src/shared/core/FootprintForceReattachManager.h b/engine/shared/library/sharedCollision/src/shared/core/FootprintForceReattachManager.h new file mode 100644 index 00000000..80211794 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/FootprintForceReattachManager.h @@ -0,0 +1,21 @@ +//====================================================================== +// +// FootprintForceReattachManager.h +// copyright (c) 2005 Sony Online Entertainment +// +//====================================================================== + +#ifndef INCLUDED_FootprintForceReattachManager_H +#define INCLUDED_FootprintForceReattachManager_H + +//====================================================================== + +class FootprintForceReattachManager +{ +public: + static bool isSceneFootprintForceReattach(std::string const & sceneId); +}; + +//====================================================================== + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/Intersect1d.cpp b/engine/shared/library/sharedCollision/src/shared/core/Intersect1d.cpp new file mode 100644 index 00000000..d349de55 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Intersect1d.cpp @@ -0,0 +1,16 @@ +// ====================================================================== +// +// Intersect1d.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/Intersect1d.h" + +namespace Intersect1d +{ + + +// ---------------------------------------------------------------------- +} // namespace Intersect1d diff --git a/engine/shared/library/sharedCollision/src/shared/core/Intersect1d.h b/engine/shared/library/sharedCollision/src/shared/core/Intersect1d.h new file mode 100644 index 00000000..626edd9e --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Intersect1d.h @@ -0,0 +1,117 @@ +// ====================================================================== +// +// Intersect1d.h +// copyright (c) 2001 Sony Online Entertainment +// +// ---------------------------------------------------------------------- + +#ifndef INCLUDED_Intersect1d_H +#define INCLUDED_Intersect1d_H + +#include "sharedMath/Range.h" + +#include + +class Range; + +namespace Intersect1d +{ + +// ---------------------------------------------------------------------- +// Static intersection + +inline Range IntersectRanges ( Range const & A, Range const & B ) +{ + return Range( std::max(A.getMin(),B.getMin()), + std::min(A.getMax(),B.getMax()) ); +} + +inline Range IntersectRanges ( Range const & A, Range const & B, Range const & C ) +{ + return Range( std::max(std::max(A.getMin(),B.getMin()),C.getMin()), + std::min(std::min(A.getMax(),B.getMax()),C.getMax()) ); + +} + +// ---------------------------------------------------------------------- +// Temporal intersect tests + +// A value P is changing with velocity V +// Find the range of time for which P is less/greater than R + +inline Range IntersectFloatLess ( float P, float V, float R ) +{ + if(V == 0) + { + if(P < R) return Range::inf; + else return Range::empty; + } + + float t = (R - P) / V; + + if(V < 0) return Range(t,REAL_MAX); + else return Range(-REAL_MAX,t); +} + +inline Range IntersectFloatGreater ( float P, float V, float R ) +{ + if(V == 0) + { + if(P > R) return Range::inf; + else return Range::empty; + } + + float t = (R - P) / V; + + if(V > 0) return Range(t,REAL_MAX); + else return Range(-REAL_MAX,t); +} + +// ---------- + +inline Range IntersectFloatFloat ( real A, real V, real B ) +{ + if(V == 0) + { + if(A == B) return Range::inf; //lint !e777 // testing floats for equality + else return Range::empty; + } + + float t = (B - A) / V; + + return Range(t,t); +} + +// A value P is moving with velocity V. +// Find the range of time for which P overlaps the range R. + +inline Range IntersectFloatRange ( float A, real V, Range const & R ) +{ + Range timeMin = IntersectFloatGreater(A,V,R.getMin()); + Range timeMax = IntersectFloatLess(A,V,R.getMax()); + + return IntersectRanges(timeMin,timeMax); +} + +inline Range IntersectRangeFloat ( Range const & A, float V, float B ) +{ + return IntersectFloatRange(B,-V,A); +} + +// A range P is moving with velocity V. +// Find the range of time for which P overlaps the range R. + +inline Range IntersectRangeRange ( Range const & A, real V, Range const & B ) +{ + Range timeMin = IntersectFloatGreater(A.getMax(),V,B.getMin()); + Range timeMax = IntersectFloatLess(A.getMin(),V,B.getMax()); + + return IntersectRanges(timeMin,timeMax); +} + +// ---------------------------------------------------------------------- + +} // namespace Intersect1d + +#endif + diff --git a/engine/shared/library/sharedCollision/src/shared/core/Intersect2d.cpp b/engine/shared/library/sharedCollision/src/shared/core/Intersect2d.cpp new file mode 100644 index 00000000..893346a3 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Intersect2d.cpp @@ -0,0 +1,456 @@ +// ====================================================================== +// +// Intersect2d.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/Intersect2d.h" + +#include "sharedCollision/Collision2d.h" +#include "sharedCollision/Intersect1d.h" +#include "sharedCollision/Distance2d.h" +#include "sharedCollision/Distance3d.h" +#include "sharedCollision/Containment1d.h" +#include "sharedCollision/CollisionUtils.h" + +#include "sharedMath/Range.h" +#include "sharedMath/Vector2d.h" +#include "sharedMath/Segment3d.h" +#include "sharedMath/Line3d.h" +#include "sharedMath/Ray3d.h" +#include "sharedMath/Quadratic.h" +#include "sharedMath/Circle.h" +#include "sharedMath/Plane3d.h" +#include "sharedMath/AxialBox.h" +#include "sharedMath/YawedBox.h" + +using namespace Collision2d; + +namespace Intersect2d +{ +// ---------------------------------------------------------------------- +// Find S and T such that + +// lA + dA * s = lB + dB * t + +// lAx + dAx * s = lBx + dBx * t +// lAy + dAy * s = lBy + dBy * t + +// dAx * s - dBx * t = lBx - lAx +// dAy * s - dBy * t = lBy - lAy + +// [ dAx -dBx | (lBx - lAx) ] +// [ dAy -dBy | (lBy - lAy) ] + +bool IntersectLineLine ( Line3d const & A, + Line3d const & B, + float & outParamA, // parametric coord of intersection point for line A + float & outParamB ) // parametric coord of intersection point for line B +{ + Vector2d lA = flatten2d( A.getPoint() ); + Vector2d dA = flatten2d( A.getNormal() ); + Vector2d lB = flatten2d( B.getPoint() ); + Vector2d dB = flatten2d( B.getNormal() ); + + Matrix2 M = Matrix2::fromColumns(dA,-dB); + + Vector2d O(0.0f,0.0f); + + if(M.solveFor(lB-lA,O)) + { + outParamA = O.x; + outParamB = O.y; + + return true; + } + else + { + return false; + } +} + +// ---------- + +Vector IntersectLineLine ( Line3d const & A, Line3d const & B ) +{ + float s(0); + float t(0); + + if(IntersectLineLine(A,B,s,t)) + { + return A.atParam(s); + } + else + { + return A.getPoint(); + } +} + +// ---------- + +bool IntersectRayRay ( Ray3d const & A, Ray3d const & B, float & outParamA, float & outParamB ) +{ + Vector2d lA = flatten2d( A.getPoint() ); + Vector2d dA = flatten2d( A.getNormal() ); + Vector2d lB = flatten2d( B.getPoint() ); + Vector2d dB = flatten2d( B.getNormal() ); + + Matrix2 M = Matrix2::fromColumns(dA,-dB); + + Vector2d O(0.0f,0.0f); + + if(M.solveFor(lB-lA,O)) + { + if(O.x >= 0.0f && O.y >= 0.0f) + { + outParamA = O.x; + outParamB = O.y; + + return true; + } + else + { + return false; + } + } + else + { + return false; + } +} + +// ---------- + +bool IntersectRayRay ( Ray3d const & A, Ray3d const & B, Vector & out ) +{ + float s; + float t; + + if(IntersectRayRay(A,B,s,t)) + { + out = A.atParam(s); + return true; + } + else + { + return false; + } +} + +// ---------- + +bool IntersectSegSeg ( Segment3d const & A, + Segment3d const & B, + float & outParamA, + float & outParamB ) +{ + float tempA(0); + float tempB(0); + + IGNORE_RETURN( IntersectLineLine( A.getLine(), B.getLine(), tempA, tempB ) ); + + if((tempA < 0.0f) || (tempA > 1.0f)) return false; + if((tempB < 0.0f) || (tempB > 1.0f)) return false; + + outParamA = tempA; + outParamB = tempB; + + return true; +} + +// ---------- + +bool IntersectLineSeg ( Line3d const & A, Segment3d const & B, float & outParamA, float & outParamB ) +{ + float tempA(0); + float tempB(0); + + IGNORE_RETURN( IntersectLineLine( A, B.getLine(), tempA, tempB ) ); + + if((tempB < 0.0f) || (tempB > 1.0f)) return false; + + outParamA = tempA; + outParamB = tempB; + + return true; +} + +// ---------- + +bool IntersectRaySeg ( Ray3d const & A, Segment3d const & B, float & outParamA, float & outParamB ) +{ + float tempA(0); + float tempB(0); + + IGNORE_RETURN( IntersectLineLine( A.getLine(), B.getLine(), tempA, tempB ) ); + + if(tempA < 0.0f) return false; + if((tempB < 0.0f) || (tempB > 1.0f)) return false; + + outParamA = tempA; + outParamB = tempB; + + return true; +} + +// ---------------------------------------------------------------------- + +Range IntersectPointSeg ( Vector const & A, Vector const & V, Segment3d const & B ) +{ + float tempA(0); + float tempB(0); + + Ray3d R(A,V); + + if( IntersectRaySeg( R, B, tempA, tempB ) ) + { + return Range(tempA,tempA); + } + else + { + return Range::empty; + } +} + +// ---------- + +Range IntersectPointCircle ( Vector const & A, Vector const & V, Circle const & B ) +{ + Quadratic Q = Distance2d::DistancePointPoint(A,V,B.getCenter()); + + float rad2 = B.getRadius() * B.getRadius(); + + float newMin(0); + float newMax(0); + + if(Q.solveFor( rad2, newMin, newMax )) + { + return Range(newMin,newMax); + } + else + { + if(V.magnitudeSquared() == 0.0f) + { + if(Q.m_C < rad2) + { + return Range::inf; + } + else + { + return Range::empty; + } + } + else + { + return Range::empty; + } + } +} + +// ---------- + +Range IntersectPointABox ( Vector const & A, Vector const & V, AxialBox const & B ) +{ + Range timeX = Intersect1d::IntersectFloatRange( A.x, V.x, B.getRangeX() ); + Range timeZ = Intersect1d::IntersectFloatRange( A.z, V.z, B.getRangeZ() ); + + return Intersect1d::IntersectRanges(timeX,timeZ); +} + +// ---------------------------------------------------------------------- + +Range IntersectSegCircle ( Segment3d const & S, Circle const & C ) +{ + Quadratic Q = Distance2d::DistancePointPoint( S.getBegin(), S.getDelta(), C.getCenter() ); + + float minT(0); + float maxT(0); + + if(Q.solveFor(C.getRadiusSquared(),minT,maxT)) + { + Range temp(minT,maxT); + + return Intersect1d::IntersectRanges( temp, Range::unit ); + } + else + { + return Range::empty; + } +} + +// ---------------------------------------------------------------------- + +Range IntersectCirclePoint ( Circle const & C, Vector const & V, Vector const & P ) +{ + return IntersectPointCircle(P,-V,C); +} + +// IntersectCircleLine in 2d is a bit easier than in 3d + +Range IntersectCircleLine ( Circle const & A, Vector const & V, Line3d const & L ) +{ + // create a line perpendicular to L that passes through L's origin + + Vector N = L.getNormal(); + + Vector D(-N.z,0.0f,N.x); + + Line3d L2(L.getPoint(),D); + + // and our intersection time with the line is the time for which the projection + // of the circle onto the perpendicular overlaps its origin. + + Range r = Distance2d::ProjectAxis(L2,A); + + float v = Collision2d::ComponentAlong(V,D); + + Range result = Intersect1d::IntersectRangeFloat(r,v,0.0f); + + return result; +} + +// This method needs explanation! + +// || -> logical union of time ranges +// && -> logical intersection of time ranges + +// Circle vs. segment +// == point vs. capsule +// == point vs. sphere A || point vs. cylinder || point vs. sphere B + +// point vs. cylinder == point vs. slab && point vs. tube +// point vs. tube == sphere vs. line + +// point vs. slab projects to float vs. range + +// -> sphere vs. segment = point vs. spheres || (sphere vs. line && float vs. range) + +Range IntersectCircleSeg ( Circle const & A, Vector const & V, Segment3d const & S ) +{ + Vector N = S.getDelta(); + N.y = 0.0f; + + Line3d L(S.getBegin(),N); + + float c = Distance2d::ProjectAxis(L,A.getCenter()); + float v = Collision2d::ComponentAlong(V,L.getNormal()); + + Range slabRange = Intersect1d::IntersectFloatRange( c, v, Range(0.0f,1.0f) ); + Range tubeRange = IntersectCircleLine(A,V,L); + + Range innerRange = Intersect1d::IntersectRanges(tubeRange,slabRange); + + Range beginRange = Intersect2d::IntersectCirclePoint(A,V,S.getBegin()); + Range endRange = Intersect2d::IntersectCirclePoint(A,V,S.getEnd()); + + Range outerRange = Containment1d::EncloseRanges(beginRange,innerRange,endRange); + + return outerRange; +} + +Range IntersectCircleCircle ( Circle const & A, Vector const & V, Circle const & B ) +{ + float sumRadius = A.getRadius() + B.getRadius(); + + float sumRad2 = sumRadius * sumRadius; + + if((V.x == 0.0f) && (V.z == 0.0f)) + { + Vector delta = B.getCenter() - A.getCenter(); + + float dist = delta.magnitude(); + + if(dist <= sumRadius) + { + return Range::inf; + } + else + { + return Range::empty; + } + } + + float newMin(0); + float newMax(0); + + Quadratic Q = Distance2d::DistancePointPoint(A.getCenter(),V,B.getCenter()); + + if(Q.solveFor( sumRad2, newMin, newMax )) + { + return Range(newMin,newMax); + } + else + { + return Range::empty; + } +} + +Range IntersectCirclePlane ( Circle const & C, Vector const & V, Plane3d const & P ) +{ + Vector N = P.getNormal(); + N.y = 0.0f; + + Range R = Distance2d::ProjectAxis(Line3d(P.getPoint(),N),C); + + float vel = Collision2d::ComponentAlong(V,N); + + return Intersect1d::IntersectRangeFloat( R, vel, 0.0f ); +} + +Range IntersectCircleABox ( Circle const & C, Vector const & V, AxialBox const & B ) +{ + Range accum = Range::empty; + Range temp = Range::empty; + + float R = C.getRadius(); + Vector P = C.getCenter(); + + // Accumulate collision ranges for the box corners + + for(int i = 0; i < 4; i++) + { + // flipping this test to do sphere-vs-point + + temp = IntersectPointCircle( B.getCorner(i), -V, C ); + + accum = Containment1d::EncloseRanges(accum,temp); + } + + // Accumulate collision ranges for the box stretched along each axis + + AxialBox xBox( B.getMin() - Vector(R,0.0f,0.0f), B.getMax() + Vector(R,0.0f,0.0f) ); + AxialBox zBox( B.getMin() - Vector(0.0f,0.0f,R), B.getMax() + Vector(0.0f,0.0f,R) ); + + Range hitX = IntersectPointABox(P,V,xBox); + Range hitZ = IntersectPointABox(P,V,zBox); + + accum = Containment1d::EncloseRanges(accum,hitX); + accum = Containment1d::EncloseRanges(accum,hitZ); + + // and the cumulative range is our result + + return accum; +} + +Range IntersectCircleYBox ( Circle const & C, Vector const & V, YawedBox const & B ) +{ + Vector localCenter = B.transformToLocal(C.getCenter()); + + Circle localCircle(localCenter,C.getRadius()); + + AxialBox localBox = B.getLocalShape(); + + Vector localVelocity = B.rotateToLocal(V); + + return IntersectCircleABox( localCircle, localVelocity, localBox ); +} + +Range Intersect ( Vector const & A, Vector const & V, Circle const & B ) { return IntersectPointCircle(A,V,B); } +Range Intersect ( Circle const & A, Vector const & V, Circle const & B ) { return IntersectCircleCircle(A,V,B); } +Range Intersect ( Circle const & A, Vector const & V, AxialBox const & B ) { return IntersectCircleABox(A,V,B); } +Range Intersect ( Circle const & A, Vector const & V, YawedBox const & B ) { return IntersectCircleYBox(A,V,B); } + +// ---------------------------------------------------------------------- +} // namespace Intersect2d diff --git a/engine/shared/library/sharedCollision/src/shared/core/Intersect2d.h b/engine/shared/library/sharedCollision/src/shared/core/Intersect2d.h new file mode 100644 index 00000000..5d647245 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Intersect2d.h @@ -0,0 +1,102 @@ +// ====================================================================== +// +// Intersect2d.h +// copyright (c) 2001 Sony Online Entertainment +// +// ---------------------------------------------------------------------- + +#ifndef INCLUDED_Intersect2d_H +#define INCLUDED_Intersect2d_H + +class Segment3d; +class Circle; +class Vector; +class Range; +class Vector2d; +class Plane3d; +class AxialBox; +class YawedBox; +class Line3d; +class Ray3d; + +// Note - 2d intersection routines performed with 3d primitives (Line3d, Segment3d, etc.) +// ignore the Y component of their inputs + +namespace Intersect2d +{ + +// ---------------------------------------------------------------------- + +bool IntersectLineLine ( Line3d const & A, + Line3d const & B, + float & outParamA, + float & outParamB ); + +Vector IntersectLineLine ( Line3d const & A, + Line3d const & B ); + +bool IntersectRayRay ( Ray3d const & A, + Ray3d const & B, + float & outParamA, + float & outParamB ); + +bool IntersectRayRay ( Ray3d const & A, + Ray3d const & B, + Vector & out ); + +bool IntersectSegSeg ( Segment3d const & A, + Segment3d const & B, + float & outParamA, + float & outParamB ); + +bool IntersectLineSeg ( Line3d const & A, + Segment3d const & B, + float & outParamA, + float & outParamB ); + +bool IntersectRaySeg ( Ray3d const & A, + Segment3d const & B, + float & outParamA, + float & outParamB ); + +// ---------- + +bool IntersectSegCircle ( Segment3d const & S, + Circle const & C, + Vector & outPoint, + float & outParam ); + +bool IntersectSegSeg ( Segment3d const & A, + Segment3d const & B, + Vector & outPoint, + float & outParamA, + float & outParamB ); + +// ---------------------------------------------------------------------- + +Range IntersectPointSeg ( Vector const & A, Vector const & V, Segment3d const & B ); +Range IntersectPointCircle ( Vector const & A, Vector const & V, Circle const & B ); +Range IntersectPointABox ( Vector const & A, Vector const & V, AxialBox const & B ); + +Range IntersectCirclePoint ( Circle const & A, Vector const & V, Vector const & B ); +Range IntersectCircleSeg ( Circle const & A, Vector const & V, Segment3d const & B ); +Range IntersectCircleLine ( Circle const & A, Vector const & V, Line3d const & B ); +Range IntersectCirclePlane ( Circle const & A, Vector const & V, Plane3d const & B ); +Range IntersectCircleCircle ( Circle const & A, Vector const & V, Circle const & B ); +Range IntersectCircleABox ( Circle const & A, Vector const & V, AxialBox const & B ); +Range IntersectCircleYBox ( Circle const & A, Vector const & V, YawedBox const & B ); + +// ---------- +// Generic aliases + +Range Intersect ( Vector const & A, Vector const & V, Circle const & B ); +Range Intersect ( Circle const & A, Vector const & V, Circle const & B ); +Range Intersect ( Circle const & A, Vector const & V, AxialBox const & B ); +Range Intersect ( Circle const & A, Vector const & V, YawedBox const & B ); + +// ---------------------------------------------------------------------- + +} // namespace Intersect2d + +#endif + diff --git a/engine/shared/library/sharedCollision/src/shared/core/Intersect3d.cpp b/engine/shared/library/sharedCollision/src/shared/core/Intersect3d.cpp new file mode 100644 index 00000000..e9d48039 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Intersect3d.cpp @@ -0,0 +1,1701 @@ +// ====================================================================== +// +// Intersect3d.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/Intersect3d.h" + +#include "sharedCollision/Containment1d.h" +#include "sharedCollision/Intersect1d.h" +#include "sharedCollision/Intersect2d.h" +#include "sharedCollision/Containment3d.h" +#include "sharedCollision/Distance3d.h" +#include "sharedCollision/Overlap3d.h" +#include "sharedCollision/CollisionUtils.h" +#include "sharedCollision/ConfigSharedCollision.h" +#include "sharedMath/Capsule.h" +#include "sharedMath/Ring.h" +#include "sharedMath/Triangle3d.h" +#include "sharedMath/Line3d.h" +#include "sharedMath/Segment3d.h" +#include "sharedMath/Plane3d.h" +#include "sharedMath/Ribbon3d.h" +#include "sharedMath/Ray3d.h" +#include "sharedMath/Quadratic.h" +#include "sharedMath/Sphere.h" +#include "sharedMath/AxialBox.h" +#include "sharedMath/YawedBox.h" +#include "sharedMath/OrientedBox.h" +#include "sharedMath/OrientedCylinder.h" +#include "sharedMath/Plane.h" +#include "sharedMath/PolySolver.h" +#include "sharedMath/Torus.h" +#include "sharedMath/Cylinder.h" +#include "sharedMath/MultiShape.h" +#include "sharedMath/ShapeUtils.h" +#include "sharedMath/Transform.h" +#include "sharedMath/Circle.h" +#include "sharedMath/Vector2d.h" + +#include + +namespace Intersect3dNamespace +{ + Vector const cs_boxPlaneNormals[6] = + { + Vector( 1.0f, 0.0f, 0.0f), + Vector(-1.0f, 0.0f, 0.0f), + Vector( 0.0f, 1.0f, 0.0f), + Vector( 0.0f, -1.0f, 0.0f), + Vector( 0.0f, 0.0f, 1.0f), + Vector( 0.0f, 0.0f, -1.0f) + }; + + int const cs_edgeIndices[] = + { + 0, 1, + 1, 3, + 3, 2, + 2, 0, + 4, 5, + 5, 7, + 7, 6, + 6, 4, + 0, 4, + 1, 5, + 3, 7, + 2, 6 + }; +} + +using namespace Intersect1d; +using namespace Intersect2d; +using namespace Intersect3dNamespace; + +namespace Intersect3d +{ + +// ---------------------------------------------------------------------- + +bool IntersectLinePlaneUnsided ( Line3d const & L, + Plane3d const & P, + Vector & outPoint, + real & outParam ) +{ + real dot = L.getNormal().dot(P.getNormal()); + + if(dot == 0) return false; + + real t = ((P.getPoint()-L.getPoint()).dot(P.getNormal())) / (L.getNormal().dot(P.getNormal())); + + outPoint = L.getPoint() + L.getNormal()*t; + outParam = t; + + return true; +} + +// ---------------------------------------------------------------------- + +bool IntersectLineTriSided ( Line3d const & L, + Triangle3d const & T, + Vector & outPoint, + real & outParam ) +{ + bool intersects = Overlap3d::TestLineTriSided( L, T ); + + if(intersects) + { + return IntersectLinePlaneUnsided( L, T.getPlane(), outPoint, outParam ); + } + else + { + return false; + } +} + +// ---------- + +bool IntersectLineTriUnsided ( Line3d const & L, + Triangle3d const & T, + Vector & outPoint, + real & outParam ) +{ + bool intersects = Overlap3d::TestLineTriUnsided( L, T ); + + if(intersects) + { + return IntersectLinePlaneUnsided( L, T.getPlane(), outPoint, outParam ); + } + else + { + return false; + } +} + +// ---------------------------------------------------------------------- + +bool IntersectRayTriSided ( Ray3d const & R, + Triangle3d const & T, + Vector & outPoint, + real & outParam ) +{ + if(Overlap3d::TestRayTriSided(R,T) == HR_Miss) return false; + + return IntersectLinePlaneUnsided( R.getLine(), T.getPlane(), outPoint, outParam); +} + +// ---------- + +bool IntersectRayTriUnsided ( Ray3d const & R, + Triangle3d const & T, + Vector & outPoint, + real & outParam ) +{ + if(Overlap3d::TestRayTriUnsided(R,T) == HR_Miss) return false; + + return IntersectLinePlaneUnsided( R.getLine(), T.getPlane(), outPoint, outParam); +} + +// ---------------------------------------------------------------------- + +bool IntersectSegPlaneUnsided( Segment3d const & S, + Plane3d const & P, + Vector & outPoint, + real & outParam ) +{ + if(!Overlap3d::TestSegPlaneUnsided(S,P)) return false; + + return IntersectLinePlaneUnsided( S.getLine(), P, outPoint, outParam ); +} + +// ---------------------------------------------------------------------- + +bool IntersectSegTriSided ( Segment3d const & S, + Triangle3d const & T, + Vector & outPoint, + real & outParam ) +{ + if(!Overlap3d::TestSegTriSided(S,T)) return false; + + return IntersectSegPlaneUnsided(S,T.getPlane(),outPoint,outParam); +} + +// ---------- + +bool IntersectSegTriUnsided ( Segment3d const & S, + Triangle3d const & T, + Vector & outPoint, + real & outParam ) +{ + if(!Overlap3d::TestSegTriUnsided(S,T)) return false; + + return IntersectSegPlaneUnsided(S,T.getPlane(),outPoint,outParam); +} + +// ---------------------------------------------------------------------- + +bool IntersectSegRibbonSided ( Segment3d const & S, Ribbon3d const & R, float & outT, float & outU, float & outV ) +{ + if(Overlap3d::TestSegRibbonSided(S,R)) + { + Range temp = IntersectSegPlane(S,R.getPlane()); + + outT = temp.getMin(); + + Vector hitPoint = S.atParam(outT); + + // Compute the ribbon's parametric point of intersection + + Vector d = hitPoint - R.getPointA(); + + Vector ribbonNormal = R.getDir().cross(R.getDelta()); + + IGNORE_RETURN(ribbonNormal.normalize()); + + Matrix3 matrix = Matrix3::fromColumns(R.getDir(),R.getDelta(),ribbonNormal); + + Matrix3 inverse; + + IGNORE_RETURN( matrix.invert(inverse) ); + + outU = d.dot( inverse.row0() ); + outV = d.dot( inverse.row1() ); + + return true; + } + else + { + return false; + } +} + +// ---------------------------------------------------------------------- +// Parametric linear-vs-planar + +Range IntersectLinePlane ( Line3d const & line, Plane3d const & plane ) +{ + return IntersectPointPlane( line.getPoint(), line.getNormal(), plane ); +} + +Range IntersectRayPlane ( Ray3d const & ray, Plane3d const & plane ) +{ + return IntersectRanges ( IntersectLinePlane( ray.getLine(), plane ), Range::plusInf ); +} + +Range IntersectSegPlane ( Segment3d const & seg, Plane3d const & plane ) +{ + return IntersectRanges( IntersectLinePlane(seg.getLine(),plane), Range::unit ); +} + +// ---------------------------------------------------------------------- +// Parametric linear-vs-volume intersection tests + +Range IntersectLineSphere ( Line3d const & L, Sphere const & S ) +{ + Quadratic Q = Distance3d::DistancePointPoint(L.getPoint(), L.getNormal(), S.getCenter()); + + float min; + float max; + + float rad2 = S.getRadius() * S.getRadius(); + + if(Q.solveFor(rad2,min,max)) + { + return Range(min,max); + } + else + { + return Range::empty; + } +} + +// ---------- + +Range IntersectRaySphere ( Ray3d const & R, Sphere const & S ) +{ + return IntersectRanges( IntersectLineSphere(R.getLine(),S), Range::plusInf ); +} + +// ---------- + +Range IntersectSegSphere ( Segment3d const & seg, Sphere const & S ) +{ + return IntersectRanges( IntersectLineSphere(seg.getLine(),S), Range::unit ); +} + +// ---------------------------------------------------------------------- + +Range IntersectLineABox ( Line3d const & L, AxialBox const & box ) +{ + return IntersectPointABox( L.getPoint(), L.getNormal(), box ); +} + +// ---------- + +Range IntersectRayABox ( Ray3d const & R, AxialBox const & box ) +{ + Range hitRange = IntersectLineABox(R.getLine(),box); + + return IntersectRanges(hitRange,Range::plusInf); +} + +// ---------- + +Range IntersectSegABox ( Segment3d const & S, AxialBox const & box ) +{ + Range hitRange = IntersectLineABox(S.getLine(),box); + + return IntersectRanges(hitRange,Range::unit); +} + +// ---------------------------------------------------------------------- +// Line-torus intersection from Graphics Gems 2 + +int inttor (Vector const & Base, Vector const & Dcos, double radius,double rplane, double rnorm, int * nhits, double rhits[4]) +{ + double rho, a0, b0; /* Related constants */ + double f, l, t, g, q, m, u; /* Ray dependent terms */ + double C[5]; /* Quartic coefficients */ + + *nhits = 0; + +/* Compute constants related to the torus. */ + + rho = rplane*rplane / (rnorm*rnorm); + a0 = 4. * radius*radius; + b0 = radius*radius - rplane*rplane; + +/* Compute ray dependent terms. */ + + f = 1. - Dcos.y*Dcos.y; + l = 2. * (Base.x*Dcos.x + Base.z*Dcos.z); + t = Base.x*Base.x + Base.z*Base.z; + g = f + rho * Dcos.y*Dcos.y; + q = a0 / (g*g); + m = (l + 2.*rho*Dcos.y*Base.y) / g; + u = (t + rho*Base.y*Base.y + b0) / g; + +/* Compute the coefficients of the quartic. */ + + C[4] = 1.0; + C[3] = 2. * m; + C[2] = m*m + 2.*u - q*f; + C[1] = 2.*m*u - q*l; + C[0] = u*u - q*t; + +/* Use quartic root solver found in "Graphics Gems" by Jochen */ +/* Schwarze. */ + + *nhits = PolySolver::solveQuartic (C,rhits); + +/* SolveQuartic returns root pairs in reversed order. */ + m = rhits[0]; u = rhits[1]; rhits[0] = u; rhits[1] = m; + m = rhits[2]; u = rhits[3]; rhits[2] = u; rhits[3] = m; + + return (*nhits != 0); +} + +Range IntersectLineTorus_local(Line3d const & L, Torus const & T) +{ + Vector P = L.getPoint(); + Vector N = L.getNormal(); + + double nmag = N.magnitude(); + + IGNORE_RETURN( N.normalize() ); + + double radius = T.getMajorRadius(); + double rminor = T.getMinorRadius(); + + int hitCount; + double hits[4]; + + IGNORE_RETURN( inttor(P,N,radius,rminor,rminor,&hitCount,hits) ); + + if(hitCount == 0) + { + return Range::empty; + } + + // ---------- + // Put a range around the roots + + double dmin = REAL_MAX; + double dmax = -REAL_MAX; + + for(int i = 0; i < hitCount; i++) + { + if(hits[i] != hits[i]) continue; + + double hit = hits[i] / nmag; + + dmin = std::min(dmin, hit); + dmax = std::max(dmax, hit); + } + + return Range( static_cast(dmin), static_cast(dmax) ); +} + +Range IntersectLineTorus( Line3d const & L, Torus const & T ) +{ + Vector P = T.transformToLocal(L.getPoint()); + Vector N = T.rotateToLocal(L.getNormal()); + + Line3d localLine(P,N); + + Torus localTorus( Vector::zero, T.getMajorRadius(), T.getMinorRadius() ); + + return IntersectLineTorus_local( localLine, localTorus ); +} + +// ---------------------------------------------------------------------- + +Range IntersectLineCylinder ( Line3d const & L, Cylinder const & C ) +{ + Range slabRange = IntersectFloatRange( L.getPoint().y, L.getNormal().y, C.getRangeY() ); + + Range tubeRange = IntersectPointCircle( L.getPoint(), L.getNormal(), C.getBaseCircle() ); + + return IntersectRanges(slabRange,tubeRange); +} + +Range IntersectRayCylinder ( Ray3d const & R, Cylinder const & C ) +{ + Range lineRange = IntersectLineCylinder( R.getLine(), C ); + + return IntersectRanges(lineRange,Range::plusInf); +} + +Range IntersectSegCylinder ( Segment3d const & S, Cylinder const & C ) +{ + Range lineRange = IntersectLineCylinder( S.getLine(), C ); + + return IntersectRanges(lineRange,Range::unit); +} + +// ---------------------------------------------------------------------- + +Range IntersectLineShape( Line3d const & L, MultiShape const & S ) +{ + Line3d localLine = ShapeUtils::transform_p2l( L, S.getTransform_l2p() ); + MultiShape::BaseType type = S.getBaseType(); + + if (type == MultiShape::MSBT_Sphere) { return Intersect( localLine, S.getLocalSphere() ); } + else if(type == MultiShape::MSBT_Cylinder) { return Intersect( localLine, S.getLocalCylinder() ); } + else if(type == MultiShape::MSBT_Box) { return Intersect( localLine, S.getLocalAxialBox() ); } + else { return Range::empty; } +} + +Range IntersectRayShape ( Ray3d const & ray, MultiShape const & shape ) +{ + Range lineRange = IntersectLineShape( ray.getLine(), shape ); + + return IntersectRanges( lineRange, Range::plusInf ); +} + +Range IntersectSegShape ( Segment3d const & seg, MultiShape const & shape ) +{ + Range lineRange = IntersectLineShape( seg.getLine(), shape ); + + return IntersectRanges( lineRange, Range::unit ); +} + + +Intersect3d::ResultData::ResultData() +: m_getSurfaceNormal(false) +, m_surfaceNormal() +, m_length(0.0f) +{} + +Intersect3d::ResultData::ResultData(bool const getSurfaceNormal) +: m_getSurfaceNormal(getSurfaceNormal) +, m_surfaceNormal() +, m_length(0.0f) +{} + +// From Real Time Rendering +bool intersectRaySphereWithData(Ray3d const & ray, Sphere const & sphere, ResultData * const data) +{ + Vector const l(sphere.getCenter() - ray.getPoint()); + float const lSquared = l.magnitudeSquared(); + float const rSquared = sqr(sphere.getRadius()); + + if (lSquared <= rSquared) + { + if (data) + { + if (data->m_getSurfaceNormal) + { + Vector normal(ray.getPoint() - sphere.getCenter()); + if (!normal.normalize()) + normal = -ray.getNormal(); + + data->m_surfaceNormal = normal; + } + + data->m_length = 0.f; + } + + return true; + } + + // project l onto d + float const d = l.dot(ray.getNormal()); + + // ray behind the sphere + if (d < 0.0f) + { + return(false); + } + + // pythagorean: Squared distance from the center to the projection + float const mSquared = lSquared - sqr(d); + + // check if missed completely + if (mSquared > rSquared) + { + return(false); + } + + float const q = sqrt(rSquared - mSquared); + float const length = (lSquared < rSquared) ? (d + q) : (d - q); // inside or outside + + Vector const hitPoint(ray.getPoint() + (ray.getNormal() * length)); + + if (data) + { + if (data->m_getSurfaceNormal) + { + Vector normal(hitPoint - sphere.getCenter()); + normal.normalize(); + data->m_surfaceNormal = normal; + } + + data->m_length = length; + } + + return(true); +} + +// this is the Woo method that is found in Graphic Gems and realtime rendering +bool intersectRayABoxWithData(Ray3d const & ray, AxialBox const & box, ResultData * const data) +{ + Vector const max(box.getMax()); + Vector const min(box.getMin()); + + float const rayOrigin[3] = { ray.getPoint().x, ray.getPoint().y, ray.getPoint().z }; + float const rayDirection[3] = { ray.getNormal().x, ray.getNormal().y, ray.getNormal().z }; + float const minBounds[3] = { min.x, min.y, min.z }; + float const maxBounds[3] = { max.x, max.y, max.z }; + bool middleQuadrant[3] = { false, false, false }; + float candidatePlane[3] = { 0.0f, 0.0f, 0.0f }; + bool inside = true; + int i; + + // find candidate planes + for (i = 0; i < 3; ++i) + { + if (rayOrigin[i] < minBounds[i]) + { + // left quadrant + candidatePlane[i] = minBounds[i]; + inside = false; + } + else if (rayOrigin[i] > maxBounds[i]) + { + // right quadrant + candidatePlane[i] = maxBounds[i]; + inside = false; + } + else + { + middleQuadrant[i] = true; + } + } + + // ray origin inside bounding box + if (inside) + { + if (data != 0) + { + // find the normal from inside if we need to + if (data->m_getSurfaceNormal) + { + float const planeD[6] = + { + Plane::computeD(cs_boxPlaneNormals[0], max), + Plane::computeD(cs_boxPlaneNormals[1], min), + Plane::computeD(cs_boxPlaneNormals[2], max), + Plane::computeD(cs_boxPlaneNormals[3], min), + Plane::computeD(cs_boxPlaneNormals[4], max), + Plane::computeD(cs_boxPlaneNormals[5], min) + }; + + float minLength = REAL_MAX; + int whichPlane = 0; + // find the closest plane intersection that is facing away from us + + for(i = 0; i < 6; ++i) + { + float const dotRayPlaneNormal = ray.getNormal().dot(cs_boxPlaneNormals[i]); + + // only test the planes facing away from us + if (dotRayPlaneNormal > 0.0f) + { + float const length = ((cs_boxPlaneNormals[i] * -1.0f).dot(ray.getPoint()) - planeD[i]) / dotRayPlaneNormal; + + if ((length > 0.0f) && (length < minLength)) + { + whichPlane = i; + minLength = length; + } + } + } + data->m_surfaceNormal = -cs_boxPlaneNormals[whichPlane]; + } + + data->m_length = 0.0f; + } + + return true; + } + + float maxT[3]; + + // calculate distances to candidate planes + for (i = 0; i < 3; i++) + { + if ((!middleQuadrant[i]) && (rayDirection[i] != 0.0f)) + maxT[i] = (candidatePlane[i] - rayOrigin[i]) / rayDirection[i]; + else + maxT[i] = -1.0f; + } + + // get largest of the maxT's for final choice of intersection + int whichPlane = 0; + for (i = 1; i < 3; i++) + { + if (maxT[whichPlane] < maxT[i]) + { + whichPlane = i; + } + } + + // check final candidate actually inside box + if (maxT[whichPlane] < 0.0f) + { + return false; + } + + float hitPoint[3] = { 0.0f, 0.0f, 0.0f }; + + for (i = 0; i < 3; i++) + { + if (whichPlane != i) + { + hitPoint[i] = rayOrigin[i] + maxT[whichPlane] * rayDirection[i]; + if (hitPoint[i] < minBounds[i] || hitPoint[i] > maxBounds[i]) + { + return false; + } + } + else + { + hitPoint[i] = candidatePlane[i]; + } + } + + if (data != 0) + { + if (data->m_getSurfaceNormal) + { + float hitNormal[3] = { 0.0f, 0.0f, 0.0f }; + hitNormal[whichPlane] = 1.0f; + + Vector normal(hitNormal[0], hitNormal[1], hitNormal[2]); + float const dotRayNormal = ray.getNormal().dot(normal); + + data->m_surfaceNormal = (dotRayNormal < 0.0f) ? normal : -normal; + } + + data->m_length = (inside) ? 0.0f : maxT[whichPlane]; + } + return true; +} + + +bool intersectRayOBoxWithData(Ray3d const & ray, OrientedBox const & box, ResultData * const data) +{ + Vector const localRayOrigin = box.transformToLocal(ray.getPoint()); + Vector const localRayNormal = box.rotateToLocal(ray.getNormal()); + Ray3d const localRay(localRayOrigin, localRayNormal); + + if (intersectRayABoxWithData(localRay, box.getLocalShape(), data)) + { + if ((data) && (data->m_getSurfaceNormal)) + { + data->m_surfaceNormal = box.rotateToWorld(data->m_surfaceNormal); + } + return(true); + } + return(false); +} + +bool intersectRayYawedBoxWithData(Ray3d const & ray, YawedBox const & box, ResultData * const data) +{ + Vector const localRayOrigin = box.transformToLocal(ray.getPoint()); + Vector const localRayNormal = box.rotateToLocal(ray.getNormal()); + Ray3d const localRay(localRayOrigin, localRayNormal); + + if (intersectRayABoxWithData(localRay, box.getLocalShape(), data)) + { + if ((data) && (data->m_getSurfaceNormal)) + { + data->m_surfaceNormal = box.rotateToWorld(data->m_surfaceNormal); + } + + return true; + } + + return false; +} + +bool intersectRayCylinderWithData(Ray3d const & ray, Cylinder const & cylinder, ResultData * const data) +{ + Range const capRange(Intersect1d::IntersectFloatRange(ray.getPoint().y, ray.getNormal().y, cylinder.getRangeY())); + Range const tubeRange(Intersect2d::IntersectPointCircle(ray.getPoint(), ray.getNormal(), cylinder.getBaseCircle())); + Range finalRange(Intersect1d::IntersectRanges(capRange, tubeRange)); + + // clamp min because we are not concerned with intersections + // that occur behind us + finalRange.setMin(clamp(0.f, finalRange.getMin(), REAL_MAX)); + + if (!finalRange.isEmpty()) + { + if (data != 0) + { + float const length = finalRange.getMin(); + + if (data->m_getSurfaceNormal) + { + Vector const hitPoint(ray.atParam(length)); + + Vector const baseCenter(cylinder.getBase()); + Vector const topCenter(baseCenter.x, baseCenter.y + cylinder.getHeight(), baseCenter.z); + + Plane const baseCapPlane(-Vector::unitY, baseCenter); + Plane const topCapPlane(Vector::unitY, topCenter); + + float const distanceToBase = baseCapPlane.computeDistanceTo(hitPoint); + float const distanceToTop = topCapPlane.computeDistanceTo(hitPoint); + + if (WithinEpsilonInclusive(distanceToBase, 0.0f, 0.0001f)) + { + data->m_surfaceNormal = -Vector::unitY; + } + + else if (WithinEpsilonInclusive(distanceToTop, 0.0f, 0.0001f)) + { + data->m_surfaceNormal = Vector::unitY; + } + + else + { + Vector normal(hitPoint - cylinder.getBase()); + normal.y = 0.0f; + normal.normalize(); + + data->m_surfaceNormal = normal; + } + } + + data->m_length = length; + } + return true; + } + + return false; +} + +bool intersectRayOCylinderWithData(Ray3d const & ray, OrientedCylinder const & cylinder, ResultData * const data) +{ + Vector const localRayOrigin = cylinder.transformToLocal(ray.getPoint()); + Vector const localRayNormal = cylinder.rotateToLocal(ray.getNormal()); + Ray3d const localRay(localRayOrigin, localRayNormal); + + if (intersectRayCylinderWithData(localRay, cylinder.getLocalShape(), data)) + { + if ((data) && (data->m_getSurfaceNormal)) + { + data->m_surfaceNormal = cylinder.rotateToWorld(data->m_surfaceNormal); + } + return(true); + } + return(false); +} + +bool intersectRayShapeWithData(Ray3d const & ray, MultiShape const & shape, ResultData * const data) +{ + MultiShape::ShapeType type = shape.getShapeType(); + + switch (type) + { + case MultiShape::MST_Sphere: + return intersectRaySphereWithData(ray, shape.getSphere(), data); + + case MultiShape::MST_AxialBox: + return intersectRayABoxWithData(ray, shape.getAxialBox(), data); + + case MultiShape::MST_OrientedBox: + return intersectRayOBoxWithData(ray, shape.getOrientedBox(), data); + + case MultiShape::MST_YawedBox: + return intersectRayYawedBoxWithData(ray, shape.getYawedBox(), data); + + case MultiShape::MST_Cylinder: + return intersectRayCylinderWithData(ray, shape.getCylinder(), data); + + case MultiShape::MST_OrientedCylinder: + return intersectRayOCylinderWithData(ray, shape.getOrientedCylinder(), data); + + default: + DEBUG_WARNING(true, ("Intersect3d::intersectRayShapeWithData: intersection with shape %i is not implemented", static_cast(type))); + break; + } + + return false; +} + +bool intersectTriangleBox(AxialBox const & box, Vector const & point1, Vector const & point2, Vector const & point3) +{ + //-- If a point is inside then return true + if (box.contains(point1)) + return true; + + if (box.contains(point2)) + return true; + + if (box.contains(point3)) + return true; + + //-- Try to early out by testing sphere/plane + //-- generate the normal first so we don't fatal if we hit 0 area triangles here + Vector normal = (point1-point3).cross(point2-point1); + if (!normal.normalize()) + return false; + + Plane const trianglePlane(normal, point1); + Vector intersection; + if (sqr(trianglePlane.computeDistanceTo(box.getCenter())) <= box.getRadiusSquared()) + { + //-- Since the bounding sphere intersects the plane, now test each edge of the axial box with the triangle + { + for (int i = 0; i < 12; ++i) + { + int const edgeIndex0 = cs_edgeIndices[i * 2 + 0]; + int const edgeIndex1 = cs_edgeIndices[i * 2 + 1]; + if (trianglePlane.findIntersection(box.getCorner(edgeIndex0), box.getCorner(edgeIndex1), intersection)) + { + if (intersection.inPolygon(point1, point2, point3)) + return true; + } + } + } + + // we now need to test if the triangle edges intersect the box planes + // and if the intersection point is then in the box + { + Vector const & max = box.getMax(); + Vector const & min = box.getMin(); + float const planeD[6] = + { + Plane::computeD(cs_boxPlaneNormals[0], max), + Plane::computeD(cs_boxPlaneNormals[1], min), + Plane::computeD(cs_boxPlaneNormals[2], max), + Plane::computeD(cs_boxPlaneNormals[3], min), + Plane::computeD(cs_boxPlaneNormals[4], max), + Plane::computeD(cs_boxPlaneNormals[5], min) + }; + + for (int i = 0; i < 6; ++i) + { + Plane const facePlane(cs_boxPlaneNormals[i], planeD[i]); + + if (facePlane.findIntersection(point1, point2, intersection)) + { + if (box.contains(intersection)) + return true; + } + + if (facePlane.findIntersection(point2, point3, intersection)) + { + if (box.contains(intersection)) + return true; + } + + if (facePlane.findIntersection(point3, point1, intersection)) + { + if (box.contains(intersection)) + return true; + } + } + } + } + + return false; +} + + +// ---------------------------------------------------------------------- +// Motion tests + +// Moving point-vs-planar + +Range IntersectPointPlane( Vector const & A, Vector const & V, Plane3d const & P ) +{ + float dist = (A - P.getPoint()).dot(P.getNormal()); + + float velocity = V.dot(P.getNormal()); + + if(velocity == 0.0f) + { + ContainmentResult result = Containment3d::TestPointPlane(A,P); + + if(result == CR_Boundary) + { + // Point is on the plane and is moving in the plane + + return Range::inf; + } + else + { + // Point is not on the plane and is moving parallel to the plane + + return Range::empty; + } + } + else + { + float t = -dist / velocity; + + return Range(t,t); + } +} + + +// ---------------------------------------------------------------------- +// Moving point-vs-volume + +Range IntersectPointABox ( Vector const & P, Vector const & V, AxialBox const & B ) +{ + Range timeX = IntersectFloatRange( P.x, V.x, B.getRangeX() ); + Range timeY = IntersectFloatRange( P.y, V.y, B.getRangeY() ); + Range timeZ = IntersectFloatRange( P.z, V.z, B.getRangeZ() ); + + return IntersectRanges(timeX,timeY,timeZ); +} + +// ---------- + +Range IntersectPointSphere ( Vector const & P, Vector const & V, Sphere const & S ) +{ + Quadratic Q = Distance3d::DistancePointPoint(P,V,S.getCenter()); + + real rad2 = S.getRadius() * S.getRadius(); + + float newMin; + float newMax; + + if(Q.solveFor( rad2, newMin, newMax )) + { + return Range(newMin,newMax); + } + else + { + return Range::empty; + } +} + +// ---------------------------------------------------------------------- +// Moving linear-vs-linear + +Range IntersectLineLine ( Line3d const & A, Vector const & V, Line3d const & B ) +{ + Vector D = B.getPoint() - A.getPoint(); + + Vector N = A.getNormal().cross(B.getNormal()); + + IGNORE_RETURN( N.normalize() ); + + float dist = D.dot(N); + + float velocity = V.dot(N); + + if(velocity == 0) + { + return Range::empty; + } + + float hitTime = dist / velocity; + + return Range(hitTime,hitTime); +} + + +Range IntersectLineSeg ( Line3d const & A, Vector const & V, Segment3d const & B ) +{ + Range hitTime = IntersectLineLine(A,V,B.getLine()); + + if(!hitTime.isEmpty()) + { + float u; + float v; + + Line3d A2(A.getPoint() + V * hitTime.getMin(), A.getNormal()); + + Distance3d::ClosestPairLineLine( A2, B.getLine(), u, v ); + + if(v < 0.0f) return Range::empty; + if(v > 1.0f) return Range::empty; + } + + return hitTime; +} + +Range IntersectRaySeg ( Ray3d const & A, Vector const & V, Segment3d const & B ) +{ + Range hitTime = IntersectLineLine(A.getLine(),V,B.getLine()); + + if(!hitTime.isEmpty()) + { + float u; + float v; + + Line3d A2(A.getPoint() + V * hitTime.getMin(), A.getNormal()); + + Distance3d::ClosestPairLineLine( A2, B.getLine(), u, v ); + + if(u < 0.0f) return Range::empty; + if(v < 0.0f) return Range::empty; + if(v > 1.0f) return Range::empty; + } + + return hitTime; +} + + +// ---------------------------------------------------------------------- +// Moving linear-vs-planar + +Range IntersectRayPlane ( Ray3d const & A, Vector const & V, Plane3d const & P ) +{ + Range hitPointPlane = IntersectPointPlane( A.getPoint(), V, P ); + + float hitTime = hitPointPlane.getMin(); + + // velocity along the plane's normal + float v = A.getNormal().dot(V); + + // relative ray direction with respect to the plane's normal + float d = A.getNormal().dot(P.getNormal()); + + if(v == 0) + { + // Ray origin isn't moving, or is moving parallel to the plane + + ContainmentResult result = Containment3d::TestPointPlane(A.getPoint(),P); + + if(d == 0) + { + // and ray is parallel to the plane + + if(result == CR_Outside) return Range::empty; + else if(result == CR_Boundary) return Range::inf; + else return Range::empty; + } + else if(d < 0) + { + // ray is pointing towards the plane + + if(result == CR_Outside) return Range::inf; + else if(result == CR_Boundary) return Range::inf; + else return Range::empty; + } + else + { + // ray is pointing away from the plane + + if(result == CR_Outside) return Range::empty; + else if(result == CR_Boundary) return Range::inf; + else return Range::inf; + } + } + else if(v < 0) + { + // Ray is moving towards the plane + + if(d == 0) return hitPointPlane; + else if( d < 0) return Range(-REAL_MAX,hitTime); + else return Range(hitTime,REAL_MAX); + } + else + { + // Ray is moving away from the plane + + if(d == 0) return hitPointPlane; + else if (d < 0) return Range(hitTime,REAL_MAX); + else return Range(-REAL_MAX,hitTime); + } +} + +// ---------- + +Range IntersectSegPlane ( Segment3d const & S, Vector const & V, Plane3d const & P ) +{ + Line3d axis(P.getPoint(),P.getNormal()); + + float vel = Collision2d::ComponentAlong(V,P.getNormal()); + + Range R = Distance3d::ProjectAxis(axis,S); + + return IntersectFloatRange( 0.0f, -vel, R ); +} + +// ---------- + +Range IntersectLineTri ( Line3d const & A, Vector const & V, Triangle3d const & T ) +{ + Range hit0 = IntersectLineSeg(A,V,T.getEdgeSegment0()); + Range hit1 = IntersectLineSeg(A,V,T.getEdgeSegment1()); + Range hit2 = IntersectLineSeg(A,V,T.getEdgeSegment2()); + + return Containment1d::EncloseRanges(hit0,hit1,hit2); +} + +// ---------- + +Range IntersectRayTri ( Ray3d const & A, Vector const & V, Triangle3d const & T ) +{ + Range hitRayPlane = IntersectRayPlane(A,V,T.getPlane()); + + Range hitLineTri = IntersectLineTri(A.getLine(),V,T); + + return IntersectRanges(hitRayPlane,hitLineTri); +} + +// ---------- + +Range IntersectSegTri ( Segment3d const & A, Vector const & V, Triangle3d const & T ) +{ + Range hitSegPlane = IntersectSegPlane(A,V,T.getPlane()); + + Range hitLineTri = IntersectLineTri(A.getLine(),V,T); + + return IntersectRanges(hitSegPlane,hitLineTri); +} + +// ---------------------------------------------------------------------- +// Moving volume-vs-point + +Range IntersectSpherePoint ( Sphere const & S, Vector const & V, Vector const & P ) +{ + return IntersectPointSphere(P,-V,S); +} + +// ---------------------------------------------------------------------- +// Moving volume-vs-linear + +Range IntersectSphereXLine ( Sphere const & S, Vector const & V, Vector const & B ) +{ + Circle newCircle( Vector(S.getCenter().y,0.0f,S.getCenter().z), S.getRadius() ); + + Vector newV(V.y,0.0f,V.z); + + Vector newB(B.y,0.0f,B.z); + + return Intersect2d::IntersectCirclePoint( newCircle, newV, newB ); +} + + +Range IntersectSphereYLine ( Sphere const & S, Vector const & V, Vector const & B ) +{ + return Intersect2d::IntersectCirclePoint( S.getCircle(), V, B); +} + +Range IntersectSphereZLine ( Sphere const & S, Vector const & V, Vector const & B ) +{ + Circle newCircle( Vector(S.getCenter().x,0.0f,S.getCenter().y), S.getRadius() ); + + Vector newV(V.x,0.0f,V.y); + + Vector newB(B.x,0.0f,B.y); + + return Intersect2d::IntersectCirclePoint( newCircle, newV, newB ); +} + +// ---------------------------------------------------------------------- +// Moving volume-vs-planar + +Range IntersectSpherePlane( Sphere const & S, Vector const & V, Plane3d const & P ) +{ + Line3d axis(P.getPoint(),P.getNormal()); + + float vel = Collision2d::ComponentAlong(V,P.getNormal()); + + Range R = Distance3d::ProjectAxis(axis,S); + + return IntersectRangeFloat( R, vel, 0.0f ); +} + +Range IntersectSphereRing ( Sphere const & S, Vector const & V, Ring const & R ) +{ + Line3d L( S.getCenter(), V ); + + Torus T( R.getCenter(), R.getRadius(), S.getRadius() ); + + return IntersectLineTorus(L,T); +} + + +// ---------------------------------------------------------------------- +// Moving volume-vs-volume + +Range IntersectSphereSphere ( Sphere const & A, Vector const & velocity, Sphere const & B ) +{ + Quadratic Q = Distance3d::DistancePointPoint(A.getCenter(),velocity,B.getCenter()); + + real sumRadius = A.getRadius() + B.getRadius(); + + real sumRad2 = sumRadius * sumRadius; + + float newMin; + float newMax; + + if(Q.solveFor( sumRad2, newMin, newMax )) + { + return Range(newMin,newMax); + } + else + { + Capsule const capsule(A, velocity); + if (capsule.intersectsSphere(B)) + { + return Range::plusInf; + } + } + + return Range::empty; +} + +// ---------- +// Accessory methods for IntersectSphereABox + +Range IntersectBoxEdgesX ( Sphere const & S, Vector const & V, AxialBox const & B ) +{ + Vector C = B.getCenter(); + Vector D = B.getDelta(); + + Range pp = IntersectSphereXLine( S, V, C + Vector(0.0f, D.y, D.z) ); + Range pn = IntersectSphereXLine( S, V, C + Vector(0.0f, D.y, -D.z) ); + Range np = IntersectSphereXLine( S, V, C + Vector(0.0f, -D.y, D.z) ); + Range nn = IntersectSphereXLine( S, V, C + Vector(0.0f, -D.y, -D.z) ); + + return Containment1d::EncloseRanges(pp,pn,np,nn); +} + +Range IntersectBoxEdgesY ( Sphere const & S, Vector const & V, AxialBox const & B ) +{ + Vector C = B.getCenter(); + Vector D = B.getDelta(); + + Range pp = IntersectSphereYLine( S, V, C + Vector( D.x, 0.0f, D.z) ); + Range pn = IntersectSphereYLine( S, V, C + Vector( D.x, 0.0f, -D.z) ); + Range np = IntersectSphereYLine( S, V, C + Vector(-D.x, 0.0f, D.z) ); + Range nn = IntersectSphereYLine( S, V, C + Vector(-D.x, 0.0f, -D.z) ); + + return Containment1d::EncloseRanges(pp,pn,np,nn); +} + +Range IntersectBoxEdgesZ ( Sphere const & S, Vector const & V, AxialBox const & B ) +{ + Vector C = B.getCenter(); + Vector D = B.getDelta(); + + Range pp = IntersectSphereZLine( S, V, C + Vector( D.x, D.y, 0.0f) ); + Range pn = IntersectSphereZLine( S, V, C + Vector( D.x, -D.y, 0.0f) ); + Range np = IntersectSphereZLine( S, V, C + Vector(-D.x, D.y, 0.0f) ); + Range nn = IntersectSphereZLine( S, V, C + Vector(-D.x, -D.y, 0.0f) ); + + return Containment1d::EncloseRanges(pp,pn,np,nn); +} + +// ---------- + +Range IntersectSphereABox ( Sphere const & S, Vector const & V, AxialBox const & B ) +{ + if (V == Vector::zero) + { + Vector const & max = B.getMax(); + Vector const & min = B.getMin(); + float const planeD[6] = + { + Plane::computeD(cs_boxPlaneNormals[0], max), + Plane::computeD(cs_boxPlaneNormals[1], min), + Plane::computeD(cs_boxPlaneNormals[2], max), + Plane::computeD(cs_boxPlaneNormals[3], min), + Plane::computeD(cs_boxPlaneNormals[4], max), + Plane::computeD(cs_boxPlaneNormals[5], min) + }; + + Vector const & sphereCenter = S.getCenter(); + float const sphereRadius = S.getRadius(); + + for (int i = 0; i < 6; ++i) + { + Plane const plane(cs_boxPlaneNormals[i], planeD[i]); + if (plane.computeDistanceTo(sphereCenter) > sphereRadius) + { + return Range::empty; + } + } + + return Range::plusInf; + } + + Range result = Range::empty; + + // Intersect collision ranges for the box slabs + + Range slabX = B.getRangeX(); + Range slabY = B.getRangeY(); + Range slabZ = B.getRangeZ(); + + Range slabHitX = Intersect1d::IntersectRangeRange(S.getRangeX(),V.x,slabX); + Range slabHitY = Intersect1d::IntersectRangeRange(S.getRangeY(),V.y,slabY); + Range slabHitZ = Intersect1d::IntersectRangeRange(S.getRangeZ(),V.z,slabZ); + + Range allSlabsHit = Intersect1d::IntersectRanges(slabHitX,slabHitY,slabHitZ); + + if(allSlabsHit.isEmpty()) + { + return Range::empty; + } + + // ---------- + // Compute the time ranges when the center of the sphere is inside the box slabs + + Vector C = S.getCenter(); + + Range centerInSlabX = Intersect1d::IntersectFloatRange(C.x,V.x,slabX); + Range centerInSlabY = Intersect1d::IntersectFloatRange(C.y,V.y,slabY); + Range centerInSlabZ = Intersect1d::IntersectFloatRange(C.z,V.z,slabZ); + + // ---------- + // Face tests + + // If the sphere's center is inside two slabs when the sphere's bounding box + // hits the test box, then that hit time is a contact time + + bool foundMin = false; + bool foundMax = false; + + int minCounter = 0; + int maxCounter = 0; + + if(centerInSlabX.contains(allSlabsHit.getMin())) minCounter++; + if(centerInSlabY.contains(allSlabsHit.getMin())) minCounter++; + if(centerInSlabZ.contains(allSlabsHit.getMin())) minCounter++; + + if(centerInSlabX.contains(allSlabsHit.getMax())) maxCounter++; + if(centerInSlabY.contains(allSlabsHit.getMax())) maxCounter++; + if(centerInSlabZ.contains(allSlabsHit.getMax())) maxCounter++; + + if((minCounter == 3) || (maxCounter == 3)) + { + // The sphere and box will always and forevermore intersect + // (which can happen if the sphere isn't moving) + + return Range::inf; + } + + if(minCounter == 2) + { + result.setMin(allSlabsHit.getMin()); + foundMin = true; + } + + if(maxCounter == 2) + { + result.setMax(allSlabsHit.getMax()); + foundMax = true; + } + + if(foundMin && foundMax) return result; + + // ---------- + // Edge tests + + // The first edge hit that occurs while the center is inside the + // corresponding slab is a contact time + + float minEdge = REAL_MAX; + float maxEdge = -REAL_MAX; + + Range edgeRangeX = IntersectBoxEdgesX(S,V,B); + Range edgeRangeY = IntersectBoxEdgesY(S,V,B); + Range edgeRangeZ = IntersectBoxEdgesZ(S,V,B); + + if(!foundMin) + { + if(centerInSlabX.contains(edgeRangeX.getMin())) + { + if(edgeRangeX.getMin() < minEdge) minEdge = edgeRangeX.getMin(); + } + + if(centerInSlabY.contains(edgeRangeY.getMin())) + { + if(edgeRangeY.getMin() < minEdge) minEdge = edgeRangeY.getMin(); + } + + if(centerInSlabZ.contains(edgeRangeZ.getMin())) + { + if(edgeRangeZ.getMin() < minEdge) minEdge = edgeRangeZ.getMin(); + } + } + + if(!foundMax) + { + if(centerInSlabX.contains(edgeRangeX.getMax())) + { + if(edgeRangeX.getMax() > maxEdge) maxEdge = edgeRangeX.getMax(); + } + + if(centerInSlabY.contains(edgeRangeY.getMax())) + { + if(edgeRangeY.getMax() > maxEdge) maxEdge = edgeRangeY.getMax(); + } + + if(centerInSlabZ.contains(edgeRangeZ.getMax())) + { + if(edgeRangeZ.getMax() > maxEdge) maxEdge = edgeRangeZ.getMax(); + } + } + + if(minEdge != REAL_MAX) + { + result.setMin(minEdge); + foundMin = true; + } + + if(maxEdge != -REAL_MAX) + { + result.setMax(maxEdge); + foundMax = true; + } + + if(foundMin && foundMax) return result; + + // ---------- + // Corner tests + + Range cornerRange = Range::empty; + + for(int i = 0; i < 8; i++) + { + Range temp = IntersectSpherePoint(S,V,B.getCorner(i)); + + cornerRange = Containment1d::EncloseRanges(cornerRange,temp); + } + + if(!cornerRange.isEmpty()) + { + if(!foundMin) + { + result.setMin(cornerRange.getMin()); + foundMin = true; + } + + if(!foundMax) + { + result.setMax(cornerRange.getMax()); + foundMax = true; + } + } + + // ---------- + // Done + + if(foundMin ^ foundMax) + { + return Range::empty; + } + + return result; +} + +// ---------- + +Range IntersectSphereYBox ( Sphere const & S, Vector const & V, YawedBox const & B ) +{ + Vector localCenter = B.transformToLocal(S.getCenter()); + + Sphere localSphere(localCenter,S.getRadius()); + + AxialBox localBox = B.getLocalShape(); + + Vector localVelocity = B.rotateToLocal(V); + + return IntersectSphereABox( localSphere, localVelocity, localBox ); +} + +// ---------- + +Range IntersectSphereOBox ( Sphere const & S, Vector const & V, OrientedBox const & B ) +{ + Vector localCenter = B.transformToLocal(S.getCenter()); + + Sphere localSphere(localCenter,S.getRadius()); + + AxialBox localBox = B.getLocalShape(); + + Vector localVelocity = B.rotateToLocal(V); + + return IntersectSphereABox( localSphere, localVelocity, localBox ); +} + +// ---------- +// IntersectSphereTorus is implemented as line-vs-expanded-torus + +Range IntersectSphereTorus ( Sphere const & S, Vector const & V, Torus const & T ) +{ + Line3d L( S.getCenter(), V); + + Torus T2( T.getCenter(), T.getAxis(), T.getMajorRadius(), T.getMinorRadius() + S.getRadius() ); + + return IntersectLineTorus( L, T2 ); +} + +// ---------- + +Range IntersectSphereCylinder ( Sphere const & S, Vector const & V, Cylinder const & C ) +{ + if (C.getHeight() < 0.0f || C.getRadius() < 0.0f) + { + WARNING(true, ("Intersect3d::IntersectSphereCylinder invalid cylinder height or radius")); + return Range::empty; + } + + Range tubeRange = IntersectCircleCircle(S.getCircle(),V,C.getBaseCircle()); + Range slabRange = IntersectRangeRange(S.getRangeY(),V.y,C.getRangeY()); + + Range volumeRange = IntersectRanges(tubeRange,slabRange); + + if(volumeRange.isEmpty()) + { + return Range::empty; + } + + Range ringRangeA = Intersect3d::IntersectSphereRing(S,V,C.getTopRing()); + Range ringRangeB = Intersect3d::IntersectSphereRing(S,V,C.getBaseRing()); + + ringRangeA = Intersect3d::IntersectSphereRing(S,V,C.getTopRing()); + ringRangeB = Intersect3d::IntersectSphereRing(S,V,C.getBaseRing()); + + Range boundaryRange = Containment1d::EncloseRanges(ringRangeA,ringRangeB); + + { + Sphere temp( S.getCenter() + V * volumeRange.getMin(), S.getRadius() ); + + if(Distance3d::DistanceSphereCylinder(temp,C) > 0.01f) + { + if(boundaryRange.isEmpty()) return Range::empty; + + volumeRange.setMin( boundaryRange.getMin() ); + } + } + + { + Sphere temp( S.getCenter() + V * volumeRange.getMax(), S.getRadius() ); + + if(Distance3d::DistanceSphereCylinder(temp,C) > 0.01f) + { + if(boundaryRange.isEmpty()) return Range::empty; + + volumeRange.setMax( boundaryRange.getMax() ); + } + } + + return volumeRange; +} + +// ---------------------------------------------------------------------- + +Range IntersectSphereOCylinder(Sphere const & sphere, Vector const & V, OrientedCylinder const & cylinder) +{ + Vector const localCenter = cylinder.transformToLocal(sphere.getCenter()); + + Sphere const localSphere(localCenter,sphere.getRadius()); + + Cylinder const localCylinder = cylinder.getLocalShape(); + + Vector const localVelocity = cylinder.rotateToLocal(V); + + return IntersectSphereCylinder(localSphere, localVelocity, localCylinder); +} + +// ---------------------------------------------------------------------- + +Range IntersectABoxABox ( AxialBox const & A, Vector const & V, AxialBox const & B ) +{ + Range timeX = IntersectRangeRange( A.getRangeX(), V.x, B.getRangeX() ); + Range timeY = IntersectRangeRange( A.getRangeY(), V.x, B.getRangeY() ); + Range timeZ = IntersectRangeRange( A.getRangeZ(), V.x, B.getRangeZ() ); + + return IntersectRanges(timeX,timeY,timeZ); +} + +// ---------------------------------------------------------------------- + +Range IntersectMultiShapes ( MultiShape const & A, Vector const & V, MultiShape const & B ) +{ + MultiShape::ShapeType typeA = A.getShapeType(); + MultiShape::ShapeType typeB = B.getShapeType(); + + // ---------- + + if(typeA == MultiShape::MST_Sphere) + { + Sphere sphereA = A.getSphere(); + + if (typeB == MultiShape::MST_Sphere) { return Intersect( sphereA, V, B.getSphere() ); } + else if(typeB == MultiShape::MST_Cylinder) { return Intersect( sphereA, V, B.getCylinder() ); } + else if(typeB == MultiShape::MST_OrientedCylinder) { return Intersect( sphereA, V, B.getOrientedCylinder() ); } + else if(typeB == MultiShape::MST_AxialBox) { return Intersect( sphereA, V, B.getAxialBox() ); } + else if(typeB == MultiShape::MST_YawedBox) { return Intersect( sphereA, V, B.getYawedBox() ); } + else if(typeB == MultiShape::MST_OrientedBox) { return Intersect( sphereA, V, B.getOrientedBox() ); } + else { return Range::empty; } + } + else if(typeB == MultiShape::MST_Sphere) + { + Sphere sphereB = B.getSphere(); + + if (typeB == MultiShape::MST_Sphere) { return Intersect( sphereB, -V, A.getSphere() ); } + else if(typeB == MultiShape::MST_Cylinder) { return Intersect( sphereB, -V, A.getCylinder() ); } + else if(typeB == MultiShape::MST_OrientedCylinder) { return Intersect( sphereB, -V, A.getOrientedCylinder() ); } + else if(typeB == MultiShape::MST_AxialBox) { return Intersect( sphereB, -V, A.getAxialBox() ); } + else if(typeB == MultiShape::MST_YawedBox) { return Intersect( sphereB, -V, A.getYawedBox() ); } + else if(typeB == MultiShape::MST_OrientedBox) { return Intersect( sphereB, -V, A.getOrientedBox() ); } + else { return Range::empty; } + } + else + { + return Range::empty; + } +} + +// ---------------------------------------------------------------------- + +Range IntersectCircleSeg_Below ( Circle const & circle, Vector const & inV, Segment3d const & S ) +{ + DEBUG_FATAL(inV.y != 0.0f,("IntersectCircleSeg_Below - can't handle Y movement yet\n")); + + Vector V(inV.x,0.0f,inV.z); + + Vector A = S.getBegin(); + Vector B = S.getEnd(); + Vector const & C = circle.getCenter(); + + if(A.y > B.y) std::swap(A,B); + + if(C.y > B.y) + { + return Range::empty; + } + else if(C.y < A.y) + { + return Intersect2d::IntersectCircleSeg(circle,V,S); + } + else + { + if(A.y == B.y) + { + return Intersect2d::IntersectCircleSeg(circle,V,S); + } + else + { + float param = (C.y - A.y) / (B.y - A.y); + + Vector D = A + (B - A) * param; + + return Intersect2d::IntersectCircleSeg(circle,V,Segment3d(D,B)); + } + } +} + +// ---------------------------------------------------------------------- + +} // namespace Intersect3d + diff --git a/engine/shared/library/sharedCollision/src/shared/core/Intersect3d.h b/engine/shared/library/sharedCollision/src/shared/core/Intersect3d.h new file mode 100644 index 00000000..c30edbe9 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Intersect3d.h @@ -0,0 +1,193 @@ +// ====================================================================== +// +// Intersect3d.h +// copyright (c) 2001 Sony Online Entertainment +// +// ---------------------------------------------------------------------- + +#ifndef INCLUDED_Intersect3d_H +#define INCLUDED_Intersect3d_H + +#include "sharedMath/Range.h" // Need this for the inlined aliases to work +#include "sharedMath/Vector.h" + +class Triangle3d; +class Segment3d; +class Plane3d; +class Ribbon3d; +class Line3d; +class Ray3d; +class Range; +class AxialBox; +class YawedBox; +class OrientedBox; +class OrientedCylinder; +class Sphere; +class Ring; +class Cylinder; +class Torus; +class Circle; +class MultiShape; + +namespace Intersect3d +{ + +// ---------------------------------------------------------------------- +// Intersection point calculation + +bool IntersectSegPlaneSided ( Segment3d const & S, Plane3d const & P, Vector & outPoint, float & outParam ); +bool IntersectSegPlaneUnsided ( Segment3d const & S, Plane3d const & P, Vector & outPoint, float & outParam ); + +bool IntersectLinePlaneSided ( Line3d const & L, Plane3d const & P, Vector & outPoint, float & outParam ); +bool IntersectLinePlaneUnsided ( Line3d const & L, Plane3d const & P, Vector & outPoint, float & outParam ); + +bool IntersectLineTriSided ( Line3d const & L, Triangle3d const & T, Vector & outPoint, float & outParam ); +bool IntersectLineTriUnsided ( Line3d const & L, Triangle3d const & T, Vector & outPoint, float & outParam ); + +bool IntersectRayTriSided ( Ray3d const & R, Triangle3d const & T, Vector & outPoint, float & outParam ); +bool IntersectRayTriUnsided ( Ray3d const & R, Triangle3d const & T, Vector & outPoint, float & outParam ); + +bool IntersectSegTriSided ( Segment3d const & S, Triangle3d const & T, Vector & outPoint, float & outParam ); +bool IntersectSegTriUnsided ( Segment3d const & S, Triangle3d const & T, Vector & outPoint, float & outParam ); + +bool IntersectSegRibbonSided ( Segment3d const & S, + Ribbon3d const & R, + float & outT, // Segment's intersection param + float & outU, // Ribbon's intersection param (along the ribbon axis) + float & outV ); // Ribbon's intersection param (across the ribbon) + +// ---------- + +inline bool IntersectSided ( Line3d const & L, Triangle3d const & T, Vector & outPoint, float & outParam ) { return IntersectLineTriSided(L,T,outPoint,outParam); } +inline bool IntersectSided ( Ray3d const & R, Triangle3d const & T, Vector & outPoint, float & outParam ) { return IntersectRayTriSided(R,T,outPoint,outParam); } +inline bool IntersectSided ( Segment3d const & S, Triangle3d const & T, Vector & outPoint, float & outParam ) { return IntersectSegTriSided(S,T,outPoint,outParam); } + +inline bool IntersectUnsided ( Line3d const & L, Triangle3d const & T, Vector & outPoint, float & outParam ) { return IntersectLineTriUnsided(L,T,outPoint,outParam); } +inline bool IntersectUnsided ( Ray3d const & R, Triangle3d const & T, Vector & outPoint, float & outParam ) { return IntersectRayTriUnsided(R,T,outPoint,outParam); } +inline bool IntersectUnsided ( Segment3d const & S, Triangle3d const & T, Vector & outPoint, float & outParam ) { return IntersectSegTriUnsided(S,T,outPoint,outParam); } + +// ---------------------------------------------------------------------- +// Parametric linear-vs-plane + +Range IntersectLinePlane ( Line3d const & line, Plane3d const & plane ); +Range IntersectSegPlane ( Segment3d const & seg, Plane3d const & plane ); + +// ---------- +// Parametric linear-vs-volume + +Range IntersectLineSphere ( Line3d const & line, Sphere const & sphere ); +Range IntersectLineABox ( Line3d const & line, AxialBox const & box ); +Range IntersectLineCylinder ( Line3d const & line, Cylinder const & cylinder ); +Range IntersectLineTorus ( Line3d const & line, Torus const & torus ); +Range IntersectLineShape ( Line3d const & line, MultiShape const & shape ); + +Range IntersectRaySphere ( Ray3d const & ray, Sphere const & sphere ); +Range IntersectRayABox ( Ray3d const & ray, AxialBox const & box ); +Range IntersectRayCylinder ( Ray3d const & ray, Cylinder const & cylinder ); +Range IntersectRayTorus ( Ray3d const & ray, Torus const & torus ); +Range IntersectRayShape ( Ray3d const & ray, MultiShape const & shape ); + +Range IntersectSegSphere ( Segment3d const & seg, Sphere const & sphere ); +Range IntersectSegABox ( Segment3d const & seg, AxialBox const & box ); +Range IntersectSegCylinder ( Segment3d const & seg, Cylinder const & cylinder ); +Range IntersectSegTorus ( Segment3d const & seg, Torus const & torus ); +Range IntersectSegShape ( Segment3d const & seg, MultiShape const & shape ); + +// ---------- + +struct ResultData +{ + ResultData(); + explicit ResultData(bool const getSurfaceNormal); + bool const m_getSurfaceNormal; + Vector m_surfaceNormal; + float m_length; +private: + ResultData(ResultData const & copy); + ResultData & operator = (ResultData const & copy); + +}; + +bool intersectRaySphereWithData(Ray3d const & ray, Sphere const &, ResultData * const); +bool intersectRayABoxWithData(Ray3d const & ray, AxialBox const &, ResultData * const); +bool intersectRayOBoxWithData(Ray3d const & ray, OrientedBox const &, ResultData * const); +bool intersectRayCylinderWithData(Ray3d const & ray, Cylinder const &, ResultData * const); +bool intersectRayOCylinderWithData(Ray3d const & ray, OrientedCylinder const &, ResultData * const); +bool intersectRayShapeWithData(Ray3d const & ray, MultiShape const &, ResultData * const); +bool intersectTriangleBox(AxialBox const & box, Vector const & point1, Vector const & point2, Vector const & point3); + +// ---------- +// Generic aliases + +inline Range Intersect ( Line3d const & A, Sphere const & B ) { return IntersectLineSphere(A,B); } +inline Range Intersect ( Line3d const & A, AxialBox const & B ) { return IntersectLineABox(A,B); } +inline Range Intersect ( Line3d const & A, Cylinder const & B ) { return IntersectLineCylinder(A,B); } +inline Range Intersect ( Line3d const & A, Torus const & B ) { return IntersectLineTorus(A,B); } +inline Range Intersect ( Line3d const & A, MultiShape const & B ) { return IntersectLineShape(A,B); } + +inline Range Intersect ( Ray3d const & A, Sphere const & B ) { return IntersectRaySphere(A,B); } +inline Range Intersect ( Ray3d const & A, AxialBox const & B ) { return IntersectRayABox(A,B); } +inline Range Intersect ( Ray3d const & A, Cylinder const & B ) { return IntersectRayCylinder(A,B); } +inline Range Intersect ( Ray3d const & A, Torus const & B ) { return IntersectRayTorus(A,B); } +inline Range Intersect ( Ray3d const & A, MultiShape const & B ) { return IntersectRayShape(A,B); } + +inline Range Intersect ( Segment3d const & A, Sphere const & B ) { return IntersectSegSphere(A,B); } +inline Range Intersect ( Segment3d const & A, Cylinder const & B ) { return IntersectSegCylinder(A,B); } +inline Range Intersect ( Segment3d const & A, AxialBox const & B ) { return IntersectSegABox(A,B); } +inline Range Intersect ( Segment3d const & A, Torus const & B ) { return IntersectSegTorus(A,B); } +inline Range Intersect ( Segment3d const & A, MultiShape const & B ) { return IntersectSegShape(A,B); } + +// ---------------------------------------------------------------------- +// Motion tests - when does A, moving with velocity V, hit B? + +Range IntersectPointPlane ( Vector const & A, Vector const & V, Plane3d const & B ); +Range IntersectPointABox ( Vector const & A, Vector const & V, AxialBox const & B ); +Range IntersectPointSphere ( Vector const & A, Vector const & V, Sphere const & B ); + +Range IntersectLineLine ( Line3d const & A, Vector const & V, Line3d const & B ); +Range IntersectLineRay ( Line3d const & A, Vector const & V, Ray3d const & B ); +Range IntersectLineSeg ( Line3d const & A, Vector const & V, Segment3d const & B ); + +Range IntersectSegPlane ( Segment3d const & A, Vector const & V, Plane3d const & B ); +Range IntersectSegTri ( Segment3d const & A, Vector const & V,Triangle3d const & B ); + +Range IntersectRayTri ( Ray3d const & A, Vector const & V, Triangle3d const & B ); + +Range IntersectSpherePoint ( Sphere const & A, Vector const & V, Vector const & B ); +Range IntersectSphereLine ( Sphere const & A, Vector const & V, Line3d const & B ); +Range IntersectSphereXLine ( Sphere const & A, Vector const & V, Vector const & B ); // Axis-aligned lines passing through point B +Range IntersectSphereYLine ( Sphere const & A, Vector const & V, Vector const & B ); +Range IntersectSphereZLine ( Sphere const & A, Vector const & V, Vector const & B ); +Range IntersectSphereSeg ( Sphere const & A, Vector const & V, Line3d const & B ); +Range IntersectSpherePlane ( Sphere const & A, Vector const & V, Plane3d const & B ); +Range IntersectSphereRing ( Sphere const & A, Vector const & V, Ring const & B ); +Range IntersectSphereSphere ( Sphere const & A, Vector const & V, Sphere const & B ); +Range IntersectSphereCylinder ( Sphere const & A, Vector const & V, Cylinder const & B ); +Range IntersectSphereOCylinder( Sphere const & A, Vector const & V, OrientedCylinder const & B ); +Range IntersectSphereABox ( Sphere const & A, Vector const & V, AxialBox const & B ); +Range IntersectSphereYBox ( Sphere const & A, Vector const & V, YawedBox const & B ); +Range IntersectSphereOBox ( Sphere const & A, Vector const & V, OrientedBox const & B ); + +Range IntersectABoxABox ( AxialBox const & A, Vector const & V, AxialBox const & B ); + +Range IntersectMultiShapes ( MultiShape const & A, Vector const & V, MultiShape const & B ); + +Range IntersectCircleSeg_Below ( Circle const & A, Vector const & V, Segment3d const & S ); + +// ---------------------------------------------------------------------- + +inline Range Intersect ( Sphere const & A, Vector const & V, Sphere const & B ) { return IntersectSphereSphere(A,V,B); } +inline Range Intersect ( Sphere const & A, Vector const & V, Cylinder const & B ) { return IntersectSphereCylinder(A,V,B); } +inline Range Intersect ( Sphere const & A, Vector const & V, OrientedCylinder const & B){ return IntersectSphereOCylinder(A,V,B); } +inline Range Intersect ( Sphere const & A, Vector const & V, AxialBox const & B ) { return IntersectSphereABox(A,V,B); } +inline Range Intersect ( Sphere const & A, Vector const & V, YawedBox const & B ) { return IntersectSphereYBox(A,V,B); } +inline Range Intersect ( Sphere const & A, Vector const & V, OrientedBox const & B ) { return IntersectSphereOBox(A,V,B); } + +inline Range Intersect ( MultiShape const & A, Vector const & V, MultiShape const & B ) { return IntersectMultiShapes(A,V,B); } + +// ---------------------------------------------------------------------- + +}; // namespace Collision3d + +#endif + diff --git a/engine/shared/library/sharedCollision/src/shared/core/MultiList.cpp b/engine/shared/library/sharedCollision/src/shared/core/MultiList.cpp new file mode 100644 index 00000000..3917cd4d --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/MultiList.cpp @@ -0,0 +1,579 @@ +// ====================================================================== +// +// MultiList.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/MultiList.h" +#include "sharedCollision/BaseClass.h" + +// ====================================================================== + +MultiListHandle::MultiListHandle( BaseClass * object, int color ) +: m_color(color), + m_object(object), + m_head(NULL), + m_tail(NULL) +{ +} + +MultiListHandle::~MultiListHandle() +{ + IGNORE_RETURN( detach() ); + + m_object = NULL; + m_head = NULL; + m_tail = NULL; +} + +// ---------------------------------------------------------------------- + +MultiListNode * MultiListHandle::getHead ( void ) +{ + return m_head; +} + +MultiListNode const * MultiListHandle::getHead ( void ) const +{ + return m_head; +} + +int MultiListHandle::getColor ( void ) const +{ + return m_color; +} + +// ---------------------------------------------------------------------- + +MultiListNode * MultiListHandle::detachHead ( void ) +{ + if(m_head) + { + return m_head->detach(); + } + else + { + return NULL; + } +} + +// ---------- + +MultiListHandle * MultiListHandle::detach ( void ) +{ + destroyNodes(); + + return this; +} + +// ---------- + +bool MultiListHandle::isConnected ( void ) const +{ + return m_head != NULL; +} + +// ---------- + +void MultiListHandle::clear ( void ) +{ + IGNORE_RETURN( detach() ); +} + +// ---------- + +void MultiListHandle::erase ( MultiListNode * node ) +{ + DEBUG_FATAL(node->m_list[m_color] != this, ("MultiListHandle::erase - Node is not part of this list")); + + IGNORE_RETURN( node->detach() ); + + destroyNode(node); +} //lint !e1762 // function could be const + +// ---------- + +bool MultiListHandle::isEmpty ( void ) const +{ + return m_head == NULL; +} + +// ---------- + +void MultiListHandle::insert( MultiListNode * node, MultiListNode * prev, MultiListNode * next ) +{ + // verify that the nodes are all distinct + DEBUG_FATAL( prev == node, ("MultiListHandle::insert - Prev and Node are the same")); + DEBUG_FATAL( next == node, ("MultiListHandle::insert - Next and node are the same ")); + DEBUG_FATAL( (prev || next) && (prev == next), ("MultiListHandle::insert - Prev and Next are the same")); + + // verify that the node to insert is not null + DEBUG_FATAL( node == NULL, ("MultiListHandle::insert - Cannot insert null node")); + + // verify that the nodes we're inserting between are adjacent + DEBUG_FATAL( prev && (prev->m_next[m_color] != next), ("MultiListHandle::insert - Prev and Next are not adjacent")); + DEBUG_FATAL( next && (next->m_prev[m_color] != prev), ("MultiListHandle::insert - Prev and Next are not adjacent")); + + // and are a part of this list + DEBUG_FATAL( prev && (prev->m_list[m_color] != this), ("MultiListHandle::insert - Prev is not part of this list")); + DEBUG_FATAL( next && (next->m_list[m_color] != this), ("MultiListHandle::insert - Next is not part of this list")); + + NOT_NULL(node); + + // ---------- + + node->detach(m_color); + + node->m_prev[m_color] = prev; + node->m_next[m_color] = next; + + if(prev) prev->m_next[m_color] = node; + if(next) next->m_prev[m_color] = node; + + node->m_list[m_color] = this; + + if(m_head == next) m_head = node; + if(m_tail == prev) m_tail = node; +} + +// ---------- + +void MultiListHandle::connectTo( MultiListHandle & handle, BaseClass * data ) +{ + DEBUG_FATAL( m_color == handle.m_color, ("MultiListHandle::connectTo - Cannot connect two handles of the same color\n")); + + MultiListNode * newNode = createNode(data); + + insert(newNode); + + handle.insert(newNode); +} + +// ---------- + +void MultiListHandle::insert( MultiListNode * node ) +{ + insert( node, NULL, m_head ); +} + +// ---------- + +MultiListNode * MultiListHandle::detach ( MultiListNode * node ) +{ + if(node) + { + MultiListNode * next = node->m_next[m_color]; + MultiListNode * prev = node->m_prev[m_color]; + + if( next ) next->m_prev[m_color] = prev; + if( prev ) prev->m_next[m_color] = next; + + if( m_head == node ) m_head = next; + if( m_tail == node ) m_tail = prev; + + // ---------- + + node->m_next[m_color] = NULL; + node->m_prev[m_color] = NULL; + node->m_list[m_color] = NULL; + } + + return node; +} + +// ---------- + +bool MultiListHandle::disconnect( MultiListHandle & handle ) +{ + MultiListNode * deadNode = find(handle); + + if(deadNode) + { + IGNORE_RETURN( deadNode->detach() ); + + destroyNode(deadNode); + + return true; + } + else + { + return false; + } +} + +// ---------- + +MultiListNode * MultiListHandle::find ( MultiListHandle & testHandle ) +{ + MultiListNode * cursor = m_head; + + while(cursor) + { + MultiListHandle * handle = cursor->m_list[testHandle.m_color]; + + if(handle == &testHandle) + { + return cursor; + } + + cursor = cursor->m_next[m_color]; + } + + return NULL; +} //lint !e1764 // testHandle could be made const ref + +MultiListNode const * MultiListHandle::find ( MultiListHandle const & testHandle ) const +{ + MultiListNode const * cursor = m_head; + + while(cursor) + { + MultiListHandle const * handle = cursor->m_list[testHandle.m_color]; + + if(handle == &testHandle) + { + return cursor; + } + + cursor = cursor->m_next[m_color]; + } + + return NULL; +} + +// ---------- + +BaseClass * MultiListHandle::getObject ( void ) +{ + return m_object; +} + +BaseClass const * MultiListHandle::getObject ( void ) const +{ + return m_object; +} + +// ---------------------------------------------------------------------- + +MultiListNode * MultiListHandle::createNode ( BaseClass * data ) const +{ + MultiListNode * newNode = new MultiListNode(); + + newNode->setData(data); + + return newNode; +} + +// ---------- + +void MultiListHandle::destroyNode( MultiListNode * node ) const +{ + delete node; +} + +// ---------- + +void MultiListHandle::destroyNodes ( void ) +{ + while(m_head) + { + MultiListNode * deadNode = m_head->detach(); + + destroyNode(deadNode); + } +} + +// ====================================================================== + +MultiListNode::MultiListNode() +: m_data(NULL) +{ + for(int i = 0; i < nColors; i++) + { + m_next[i] = NULL; + m_prev[i] = NULL; + m_list[i] = NULL; + } +} + +MultiListNode::~MultiListNode() +{ + IGNORE_RETURN( detach() ); + + BaseClass * data = m_data; + m_data = NULL; + delete data; +} + +// ---------- + +MultiListNode * MultiListNode::detach ( void ) +{ + for(int i = 0; i < nColors; i++) + { + detach(i); + } + + return this; +} + +// ---------- + +void MultiListNode::detach ( int color ) +{ + if(m_list[color]) + IGNORE_RETURN( m_list[color]->detach(this) ); +} + +// ---------- + +MultiListNode * MultiListNode::getNext ( int color ) +{ + return m_next[color]; +} //lint !e1762 // function could be const + +MultiListNode const * MultiListNode::getNext ( int color ) const +{ + return m_next[color]; +} + +// ---------- + +BaseClass * MultiListNode::getObject ( int color ) +{ + if(m_list[color]) + return m_list[color]->getObject(); + else + return NULL; +} //lint !e1762 // function could be const + + +BaseClass const * MultiListNode::getObject ( int color ) const +{ + if(m_list[color]) + return m_list[color]->getObject(); + else + return NULL; +} + +// ---------- + +BaseClass * MultiListNode::getData ( void ) +{ + return m_data; +} + +BaseClass const * MultiListNode::getData ( void ) const +{ + return m_data; +} + +void MultiListNode::setData( BaseClass * data ) +{ + if(m_data == data) return; + + BaseClass * deadData = m_data; + m_data = NULL; + delete deadData; + m_data = data; +} + +// ---------------------------------------------------------------------- + +void MultiListNode::swapNext ( int color ) +{ + if(!m_next[color]) return; + + MultiListNode * A = this; + MultiListNode * B = m_next[color]; + + MultiListNode * prev = A->m_prev[color]; + MultiListNode * next = B->m_next[color]; + + B->m_prev[color] = prev; + B->m_next[color] = A; + + A->m_prev[color] = B; + A->m_next[color] = next; +} + +// ====================================================================== + +/* +MultiList::MultiList() +: m_free(0,NULL) +{ +} + +// ---------- + +bool MultiList::connect ( MultiListHandle * red, MultiListHandle * blue ) +{ + return connect(red,blue,NULL); +} + +// ---------- + +bool MultiList::connect ( MultiListHandle & red, MultiListHandle & blue, BaseClass * data ) +{ + DEBUG_FATAL( red.m_color == blue.m_color, ("MultiList::connect - Cannot connect two handles of the same color")); + + // ---------- + + if(connected(red,blue)) return false; + + insert(red); + insert(blue); + + red.connectTo(blue); + + return true; +} + +// ---------- + +bool MultiList::connected( MultiListHandle & red, MultiListHandle & blue ) +{ + return red.find(blue) != NULL; +} + +// ---------- + +bool MultiList::disconnect( MultiListHandle & red, MultiListHandle & blue ) +{ + return red.disconnect(blue); +} + +// ---------- + +MultiListNode * MultiList::getFreeNode ( void ) +{ + if(m_free.m_head == NULL) + { + return new MultiListNode(); + } + else + { + return m_free.detachHead(); + } +} + +// ---------- + +void MultiList::addFreeNode( MultiListNode * freeNode ) +{ + freeNode->detach(); + + m_free.insert(freeNode); +} + +// ---------- + +void MultiList::insert( MultiListNode * node, MultiListNode * prev, MultiListNode * next, int color ) +{ + DEBUG_FATAL( prev == node, ("MultiList::insert - Prev and Node are the same")); + DEBUG_FATAL( next == node, ("MultiList::insert - Next and Node are the same")); + DEBUG_FATAL( prev && (prev->m_next[color] != next), ("MultiList::insert - Prev and Next are not adjacent")); + DEBUG_FATAL( next && (next->m_prev[color] != prev), ("MultiList::insert - Prev and Next are not adjacent")); + + node->detach(); + + if(prev) + { + prev->m_next[color] = node; + node->m_prev[color] = prev; + } + + if(next) + { + next->m_prev[color] = node; + node->m_next[color] = next; + } +} + +// ---------- + +void MultiList::insert( MultiListHandle * node, MultiListHandle * prev, MultiListHandle * next ) +{ + // verify that the nodes are all distinct + DEBUG_FATAL( prev == node, ("MultiList::insert - Prev and Node are the same")); + DEBUG_FATAL( next == node, ("MultiList::insert - Next and Node are the same")); + DEBUG_FATAL( prev == next, ("MultiList::insert - Prev and Next are the same")); + + // verify that the node to insert is not null + DEBUG_FATAL( node == NULL, ("MultiList::insert - Cannot insert a null node")); + + // verify that the two nodes are adjacent + DEBUG_FATAL( prev && (prev->m_next != next), ("MultiList::insert - Prev and Next are not adjacent")); + DEBUG_FATAL( next && (next->m_prev != prev), ("MultiList::insert - Prev and Next are not adjacent")); + + // and are a part of this list + DEBUG_FATAL( prev && (prev->m_list != this), ("MultiList::insert - Prev is not part of this list")); + DEBUG_FATAL( next && (next->m_list != this), ("MultiList::insert - Next is not part of this list")); + + // and are the same color as the node + DEBUG_FATAL( prev && (prev->m_color != node->m_color), ("MultiList::insert - Prev and Node are different colors")); + DEBUG_FATAL( next && (next->m_color != node->m_color), ("MultiList::insert - Next and Node are different colors")); + + // ---------- + + node->detach(); + + node->m_prev = prev; + node->m_next = next; + + if(prev) prev->m_next = node; + if(next) next->m_prev = node; + + node->m_list = this; + + int color = node->m_color; + + if(m_head[color] == next) m_head[color] = node; + if(m_tail[color] == prev) m_tail[color] = node; +} + +// ---------- + +MultiListHandle * MultiList::detach( MultiListHandle * node ) +{ + DEBUG_FATAL( node == NULL, ("MultiList::detach - Cannot detach null node")); + DEBUG_FATAL( node->m_list != this, ("MultiList::detach - Node is not part of this list")); + + // ---------- + + int color = node->m_color; + + MultiListHandle * next = node->m_next; + MultiListHandle * prev = node->m_prev; + + if(next) next->m_prev = prev; + if(prev) prev->m_next = next; + + if(m_head[color] == node) m_head[color] = node->m_next; + if(m_tail[color] == node) m_tail[color] = node->m_prev; + + node->m_prev = NULL; + node->m_next = NULL; + node->m_list = NULL; + + return node; +} + +// ---------- + +void MultiList::insert( MultiListHandle * node ) +{ + DEBUG_FATAL(node == NULL, ("MultiList::insert - Cannot insert NULL node")); + + // ---------- + + int color = node->m_color; + + insert(node,NULL,m_head[color]); +} +*/ + +// ====================================================================== diff --git a/engine/shared/library/sharedCollision/src/shared/core/MultiList.h b/engine/shared/library/sharedCollision/src/shared/core/MultiList.h new file mode 100644 index 00000000..48c5e92d --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/MultiList.h @@ -0,0 +1,376 @@ +// ====================================================================== +// +// MultiList.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_MultiList_H +#define INCLUDED_MultiList_H + +class MultiListNode; +class MultiListHandle; +class BaseClass; + +// ====================================================================== + +class MultiListHandle +{ +public: + + MultiListHandle( BaseClass * object, int color ); + virtual ~MultiListHandle(); + + // ---------- + + MultiListNode * getHead ( void ); + MultiListNode const * getHead ( void ) const; + int getColor ( void ) const; + + BaseClass * getObject ( void ); + BaseClass const * getObject ( void ) const; + + // ---------- + + void connectTo ( MultiListHandle & handle, BaseClass * data ); + bool disconnect ( MultiListHandle & handle ); + bool isConnected ( void ) const; + + // ---------- + + void erase ( MultiListNode * node ); + void clear ( void ); + + // ---------- + + bool isEmpty ( void ) const; + + MultiListNode * find ( MultiListHandle & testHandle ); + MultiListNode const * find ( MultiListHandle const & testHandle ) const; + +protected: + + // ---------- + + MultiListNode * detachHead ( void ); + MultiListHandle * detach ( void ); + + // ---------- + + MultiListNode * detach ( MultiListNode * node ); + + void insert ( MultiListNode * node ); + + void insert ( MultiListNode * node, + MultiListNode * prev, + MultiListNode * next ); + + // ---------- + + MultiListNode * createNode ( BaseClass * data ) const; + void destroyNode ( MultiListNode * node ) const; + void destroyNodes ( void ); + + // ---------- + +private: + + friend class MultiListNode; + + // ---------- + + int m_color; + BaseClass * m_object; + + MultiListNode * m_head; + MultiListNode * m_tail; + + MultiListHandle( MultiListHandle const & copy ); + MultiListHandle & operator = ( MultiListHandle const & copy ); +}; + +// ---------------------------------------------------------------------- +// Multi-dimensional list node with backpointers + +class MultiListNode +{ +public: + + MultiListNode(); + virtual ~MultiListNode(); + + // ---------- + + MultiListNode * getNext ( int color ); + MultiListNode const * getNext ( int color ) const; + + BaseClass * getObject ( int color ); + BaseClass const * getObject ( int color ) const; + + BaseClass * getData ( void ); + BaseClass const * getData ( void ) const; + + void swapNext ( int color ); + +protected: + + MultiListNode * detach ( void ); + void detach ( int color ); + + void insert ( MultiListNode * node, + MultiListNode * prev, + MultiListNode * next, + int color ); + + void clear ( void ); + + void setData ( BaseClass * data ); + + // ---------- + + friend class MultiListHandle; + + enum + { + nColors = 2 + }; + + MultiListNode * m_next[nColors]; + MultiListNode * m_prev[nColors]; + MultiListHandle * m_list[nColors]; + + BaseClass * m_data; + + // ---------- + +private: + + MultiListNode( MultiListNode const & copy ); + MultiListNode & operator = ( MultiListNode const & copy ); +}; + +// ---------------------------------------------------------------------- + +template +class MultiListConstIterator +{ +public: + + MultiListConstIterator( MultiListHandle const & handle ) + { + m_node = handle.getHead(); + m_iterateColor = handle.getColor(); + m_valueColor = (m_iterateColor == 0) ? 1 : 0; + } + + T const * operator * ( void ) + { + if(m_node) + return safe_cast(m_node->getObject(m_valueColor)); + else + return NULL; + } + + operator bool ( void ) const + { + return m_node != NULL; + } + + void operator ++ ( void ) + { + if(m_node) + m_node = m_node->getNext(m_iterateColor); + } + +private: + + MultiListNode const * m_node; + int m_iterateColor; + int m_valueColor; + + MultiListConstIterator(); + MultiListConstIterator( MultiListConstIterator const & copy ); + MultiListConstIterator & operator = ( MultiListConstIterator const & copy ); +}; + +// ---------------------------------------------------------------------- + +template +class MultiListIterator +{ +public: + + MultiListIterator( MultiListHandle & handle ) + { + m_node = handle.getHead(); + m_iterateColor = handle.getColor(); + m_valueColor = (m_iterateColor == 0) ? 1 : 0; + } + + T * operator * ( void ) + { + if(m_node) + return safe_cast(m_node->getObject(m_valueColor)); + else + return NULL; + } + + operator bool ( void ) const + { + return m_node != NULL; + } + + void operator ++ ( void ) + { + if(m_node) + m_node = m_node->getNext(m_iterateColor); + } + +private: + + MultiListNode * m_node; + int m_iterateColor; + int m_valueColor; + + MultiListIterator(); + MultiListIterator( MultiListIterator const & copy ); + MultiListIterator & operator = ( MultiListIterator const & copy ); +}; + +// ---------------------------------------------------------------------- + +template +class MultiListConstDataIterator +{ +public: + + MultiListConstDataIterator( void ) + { + m_node = NULL; + m_color = -1; + } + + MultiListConstDataIterator( MultiListHandle const & handle ) + { + m_node = handle.getHead(); + m_color = handle.getColor(); + } + + MultiListConstDataIterator & operator = ( MultiListHandle const & handle ) + { + m_node = handle.getHead(); + m_color = handle.getColor(); + + return *this; + } + + MultiListConstDataIterator ( MultiListConstDataIterator const & it ) + { + m_node = it.m_node; + m_color = it.m_color; + } + + MultiListConstDataIterator & operator = ( MultiListConstDataIterator const & it ) + { + m_node = it.m_node; + m_color = it.m_color; + } + + T const * operator * ( void ) const + { + if(m_node) + return safe_cast(m_node->getData()); + else + return NULL; + } + + operator bool ( void ) const + { + return m_node != NULL; + } + + void operator ++ ( void ) + { + if(m_node) + m_node = m_node->getNext(m_color); + } + +private: + + MultiListNode const * m_node; + int m_color; +}; + +// ---------------------------------------------------------------------- + +template +class MultiListDataIterator +{ +public: + + MultiListDataIterator( void ) + { + m_node = NULL; + m_color = -1; + } + + MultiListDataIterator( MultiListHandle & handle ) + { + m_node = handle.getHead(); + m_color = handle.getColor(); + } + + MultiListDataIterator & operator = ( MultiListHandle & handle ) + { + m_node = handle.getHead(); + m_color = handle.getColor(); + + return *this; + } + + MultiListDataIterator ( MultiListDataIterator const & it ) + { + m_node = it.m_node; + m_color = it.m_color; + } + + MultiListDataIterator & operator = ( MultiListDataIterator const & it ) + { + m_node = it.m_node; + m_color = it.m_color; + } + + T * operator * ( void ) const + { + if(m_node) + return safe_cast(m_node->getData()); + else + return NULL; + } + + operator bool ( void ) const + { + return m_node != NULL; + } + + void operator ++ ( void ) + { + if(m_node) + m_node = m_node->getNext(m_color); + } + + MultiListNode * getNode ( void ) + { + return m_node; + } + +private: + + MultiListNode * m_node; + int m_color; +}; + +// ====================================================================== + +#endif // #ifndef INCLUDED_MultiList_H + diff --git a/engine/shared/library/sharedCollision/src/shared/core/NeighborObject.cpp b/engine/shared/library/sharedCollision/src/shared/core/NeighborObject.cpp new file mode 100644 index 00000000..a5a0f37d --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/NeighborObject.cpp @@ -0,0 +1,28 @@ +// ====================================================================== +// +// NeighborObject.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/NeighborObject.h" + + +// ---------------------------------------------------------------------- + +NeighborObject::NeighborObject() +: m_neighbor(NULL) +{ +} + +void NeighborObject::setNeighbor ( NeighborObject * neighbor ) +{ + m_neighbor = neighbor; +} + +bool NeighborObject::shouldRender ( void ) const +{ + return true; +} + diff --git a/engine/shared/library/sharedCollision/src/shared/core/NeighborObject.h b/engine/shared/library/sharedCollision/src/shared/core/NeighborObject.h new file mode 100644 index 00000000..295a2685 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/NeighborObject.h @@ -0,0 +1,37 @@ +// ====================================================================== +// +// NeighborObject.h +// Copyright 2002 Sony Online Entertainment +// All Rights Reserved. +// +// ====================================================================== + +#ifndef INCLUDED_NeighborObject_H +#define INCLUDED_NeighborObject_H + +#include "sharedObject/Object.h" + +// ---------------------------------------------------------------------- + +class NeighborObject : public Object +{ +public: + + NeighborObject(); + + virtual void setNeighbor ( NeighborObject * neighbor ); + virtual bool shouldRender ( void ) const; + +protected: + + NeighborObject * m_neighbor; + +private: + + NeighborObject( NeighborObject const & copy ); + NeighborObject & operator = ( NeighborObject const & copy ); +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/Overlap2d.cpp b/engine/shared/library/sharedCollision/src/shared/core/Overlap2d.cpp new file mode 100644 index 00000000..41954436 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Overlap2d.cpp @@ -0,0 +1,92 @@ +// ====================================================================== +// +// Intersect2d.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/Intersect2d.h" + +#include "sharedMath/Line3d.h" +#include "sharedMath/Segment3d.h" +#include "sharedMath/Circle.h" +#include "sharedMath/Range.h" +#include "sharedMath/AxialBox.h" +#include "sharedMath/Triangle3d.h" + +#include "sharedCollision/Intersect1d.h" +#include "sharedCollision/Distance2d.h" +#include "sharedCollision/Containment2d.h" + +namespace Overlap2d +{ + +// ---------------------------------------------------------------------- + +bool TestPointCircle ( Vector const & V, Circle const & C ) +{ + return Distance2d::Distance2PointPoint( C.getCenter(), V ) <= C.getRadiusSquared(); +} + +bool TestLineCircle ( Line3d const & L, Circle const & C ) +{ + return Distance2d::Distance2PointLine( C.getCenter(), L ) <= C.getRadiusSquared(); +} + +bool TestCircleCircle ( Circle const & A, Circle const & B ) +{ + float dist2 = Distance2d::Distance2PointPoint( A.getCenter(), B.getCenter() ); + + float rad = A.getRadius() * B.getRadius(); + + float rad2 = rad * rad; + + return dist2 <= rad2; +} + +bool TestSegCircle ( Segment3d const & S, Circle const & C ) +{ + return Distance2d::Distance2PointSeg( C.getCenter(), S ) <= C.getRadiusSquared(); +} + +// ---------------------------------------------------------------------- + +bool TestSegABox ( Segment3d const & S, AxialBox const & B ) +{ + Vector const & P = S.getBegin(); + Vector const & V = S.getDelta(); + + Range temp = Range::unit; + + Range timeX = Intersect1d::IntersectFloatRange( P.x, V.x, B.getRangeX() ); + + temp = Intersect1d::IntersectRanges(temp,timeX); + + if(temp.isEmpty()) return false; + + Range timeZ = Intersect1d::IntersectFloatRange( P.z, V.z, B.getRangeZ() ); + + temp = Intersect1d::IntersectRanges(temp,timeZ); + + return !temp.isEmpty(); +} + +// ---------------------------------------------------------------------- + +bool TestPointTri ( Vector const & P, Triangle3d const & T ) +{ + Vector const & A = T.getCornerA(); + Vector const & B = T.getCornerB(); + Vector const & C = T.getCornerC(); + + if(Containment2d::TestPointSeg(P,A,B) == CR_Outside) return false; + if(Containment2d::TestPointSeg(P,B,C) == CR_Outside) return false; + if(Containment2d::TestPointSeg(P,C,A) == CR_Outside) return false; + + return true; +} + +// ---------------------------------------------------------------------- + +} // namespace Overlap2d diff --git a/engine/shared/library/sharedCollision/src/shared/core/Overlap2d.h b/engine/shared/library/sharedCollision/src/shared/core/Overlap2d.h new file mode 100644 index 00000000..2530b55f --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Overlap2d.h @@ -0,0 +1,37 @@ +// ====================================================================== +// +// Overlap2d.h +// copyright (c) 2001 Sony Online Entertainment +// +// ---------------------------------------------------------------------- + +#ifndef INCLUDED_Overlap2d_H +#define INCLUDED_Overlap2d_H + +class Line3d; +class Segment3d; +class Circle; +class AxialBox; + +namespace Overlap2d +{ + +// ---------------------------------------------------------------------- + +bool TestPointCircle ( Vector const & V, Circle const & C ); +bool TestLineCircle ( Line3d const & S, Circle const & C ); +bool TestCircleCircle ( Circle const & A, Circle const & B ); +bool TestSegCircle ( Segment3d const & S, Circle const & C ); +bool TestSegABox ( Segment3d const & S, AxialBox const & B ); +bool TestPointTri ( Vector const & V, Triangle3d const & T ); +bool TestSegSeg ( Vector const & A, Vector const & B1, Vector const & A2, Vector const & B2 ); + +inline bool Test ( Segment3d const & S, AxialBox const & B ) { return TestSegABox(S,B); } +inline bool Test ( Segment3d const & S, Circle const & C ) { return TestSegCircle(S,C); } + +// ---------------------------------------------------------------------- + +} // namespace Overlap2d + +#endif + diff --git a/engine/shared/library/sharedCollision/src/shared/core/Overlap3d.cpp b/engine/shared/library/sharedCollision/src/shared/core/Overlap3d.cpp new file mode 100644 index 00000000..d5106836 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Overlap3d.cpp @@ -0,0 +1,761 @@ +// ====================================================================== +// +// Overlap3d.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/Overlap3d.h" + +#include "sharedCollision/CollisionUtils.h" + +#include "sharedCollision/Containment1d.h" + +#include "sharedCollision/Containment3d.h" +#include "sharedCollision/Intersect1d.h" +#include "sharedCollision/Intersect3d.h" +#include "sharedCollision/Distance2d.h" +#include "sharedCollision/Distance3d.h" +#include "sharedCollision/Overlap2d.h" + +#include "sharedMath/Triangle3d.h" +#include "sharedMath/AxialBox.h" +#include "sharedMath/YawedBox.h" +#include "sharedMath/Line3d.h" +#include "sharedMath/Segment3d.h" +#include "sharedMath/Ray3d.h" +#include "sharedMath/Plane3d.h" +#include "sharedMath/Ribbon3d.h" +#include "sharedMath/OrientedBox.h" +#include "sharedMath/Circle.h" +#include "sharedMath/Sphere.h" +#include "sharedMath/Cylinder.h" +#include "sharedMath/Vector.h" +#include "sharedMath/MultiShape.h" + + +#include // for swap, max, min + +using namespace Collision3d; +using namespace Intersect1d; + +// ====================================================================== + +namespace Overlap3d +{ + +// ---------------------------------------------------------------------- + +HitResult TestSegPlane ( Segment3d const & S, Plane3d const & P ) +{ + ContainmentResult sideA = Containment3d::TestPointPlane( S.getBegin(), P ); + ContainmentResult sideB = Containment3d::TestPointPlane( S.getEnd(), P ); + + if(sideA == sideB) + { + return HR_Miss; //lint !e731 + } + else + { + return (sideA == CR_Outside) ? HR_HitFront : HR_HitBack; + } +} + +// ---------------------------------------------------------------------- + +// This table translates the sum of the hit results for each edge into +// a hit result for the triangle + +static HitResult returnTable[13] = +{ + HR_Miss, // 000 = 0 - degenerate, hits all three edges + HR_HitFront, // 001 = 1 - hit corner from the front + HR_HitFront, // 011 = 2 - hit edge from the front + HR_HitFront, // 111 = 3 - hit front + HR_HitBack, // 004 = 4 - hit corner from the back + HR_Miss, // 014 = 5 - miss + HR_Miss, // 114 = 6 - miss + HR_Miss, // XXX = 7 - should never happen + HR_HitBack, // 044 = 8 - hit edge from the back + HR_Miss, // 144 = 9 - miss + HR_Miss, // XXX = 10 - should never happen + HR_Miss, // XXX = 11 - should never happen + HR_HitBack, // 444 = 12 - hit the back +}; + +HitResult TestLineTri ( Line3d const & L, Triangle3d const & T ) +{ + int a = TestLineSegTwist( L, T.getEdgeSegment0() ); + int b = TestLineSegTwist( L, T.getEdgeSegment1() ); + int c = TestLineSegTwist( L, T.getEdgeSegment2() ); + + if(a == -1) a = 4; + if(b == -1) b = 4; + if(c == -1) c = 4; + + return returnTable[a+b+c]; +} + +// ---------------------------------------------------------------------- + +HitResult TestRayTri ( Ray3d const & R, Triangle3d const & T ) +{ + int whichSideHit = TestLineTri( R.getLine(), T ); + + if(whichSideHit == HR_Miss) return HR_Miss; + + // ---------- + + ContainmentResult sideFlag = Containment3d::TestPointPlane(R.getPoint(),T.getPlane()); + + if((whichSideHit == HR_HitFront) && (sideFlag == CR_Outside)) + { + // Front hit by line and ray origin is in front of the tri - front hit + return HR_HitFront; + } + else if((whichSideHit == HR_HitBack) && (sideFlag == CR_Inside)) + { + // Back hit by line and ray origin is behind tri - back hit + return HR_HitBack; + } + else + { + // No hit + return HR_Miss; + } +} + +// ---------------------------------------------------------------------- + +HitResult TestSegTri ( Segment3d const & S, Triangle3d const & T ) +{ + Plane3d P = T.getPlane(); + + if(TestSegPlane(S,P) == HR_Miss) return HR_Miss; + + return TestLineTri(S.getLine(),T); +} + +// ---------------------------------------------------------------------- +// A line intersects a ribbon if it twists around the edges of the ribbon +// in different directions + + +HitResult TestLineRibbon ( Line3d const & L, Ribbon3d const & R ) +{ + int A = TestLineLineTwist(L,R.getEdgeA()); + int B = TestLineLineTwist(L,R.getEdgeB()); + + HitResult result; + + if(A == 0) + { + if(B == 0) result = HR_Miss; + else if(B == 1) result = HR_HitFront; + else result = HR_HitBack; + } + else if(A == 1) + { + if(B == 0) result = HR_HitBack; + else if(B == 1) result = HR_Miss; + else result = HR_HitBack; + } + else + { + if(B == 0) result = HR_HitFront; + else if(B == 1) result = HR_HitFront; + else result = HR_Miss; + } + + return result; +} + +// ---------------------------------------------------------------------- +// A segment overlaps a ribbon if it overlaps the ribbon's plane and +// the line through it overlaps the ribbon. + +HitResult TestSegRibbon ( Segment3d const & S, Ribbon3d const & R ) +{ + if(TestLineRibbon(S.getLine(),R) == HR_Miss) return HR_Miss; + + return TestSegPlane(S,R.getPlane()); +} + +// ---------------------------------------------------------------------- + +bool TestRibbonRibbon ( Ribbon3d const & A, Ribbon3d const & B ) +{ + // Easy test - two ribbons overlap if an edge of either ribbon overlaps + // the other ribbon + + if(TestLineRibbon(A.getEdgeA(),B) == HR_Hit) return HR_Hit; + if(TestLineRibbon(A.getEdgeB(),B) == HR_Hit) return HR_Hit; + if(TestLineRibbon(B.getEdgeA(),A) == HR_Hit) return HR_Hit; + if(TestLineRibbon(B.getEdgeB(),A) == HR_Hit) return HR_Hit; + + return HR_Miss; +} + +// ---------------------------------------------------------------------- + +// The line only penetrates the triangle if it goes through from front to back + +bool TestSegPlaneSided ( Segment3d const & S, Plane3d const & P ) { return TestSegPlane(S,P) == HR_HitFront; } +bool TestSegPlaneUnsided ( Segment3d const & S, Plane3d const & P ) { return TestSegPlane(S,P) != HR_Miss; } + +bool TestLineTriSided ( Line3d const & L, Triangle3d const & T ) { return TestLineTri(L,T) == HR_HitFront; } +bool TestLineTriUnsided ( Line3d const & L, Triangle3d const & T ) { return TestLineTri(L,T) != HR_Miss; } + +bool TestRayTriSided ( Ray3d const & R, Triangle3d const & T ) { return TestRayTri(R,T) == HR_HitFront; } +bool TestRayTriUnsided ( Ray3d const & R, Triangle3d const & T ) { return TestRayTri(R,T) != HR_Miss; } + +bool TestSegTriSided ( Segment3d const & S, Triangle3d const & T ) { return TestSegTri(S,T) == HR_HitFront; } +bool TestSegTriUnsided ( Segment3d const & S, Triangle3d const & T ) { return TestSegTri(S,T) != HR_Miss; } + +bool TestSegRibbonSided ( Segment3d const & S, Ribbon3d const & R ) { return TestSegRibbon(S,R) == HR_HitFront; } +bool TestSegRibbonUnsided ( Segment3d const & S, Ribbon3d const & R ) { return TestSegRibbon(S,R) != HR_Miss; } + +// ---------------------------------------------------------------------- + +bool TestSegCircle ( Segment3d const & S, Circle const & circle ) +{ + Vector A = S.getBegin(); + Vector B = S.getEnd(); + Vector const & C = circle.getCenter(); + + if(A.y > B.y) std::swap(A,B); + + if(C.y > B.y) + { + return false; + } + else if(C.y < A.y) + { + return false; + } + else + { + if(A.y == B.y) + { + return Overlap2d::TestSegCircle(S,circle); + } + else + { + float param = (C.y - A.y) / (B.y - A.y); + + Vector D = A + (B - A) * param; + + return Overlap2d::TestPointCircle( D, circle ); + } + } +} + +bool TestSegCircle_Below ( Segment3d const & S, Circle const & circle ) +{ + Vector A = S.getBegin(); + Vector B = S.getEnd(); + Vector const & C = circle.getCenter(); + + if(A.y > B.y) std::swap(A,B); + + if(C.y > B.y) + { + return false; + } + else if(C.y < A.y) + { + return Overlap2d::TestSegCircle(S,circle); + } + else + { + if(A.y == B.y) + { + return Overlap2d::TestSegCircle(S,circle); + } + else + { + float param = (C.y - A.y) / (B.y - A.y); + + Vector D = A + (B - A) * param; + + return Overlap2d::TestSegCircle( Segment3d(D,B), circle ); + } + + } +} + +// ---------------------------------------------------------------------- + +bool TestLineABox ( Line3d const & L, AxialBox const & B ) +{ + Vector const & P = L.getPoint(); + Vector const & V = L.getNormal(); + + Range timeX = IntersectFloatRange( P.x, V.x, B.getRangeX() ); + + if(timeX.isEmpty()) return false; + + Range timeY = IntersectFloatRange( P.y, V.y, B.getRangeY() ); + + Range temp = IntersectRanges(timeX,timeY); + + if(temp.isEmpty()) return false; + + Range timeZ = IntersectFloatRange( P.z, V.z, B.getRangeZ() ); + + temp = IntersectRanges(temp,timeZ); + + return !temp.isEmpty(); +} + +// ---------- + +bool TestRayABox ( Ray3d const & R, AxialBox const & B ) +{ + Vector const & P = R.getPoint(); + Vector const & V = R.getNormal(); + + Range temp = Range::plusInf; + + Range timeX = IntersectFloatRange( P.x, V.x, B.getRangeX() ); + + temp = IntersectRanges(temp,timeX); + + if(temp.isEmpty()) return false; + + Range timeY = IntersectFloatRange( P.y, V.y, B.getRangeY() ); + + temp = IntersectRanges(temp,timeY); + + if(temp.isEmpty()) return false; + + Range timeZ = IntersectFloatRange( P.z, V.z, B.getRangeZ() ); + + temp = IntersectRanges(temp,timeZ); + + return !temp.isEmpty(); +} + +// ---------- + +bool TestSegABox ( Segment3d const & S, AxialBox const & B ) +{ + Vector const & P = S.getBegin(); + Vector const & V = S.getDelta(); + + Range temp = Range::unit; + + Range timeY = IntersectFloatRange( P.y, V.y, B.getRangeY() ); + + temp = IntersectRanges(temp,timeY); + + if(temp.isEmpty()) return false; + + Range timeX = IntersectFloatRange( P.x, V.x, B.getRangeX() ); + + temp = IntersectRanges(temp,timeX); + + if(temp.isEmpty()) return false; + + Range timeZ = IntersectFloatRange( P.z, V.z, B.getRangeZ() ); + + temp = IntersectRanges(temp,timeZ); + + return !temp.isEmpty(); +} + +// ---------- + +bool TestYLineCircle ( Vector const & V, Circle const & C ) +{ + return Distance2d::Distance2PointPoint(V,C.getCenter()) < sqr(C.getRadius()); +} + +bool TestYLineSphere ( Vector const & V, Sphere const & S ) +{ + return Distance2d::Distance2PointPoint(V,S.getCenter()) < sqr(S.getRadius()); +} + +bool TestYLineCylinder ( Vector const & V, Cylinder const & C ) +{ + return Distance2d::Distance2PointPoint(V,C.getBase()) < sqr(C.getRadius()); +} + +bool TestYLineABox ( Vector const & V, AxialBox const & B ) +{ + return B.getRangeX().contains(V.x) && B.getRangeZ().contains(V.z); +} + +bool TestYLineYBox ( Vector const & V, YawedBox const & B ) +{ + return TestYLineABox( B.transformToLocal(V), B.getLocalShape() ); +} + +bool TestYLineOBox ( Vector const & V, OrientedBox const & B ) +{ + Vector point = B.transformToLocal(V); + Vector normal = B.rotateToLocal(Vector::unitY); + + return TestLineABox( Line3d(point,normal), B.getLocalShape() ); +} + +bool TestYLineShape ( Vector const & V, MultiShape const & S ) +{ + MultiShape::ShapeType type = S.getShapeType(); + + // ---------- + + if (type == MultiShape::MST_Sphere) { return TestYLine( V, S.getSphere() ); } + else if(type == MultiShape::MST_Cylinder) { return TestYLine( V, S.getCylinder() ); } + else if(type == MultiShape::MST_AxialBox) { return TestYLine( V, S.getAxialBox() ); } + else if(type == MultiShape::MST_YawedBox) { return TestYLine( V, S.getYawedBox() ); } + else if(type == MultiShape::MST_OrientedBox) { return TestYLine( V, S.getOrientedBox() ); } + else { return false; } +} + +// ---------------------------------------------------------------------- +// Moving point vs. volume + +bool TestPointABox ( Vector const & P, Vector const & V, AxialBox const & B ) +{ + Range timeX = IntersectFloatRange( P.x, V.x, B.getRangeX() ); + + if(timeX.isEmpty()) return false; + + Range timeY = IntersectFloatRange( P.y, V.y, B.getRangeY() ); + + Range temp = IntersectRanges(timeX,timeY); + + if(temp.isEmpty()) return false; + + Range timeZ = IntersectFloatRange( P.z, V.z, B.getRangeZ() ); + + temp = IntersectRanges(temp,timeZ); + + return !temp.isEmpty(); +} + +// ---------------------------------------------------------------------- +// Planar-vs-planar overlap tests + +bool TestTriTri ( Triangle3d const & A, Triangle3d const & B ) +{ + // Two triangles overlap iff any edge from one triangle overlaps the other triangle + + if(TestSegTriUnsided(A.getEdgeSegment0(),B)) return true; + if(TestSegTriUnsided(A.getEdgeSegment1(),B)) return true; + if(TestSegTriUnsided(A.getEdgeSegment2(),B)) return true; + + if(TestSegTriUnsided(B.getEdgeSegment0(),A)) return true; + if(TestSegTriUnsided(B.getEdgeSegment1(),A)) return true; + if(TestSegTriUnsided(B.getEdgeSegment2(),A)) return true; + + return false; +} + + + +// ---------------------------------------------------------------------- +// Volume-vs-planar overlap tests + +bool TestSpherePlane ( Sphere const & A, Plane3d const & B ) +{ + float dist = abs(Distance3d::DistancePointPlane(A.getCenter(),B)); + + return dist <= A.getRadius(); +} + +bool TestSpherePoly ( Sphere const & A, VertexList const & verts, Plane3d const & P ) +{ + if(!TestSpherePlane(A,P)) return false; + + float dist2 = Distance3d::Distance2PointPoly(A.getCenter(),verts); + + return dist2 < (A.getRadius() * A.getRadius()); +} + +// ---------------------------------------------------------------------- +// Volume-vs-volume overlap tests + +bool TestABoxABox ( AxialBox const & A, AxialBox const & B ) +{ + Vector const & minA = A.getMin(); + Vector const & maxA = A.getMax(); + + Vector const & minB = B.getMin(); + Vector const & maxB = B.getMax(); + + if(minA.x > maxB.x) return false; + if(minA.y > maxB.y) return false; + if(minA.z > maxB.z) return false; + + if(minB.x > maxA.x) return false; + if(minB.y > maxA.y) return false; + if(minB.z > maxA.z) return false; + + return true; +} + +// ---------------------------------------------------------------------- +// Overlap test for two oriented boxes adapted from David Eberly's version +// at www.magic-software.com + + +bool TestOBoxOBox ( OrientedBox const & boxA, OrientedBox const & boxB ) +{ + // convenience variables + const Vector* akA = boxA.getAxes(); + const Vector* akB = boxB.getAxes(); + const float* afEA = boxA.getExtents(); + const float* afEB = boxB.getExtents(); + + // compute difference of box centers, D = C1-C0 + Vector kD = boxB.getCenter() - boxA.getCenter(); + + float aafC[3][3]; // matrix C = A^T B, c_{ij} = Dot(A_i,B_j) + float aafAbsC[3][3]; // |c_{ij}| + float afAD[3]; // Dot(A_i,D) + float fR0, fR1, fR; // interval radii and distance between centers + float fR01; // = R0 + R1 + + // axis C0+t*A0 + aafC[0][0] = akA[0].dot(akB[0]); + aafC[0][1] = akA[0].dot(akB[1]); + aafC[0][2] = akA[0].dot(akB[2]); + afAD[0] = akA[0].dot(kD); + aafAbsC[0][0] = static_cast(fabs(aafC[0][0])); + aafAbsC[0][1] = static_cast(fabs(aafC[0][1])); + aafAbsC[0][2] = static_cast(fabs(aafC[0][2])); + fR = static_cast(fabs(afAD[0])); + fR1 = afEB[0]*aafAbsC[0][0]+afEB[1]*aafAbsC[0][1]+afEB[2]*aafAbsC[0][2]; + fR01 = afEA[0] + fR1; + if ( fR > fR01 ) + return false; + + // axis C0+t*A1 + aafC[1][0] = akA[1].dot(akB[0]); + aafC[1][1] = akA[1].dot(akB[1]); + aafC[1][2] = akA[1].dot(akB[2]); + afAD[1] = akA[1].dot(kD); + aafAbsC[1][0] = static_cast(fabs(aafC[1][0])); + aafAbsC[1][1] = static_cast(fabs(aafC[1][1])); + aafAbsC[1][2] = static_cast(fabs(aafC[1][2])); + fR = static_cast(fabs(afAD[1])); + fR1 = afEB[0]*aafAbsC[1][0]+afEB[1]*aafAbsC[1][1]+afEB[2]*aafAbsC[1][2]; + fR01 = afEA[1] + fR1; + if ( fR > fR01 ) + return false; + + // axis C0+t*A2 + aafC[2][0] = akA[2].dot(akB[0]); + aafC[2][1] = akA[2].dot(akB[1]); + aafC[2][2] = akA[2].dot(akB[2]); + afAD[2] = akA[2].dot(kD); + aafAbsC[2][0] = static_cast(fabs(aafC[2][0])); + aafAbsC[2][1] = static_cast(fabs(aafC[2][1])); + aafAbsC[2][2] = static_cast(fabs(aafC[2][2])); + fR = static_cast(fabs(afAD[2])); + fR1 = afEB[0]*aafAbsC[2][0]+afEB[1]*aafAbsC[2][1]+afEB[2]*aafAbsC[2][2]; + fR01 = afEA[2] + fR1; + if ( fR > fR01 ) + return false; + + // axis C0+t*B0 + fR = static_cast(fabs(akB[0].dot(kD))); + fR0 = afEA[0]*aafAbsC[0][0]+afEA[1]*aafAbsC[1][0]+afEA[2]*aafAbsC[2][0]; + fR01 = fR0 + afEB[0]; + if ( fR > fR01 ) + return false; + + // axis C0+t*B1 + fR = static_cast(fabs(akB[1].dot(kD))); + fR0 = afEA[0]*aafAbsC[0][1]+afEA[1]*aafAbsC[1][1]+afEA[2]*aafAbsC[2][1]; + fR01 = fR0 + afEB[1]; + if ( fR > fR01 ) + return false; + + // axis C0+t*B2 + fR = static_cast(fabs(akB[2].dot(kD))); + fR0 = afEA[0]*aafAbsC[0][2]+afEA[1]*aafAbsC[1][2]+afEA[2]*aafAbsC[2][2]; + fR01 = fR0 + afEB[2]; + if ( fR > fR01 ) + return false; + + // axis C0+t*A0xB0 + fR = static_cast(fabs(afAD[2]*aafC[1][0]-afAD[1]*aafC[2][0])); + fR0 = afEA[1]*aafAbsC[2][0] + afEA[2]*aafAbsC[1][0]; + fR1 = afEB[1]*aafAbsC[0][2] + afEB[2]*aafAbsC[0][1]; + fR01 = fR0 + fR1; + if ( fR > fR01 ) + return false; + + // axis C0+t*A0xB1 + fR = static_cast(fabs(afAD[2]*aafC[1][1]-afAD[1]*aafC[2][1])); + fR0 = afEA[1]*aafAbsC[2][1] + afEA[2]*aafAbsC[1][1]; + fR1 = afEB[0]*aafAbsC[0][2] + afEB[2]*aafAbsC[0][0]; + fR01 = fR0 + fR1; + if ( fR > fR01 ) + return false; + + // axis C0+t*A0xB2 + fR = static_cast(fabs(afAD[2]*aafC[1][2]-afAD[1]*aafC[2][2])); + fR0 = afEA[1]*aafAbsC[2][2] + afEA[2]*aafAbsC[1][2]; + fR1 = afEB[0]*aafAbsC[0][1] + afEB[1]*aafAbsC[0][0]; + fR01 = fR0 + fR1; + if ( fR > fR01 ) + return false; + + // axis C0+t*A1xB0 + fR = static_cast(fabs(afAD[0]*aafC[2][0]-afAD[2]*aafC[0][0])); + fR0 = afEA[0]*aafAbsC[2][0] + afEA[2]*aafAbsC[0][0]; + fR1 = afEB[1]*aafAbsC[1][2] + afEB[2]*aafAbsC[1][1]; + fR01 = fR0 + fR1; + if ( fR > fR01 ) + return false; + + // axis C0+t*A1xB1 + fR = static_cast(fabs(afAD[0]*aafC[2][1]-afAD[2]*aafC[0][1])); + fR0 = afEA[0]*aafAbsC[2][1] + afEA[2]*aafAbsC[0][1]; + fR1 = afEB[0]*aafAbsC[1][2] + afEB[2]*aafAbsC[1][0]; + fR01 = fR0 + fR1; + if ( fR > fR01 ) + return false; + + // axis C0+t*A1xB2 + fR = static_cast(fabs(afAD[0]*aafC[2][2]-afAD[2]*aafC[0][2])); + fR0 = afEA[0]*aafAbsC[2][2] + afEA[2]*aafAbsC[0][2]; + fR1 = afEB[0]*aafAbsC[1][1] + afEB[1]*aafAbsC[1][0]; + fR01 = fR0 + fR1; + if ( fR > fR01 ) + return false; + + // axis C0+t*A2xB0 + fR = static_cast(fabs(afAD[1]*aafC[0][0]-afAD[0]*aafC[1][0])); + fR0 = afEA[0]*aafAbsC[1][0] + afEA[1]*aafAbsC[0][0]; + fR1 = afEB[1]*aafAbsC[2][2] + afEB[2]*aafAbsC[2][1]; + fR01 = fR0 + fR1; + if ( fR > fR01 ) + return false; + + // axis C0+t*A2xB1 + fR = static_cast(fabs(afAD[1]*aafC[0][1]-afAD[0]*aafC[1][1])); + fR0 = afEA[0]*aafAbsC[1][1] + afEA[1]*aafAbsC[0][1]; + fR1 = afEB[0]*aafAbsC[2][2] + afEB[2]*aafAbsC[2][0]; + fR01 = fR0 + fR1; + if ( fR > fR01 ) + return false; + + // axis C0+t*A2xB2 + fR = static_cast(fabs(afAD[1]*aafC[0][2]-afAD[0]*aafC[1][2])); + fR0 = afEA[0]*aafAbsC[1][2] + afEA[1]*aafAbsC[0][2]; + fR1 = afEB[0]*aafAbsC[2][1] + afEB[1]*aafAbsC[2][0]; + fR01 = fR0 + fR1; + if ( fR > fR01 ) + return false; + + return true; +} + +// ---------------------------------------------------------------------- + +bool TestSphereABox ( Sphere const & A, AxialBox const & B ) +{ + Vector close = Distance3d::ClosestPointABox(A.getCenter(),B); + + float dist2 = close.magnitudeBetweenSquared(A.getCenter()); + + float radius = A.getRadius(); + + return dist2 < (radius * radius); +} + +bool TestSphereYBox ( Sphere const & A, YawedBox const & B ) +{ + Vector close = Distance3d::ClosestPointYBox(A.getCenter(),B); + + float dist2 = close.magnitudeBetweenSquared(A.getCenter()); + + float radius = A.getRadius(); + + return dist2 < (radius * radius); +} + +bool TestSphereOBox ( Sphere const & A, OrientedBox const & B ) +{ + Vector close = Distance3d::ClosestPointOBox(A.getCenter(),B); + + float dist2 = close.magnitudeBetweenSquared(A.getCenter()); + + float radius = A.getRadius(); + + return dist2 < (radius * radius); +} + + + + + + + +// ---------------------------------------------------------------------- +// Projected overlap tests + +// This test tries to find a separating plane for the two triangles +// + +bool TestTriTri_proj ( Triangle3d const & inTriA, + Triangle3d const & inTriB, + Vector const & direction, + real epsilon ) +{ + Triangle3d triA = inTriA; + Triangle3d triB = inTriB; + + if(!triA.isFacing(direction)) triA.flip(); + if(!triB.isFacing(direction)) triB.flip(); + + int i; + + // ---------- + + for(i = 0; i < 3; i++) + { + Plane3d P = Plane3d( triA.getCorner(i), triA.getEdgeDir(i).cross( direction ) ); + + ContainmentResult test = Containment3d::TestTriPlane( triB, P, epsilon ); + + if( Containment::isNonContainment(test) ) return false; + } + + // ---------- + + for(i = 0; i < 3; i++) + { + Plane3d P = Plane3d( triB.getCorner(i), triB.getEdgeDir(i).cross( direction ) ); + + ContainmentResult test = Containment3d::TestTriPlane( triA, P, epsilon ); + + if( Containment::isNonContainment(test) ) return false; + } + + // ---------- + + return true; +} + +// ---------- + +bool TestSegSeg_proj ( Segment3d const & segA, + Segment3d const & segB, + Vector const & direction ) +{ + Ribbon3d R(segA,direction); + + return TestSegRibbonUnsided(segB,R); +} + +// ---------------------------------------------------------------------- + +} // namespace Overlap3d + diff --git a/engine/shared/library/sharedCollision/src/shared/core/Overlap3d.h b/engine/shared/library/sharedCollision/src/shared/core/Overlap3d.h new file mode 100644 index 00000000..96e4bba1 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Overlap3d.h @@ -0,0 +1,157 @@ +// ====================================================================== +// +// Overlap3d.h +// copyright (c) 2001 Sony Online Entertainment +// +// ---------------------------------------------------------------------- + +#ifndef INCLUDED_Overlap3d_H +#define INCLUDED_Overlap3d_H + +#include "sharedCollision/CollisionEnums.h" + +class Triangle3d; +class Line3d; +class Segment3d; +class Ray3d; +class Vector; +class AxialBox; +class YawedBox; +class OrientedBox; +class Plane3d; +class Ribbon3d; +class Circle; +class Sphere; +class Range; +class Cylinder; +class MultiShape; + +typedef stdvector::fwd VertexList; + +// ====================================================================== + +namespace Overlap3d +{ + +// ---------------------------------------------------------------------- +// Linear-vs-planar overlap tests + +HitResult TestLineTri ( Line3d const & L, Triangle3d const & T ); +HitResult TestRayTri ( Ray3d const & R, Triangle3d const & T ); +HitResult TestSegTri ( Segment3d const & S, Triangle3d const & T ); +HitResult TestSegPlane ( Segment3d const & S, Plane3d const & P ); +HitResult TestSegRibbon ( Segment3d const & S, Ribbon3d const & R ); + +// ---------------------------------------------------------------------- +// Linear-vs-planar overlap tests + +bool TestLineTriSided ( Line3d const & L, Triangle3d const & T ); +bool TestLineTriUnsided ( Line3d const & L, Triangle3d const & T ); + +bool TestRayTriSided ( Ray3d const & R, Triangle3d const & T ); +bool TestRayTriUnsided ( Ray3d const & R, Triangle3d const & T ); + +bool TestSegTriSided ( Segment3d const & S, Triangle3d const & T ); +bool TestSegTriUnsided ( Segment3d const & S, Triangle3d const & T ); + +bool TestSegPlaneSided ( Segment3d const & S, Plane3d const & P ); +bool TestSegPlaneUnsided ( Segment3d const & S, Plane3d const & P ); + +bool TestSegRibbonSided ( Segment3d const & S, Ribbon3d const & R ); +bool TestSegRibbonUnsided ( Segment3d const & S, Ribbon3d const & R ); + +bool TestSegCircle ( Segment3d const & S, Circle const & C ); + +bool TestSegCircle_Below ( Segment3d const & S, Circle const & C ); // returns true if the circle overlaps the ribbon created by projecting the segment downwards + +// ---------- +// Linear-vs-volume overlap tests + +bool TestRayABox ( Ray3d const & R, AxialBox const & box ); +bool TestLineABox ( Line3d const & L, AxialBox const & box ); +bool TestSegABox ( Segment3d const & S, AxialBox const & B ); + +bool TestYLineCircle ( Vector const & V, Circle const & C ); +bool TestYLineSphere ( Vector const & V, Sphere const & S ); +bool TestYLineCylinder ( Vector const & V, Cylinder const & C ); +bool TestYLineABox ( Vector const & V, AxialBox const & B ); // Y-axis-aligned line passing through V +bool TestYLineYBox ( Vector const & V, YawedBox const & B ); +bool TestYLineOBox ( Vector const & V, OrientedBox const & B ); + +bool TestYLineShape ( Vector const & V, MultiShape const & S ); + +inline bool TestYLine ( Vector const & V, Circle const & C ) { return TestYLineCircle(V,C); } +inline bool TestYLine ( Vector const & V, Sphere const & S ) { return TestYLineSphere(V,S); } +inline bool TestYLine ( Vector const & V, Cylinder const & C ) { return TestYLineCylinder(V,C); } +inline bool TestYLine ( Vector const & V, AxialBox const & B ) { return TestYLineABox(V,B); } +inline bool TestYLine ( Vector const & V, YawedBox const & B ) { return TestYLineYBox(V,B); } +inline bool TestYLine ( Vector const & V, OrientedBox const & B ) { return TestYLineOBox(V,B); } +inline bool TestYLine ( Vector const & V, MultiShape const & S ) { return TestYLineShape(V,S); } + +// ---------- +// Moving point-vs-volume tests + +bool TestPointABox ( Vector const & P, Vector const & V, AxialBox const & B ); + +// ---------- +// Planar-vs-planar overlap tests + +bool TestTriTri ( Triangle3d const & A, Triangle3d const & B ); + +// ---------- +// Volume-vs-planar overlap tests + +bool TestSpherePlane ( Sphere const & A, Plane3d const & B ); +bool TestSpherePoly ( Sphere const & A, VertexList const & B, Plane3d const & P ); + +// ---------- +// Volume-vs-volume overlap tests + +bool TestABoxABox ( AxialBox const & A, AxialBox const & B ); +bool TestOBoxOBox ( OrientedBox const & A, OrientedBox const & B ); + +bool TestSphereABox ( Sphere const & A, AxialBox const & B ); +bool TestSphereYBox ( Sphere const & A, YawedBox const & B ); +bool TestSphereOBox ( Sphere const & A, OrientedBox const & B ); + +bool TestYSlabABox ( Range const & A, AxialBox const & B ); +bool TestYSlabYBox ( Range const & A, AxialBox const & B ); +bool TestYSlabOBox ( Range const & A, AxialBox const & B ); + + +// ---------- +// Projected overlap tests - determine if two primitives overlap when +// projected onto a plane perpendicular to the given view direction. + +bool TestTriTri_proj ( Triangle3d const & triA, + Triangle3d const & triB, + Vector const & direction, + real epsilon ); + +bool TestSegSeg_proj ( Segment3d const & segA, + Segment3d const & segB, + Vector const & direction ); + + +// ---------------------------------------------------------------------- +// Aliases for templates + +inline bool TestSided ( Line3d const & L, Triangle3d const & T ) { return TestLineTriSided(L,T); } +inline bool TestSided ( Ray3d const & R, Triangle3d const & T ) { return TestRayTriSided(R,T); } +inline bool TestSided ( Segment3d const & S, Triangle3d const & T ) { return TestSegTriSided(S,T); } + +inline bool TestUnsided ( Line3d const & L, Triangle3d const & T ) { return TestLineTriUnsided(L,T); } +inline bool TestUnsided ( Ray3d const & R, Triangle3d const & T ) { return TestRayTriUnsided(R,T); } +inline bool TestUnsided ( Segment3d const & S, Triangle3d const & T ) { return TestSegTriUnsided(S,T); } + +inline bool Test ( Line3d const & L, AxialBox const & B ) { return TestLineABox(L,B); } +inline bool Test ( Ray3d const & R, AxialBox const & B ) { return TestRayABox(R,B); } +inline bool Test ( Segment3d const & S, AxialBox const & B ) { return TestSegABox(S,B); } + +inline bool Test ( AxialBox const & A, AxialBox const & B ) { return TestABoxABox(A,B); } + +// ---------------------------------------------------------------------- + +}; // namespace Collision3d + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/Resolve3d.cpp b/engine/shared/library/sharedCollision/src/shared/core/Resolve3d.cpp new file mode 100644 index 00000000..24f3c63d --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Resolve3d.cpp @@ -0,0 +1,217 @@ +// ====================================================================== +// +// Resolve3d.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/Resolve3d.h" + +#include "sharedCollision/CollisionUtils.h" +#include "sharedCollision/Contact3d.h" +#include "sharedCollision/Containment3d.h" +#include "sharedCollision/Distance3d.h" + +#include "sharedMath/Vector.h" +#include "sharedMath/MultiShape.h" +#include "sharedMath/ShapeUtils.h" +#include "sharedMath/Circle.h" + +#include "sharedMath/Sphere.h" +#include "sharedMath/Cylinder.h" +#include "sharedMath/OrientedCylinder.h" +#include "sharedMath/AxialBox.h" +#include "sharedMath/YawedBox.h" +#include "sharedMath/OrientedBox.h" + +float maxContactDist = -REAL_MAX; + +namespace Resolve3d +{ + +// ---------------------------------------------------------------------- + +ResolutionResult ResolveContact_Slide3 ( Contact const & contact, Vector const & V, Vector & outV ) +{ + float stepDistance = V.magnitude(); + + if(contact.m_resolvable) + { + if(contact.m_exists) + { + if(contact.m_time > 0.0f) + { + if(contact.m_time > 1.0f) + { + // Contact occurs too far in the future - nothing to do + + outV = V; + return RR_Future; + } + else + { + outV = Collision3d::SlideAlong( V, contact.m_normal, contact.m_time ); + return RR_Resolved; + } + } + else + { + // Contact occurs in the past + + float contactDistance = stepDistance * -contact.m_time; + + if(contactDistance > maxContactDist) maxContactDist = contactDistance; + + if(contactDistance < 0.05f) + { + // but not too far in the past - resolve it + outV = Collision3d::SlideAlong( V, contact.m_normal, contact.m_time ); + + return RR_Resolved; + } + else + { + return RR_Past; + } + } + } + else + { + // No contact + return RR_NoCollision; + } + } + else + { + return RR_DontKnowHow; + } +} + +// ---------------------------------------------------------------------- + +template< typename TypeA, typename TypeB > +ResolutionResult GenericSlideResolve ( TypeA const & A, Vector const & V, TypeB const & B, Vector & outV ) +{ + Contact contact = Contact3d::FindContact(A,V,B); + + return ResolveContact_Slide3(contact,V,outV); +} + +// ---------------------------------------------------------------------- + +ResolutionResult ResolveCirclePoint ( Circle const & A, Vector const & V, Vector const & B, Vector & outV ) +{ + return GenericSlideResolve(A,V,B,outV); +} + +ResolutionResult ResolveCircleCircle ( Circle const & A, Vector const & V, Circle const & B, Vector & outV ) +{ + return GenericSlideResolve(A,V,B,outV); +} + +ResolutionResult ResolveCircleABox ( Circle const & A, Vector const & V, AxialBox const & B, Vector & outV ) +{ + return GenericSlideResolve(A,V,B,outV); +} + +ResolutionResult ResolveCircleYBox ( Circle const & A, Vector const & V, YawedBox const & B, Vector & outV ) +{ + return GenericSlideResolve(A,V,B,outV); +} + +// ---------------------------------------------------------------------- + +ResolutionResult ResolveSphereSphere ( Sphere const & A, Vector const & V, Sphere const& B, Vector & outV ) +{ + return GenericSlideResolve(A,V,B,outV); +} + +ResolutionResult ResolveSphereCylinder ( Sphere const & A, Vector const & V, Cylinder const & B, Vector & outV ) +{ + return GenericSlideResolve(A,V,B,outV); +} + +ResolutionResult ResolveSphereOCylinder ( Sphere const & A, Vector const & V, OrientedCylinder const & B, Vector & outV ) +{ + return GenericSlideResolve(A,V,B,outV); +} + +ResolutionResult ResolveSphereABox ( Sphere const & A, Vector const & V, AxialBox const & B, Vector & outV ) +{ + return GenericSlideResolve(A,V,B,outV); +} + +ResolutionResult ResolveSphereYBox ( Sphere const & A, Vector const & V, YawedBox const & B, Vector & outV ) +{ + return GenericSlideResolve(A,V,B,outV); +} + +ResolutionResult ResolveSphereOBox ( Sphere const & A, Vector const & V, OrientedBox const & B, Vector & outV ) +{ + return GenericSlideResolve(A,V,B,outV); +} + +// ---------------------------------------------------------------------- +// Cylinder-vs-abox/ybox collisions are resolved in 2d + +ResolutionResult ResolveCylinderCylinder ( Cylinder const & A, Vector const & V, Cylinder const & B, Vector & outV ) +{ + return Resolve(A.getBaseCircle(),V,B.getBaseCircle(),outV); +} + +ResolutionResult ResolveCylinderABox ( Cylinder const & A, Vector const & V, AxialBox const & B, Vector & outV ) +{ + return Resolve(A.getBaseCircle(),V,B,outV); +} + +ResolutionResult ResolveCylinderYBox ( Cylinder const & A, Vector const & V, YawedBox const & B, Vector & outV ) +{ + return Resolve(A.getBaseCircle(),V,B,outV); +} + +ResolutionResult ResolveCylinderSphere ( Cylinder const & A, Vector const & V, Sphere const & B, Vector & outV ) +{ + return GenericSlideResolve(A,V,B,outV); +} + +// ---------------------------------------------------------------------- + +ResolutionResult ResolveMultiShapes ( MultiShape const & A, Vector const & V, MultiShape const & B, Vector & outV ) +{ + MultiShape::ShapeType typeA = A.getShapeType(); + MultiShape::ShapeType typeB = B.getShapeType(); + + // ---------- + + if(typeA == MultiShape::MST_Cylinder) + { + Cylinder cylA = A.getCylinder(); + + if (typeB == MultiShape::MST_Sphere) { return Resolve3d::Resolve( cylA, V, B.getSphere(), outV ); } + else if(typeB == MultiShape::MST_Cylinder) { return Resolve3d::Resolve( cylA, V, B.getCylinder(), outV ); } + else if(typeB == MultiShape::MST_AxialBox) { return Resolve3d::Resolve( cylA, V, B.getAxialBox(), outV ); } + else if(typeB == MultiShape::MST_YawedBox) { return Resolve3d::Resolve( cylA, V, B.getYawedBox(), outV ); } + else { return RR_DontKnowHow; } + } + else if(typeA == MultiShape::MST_Sphere) + { + Sphere sphereA = A.getSphere(); + + if (typeB == MultiShape::MST_Sphere) { return Resolve3d::Resolve( sphereA, V, B.getSphere(), outV ); } + else if(typeB == MultiShape::MST_Cylinder) { return Resolve3d::Resolve( sphereA, V, B.getCylinder(), outV ); } + else if(typeB == MultiShape::MST_OrientedCylinder) { return Resolve3d::Resolve( sphereA, V, B.getOrientedCylinder(), outV ); } + else if(typeB == MultiShape::MST_AxialBox) { return Resolve3d::Resolve( sphereA, V, B.getAxialBox(), outV ); } + else if(typeB == MultiShape::MST_YawedBox) { return Resolve3d::Resolve( sphereA, V, B.getYawedBox(), outV ); } + else if(typeB == MultiShape::MST_OrientedBox) { return Resolve3d::Resolve( sphereA, V, B.getOrientedBox(), outV ); } + else { return RR_DontKnowHow; } + } + else + { + return RR_DontKnowHow; + } +} + +// ---------------------------------------------------------------------- + +} // namespace Resolve3d diff --git a/engine/shared/library/sharedCollision/src/shared/core/Resolve3d.h b/engine/shared/library/sharedCollision/src/shared/core/Resolve3d.h new file mode 100644 index 00000000..17347f14 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/Resolve3d.h @@ -0,0 +1,96 @@ +// ====================================================================== +// +// Resolve3d.h +// copyright (c) 2001 Sony Online Entertainment +// +// This is where all the different overlap/intersect/temporal/etc. tests +// come together and allow us to take two primitives and a motion step +// and produce their position at the end of the step. +// +// ====================================================================== + +#ifndef INCLUDED_Resolve3d_H +#define INCLUDED_Resolve3d_H + +#include "sharedCollision/CollisionEnums.h" + +class OrientedCylinder; +class YawedBox; +class Circle; +class Vector; +class Triangle3d; +class Segment3d; +class Plane3d; +class Ribbon3d; +class Line3d; +class Ray3d; +class Range; +class AxialBox; +class Sphere; +class OrientedBox; +class Ring; +class Cylinder; +class MultiShape; +class Contact; + +namespace Resolve3d +{ + +// ---------------------------------------------------------------------- + +ResolutionResult ResolveContact_Slide3 ( Contact const & contact, Vector const & V, Vector & outV ); + +// ---------------------------------------------------------------------- +// 2d resolution (need a Resolve2d) + +ResolutionResult ResolveCirclePoint ( Circle const & A, Vector const & V, Vector const & B, Vector & outV ); +ResolutionResult ResolveCircleCircle ( Circle const & A, Vector const & V, Circle const & B, Vector & outV ); + +ResolutionResult ResolveCircleABox ( Circle const & A, Vector const & V, AxialBox const & B, Vector & outV ); +ResolutionResult ResolveCircleYBox ( Circle const & A, Vector const & V, YawedBox const & B, Vector & outV ); + +inline ResolutionResult Resolve ( Circle const & A, Vector const & V, Circle const & B, Vector & outV ) { return ResolveCircleCircle(A,V,B,outV); } +inline ResolutionResult Resolve ( Circle const & A, Vector const & V, AxialBox const & B, Vector & outV ) { return ResolveCircleABox(A,V,B,outV); } +inline ResolutionResult Resolve ( Circle const & A, Vector const & V, YawedBox const & B, Vector & outV ) { return ResolveCircleYBox(A,V,B,outV); } + +// ---------- + +ResolutionResult ResolveSphereSphere ( Sphere const & A, Vector const & V, Sphere const & B, Vector & outV ); +ResolutionResult ResolveSphereCylinder ( Sphere const & A, Vector const & V, Cylinder const & B, Vector & outV ); +ResolutionResult ResolveSphereOCylinder ( Sphere const & A, Vector const & V, OrientedCylinder const & B, Vector & outV ); +ResolutionResult ResolveSphereABox ( Sphere const & A, Vector const & V, AxialBox const & B, Vector & outV ); +ResolutionResult ResolveSphereYBox ( Sphere const & A, Vector const & V, YawedBox const & B, Vector & outV ); +ResolutionResult ResolveSphereOBox ( Sphere const & A, Vector const & V, OrientedBox const & B, Vector & outV ); + +ResolutionResult ResolveCylinderSphere ( Cylinder const & A, Vector const & V, Sphere const & B, Vector & outV ); +ResolutionResult ResolveCylinderCylinder ( Cylinder const & A, Vector const & V, Cylinder const & B, Vector & outV ); +ResolutionResult ResolveCylinderABox ( Cylinder const & A, Vector const & V, AxialBox const & B, Vector & outV ); +ResolutionResult ResolveCylinderYBox ( Cylinder const & A, Vector const & V, YawedBox const & B, Vector & outV ); + + +ResolutionResult ResolveMultiShapes ( MultiShape const & A, Vector const & V, MultiShape const & B, Vector & outV ); + +// ---------------------------------------------------------------------- + +inline ResolutionResult Resolve ( Sphere const & A, Vector const & V, Sphere const & B, Vector & outV ) { return ResolveSphereSphere(A,V,B,outV); } +inline ResolutionResult Resolve ( Sphere const & A, Vector const & V, Cylinder const & B, Vector & outV ) { return ResolveSphereCylinder(A,V,B,outV); } +inline ResolutionResult Resolve ( Sphere const & A, Vector const & V, OrientedCylinder const & B, Vector & outV ) { return ResolveSphereOCylinder(A,V,B,outV); } +inline ResolutionResult Resolve ( Sphere const & A, Vector const & V, AxialBox const & B, Vector & outV ) { return ResolveSphereABox(A,V,B,outV); } +inline ResolutionResult Resolve ( Sphere const & A, Vector const & V, YawedBox const & B, Vector & outV ) { return ResolveSphereYBox(A,V,B,outV); } +inline ResolutionResult Resolve ( Sphere const & A, Vector const & V, OrientedBox const & B, Vector & outV ) { return ResolveSphereOBox(A,V,B,outV); } + +inline ResolutionResult Resolve ( Cylinder const & A, Vector const & V, Cylinder const & B, Vector & outV ) { return ResolveCylinderCylinder(A,V,B,outV); } +inline ResolutionResult Resolve ( Cylinder const & A, Vector const & V, AxialBox const & B, Vector & outV ) { return ResolveCylinderABox(A,V,B,outV); } +inline ResolutionResult Resolve ( Cylinder const & A, Vector const & V, YawedBox const & B, Vector & outV ) { return ResolveCylinderYBox(A,V,B,outV); } +inline ResolutionResult Resolve ( Cylinder const & A, Vector const & V, Sphere const & B, Vector & outV ) { return ResolveCylinderSphere(A,V,B,outV); } + + + +inline ResolutionResult Resolve ( MultiShape const & A, Vector const & V, MultiShape const & B, Vector & outV ) { return ResolveMultiShapes(A,V,B,outV); } + +// ---------------------------------------------------------------------- + +} // namespace Resolve3d + +#endif // #ifndef INCLUDED_Resolve3d_H + diff --git a/engine/shared/library/sharedCollision/src/shared/core/SetupSharedCollision.cpp b/engine/shared/library/sharedCollision/src/shared/core/SetupSharedCollision.cpp new file mode 100644 index 00000000..37c82264 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/SetupSharedCollision.cpp @@ -0,0 +1,76 @@ +// ====================================================================== +// +// SetupSharedCollision.cpp +// copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/SetupSharedCollision.h" + +#include "sharedCollision/BoxTree.h" +#include "sharedCollision/CollisionWorld.h" +#include "sharedCollision/ConfigSharedCollision.h" +#include "sharedCollision/SimpleExtent.h" +#include "sharedCollision/SpatialDatabase.h" +#include "sharedDebug/InstallTimer.h" +#include "sharedFoundation/ExitChain.h" + +// ---------------------------------------------------------------------- + +namespace SetupSharedCollisionNamespace +{ + bool ms_installed = false; + + void remove(); +} + +using namespace SetupSharedCollisionNamespace; + +// ---------------------------------------------------------------------- + +void SetupSharedCollision::install ( SetupSharedCollision::Data const & data ) +{ + InstallTimer const installTimer("SetupSharedCollision::install"); + + DEBUG_FATAL(ms_installed, ("SetupSharedCollision::install - Collision is already installed")); + ms_installed = true; + + SimpleExtent::install(); + + // ---------- + + //@todo - FIXME - ExtentList::install is currently called by SetupSharedObject::install +// if(data.installExtents) ExtentList::install(); + + if(data.installCollisionWorld) + { + CollisionWorld::install(data.serverSide); + BoxTree::install(); + } + + if (!data.serverSide) + { + ConfigSharedCollision::setSpatialSweepAndResolveDefaultMask(SpatialDatabase::Q_Physicals); + } + + ConfigSharedCollision::setPlayEffectHook(data.playEffect); + ConfigSharedCollision::setIsPlayerHouseHook(data.isPlayerHouse); + + // ---------- + + ExitChain::add (remove, "SetupSharedCollision"); +} + +// ---------------------------------------------------------------------- + +void SetupSharedCollisionNamespace::remove() +{ + ms_installed = false; + + ConfigSharedCollision::setPlayEffectHook(0); + ConfigSharedCollision::setIsPlayerHouseHook(0); +} + +// ====================================================================== + diff --git a/engine/shared/library/sharedCollision/src/shared/core/SetupSharedCollision.h b/engine/shared/library/sharedCollision/src/shared/core/SetupSharedCollision.h new file mode 100644 index 00000000..7173b1d9 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/SetupSharedCollision.h @@ -0,0 +1,37 @@ +// ====================================================================== +// +// SetupSharedCollision.h +// copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_SetupSharedCollision_H +#define INCLUDED_SetupSharedCollision_H + +class CrcLowerString; +class Object; + +typedef bool (*PlayEffectHook)(CrcLowerString const & effectName, Object * object, CrcLowerString const & hardpoint ); +typedef bool (*IsPlayerHouseHook)(Object const * object); + +// ====================================================================== + +class SetupSharedCollision +{ +public: + + struct Data + { + bool installExtents; + bool installCollisionWorld; + PlayEffectHook playEffect; + IsPlayerHouseHook isPlayerHouse; + bool serverSide; + }; + + static void install ( Data const & data ); +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/SimpleCollisionMesh.cpp b/engine/shared/library/sharedCollision/src/shared/core/SimpleCollisionMesh.cpp new file mode 100644 index 00000000..3fa962f9 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/SimpleCollisionMesh.cpp @@ -0,0 +1,286 @@ +// ====================================================================== +// +// SimpleCollisionMesh.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/SimpleCollisionMesh.h" + +#include "sharedCollision/CollisionBuckets.h" +#include "sharedCollision/ConfigSharedCollision.h" +#include "sharedCollision/FloorTri.h" // for IndexedTri +#include "sharedMath/DebugShapeRenderer.h" +#include "sharedMath/IndexedTriangleList.h" +#include "sharedMath/Triangle3d.h" + +#include +#include + +namespace SimpleCollisionMeshNamespace +{ + bool s_intersectWithCollisionBuckets = true; + bool s_renderCollisionBuckets = false; + std::vector gs_tempResultVec; +} + +// ====================================================================== + +using namespace SimpleCollisionMeshNamespace; + +// ====================================================================== + +SimpleCollisionMesh::SimpleCollisionMesh() +: CollisionMesh() +, m_tris( new IndexedTriangleList() ) +, m_bucket(new CollisionBuckets(m_tris)) +{ +} + +SimpleCollisionMesh::SimpleCollisionMesh( IndexedTriangleList * tris ) +: CollisionMesh(), + m_tris( tris ) +, m_bucket(new CollisionBuckets(m_tris)) +{ +} + +SimpleCollisionMesh::~SimpleCollisionMesh() +{ + delete m_tris; + m_tris = NULL; + delete m_bucket; +} + +SimpleCollisionMesh * SimpleCollisionMesh::clone ( void ) const +{ + return new SimpleCollisionMesh( m_tris->clone() ); +} + +// ---------- +// Basic interface - accessors + +int SimpleCollisionMesh::getVertexCount( void ) const +{ + return static_cast(m_tris->getVertices().size()); +} + +Vector const & SimpleCollisionMesh::getVertex( int whichVertex ) const +{ + return m_tris->getVertices().at(static_cast(whichVertex)); +} + +void SimpleCollisionMesh::setVertex( int whichVertex, Vector const & newPoint ) +{ + m_tris->getVertices().at(static_cast(whichVertex)) = newPoint; + + setBoundsDirty(true); +} + +int SimpleCollisionMesh::getTriCount( void ) const +{ + return static_cast(m_tris->getIndices().size()) / 3; +} + +IndexedTri const & SimpleCollisionMesh::getIndexedTri( int whichTri ) const +{ + // This is yucky, but it works... + + return * reinterpret_cast( &(m_tris->getIndices().at(static_cast(whichTri) * 3)) ); +} + +void SimpleCollisionMesh::setIndexedTri( int whichTri, IndexedTri const & newTri ) +{ + std::vector & indices = m_tris->getIndices(); + + indices[static_cast(whichTri) * 3 + 0] = newTri.getCornerIndex(0); + indices[static_cast(whichTri) * 3 + 1] = newTri.getCornerIndex(1); + indices[static_cast(whichTri) * 3 + 2] = newTri.getCornerIndex(2); + + setBoundsDirty(true); +} + +Triangle3d SimpleCollisionMesh::getTriangle( int whichTri ) const +{ + IndexedTri const & T = getIndexedTri(whichTri); + + std::vector & verts = m_tris->getVertices(); + + unsigned int A = static_cast(T.getCornerIndex(0)); + unsigned int B = static_cast(T.getCornerIndex(1)); + unsigned int C = static_cast(T.getCornerIndex(2)); + + return Triangle3d( verts[A], verts[B], verts[C] ); +} + +void SimpleCollisionMesh::deleteVertex ( int whichVertex ) +{ + UNREF(whichVertex); + + FATAL(true,("can't delete vertices yet...")); +} + +void SimpleCollisionMesh::deleteVertices ( IntVector const & vertIndices ) +{ + UNREF(vertIndices); + + FATAL(true,("can't delete vertices yet...")); +} + +void SimpleCollisionMesh::deleteTri ( int whichTri ) +{ + std::vector & indices = m_tris->getIndices(); + + indices[static_cast(whichTri) * 3 + 0] = -2; + indices[static_cast(whichTri) * 3 + 1] = -2; + indices[static_cast(whichTri) * 3 + 2] = -2; + + std::vector::iterator newEnd = std::remove( indices.begin(), indices.end(), -2 ); + + if(newEnd != indices.end()) + { + IGNORE_RETURN(indices.erase(newEnd,indices.end())); + } + + setBoundsDirty(true); +} + +void SimpleCollisionMesh::deleteTris( IntVector const & triIndices ) +{ + std::vector & indices = m_tris->getIndices(); + + unsigned int indexCount = triIndices.size(); + + for(unsigned int i = 0; i < indexCount; i++) + { + int base = triIndices[i]; + + indices[static_cast(base) * 3 + 0] = -2; + indices[static_cast(base) * 3 + 1] = -2; + indices[static_cast(base) * 3 + 2] = -2; + } + + std::vector::iterator newEnd = std::remove( indices.begin(), indices.end(), -2 ); + + if(newEnd != indices.end()) + { + IGNORE_RETURN(indices.erase(newEnd,indices.end())); + } + + setBoundsDirty(true); +} + +// ---------- +// Debugging + +void SimpleCollisionMesh::drawDebugShapes ( DebugShapeRenderer * renderer ) const +{ + UNREF(renderer); + +#ifdef _DEBUG + if(renderer == NULL) return; + + if (s_renderCollisionBuckets) + { + m_bucket->drawDebugShapes(renderer); + } + else + { + renderer->setColor(VectorArgb::solidWhite); + renderer->drawMesh(m_tris); + } + + if (ConfigSharedCollision::getDrawCollisionNormals()) + { + renderer->setColor(VectorArgb::solidMagenta); + renderer->drawMeshNormals(m_tris); + } +#endif +} + +// ---------- +// Misc + +void SimpleCollisionMesh::attachTris ( IndexedTriangleList * tris ) +{ + if(m_tris != tris) + { + delete m_tris; + m_tris = tris; + + //-- SimpleCollisionMesh::attachTris is only called by the MayaExporter. + // Since we have swapped out the IndexedTriangleList we were constructed + // with, we need to rebuild the collision buckets. + delete m_bucket; + m_bucket = new CollisionBuckets(m_tris); + } + + setBoundsDirty(true); +} + +void SimpleCollisionMesh::copy( CollisionMesh const * mesh ) +{ + SimpleCollisionMesh const * simpleMesh = dynamic_cast< SimpleCollisionMesh const * >(mesh); + + if(simpleMesh) + { + m_tris->copy( simpleMesh->getTris() ); + + setBoundsDirty(true); + } +} + +IndexedTriangleList const * SimpleCollisionMesh::getTris ( void ) const +{ + return m_tris; +} + +// ---------------------------------------------------------------------- + +void SimpleCollisionMesh::load(Iff &file) +{ + m_tris->clear(); + + m_tris->load(file); +} + +void SimpleCollisionMesh::write( Iff & file ) const +{ + m_tris->write(file); +} + +// ---------------------------------------------------------------------- + +void SimpleCollisionMesh::transform( CollisionMesh const * sourceMesh, Transform const & tform, float scale ) +{ + CollisionMesh::transform(sourceMesh,tform,scale); + + setBoundsDirty(true); +} + +// ---------------------------------------------------------------------- + +void SimpleCollisionMesh::setIntersectWithCollisionBuckets(bool const intersectWithCollisionBuckets) +{ + s_intersectWithCollisionBuckets = intersectWithCollisionBuckets; +} + +// ---------------------------------------------------------------------- + +bool SimpleCollisionMesh::intersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, float * time ) const +{ + if (s_intersectWithCollisionBuckets) + { + return m_bucket->intersect(begin, end, surfaceNormal, time); + } + return CollisionMesh::intersect(begin, end, surfaceNormal, time); +} + +// ---------------------------------------------------------------------- + +void SimpleCollisionMesh::finalizeData() +{ + m_bucket->destroy(); + m_bucket->build(getBoundingABox().getMin(), getBoundingABox().getMax()); +} + diff --git a/engine/shared/library/sharedCollision/src/shared/core/SimpleCollisionMesh.h b/engine/shared/library/sharedCollision/src/shared/core/SimpleCollisionMesh.h new file mode 100644 index 00000000..832b548f --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/SimpleCollisionMesh.h @@ -0,0 +1,91 @@ +// ====================================================================== +// +// SimpleCollisionMesh.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_SimpleCollisionMesh_H +#define INCLUDED_SimpleCollisionMesh_H + +// ====================================================================== + +#include "sharedCollision/CollisionMesh.h" + +class DebugShapeRenderer; +class CollisionBuckets; + +// ====================================================================== + +class SimpleCollisionMesh : public CollisionMesh +{ +public: + + SimpleCollisionMesh(); + SimpleCollisionMesh( IndexedTriangleList * tris ); + virtual ~SimpleCollisionMesh(); + + static void setIntersectWithCollisionBuckets(bool const intersectWithCollisionBuckets); + + SimpleCollisionMesh * clone ( void ) const; + + // ---------- + // Basic interface - accessors + + virtual int getVertexCount ( void ) const; + virtual Vector const & getVertex ( int whichVertex ) const; + virtual void setVertex ( int whichVertex, Vector const & newPoint ); + + virtual int getTriCount ( void ) const; + virtual IndexedTri const & getIndexedTri ( int whichTri ) const; + virtual void setIndexedTri ( int whichTri, IndexedTri const & newTri ); + + virtual Triangle3d getTriangle ( int whichTri ) const; + + virtual void deleteVertex ( int whichVertex ); + virtual void deleteVertices ( IntVector const & vertIndices ); + + virtual void deleteTri ( int whichTri ); + virtual void deleteTris ( IntVector const & triIndices ); + + // ---------- + + virtual void load ( Iff & iff ); + virtual void write ( Iff & iff ) const; + + // ---------- + // Debugging + + virtual void drawDebugShapes ( DebugShapeRenderer * renderer ) const; + + // ---------- + // Misc + + virtual void attachTris ( IndexedTriangleList * tris ); + virtual void copy ( CollisionMesh const * mesh ); + virtual IndexedTriangleList const * getTris ( void ) const; + + virtual void transform ( CollisionMesh const * sourceMesh, Transform const & tform, float scale ); + + virtual bool intersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, float * time ) const; + +protected: + + // Disable copying + + SimpleCollisionMesh(const SimpleCollisionMesh &source); + SimpleCollisionMesh & operator =(const SimpleCollisionMesh &source); + +private: + + virtual void finalizeData(); + +protected: + + IndexedTriangleList * m_tris; + CollisionBuckets * m_bucket; +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/core/SpaceAvoidanceManager.cpp b/engine/shared/library/sharedCollision/src/shared/core/SpaceAvoidanceManager.cpp new file mode 100644 index 00000000..bb8e6551 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/SpaceAvoidanceManager.cpp @@ -0,0 +1,193 @@ +// ====================================================================== +// +// SpaceAvoidanceManager.cpp +// Copyright Sony Online Entertainment, Inc. +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/SpaceAvoidanceManager.h" + +#include "sharedCollision/BaseExtent.h" +#include "sharedCollision/CollisionDetect.h" +#include "sharedCollision/CollisionProperty.h" +#include "sharedCollision/CollisionWorld.h" +#include "sharedCollision/ConfigSharedCollision.h" +#include "sharedCollision/Intersect3d.h" +#include "sharedCollision/SpatialDatabase.h" +#include "sharedMath/Range.h" +#include "sharedObject/CellProperty.h" +#include "sharedObject/Object.h" + +#include + +// ====================================================================== +// +// SpaceAvoidanceManagerNamespace +// +// ====================================================================== + +namespace SpaceAvoidanceManagerNamespace +{ + // Lag/Slop factors. + float const cs_lookAheadGain = 1.5f; + float const cs_shipBoundSphereRadiusGain = 1.5f; + + // Dynamic and static query flags. + int const cs_queryDynamicObjectFlags = static_cast(SpatialDatabase::Q_Static) | static_cast(SpatialDatabase::Q_Dynamic); + int const cs_queryStaticObjectFlags = static_cast(SpatialDatabase::Q_Static); +} + +using namespace SpaceAvoidanceManagerNamespace; + +// ====================================================================== +// +// SpaceAvoidanceManager +// +// ====================================================================== + +// ---------------------------------------------------------------------- + +void SpaceAvoidanceManager::install() +{ +} + +// ---------------------------------------------------------------------- +void SpaceAvoidanceManager::remove() +{ +} + +// ---------------------------------------------------------------------- + +bool SpaceAvoidanceManager::getAvoidancePosition(Object const & unit, Vector const & velocity, Vector const & targetPosition_w, Vector & avoidancePosition_w, bool const collideWithDynamicObjects) +{ + float const collisionRadius = unit.getCollisionProperty()->getBoundingSphere_w().getRadius(); + return getAvoidancePosition(unit.getTransform_o2w(), collisionRadius, velocity, targetPosition_w, avoidancePosition_w, &unit, collideWithDynamicObjects); +} + +// ---------------------------------------------------------------------- + +bool SpaceAvoidanceManager::getAvoidancePosition(Transform const & objectTransform_o2w, float const collisionRadius, Vector const & velocity, Vector const & targetPosition_w, Vector & avoidancePosition_w, const Object * const excludeObject, bool const collideWithDynamicObjects) +{ + bool hasValidCollision = false; + + float const currentSpeed = velocity.approximateMagnitude(); + + if (currentSpeed > FLT_MIN) + { + // Keep local in case of a recursive call. + ColliderList colliderList; + + // Sweep the collision representation. + Vector const & objectPosition_w = objectTransform_o2w.getPosition_p(); + Sphere const querySphere_w(objectPosition_w, collisionRadius * cs_shipBoundSphereRadiusGain); + Vector const & intersectVector = targetPosition_w - objectPosition_w; + Capsule const queryCapsule_w(querySphere_w, intersectVector); + CellProperty const * const queryCell_w = CellProperty::getWorldCellProperty(); + CollisionWorld::getDatabase()->queryFor(collideWithDynamicObjects ? cs_queryDynamicObjectFlags : cs_queryStaticObjectFlags, queryCell_w, false, queryCapsule_w, colliderList); + + if (!colliderList.empty()) + { + Sphere collisionObjectSphere_w; + Vector collisionPosition_w; + float collisionClosestDistance = FLT_MAX; + Vector collisionNormal_w; + + for(ColliderList::const_iterator iterColliderList = colliderList.begin(); iterColliderList != colliderList.end(); ++iterColliderList) + { + CollisionProperty * collisionProperty = NON_NULL(*iterColliderList); + Object const * const owner = NON_NULL(&collisionProperty->getOwner()); + + // Don't collide with the excluded object. + if (excludeObject != owner) + { + Sphere const & collisionSphere_w = collisionProperty->getBoundingSphere_w(); + BaseExtent const * const collisionExtent = NON_NULL(collisionProperty->getExtent_l()); + Vector const & endPoint_l = owner->rotateTranslate_w2o(objectPosition_w); + Vector const & startPoint_l = owner->rotateTranslate_w2o(targetPosition_w); + float intersectTime = 0.0f; + Vector collisionSurfaceNormal_l; + + if (collisionExtent->intersect(startPoint_l, endPoint_l, &collisionSurfaceNormal_l, &intersectTime)) + { + Vector const & closestCollisionPosition = linearInterpolate(objectPosition_w, targetPosition_w, intersectTime); + float const collisionDistance = (closestCollisionPosition - objectPosition_w).approximateMagnitude(); + + // Grab the closest collision. + if (collisionDistance < collisionClosestDistance && collisionDistance > collisionRadius) + { + collisionClosestDistance = collisionDistance; + collisionPosition_w = closestCollisionPosition; + collisionObjectSphere_w = collisionSphere_w; + collisionNormal_w = owner->rotate_o2w(collisionSurfaceNormal_l); + hasValidCollision = true; + } + } + } + } + + if (hasValidCollision) + { + if(ConfigSharedCollision::useOriginalAvoidanceAlgorithm()) + { + Vector const & collisionSphereCenter_w = collisionObjectSphere_w.getCenter(); + Vector perpendiculara(Vector::perpendicular(collisionSphereCenter_w - objectPosition_w)); + Vector perpendicularb = -perpendiculara; + + // If normalize fails, do not return the collision. + hasValidCollision = perpendiculara.approximateNormalize() && perpendicularb.approximateNormalize(); + if(hasValidCollision) + { + float const collisionOffset = collisionObjectSphere_w.getRadius() + (collisionRadius * cs_shipBoundSphereRadiusGain * 2.f); + const Vector & avoidancePositionOption1_w = collisionSphereCenter_w + (perpendiculara * collisionOffset); + const Vector & avoidancePositionOption2_w = collisionSphereCenter_w + (perpendicularb * collisionOffset); + + // Make sure the ship takes the shortest path around the sphere + float const distance1 = avoidancePositionOption1_w.magnitudeBetweenSquared(targetPosition_w); + float const distance2 = avoidancePositionOption2_w.magnitudeBetweenSquared(targetPosition_w); + if (distance1 < distance2) + { + avoidancePosition_w = avoidancePositionOption1_w; + } + else + { + avoidancePosition_w = avoidancePositionOption2_w; + } + } + } + else + { + // Add the intersect and collision vectors. + Vector collisionVector(intersectVector + collisionNormal_w); + hasValidCollision = collisionVector.normalize(); + + // If normalize fails, do not use this collision. + if (hasValidCollision) + { + // Find a vector perpendicular to collision vector. + Vector const perpendiculara(Vector::perpendicular(collisionVector)); + Vector const perpendicularb(-perpendiculara); + + // Build a collision offset. + float const collisionOffset = ((collisionRadius * 2.0f) + collisionClosestDistance) * cs_shipBoundSphereRadiusGain; + Vector const & avoidancePositionOption1_w = collisionPosition_w + (perpendiculara * collisionOffset); + Vector const & avoidancePositionOption2_w = collisionPosition_w + (perpendicularb * collisionOffset); + + // Ensure the ship takes the shortest path around the sphere. + float const distance1 = avoidancePositionOption1_w.magnitudeBetweenSquared(targetPosition_w); + float const distance2 = avoidancePositionOption2_w.magnitudeBetweenSquared(targetPosition_w); + avoidancePosition_w = (distance1 < distance2) ? avoidancePositionOption1_w : avoidancePositionOption2_w; + } + else + { + WARNING_STRICT_FATAL(ConfigSharedCollision::isSpaceAiLoggingEnabled(), ("SpaceAvoidanceManager::getAvoidancePosition() - perpendicular.approximateNormalize() failed.")); + } + } + } + } + } + + return hasValidCollision; +} + +// ====================================================================== diff --git a/engine/shared/library/sharedCollision/src/shared/core/SpaceAvoidanceManager.h b/engine/shared/library/sharedCollision/src/shared/core/SpaceAvoidanceManager.h new file mode 100644 index 00000000..cdc8da11 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/SpaceAvoidanceManager.h @@ -0,0 +1,38 @@ +// ====================================================================== +// +// SpaceAvoidanceManager.h +// Copyright Sony Online Entertainment, Inc. +// +// ====================================================================== + +#ifndef INCLUDED_SpaceAvoidanceManager_H +#define INCLUDED_SpaceAvoidanceManager_H + +class Object; +class Transform; +class Vector; + +// ---------------------------------------------------------------------- +class SpaceAvoidanceManager +{ +public: + + static void install(); + static bool getAvoidancePosition(Object const & object, Vector const & sweepVector, Vector const & targetPosition, Vector & avoidancePosition_w, bool collideWithDynamicObjects = false); + static bool getAvoidancePosition(Transform const & transform_o2w, float const collisionRadius, Vector const & sweepVector, Vector const & targetPosition, Vector & avoidancePosition_w, const Object * const excludeObject, bool useDynamics = false); + +private: + + static void remove(); + + // Disable + + SpaceAvoidanceManager(); + ~SpaceAvoidanceManager(); + SpaceAvoidanceManager(SpaceAvoidanceManager const &); + SpaceAvoidanceManager & operator =(SpaceAvoidanceManager const &); +}; + +// ====================================================================== + +#endif // INCLUDED_SpaceAvoidanceManager_H diff --git a/engine/shared/library/sharedCollision/src/shared/core/SpatialDatabase.cpp b/engine/shared/library/sharedCollision/src/shared/core/SpatialDatabase.cpp new file mode 100644 index 00000000..05abd3be --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/SpatialDatabase.cpp @@ -0,0 +1,1314 @@ +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/SpatialDatabase.h" + +#include "sharedCollision/CollisionWorld.h" +#include "sharedCollision/CollisionDetect.h" +#include "sharedCollision/CollisionProperty.h" + +#include "sharedObject/Appearance.h" +#include "sharedObject/Object.h" +#include "sharedObject/World.h" +#include "sharedObject/ObjectTemplate.h" +#include "sharedObject/CellProperty.h" + +#include "sharedCollision/BarrierObject.h" +#include "sharedCollision/CollisionResolve.h" +#include "sharedCollision/CollisionUtils.h" +#include "sharedCollision/ConfigSharedCollision.h" +#include "sharedCollision/Containment3d.h" +#include "sharedCollision/DoorObject.h" +#include "sharedCollision/Extent.h" +#include "sharedCollision/Floor.h" +#include "sharedCollision/Footprint.h" +#include "sharedCollision/Intersect1d.h" +#include "sharedCollision/MeshExtent.h" +#include "sharedCollision/SimpleExtent.h" + +#include "sharedMath/Line3d.h" +#include "sharedMath/Ray3d.h" +#include "sharedMath/Segment3d.h" +#include "sharedMath/SphereTree.h" +#include "sharedMath/Range.h" +#include "sharedMath/ShapeUtils.h" + +#include +#include +#include + +// Need to disable this warning to make MultiListHandle work + +#pragma warning(disable : 4355) // 'this' used in base member initializer list + +// ====================================================================== + +namespace SpatialDatabaseNamespace +{ + SpatialDatabase::ShouldTriggerDoorHitCallback s_shouldTriggerDoorHitCallback; +} + +using namespace SpatialDatabaseNamespace; + +// ====================================================================== + +class CollisionSphereAccessor: public BaseSphereTreeAccessor +{ +public: + + static Sphere getExtent(CollisionProperty const * const collision) + { + return collision->getBoundingSphere_w(); + } + + static char const *getDebugName(CollisionProperty const * const collision) + { + if (collision) + { + Object const &object = collision->getOwner(); + ObjectTemplate const * const objectTemplate = object.getObjectTemplate(); + if (objectTemplate) + return objectTemplate->getName(); + } + + return 0; + } + +}; + +// ---------- + +class FloorSphereAccessor: public BaseSphereTreeAccessor +{ +public: + + static Sphere getExtent(Floor const * const floor) + { + return floor->getBoundingSphere_w(); + } + + static char const *getDebugName(Floor const * const floor) + { + if (floor) + { + Object const * const object = floor->getOwner(); + + if (object) + { + ObjectTemplate const * const objectTemplate = object->getObjectTemplate(); + if (objectTemplate) + return objectTemplate->getName(); + } + } + + return 0; + } + +}; + +// ====================================================================== +// class SpatialDatabase: PUBLIC STATIC +// ====================================================================== +/** + * This callback allows the user to prevent mobile/door hits from triggering + * the hit() and hitBy() collision logic on a door based on the return value of + * the callback. + * + * This functionality is used on the client to prevent a door from visually opening for + * a mounted creature or a rider. The actual stop logic for movement is on the server. + * + * @param callback If NULL, no check is made. If non-NULL, the callback is called + * any time a mobile is detected to have collided with a DoorObject. + * The return value of the callback dictates whether the hit() and hitBy() + * logic is called on the mobile and door. If the return value of callback + * is true, then the hit()/hitBy() functions are called; otherwise, hit()/hitBy() + * are not called. + */ + +void SpatialDatabase::setShouldTriggerDoorHitCallback (ShouldTriggerDoorHitCallback callback) +{ + s_shouldTriggerDoorHitCallback = callback; +} + +// ---------------------------------------------------------------------- + +SpatialDatabase::ShouldTriggerDoorHitCallback SpatialDatabase::getShouldTriggerDoorHitCallback () +{ + return s_shouldTriggerDoorHitCallback; +} + +// ====================================================================== + +SpatialDatabase::SpatialDatabase() +: m_staticTree(new CollisionSphereTree()) +, m_dynamicTree(new CollisionSphereTree()) +, m_doorTree(new CollisionSphereTree()) +, m_barrierTree(new CollisionSphereTree()) +, m_floorTree(new FloorSphereTree()) +, m_ignoreStack(new ObjectConstVec()) +{ +} + +// ---------- + +SpatialDatabase::~SpatialDatabase ( void ) +{ + delete m_staticTree; + m_staticTree = NULL; + + delete m_dynamicTree; + m_dynamicTree = NULL; + + delete m_doorTree; + m_doorTree = NULL; + + delete m_barrierTree; + m_barrierTree = NULL; + + delete m_floorTree; + m_floorTree = NULL; + + delete m_ignoreStack; + m_ignoreStack = NULL; +} + +// ---------------------------------------------------------------------- + +bool SpatialDatabase::canCollideWithStatics ( CollisionProperty * collision ) const +{ + if(collision == NULL) return false; + + // On the client, remote creatures and players don't collide with statics + + if( (!collision->isServerSide()) && (!collision->isPlayer())) + { + return false; + } + + if(collision->isPlayerControlled() && collision->isServerSide()) + { + return false; + } + + Vector oldPos = CollisionUtils::transformToCell(collision->getLastCell(),collision->getLastPos_p(),collision->getOwner().getParentCell()); + Vector newPos = collision->getOwner().getPosition_p(); + + Vector delta(newPos.x - oldPos.x, newPos.y - oldPos.y, newPos.z - oldPos.z); + + // If we didn't hit anything last frame, and we haven't moved, don't do collision. + + if((delta.magnitudeSquared() < 0.0000000001f ) && collision->isIdle() ) + { + return false; + } + + return true; +} + +// ---------- + +bool SpatialDatabase::canCollideWithStatic ( Object const * mobObject, Object * staticObject ) const +{ + // Objects can't collide with NULL + + if(mobObject == NULL) return false; + if(staticObject == NULL) return false; + + // Objects can't collide with themselves + + if(mobObject == staticObject) return false; + + return true; +} + +// ---------- + +bool SpatialDatabase::canCollideWithCreatures ( CollisionProperty const * collision ) const +{ + if(collision == NULL) return false; + + if(collision->isPlayerControlled() && collision->isServerSide()) + { + return false; + } + + return ConfigSharedCollision::getEnableCreatureCollision(); +} + +bool SpatialDatabase::canCollideWithCreature ( Object * player, Object * creature ) const +{ + if(!player) return false; + if(!creature) return false; + + if(player->getParentCell() != creature->getParentCell()) return false; + + CollisionProperty * playerCollision = player->getCollisionProperty(); + CollisionProperty * creatureCollision = creature->getCollisionProperty(); + + if(!playerCollision) return false; + if(!creatureCollision) return false; + + return playerCollision->canCollideWith(creatureCollision); +} + +// ---------- + +bool SpatialDatabase::canWalkOnFloor ( CollisionProperty * collision ) const +{ + if(collision == NULL) return false; + + if(collision->getFootprint() == NULL) return false; + + return true; +} + +// ---------------------------------------------------------------------- + +void SpatialDatabase::updateStaticCollision(CollisionProperty * mobCollision, ColliderList & colliderList) +{ + NOT_NULL(mobCollision); + + Object * mobObject = &mobCollision->getOwner(); + + NOT_NULL(mobObject); + + if(!canCollideWithStatics(mobCollision)) return; + + // ---------- + + Capsule queryCapsule_w = mobCollision->getQueryCapsule_w(); + + { + static ColliderList queryResults; + queryResults.clear(); + + m_staticTree->findInRange(queryCapsule_w, queryResults); + m_barrierTree->findInRange(queryCapsule_w, queryResults); + + int staticCount = static_cast(queryResults.size()); + + for(int i = 0; i < staticCount; i++) + { + CollisionProperty * staticCollision = queryResults.at(static_cast(i)); + + NOT_NULL(staticCollision); + + Object * staticObject = &staticCollision->getOwner(); + + NOT_NULL(staticObject); + + if(!canCollideWithStatic(mobObject,staticObject)) continue; + + if(!staticCollision->canCollideWith(mobCollision)) continue; + if(!mobCollision->canCollideWith(staticCollision)) continue; + + // ---------- + + Capsule queryCapsule_p = CollisionUtils::transformFromWorld(queryCapsule_w,staticObject->getParentCell()); + + DetectResult result = CollisionDetect::testCapsuleObject(queryCapsule_p,staticObject); + + if(result.collided) + { + colliderList.push_back(staticCollision); + } + } + } + + { + static ColliderList queryResults; + queryResults.clear(); + + m_doorTree->findInRange(queryCapsule_w, queryResults); + + int const staticCount = static_cast(queryResults.size()); + for(int i = 0; i < staticCount; i++) + { + CollisionProperty * const staticCollision = queryResults.at(static_cast(i)); + NOT_NULL(staticCollision); + + Object * const staticObject = &staticCollision->getOwner(); + NOT_NULL(staticObject); + + if (!canCollideWithStatic(mobObject,staticObject)) + continue; + + if (!staticCollision->canCollideWith(mobCollision)) + continue; + + if (!mobCollision->canCollideWith(staticCollision)) + continue; + + Capsule const queryCapsule_p = CollisionUtils::transformFromWorld(queryCapsule_w,staticObject->getParentCell()); + + DetectResult result = CollisionDetect::testCapsuleObject(queryCapsule_p,staticObject); + if (result.collided) + { + DoorObject * const door = safe_cast(staticObject); + if (!s_shouldTriggerDoorHitCallback || (*s_shouldTriggerDoorHitCallback)(mobCollision->getOwner(), *door)) + { + mobCollision->hit(staticCollision); + staticCollision->hitBy(mobCollision); + } + } + } + } +} //lint !e1762 // member function could be made const // Semantically this is non-const. + +// ---------------------------------------------------------------------- + +void SpatialDatabase::updateFloorCollision ( CollisionProperty * mobCollision, bool attachFromObject ) +{ + NOT_NULL(mobCollision); + + Object * mobObject = &mobCollision->getOwner(); + + NOT_NULL(mobObject); + + if(!canWalkOnFloor(mobCollision)) return; + + Footprint const * foot = mobCollision->getFootprint(); + + NOT_NULL(foot); + + NOT_NULL(mobCollision); + + // ---------- + // Test for collisions with floors + + Capsule queryCapsule_w = mobCollision->getQueryCapsule_w(); + + static FloorVec queryResults; + + queryResults.clear(); + + m_floorTree->findInRange( queryCapsule_w, queryResults ); + + for(uint i = 0; i < queryResults.size(); i++) + { + Floor * floor = queryResults.at(i); + + if(!floor) continue; + + Capsule queryCapsule_p = CollisionUtils::transformFromWorld(queryCapsule_w,floor->getCell()); + + BaseExtent const * floorExtent_p = floor->getExtent_p(); + + DetectResult result = CollisionDetect::testCapsuleExtent(queryCapsule_p,floorExtent_p); + + if(result.collided) + { + Object const * owner = floor->getOwner(); + + if(owner) + { + if(mobObject->getParentCell() != owner->getParentCell()) + { + continue; + } + } + + Footprint * pFoot = mobCollision->getFootprint(); + + if(pFoot) + { + IGNORE_RETURN( pFoot->attachTo(floor,attachFromObject) ); + } + } + } +} //lint !e1762 // member function could be made const // Semantically this is non-const. + +// ---------------------------------------------------------------------- + +void SpatialDatabase::updateCreatureCollision(CollisionProperty * playerCollision, ColliderList & colliderList) +{ + NOT_NULL(playerCollision); + + Object * playerObject = &playerCollision->getOwner(); + + NOT_NULL(playerObject); + + if(!canCollideWithCreatures(playerCollision)) return; + + // ---------- + + Capsule queryCapsule_w = playerCollision->getQueryCapsule_w(); + + static ColliderList queryResults; + + queryResults.clear(); + + m_dynamicTree->findInRange( queryCapsule_w, queryResults ); + + int creatureCount = static_cast(queryResults.size()); + + for(int j = 0; j < creatureCount; j++) + { + CollisionProperty * creatureCollision = queryResults.at(static_cast(j)); + + NOT_NULL(creatureCollision); + + Object * creatureObject = &creatureCollision->getOwner(); + + NOT_NULL(creatureObject); + + if(!canCollideWithCreature(playerObject,creatureObject)) continue; + + // ---------- + + Capsule queryCapsule_p = CollisionUtils::transformFromWorld(queryCapsule_w,creatureObject->getParentCell()); + + DetectResult result = CollisionDetect::testCapsuleObject(queryCapsule_p,creatureObject); + + if(result.collided) + { + colliderList.push_back(creatureCollision); + } + + } +} //lint !e1762 // member function could be made const // Semantically this is non-const. + +// ---------------------------------------------------------------------- + +bool SpatialDatabase::addObject(Query const query, Object * const object) +{ + if(object == NULL) + return false; + + CollisionProperty * collision = object->getCollisionProperty(); + + if(collision == NULL) + return false; + + if(collision->getSpatialSubdivisionHandle()) + { + WARNING(true, ("SpatialDatabase:: %s has already been added and has a SpatialSubdivisionHandle", object->getObjectTemplate()->getName())); + return false; + } + + switch (query) + { + case Q_Static: + collision->setSpatialSubdivisionHandle(m_staticTree->addObject(collision)); + break; + case Q_Dynamic: + collision->setSpatialSubdivisionHandle(m_dynamicTree->addObject(collision)); + break; + case Q_Barriers: + collision->setSpatialSubdivisionHandle(m_barrierTree->addObject(collision)); + break; + case Q_Doors: + collision->setSpatialSubdivisionHandle(m_doorTree->addObject(collision)); + break; + case Q_All: + FATAL(true, ("SpatialDatabase::addObject: Q_All is not a valid parameter for add")); + default: + break; + } + + return true; +} + +// ---------------------------------------------------------------------- + +bool SpatialDatabase::removeObject(Query const query, Object * const object) +{ + if(object == NULL) + return false; + + CollisionProperty * collision = object->getCollisionProperty(); + + if(collision == NULL) + return false; + + if(collision->getSpatialSubdivisionHandle() == NULL) + return false; + + switch (query) + { + case Q_Static: + m_staticTree->removeObject(collision->getSpatialSubdivisionHandle()); + break; + case Q_Dynamic: + m_dynamicTree->removeObject(collision->getSpatialSubdivisionHandle()); + break; + case Q_Barriers: + m_barrierTree->removeObject(collision->getSpatialSubdivisionHandle()); + break; + case Q_Doors: + m_doorTree->removeObject(collision->getSpatialSubdivisionHandle()); + break; + case Q_All: + WARNING(true, ("SpatialDatabase::removeObject: is being called with Q_All")); + m_staticTree->removeObject(collision->getSpatialSubdivisionHandle()); + m_dynamicTree->removeObject(collision->getSpatialSubdivisionHandle()); + m_barrierTree->removeObject(collision->getSpatialSubdivisionHandle()); + m_doorTree->removeObject(collision->getSpatialSubdivisionHandle()); + break; + + default: + break; + } + + collision->setSpatialSubdivisionHandle(NULL); + + return true; +} + +// ---------------------------------------------------------------------- + +int SpatialDatabase::getObjectCount(int queryMask) const +{ + int count = 0; + + if (queryMask & Q_Static) + count += m_staticTree->getObjectCount(); + + if (queryMask & Q_Dynamic) + count += m_dynamicTree->getObjectCount(); + + return count; +} + +// ---------------------------------------------------------------------- + +void SpatialDatabase::queryFor(int queryMask, CellProperty const * const cell_p, bool restrictToSameCell, Capsule const & capsule_p, ColliderList & collidedWith) const +{ + Capsule const capsule_w(CollisionUtils::transformToWorld(cell_p, capsule_p)); + + ColliderList temporaryCollidedWith; + + if (queryMask & Q_Static) + m_staticTree->findInRange(capsule_w, temporaryCollidedWith); + + if (queryMask & Q_Dynamic) + m_dynamicTree->findInRange(capsule_w, temporaryCollidedWith); + + if (queryMask & Q_Barriers) + m_barrierTree->findInRange(capsule_w, temporaryCollidedWith); + + if (queryMask & Q_Doors) + m_doorTree->findInRange(capsule_w, temporaryCollidedWith); + + int const potentialCount = static_cast(temporaryCollidedWith.size()); + + for(int i = 0; i < potentialCount; ++i) + { + CollisionProperty * const toTestCollision = temporaryCollidedWith.at(static_cast(i)); + + Object const * const toTestObject = &toTestCollision->getOwner(); + + if ((restrictToSameCell) && (cell_p != toTestObject->getParentCell())) + { + continue; + } + + Capsule const toTestCapsule_p(CollisionUtils::transformFromWorld(capsule_w,toTestObject->getParentCell())); + + DetectResult const result(CollisionDetect::testCapsuleObjectAgainstAllTypes(toTestCapsule_p, toTestObject)); + + if(result.collided) + { + collidedWith.push_back(toTestCollision); + } + } +} + +// ---------------------------------------------------------------------- +// The ignore stack is used for temporarily "hiding" objects during +// a queryInteraction or queryMaterial test + +void SpatialDatabase::pushIgnoreObject ( Object const * object ) +{ + m_ignoreStack->push_back(object); +} + +void SpatialDatabase::popIgnoreObject ( void ) +{ + m_ignoreStack->pop_back(); +} + +bool SpatialDatabase::checkIgnoreObject ( Object const * object ) const +{ + unsigned int ignoreCount = m_ignoreStack->size(); + + for(unsigned int i = 0; i < ignoreCount; i++) + { + if(object == m_ignoreStack->at(i)) + { + return true; + } + } + + return false; +} + +// ---------------------------------------------------------------------- + +bool SpatialDatabase::addFloor ( Floor * floor ) +{ + if(floor == NULL) return false; + + if(floor->getSpatialSubdivisionHandle()) return false; + + // ---------- + + floor->setSpatialSubdivisionHandle( m_floorTree->addObject(floor) ); + + return true; +} + +// ---------- + +bool SpatialDatabase::removeFloor ( Floor * floor ) +{ + if(floor == NULL) return false; + + if(floor->getSpatialSubdivisionHandle() == NULL) return false; + + // ---------- + + m_floorTree->removeObject( floor->getSpatialSubdivisionHandle() ); + + floor->setSpatialSubdivisionHandle(NULL); + + return true; +} + +// ---------- + +int SpatialDatabase::getFloorCount ( void ) const +{ + return m_floorTree->getObjectCount(); +} + +// ---------------------------------------------------------------------- + +bool SpatialDatabase::moveObject ( CollisionProperty * collision ) +{ + if(collision == NULL) return false; + + SpatialSubdivisionHandle * handle = collision->getSpatialSubdivisionHandle(); + + if(handle == NULL) return false; + + // ---------- + + if (collision->isMobile()) + { + m_dynamicTree->move(handle); + } + else + { + m_staticTree->move(handle); + } + + Floor * floor = collision->getFloor(); + + if (floor) + { + m_floorTree->move(floor->getSpatialSubdivisionHandle()); + } + + return true; +} + +// ---------------------------------------------------------------------- + +bool SpatialDatabase::queryStatics ( Sphere const & sphere, ObjectVec * outList ) const +{ + return queryStatics(MultiShape(sphere),outList); +} + +// ---------- + +bool SpatialDatabase::queryStatics ( AxialBox const & box, ObjectVec * outList ) const +{ + return queryStatics(MultiShape(box),outList); +} + +// ---------- + +bool SpatialDatabase::queryStatics ( MultiShape const & shape, ObjectVec * outList ) const +{ + return queryStatics( NULL, shape, outList ); +} + +// ---------- + +bool SpatialDatabase::queryStatics ( Line3d const & line, ObjectVec * outList ) const +{ + bool result = false; + + Vector normDir = line.getNormal(); + IGNORE_RETURN(normDir.normalize()); + + static ColliderList results; + + results.clear(); + + m_staticTree->findOnRay(line.getPoint(),normDir,results); + m_barrierTree->findOnRay(line.getPoint(),normDir,results); + + uint const resultSize = results.size(); + for(uint i = 0; i < resultSize; ++i) + { + CollisionProperty * collision = results[i]; + + BaseExtent const * extent = collision->getExtent_p(); + + Range hitRange = extent->rangedIntersect(line); + + if(!hitRange.isEmpty()) + { + result = true; + + if(outList) + { + outList->push_back( &collision->getOwner() ); + } + else + { + break; + } + } + } + + return result; +} + +// ---------- + +bool SpatialDatabase::queryStatics ( CellProperty const * cell, MultiShape const & shape, ObjectVec * outList ) const +{ + return queryObjects(cell,shape,outList,NULL); +} + +// ---------------------------------------------------------------------- + +bool SpatialDatabase::queryDynamics ( Sphere const & sphere, ObjectVec * outList ) const +{ + return queryObjects( NULL, MultiShape(sphere), NULL, outList ); +} + +// ---------- + +bool SpatialDatabase::queryDynamics ( MultiShape const & shape, ObjectVec * outList ) const +{ + return queryObjects( NULL, shape, NULL, outList ); +} + +// ---------------------------------------------------------------------- + +bool SpatialDatabase::queryObjects ( CellProperty const * cell, MultiShape const & shape, ObjectVec * staticList, ObjectVec * creatureList ) const +{ + return queryObjects(cell,shape,Vector::zero,staticList,creatureList); +} + +// ---------------------------------------------------------------------- + +bool SpatialDatabase::queryObjects ( CellProperty const * cell, MultiShape const & shape, Vector const & velocity, + ObjectVec * staticList, ObjectVec * creatureList ) const +{ + SimpleExtent tempExtent(shape); + + bool result = false; + + static ColliderList tempResults; + + Sphere sphere(shape.getBoundingSphere()); + + Capsule capsule(sphere,velocity); + + // ---------- + + if(staticList) + { + tempResults.clear(); + + m_staticTree->findInRange( capsule, tempResults ); + m_dynamicTree->findInRange( capsule, tempResults ); + m_barrierTree->findInRange( capsule, tempResults ); + + for(uint i = 0; i < tempResults.size(); i++) + { + CollisionProperty * staticCollision = tempResults[i]; + + NOT_NULL(staticCollision); + + Object * owner = &(staticCollision->getOwner()); + + if(cell && (owner->getParentCell() != cell)) continue; + + DetectResult detectResult = CollisionDetect::testExtents(&tempExtent,velocity,staticCollision->getExtent_p()); + + if(detectResult.collided) + { + result = true; + + staticList->push_back(owner); + } + } + } + + // ---------- + + if(creatureList) + { + tempResults.clear(); + + m_dynamicTree->findInRange( capsule, tempResults ); + + for(uint i = 0; i < tempResults.size(); i++) + { + CollisionProperty * mobCollision = tempResults[i]; + + NOT_NULL(mobCollision); + + Object * owner = &(mobCollision->getOwner()); + + if(cell && (owner->getParentCell() != cell)) continue; + + DetectResult detectResult = CollisionDetect::testExtents(&tempExtent,velocity,mobCollision->getExtent_p()); + + if(detectResult.collided) + { + result = true; + + creatureList->push_back(&mobCollision->getOwner()); + } + } + } + + // ---------- + + return result; +} + +// ---------------------------------------------------------------------- + +bool SpatialDatabase::queryFloors ( Sphere const & sphere_w, FloorVec * outList ) const +{ + return queryFloors(sphere_w,Vector::zero,outList); +} + +bool SpatialDatabase::queryFloors ( Capsule const & capsule, FloorVec * outList ) const +{ + return queryFloors( capsule.getSphereA(), capsule.getDelta(), outList ); +} + +// ---------- + +bool SpatialDatabase::queryFloors ( Sphere const & sphere_w, Vector const & delta, FloorVec * outList ) const +{ + if(outList) outList->clear(); + + static FloorVec tempResults; + + tempResults.clear(); + + m_floorTree->findInRange( Capsule(sphere_w,delta), tempResults ); + + bool result = false; + + for(uint i = 0; i < tempResults.size(); i++) + { + Floor * floor = tempResults[i]; + + // ---------- + + if(!floor) continue; + + if(floor->getCell() != CellProperty::getWorldCellProperty() ) continue; + + // ---------- + // testing world-space sphere with parent-space extent is OK because we know the extent is in the world cell + + DetectResult detectResult = CollisionDetect::testSphereExtent(sphere_w,delta,floor->getExtent_p()); + + if(detectResult.collided) + { + result = true; + + if(outList) outList->push_back(floor); + } + } + + // ---------- + + return result; +} + +// ---------- + +bool SpatialDatabase::queryFloors ( CellProperty const * cell, Segment3d const & seg, FloorVec * outList ) const +{ + bool result = false; + + // ---------- + + FloorVec tempResults; + + m_floorTree->findOnSegment( seg.getBegin(), seg.getEnd(), tempResults ); + + for(uint i = 0; i < tempResults.size(); i++) + { + Floor * floor = tempResults[i]; + + if(!floor) continue; + + if(cell) + { + if(floor->getCell() != cell) continue; + } + + BaseExtent const * floorExtent = floor->getExtent_p(); + + if(!floorExtent->rangedIntersect(seg).isEmpty()) + { + result = true; + + if(outList) outList->push_back(floor); + } + } + + // ---------- + + return result; +} + +// ---------------------------------------------------------------------- + +bool SpatialDatabase::queryCloseStatics ( Vector const & point_w, float maxDistance, ObjectVec * outList ) const +{ + float minClose; + float maxClose; + + CollisionProperty * dummy = NULL; + + if(m_staticTree->findClosest(point_w,maxDistance,dummy,minClose,maxClose)) + { + return queryStatics(Sphere(point_w,maxClose),outList); + } + else + { + return false; + } +} + +// ---------------------------------------------------------------------- + +bool SpatialDatabase::queryCloseFloors ( Vector const & point_w, float maxDistance, FloorVec * outList ) const +{ + float minClose; + float maxClose; + + Floor * dummy = NULL; + + if(m_floorTree->findClosest(point_w,maxDistance,dummy,minClose,maxClose)) + { + return queryFloors(Sphere(point_w,maxClose),outList); + } + else + { + return false; + } +} + +// ---------------------------------------------------------------------- + +bool SpatialDatabase::queryInteraction ( CellProperty const * cell, + Segment3d const & seg_p, + Object const * ignoreObject, + Object const * & outHitObject, + float & outHitTime) const +{ + // NOTE -- this returns the first object tested against and not + // the closest as is implied; + + static ColliderList results; + static ColliderList barrierResults; + + results.clear(); + barrierResults.clear(); + + Vector begin_w = CollisionUtils::transformToWorld(cell,seg_p.getBegin()); + Vector end_w = CollisionUtils::transformToWorld(cell,seg_p.getEnd()); + + m_staticTree->findOnSegment(begin_w,end_w,results); + m_barrierTree->findOnSegment(begin_w,end_w,barrierResults); + + ColliderList::const_iterator ii = barrierResults.begin(); + ColliderList::const_iterator iiEnd = barrierResults.end(); + + for(; ii != iiEnd; ++ii) + { + CollisionProperty * const collision = *ii; + BarrierObject const * const barrier = safe_cast(&collision->getOwner()); + + if (barrier && barrier->isActive()) + results.push_back(collision); + } + + ii = results.begin(); + iiEnd = results.end(); + + for(; ii != iiEnd; ++ii) + { + CollisionProperty * const collision = *ii; + + if (&collision->getOwner() == ignoreObject) + continue; + + if (checkIgnoreObject(&collision->getOwner())) + continue; + + if (cell && (collision->getOwner().getParentCell() != cell)) + continue; + + // _must_ do this test - lairs use the flags to keep from blocking LOS + + if( !collision->blocksInteraction(IT_See) ) continue; + + // ---------- + + BaseExtent const * extent = collision->getExtent_p(); + + float t = 0.0f; + if (extent->intersect(seg_p.getBegin(), seg_p.getEnd(), &t)) + { + outHitObject = &collision->getOwner(); + outHitTime = t * seg_p.getBegin().magnitudeBetween(seg_p.getEnd()); + return true; + } + } + + return false; +} + +// ---------------------------------------------------------------------- + +bool SpatialDatabase::queryMaterial ( CellProperty const * cell, + Vector const & point_p, + MaterialType /*material*/ ) const +{ + static ColliderList results; + + results.clear(); + + Vector point_w = CollisionUtils::transformToWorld(cell,point_p); + + m_staticTree->findAtPoint(point_w,results); + m_barrierTree->findAtPoint(point_w,results); + + for(uint i = 0; i < results.size(); i++) + { + CollisionProperty * collision = results[i]; + + if(checkIgnoreObject(&collision->getOwner())) continue; + + if(cell && (collision->getOwner().getParentCell() != cell)) + { + continue; + } + + if( !collision->blocksMovement() ) continue; + + // ---------- + + BaseExtent const * extent = collision->getExtent_p(); + + if(extent->contains(point_p)) + { + return true; + } + } + + return false; +} + +// ---------------------------------------------------------------------- + +bool SpatialDatabase::queryMaterial ( CellProperty const * cell, + Segment3d const & seg_p, + MaterialType /*material*/ ) const +{ + static ColliderList results; + + results.clear(); + + Vector begin_w = CollisionUtils::transformToWorld(cell,seg_p.getBegin()); + Vector end_w = CollisionUtils::transformToWorld(cell,seg_p.getEnd()); + + m_staticTree->findOnSegment(begin_w,end_w,results); + m_barrierTree->findOnSegment(begin_w,end_w,results); + + for(uint i = 0; i < results.size(); i++) + { + CollisionProperty * collision = results[i]; + + if(checkIgnoreObject(&collision->getOwner())) continue; + + if(cell && (collision->getOwner().getParentCell() != cell)) + { + continue; + } + + if( !collision->blocksMovement() ) continue; + + // ---------- + + BaseExtent const * extent = collision->getExtent_p(); + + // don't do material queries on mesh extents + + // TODO: why are we skipping out on the tests when the extent is a MeshExtent??? + MeshExtent const * mesh = dynamic_cast(extent); + + if(mesh != NULL) continue; + + Range hitRange = extent->rangedIntersect(seg_p); + + if(!hitRange.isEmpty()) + { + return true; + } + } + + return false; +} + +// ---------------------------------------------------------------------- + +bool SpatialDatabase::queryMaterial ( CellProperty const * cellA, + Sphere const & sphereA_p, + Vector const & velocityA_p, + MaterialType /*materialA*/, + bool checkFlora, + bool checkFauna, + Object const * ignoreObject, + float & outCollisionTime ) const +{ + Capsule queryCapsule_w = CollisionUtils::transformToWorld(cellA, Capsule(sphereA_p,velocityA_p)); + + static ColliderList results; + + results.clear(); + + m_staticTree->findInRange(queryCapsule_w,results); + m_barrierTree->findInRange(queryCapsule_w,results); + + { + for(uint i = 0; i < results.size(); i++) + { + CollisionProperty * collisionB = results[i]; + + NOT_NULL(collisionB); + + if(&collisionB->getOwner() == ignoreObject) continue; + + if(checkIgnoreObject(&collisionB->getOwner())) continue; + + if(!checkFlora && collisionB->isFlora()) + { + continue; + } + + if( !collisionB->blocksMovement() ) continue; + + // ---------- + + CellProperty const * cellB = collisionB->getCell(); + + Sphere sphereA_B = CollisionUtils::transformToCell(cellA,sphereA_p,cellB); + + Vector velocityA_B = CollisionUtils::rotateToCell(cellA,velocityA_p,cellB); + + DetectResult result = CollisionDetect::testSphereObject(sphereA_B,velocityA_B,&collisionB->getOwner()); + + if(result.collided) + { + outCollisionTime = result.collisionTime.getMin(); + + return true; + } + } + } + + if(checkFauna) + { + results.clear(); + + m_dynamicTree->findInRange(queryCapsule_w,results); + + for(uint i = 0; i < results.size(); i++) + { + CollisionProperty * collisionB = results[i]; + + NOT_NULL(collisionB); + if (collisionB == NULL) + continue; + + // ---------- + + if(&collisionB->getOwner() == ignoreObject) continue; + + if(checkIgnoreObject(&collisionB->getOwner())) continue; + + CellProperty const * cellB = collisionB->getCell(); + + Sphere sphereA_B = CollisionUtils::transformToCell(cellA,sphereA_p,cellB); + + Vector velocityA_B = CollisionUtils::rotateToCell(cellA,velocityA_p,cellB); + + DetectResult result = CollisionDetect::testSphereObject(sphereA_B,velocityA_B,&collisionB->getOwner()); + + if(result.collided) + { + outCollisionTime = result.collisionTime.getMin(); + + return true; + } + } + } + + return false; +} + +// ---------------------------------------------------------------------- + +bool SpatialDatabase::queryMaterial ( CellProperty const * cellA, + Sphere const & sphereA_p, + MaterialType /*materialA*/ ) const +{ + Sphere sphereA_w = CollisionUtils::transformToWorld(cellA,sphereA_p); + + static ColliderList results; + + results.clear(); + + m_staticTree->findInRange(sphereA_w.getCenter(),sphereA_w.getRadius(),results); + m_barrierTree->findInRange(sphereA_w.getCenter(),sphereA_w.getRadius(),results); + + for(uint i = 0; i < results.size(); i++) + { + CollisionProperty * collisionB = results[i]; + + if(checkIgnoreObject(&collisionB->getOwner())) continue; + + if( !collisionB->blocksMovement() ) continue; + + // ---------- + + CellProperty const * cellB = collisionB->getCell(); + + Sphere sphereA_B = CollisionUtils::transformToCell(cellA,sphereA_p,cellB); + + DetectResult result = CollisionDetect::testSphereObject(sphereA_B,&collisionB->getOwner()); + + if(result.collided) + { + return true; + } + } + + return false; +} diff --git a/engine/shared/library/sharedCollision/src/shared/core/SpatialDatabase.h b/engine/shared/library/sharedCollision/src/shared/core/SpatialDatabase.h new file mode 100644 index 00000000..1b6553ba --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/core/SpatialDatabase.h @@ -0,0 +1,224 @@ +// ====================================================================== +// +// SpatialDatabase.h +// copyright (c) 2001 Sony Online Entertainment +// +// ---------------------------------------------------------------------- + +#ifndef INCLUDED_SpatialDatabase_H +#define INCLUDED_SpatialDatabase_H + +#include "sharedCollision/CollisionEnums.h" + +class AxialBox; +class Capsule; +class CellProperty; +class CollisionProperty; +class CollisionSphereAccessor; +class DebugShapeRenderer; +class DoorObject; +class Floor; +class FloorSphereAccessor; +class Line3d; +class MultiShape; +class Object; +class Range; +class Segment3d; +class SpatialSubdivisionHandle; +class Sphere; +class Vector; +class VectorArgb; + +typedef stdvector::fwd FloorVec; +typedef stdvector::fwd ObjectVec; +typedef stdvector::fwd ObjectConstVec; +typedef stdvector::fwd ColliderList; + + +template +class SphereTree; + +// ====================================================================== + +class SpatialDatabase +{ +public: + enum Query + { + Q_None = 0, + Q_Static = (1 << 0), + Q_Dynamic = (1 << 1), + Q_Barriers = (1 << 2), + Q_Doors = (1 << 3), + //Q_Floor = (1 << X), // not yet implemented + Q_Physicals = (Q_Static | Q_Dynamic), + Q_All = (Q_Static | Q_Dynamic | Q_Barriers | Q_Doors) + }; + + typedef bool (*ShouldTriggerDoorHitCallback)(Object const &mobileObject, DoorObject const &doorObject); + +public: + + static void setShouldTriggerDoorHitCallback (ShouldTriggerDoorHitCallback callback); + static ShouldTriggerDoorHitCallback getShouldTriggerDoorHitCallback (); + +public: + + SpatialDatabase(); + ~SpatialDatabase(); + + // ---------- + + void update ( float time ); + + void drawDebugShapes ( DebugShapeRenderer * renderer, VectorArgb color ) const; + + bool moveObject ( CollisionProperty * collision ); + + + // ---------------------------------------------------------------------- + + // This interface is meant to be a simpler interface for finding potential + // collisions with objects in the world. It is primarily used for the + // space game and is meant to eventually be the focal point for accessing + // data from the databases + + // this doesn't support floors, yet... + + bool addObject(Query query, Object * object); + bool removeObject(Query query, Object * object); + + bool hasObject(int queryMask, Object * object) const; + int getObjectCount(int queryMask) const; + + void queryFor(int queryMask, CellProperty const * cell_p, bool restrictToSameCell, Capsule const & capsule_p, ColliderList & collidedWith) const; + + // ---------------------------------------------------------------------- + + bool addFloor ( Floor * floor ); + bool removeFloor ( Floor * floor ); + bool hasFloor ( Floor * floor ) const; + + int getFloorCount ( void ) const; + + // ---------- + + bool queryInteraction ( CellProperty const * cell, + Segment3d const & seg, + Object const * ignoreObject, + Object const * & outHitObject, + float & outHitTime ) const; + + bool queryMaterial ( CellProperty const * cell, + Vector const & point, + MaterialType material ) const; + + bool queryMaterial ( CellProperty const * cell, + Segment3d const & seg, + MaterialType material ) const; + + bool queryMaterial ( CellProperty const * cell, + Sphere const & sphere, + Vector const & velocity, + MaterialType material, + bool checkFlora, + bool checkFauna, + Object const * ignoreObject, + float & outCollisionTime ) const; + + bool queryMaterial ( CellProperty const * cell, + Sphere const & sphere, + MaterialType material) const; + + // ---------- + // The versions that don't take a cell argument query the worldspace + + bool queryStatics ( Sphere const & sphere, ObjectVec * outList ) const; + bool queryStatics ( AxialBox const & box, ObjectVec * outList ) const; + bool queryStatics ( MultiShape const & shape, ObjectVec * outList ) const; + bool queryStatics ( Line3d const & line, ObjectVec * outList ) const; + + bool queryStatics ( CellProperty const * cell, MultiShape const & shape, ObjectVec * outList ) const; + + bool queryDynamics ( Sphere const & sphere, ObjectVec * outList ) const; + bool queryDynamics ( MultiShape const & shape, ObjectVec * outList ) const; + + bool queryObjects ( CellProperty const * cell, MultiShape const & shape, ObjectVec * outStatics, ObjectVec * outCreatures ) const; + bool queryObjects ( CellProperty const * cell, MultiShape const & shape, Vector const & delta, ObjectVec * outStatics, ObjectVec * outCreatures ) const; + + bool queryFloors ( Sphere const & sphere_w, FloorVec * outList ) const; + bool queryFloors ( Capsule const & capsule_w, FloorVec * outList ) const; + bool queryFloors ( Sphere const & sphere_w, Vector const & delta, FloorVec * outList ) const; + + bool queryFloors ( CellProperty const * cell, Segment3d const & seg, FloorVec * outList ) const; + + // [1] + + bool queryCloseStatics ( Vector const & point, float maxDistance, ObjectVec * outList ) const; + bool queryCloseFloors ( Vector const & point, float maxDistance, FloorVec * outList ) const; + + // ---------- + + void updateStaticCollision(CollisionProperty * mobCollision, ColliderList & colliderList); + void updateFloorCollision(CollisionProperty * mobCollision, bool attachFromObject ); + void updateCreatureCollision(CollisionProperty * playerCollision, ColliderList & colliderList); + + // ---------------------------------------------------------------------- + + void pushIgnoreObject ( Object const * object ); + void popIgnoreObject ( void ); + +protected: + + typedef SphereTree CollisionSphereTree; + typedef SphereTree FloorSphereTree; + + // ---------- + + bool canCollideWithStatics ( CollisionProperty * collision ) const; + bool canCollideWithStatic ( Object const * pDynamic, Object * pStatic ) const; + + bool canCollideWithCreatures ( CollisionProperty const * collision ) const; + bool canCollideWithCreature ( Object * pPlayer, Object * pCreature ) const; + + bool canWalkOnFloor ( CollisionProperty * collision ) const; + + bool checkIgnoreObject ( Object const * object ) const; + + // ---------- + + CollisionSphereTree * m_staticTree; + CollisionSphereTree * m_dynamicTree; + CollisionSphereTree * m_doorTree; + CollisionSphereTree * m_barrierTree; + + FloorSphereTree * m_floorTree; + + ObjectConstVec * m_ignoreStack; + +private: + + SpatialDatabase( SpatialDatabase const & copy ); + SpatialDatabase & operator = ( SpatialDatabase const & copy ); +}; + +// ---------- +// [1] - queryCloseStatics/Floors - + +// If I'm trying to find the closest static object to a given point, I +// can't just use the closest sphere in the sphere tree - since the spheres +// fit their objects loosely, there may be a sphere slightly farther away +// than the closest sphere whose contents are closer than the contents of +// the closest sphere. So, in order to find the actual closest static we +// find the closest sphere, find the farthest distance from the point to +// the closest sphere, query the static tree for everything within +// that far distance, and then look through the results of the query to +// see which object is actually closest. The closest static object _must_ +// be inside that far distance, and most of the time it'll be the object +// in the closest sphere (but not always) + + +// ====================================================================== + +#endif // #ifndef INCLUDED_SpatialDatabase_H + diff --git a/engine/shared/library/sharedCollision/src/shared/extent/BaseExtent.cpp b/engine/shared/library/sharedCollision/src/shared/extent/BaseExtent.cpp new file mode 100644 index 00000000..cc0b652a --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/BaseExtent.cpp @@ -0,0 +1,75 @@ +// ====================================================================== +// +// BaseExtent.cpp +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/BaseExtent.h" + +#include "sharedCollision/Containment3d.h" + +#include "sharedMath/Transform.h" +#include "sharedMath/Sphere.h" +#include "sharedMath/Range.h" +#include "sharedMath/Segment3d.h" + + +// ---------------------------------------------------------------------- + +BaseExtent::BaseExtent(void) +: m_extentType(ET_Null) +{ +} + +BaseExtent::BaseExtent( ExtentType extentType ) +: m_extentType(extentType) +{ +} + +BaseExtent::~BaseExtent(void) +{ +} + +// ---------------------------------------------------------------------- + +bool BaseExtent::intersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, float * time) const +{ + return(realIntersect(begin, end, surfaceNormal, time)); +} + +bool BaseExtent::intersect(Vector const & begin, Vector const & end, float * time ) const +{ + Vector * surfaceNormal = 0; + return(realIntersect(begin, end, surfaceNormal, time)); +} + +bool BaseExtent::intersect(Vector const & begin, Vector const & end) const +{ + Vector * surfaceNormal = 0; + float * time = 0; + return(realIntersect(begin, end, surfaceNormal, time)); +} + +// ---------------------------------------------------------------------- + +Vector BaseExtent::getCenter ( void ) const +{ + return getBoundingSphere().getCenter(); +} + +// ---------- + +float BaseExtent::getRadius ( void ) const +{ + return getBoundingSphere().getRadius(); +} + +// ---------------------------------------------------------------------- + +bool BaseExtent::validate ( void ) const +{ + return true; +} + +// ---------------------------------------------------------------------- diff --git a/engine/shared/library/sharedCollision/src/shared/extent/BaseExtent.h b/engine/shared/library/sharedCollision/src/shared/extent/BaseExtent.h new file mode 100644 index 00000000..94a3833c --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/BaseExtent.h @@ -0,0 +1,90 @@ +// ====================================================================== +// +// BaseExtent.h +// Austin Appleby +// +// copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_BaseExtent_H +#define INCLUDED_BaseExtent_H + +#include "sharedCollision/CollisionEnums.h" + +class Iff; +class Vector; +class DebugShapeRenderer; +class Transform; +class Sphere; +class Line3d; +class Ray3d; +class Segment3d; +class Range; +class AxialBox; + +// ====================================================================== + +class BaseExtent +{ +public: + + BaseExtent(void); + BaseExtent( ExtentType extentType ); + virtual ~BaseExtent(void); + + // ---------- + // Virtual Extent interface + + virtual void load ( Iff & iff ) = 0; + virtual void write ( Iff & iff ) const = 0; + + virtual bool contains ( Vector const & vector ) const = 0; + virtual bool contains ( Vector const & begin, Vector const & end ) const = 0; + + bool intersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, real * time) const; + bool intersect(Vector const & begin, Vector const & end, real * time) const; + bool intersect(Vector const & begin, Vector const & end) const; + + virtual Range rangedIntersect(Line3d const & line) const = 0; + virtual Range rangedIntersect(Ray3d const & ray) const = 0; + virtual Range rangedIntersect(Segment3d const & seg) const = 0; + + virtual void drawDebugShapes ( DebugShapeRenderer * renderer ) const = 0; + + virtual BaseExtent * clone ( void ) const = 0; + virtual void copy ( BaseExtent const * source ) = 0; + virtual void transform ( BaseExtent const * parent, Transform const & tform, float scale ) = 0; + virtual AxialBox getBoundingBox ( void ) const = 0; + virtual Sphere getBoundingSphere ( void ) const = 0; + virtual Vector getCenter ( void ) const; + virtual float getRadius ( void ) const; // not necessarily the same as the bounding sphere radius + + virtual bool validate ( void ) const; + + // ---------- + // Concrete Extent interface + + ExtentType const & getType ( void ) const; +protected: + virtual bool realIntersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, real * time) const = 0; +private: + + ExtentType m_extentType; + + // ---------- + + BaseExtent(const BaseExtent &); + BaseExtent &operator =(const BaseExtent &); +}; + +// ---------------------------------------------------------------------- + +inline ExtentType const & BaseExtent::getType ( void ) const +{ + return m_extentType; +} + +// ====================================================================== + +#endif // #ifndef INCLUDED_BaseExtent_H diff --git a/engine/shared/library/sharedCollision/src/shared/extent/BoxExtent.cpp b/engine/shared/library/sharedCollision/src/shared/extent/BoxExtent.cpp new file mode 100644 index 00000000..3adb1a61 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/BoxExtent.cpp @@ -0,0 +1,458 @@ +// ====================================================================== +// +// BoxExtent.cpp +// jeff grills +// +// copyright 1999 Bootprint Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/BoxExtent.h" + +#include "sharedCollision/ExtentList.h" +#include "sharedCollision/Intersect3d.h" +#include "sharedFile/Iff.h" +#include "sharedFoundation/ExitChain.h" +#include "sharedMath/DebugShapeRenderer.h" +#include "sharedMath/Range.h" +#include "sharedMath/Ray3d.h" +#include "sharedMath/ShapeUtils.h" +#include "sharedFoundation/MemoryBlockManager.h" + +#include + +//----------------------------------------------------------------- + +static const Tag TAG_BOX = TAG3 (B,O,X); +static const Tag TAG_EXBX = TAG (E,X,B,X); + +namespace +{ + MemoryBlockManager* ms_memoryBlockManager; +} + +//------------------------------------------------------------------- + +void BoxExtent::install (void) +{ + DEBUG_FATAL(ms_memoryBlockManager, ("BoxExtent already installed")); + + ms_memoryBlockManager = new MemoryBlockManager("BoxExtent::memoryBlockManager", true, sizeof(BoxExtent), 0, 0, 0); + + ExtentList::assignBinding(TAG_EXBX,BoxExtent::create); + + ExitChain::add(BoxExtent::remove,"BoxExtent::remove"); +} + +//------------------------------------------------------------------- + +void BoxExtent::remove () +{ + DEBUG_FATAL(!ms_memoryBlockManager, ("BoxExtent is not installed")); + + delete ms_memoryBlockManager; + ms_memoryBlockManager = 0; +} + +//------------------------------------------------------------------- +// The terrain system creates BoxExtents for its chunks, which means +// that many thousands of BoxExtents are created and destroyed during +// a run of the game. Because of this, making BoxExtent use a block +// manager is probably a good idea. + +void *BoxExtent::operator new(size_t size) +{ + UNREF(size); + NOT_NULL(ms_memoryBlockManager); + DEBUG_FATAL(size != sizeof(BoxExtent), ("bad size")); + DEBUG_FATAL(size != static_cast(ms_memoryBlockManager->getElementSize()), ("BoxExtent installed with bad size")); + + return ms_memoryBlockManager->allocate(); +} + +// ---------- + +void BoxExtent::operator delete(void* pointer) +{ + NOT_NULL(ms_memoryBlockManager); + + ms_memoryBlockManager->free(pointer); +} + +// ====================================================================== +// Create a BoxExtent from the file + +Extent *BoxExtent::create( Iff & iff ) +{ + BoxExtent * extent = new BoxExtent(); + + extent->load(iff); + + return extent; +} + +// ---------------------------------------------------------------------- +/** + * Create a BoxExtent. + */ + +BoxExtent::BoxExtent(void) +: Extent(ET_Box), + m_box() +{ +} + +// ---------- + +BoxExtent::BoxExtent( AxialBox const & newBox ) +: Extent(ET_Box), + m_box(newBox) +{ +} + +// ---------- + +BoxExtent::BoxExtent(const Vector &vmin, const Vector &vmax, const Sphere & s) +: Extent(ET_Box,s), + m_box(vmin,vmax) +{ +} + +// ---------------------------------------------------------------------- +/** + * Destroy a BoxExtent. + */ + +BoxExtent::~BoxExtent(void) +{ +} + +// ---------------------------------------------------------------------- + +void BoxExtent::loadBox ( Iff & iff ) +{ + iff.enterChunk(TAG_BOX); + + Vector max = iff.read_floatVector(); + Vector min = iff.read_floatVector(); + + if (max.x < min.x || max.y < min.y || max.z < min.z) + { + DEBUG_WARNING(true, ("box extent is inside out")); + min = -Vector::xyz111; + max = Vector::xyz111; + m_sphere.setCenter(Vector::zero); + m_sphere.setRadius(Vector::xyz111.magnitude()); + } + + m_box = AxialBox(min,max); + + iff.exitChunk(TAG_BOX); +} + +// ---------------------------------------------------------------------- + +void BoxExtent::load_0000(Iff & iff) +{ + iff.enterForm(TAG_0000); + + loadBox(iff); + Extent::loadSphere_old(iff); + + iff.exitForm(TAG_0000); +} + +// ---------------------------------------------------------------------- + +void BoxExtent::load_0001(Iff & iff) +{ + iff.enterForm(TAG_0001); + + Extent::load(iff); + loadBox(iff); + + iff.exitForm(TAG_0001); +} + +// ---------------------------------------------------------------------- + +void BoxExtent::load(Iff & iff) +{ + iff.enterForm(TAG_EXBX); + + switch (iff.getCurrentName()) + { + case TAG_0000: + load_0000(iff); + break; + + case TAG_0001: + load_0001(iff); + break; + + default: + FATAL (true, ("BoxExtent::load - unsupported format")); + break; + } + + iff.exitForm(TAG_EXBX); +} + +//----------------------------------------------------------------- + +void BoxExtent::writeBox( Iff & iff ) const +{ + // insert bounding box extent + iff.insertChunk(TAG_BOX); + + iff.insertChunkFloatVector( m_box.getMax() ); + iff.insertChunkFloatVector( m_box.getMin() ); + + iff.exitChunk(TAG_BOX); +} + +// ---------------------------------------------------------------------- + +void BoxExtent::write(Iff & iff) const +{ + iff.insertForm(TAG_EXBX); + + iff.insertForm(TAG_0001); + + Extent::write(iff); + + writeBox(iff); + + iff.exitForm(TAG_0001); + + iff.exitForm(TAG_EXBX); +} + +// ---------------------------------------------------------------------- + +void BoxExtent::setNegative () +{ + setMin (Vector::maxXYZ); + setMax (Vector::negativeMaxXYZ); + setSphere (Sphere (Vector::zero, 0.f)); +} + +// ---------------------------------------------------------------------- +/** + * Update members if vector is beyond the current box extent. + */ + +void BoxExtent::updateMinAndMax (const Vector &vector) +{ + m_box.add(vector); +} + +// ---------------------------------------------------------------------- +/** + * Recompute the sphere extent around this box extent. + * + * Based on the current values of the extents, calculate the enclosing + * sphere's center and radius. + */ + +void BoxExtent::calculateCenterAndRadius (void) +{ + Vector center = m_box.getCenter(); + const real radius = m_box.getRadius(); + + setSphere(Sphere(center, radius)); +} + +// ---------------------------------------------------------------------- +/** + * Check if a point is contained within the box extent. + * + * This routine will take care of trivial rejection itself, so do not + * call the testSphereOnly() routine before calling this routine. + * + * @param vector Point to test to see if it is within the extent + * @return True if the point is inside the extent, otherwise false. + */ + +bool BoxExtent::contains(const Vector &vector) const +{ + return testSphereOnly(vector) && m_box.contains(vector); +} + +// Boxes are convex volumes; a line segment is contained in a box if both of its endpoints are +// contained by the box. + +bool BoxExtent::contains(const Vector &begin, const Vector &end) const +{ + return contains(begin) && contains(end); +} + +// ---------------------------------------------------------------------- +/** + * Determine if a line segment intersects the BoxExtent. + * + * This routine will take care of trivial rejection itself, so do not + * call the testSphereOnly() routine before calling this routine. + * + * @param begin Beginning of the line segment + * @param end End of the line segment + * @param time [OUT] Parametric time of the intersection + * @return True if the line segment intersects the Extent volume, otherwise false + */ + +bool BoxExtent::realIntersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, real * time) const +{ + Vector direction(end - begin); + if (direction.normalize()) + { + Ray3d const ray(begin, direction); + bool const getSurfaceNormal = (surfaceNormal != 0); + Intersect3d::ResultData data(getSurfaceNormal); + if (Intersect3d::intersectRayABoxWithData(ray, m_box, &data)) + { + float const magnitude = (end - begin).magnitude(); + if (data.m_length <= magnitude) + { + if (surfaceNormal) + *surfaceNormal = data.m_surfaceNormal; + + if (time) + *time = data.m_length / magnitude; + + return true; + } + } + } + return(false); +} + + +// ---------------------------------------------------------------------- +/** + * Determine if a line segment intersects the BoxExtent. + * + * This routine will take care of trivial rejection itself, so do not + * call the testSphereOnly() routine before calling this routine. + * + * @param begin Beginning of the line segment + * @param end End of the line segment + * @return True if the line segment intersects the Extent volume, otherwise false + */ + +Range BoxExtent::rangedIntersect ( Line3d const & line ) const +{ + return Intersect3d::Intersect(line,m_box); +} + +Range BoxExtent::rangedIntersect ( Ray3d const & ray ) const +{ + return Intersect3d::Intersect(ray,m_box); +} + +Range BoxExtent::rangedIntersect ( Segment3d const & seg ) const +{ + return Intersect3d::Intersect(seg,m_box); +} + +/** ---------------------------------------------------------------------- +* +* Causes a BoxExtent to grow large enough to contain the other. +* @param other The BoxExtent which we must grow to contain +*/ + +void BoxExtent::grow( const BoxExtent & other) +{ + m_box.add(other.m_box); + + calculateCenterAndRadius (); +} + +//---------------------------------------------------------------------- + +void BoxExtent::grow (const Vector & v, bool recompute) +{ + m_box.add (v); + if (recompute) + calculateCenterAndRadius (); +} + +// ---------------------------------------------------------------------- + +void BoxExtent::setBox( AxialBox const & box ) +{ + m_box = box; + + calculateCenterAndRadius(); +} + +// ---------------------------------------------------------------------- + +void BoxExtent::drawDebugShapes ( DebugShapeRenderer * renderer ) const +{ + UNREF(renderer); + +#ifdef _DEBUG + + if(renderer == NULL) return; + + renderer->setColor( VectorArgb::solidBlue ); + renderer->drawBox(m_box); + +#endif +} + +// ---------------------------------------------------------------------- + +real BoxExtent::getRight ( void ) const { return m_box.getMax().x; } +real BoxExtent::getTop ( void ) const { return m_box.getMax().y; } +real BoxExtent::getFront ( void ) const { return m_box.getMax().z; } +real BoxExtent::getLeft ( void ) const { return m_box.getMin().x; } +real BoxExtent::getBottom ( void ) const { return m_box.getMin().y; } +real BoxExtent::getBack ( void ) const { return m_box.getMin().z; } + +// ---------------------------------------------------------------------- + +BaseExtent * BoxExtent::clone ( void ) const +{ + return new BoxExtent( getBox() ); +} + +// ---------- + +void BoxExtent::copy ( BaseExtent const * source ) +{ + if(!source) return; + + BoxExtent const * boxSource = safe_cast< BoxExtent const * >(source); + + setBox( boxSource->getBox() ); +} + +// ---------- + +void BoxExtent::transform ( BaseExtent const * source, Transform const & tform, float scale ) +{ + if(!source) return; + + BoxExtent const * boxSource = safe_cast< BoxExtent const * >(source); + + setBox( ShapeUtils::transform( boxSource->getBox(), tform, scale ) ); +} + +// ---------- + +AxialBox BoxExtent::getBoundingBox ( void ) const +{ + return m_box; +} + +// ---------- + +Sphere BoxExtent::getBoundingSphere ( void ) const +{ + return getSphere(); +} + +// ====================================================================== + diff --git a/engine/shared/library/sharedCollision/src/shared/extent/BoxExtent.h b/engine/shared/library/sharedCollision/src/shared/extent/BoxExtent.h new file mode 100644 index 00000000..1153941f --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/BoxExtent.h @@ -0,0 +1,240 @@ +// ====================================================================== +// +// BoxExtent.h +// jeff grills +// +// copyright 1999 Bootprint Entertainment +// +// ====================================================================== + +#ifndef BOX_EXTENT_H +#define BOX_EXTENT_H + +// ====================================================================== + +#include "sharedCollision/Extent.h" +#include "sharedMath/AxialBox.h" + +// ====================================================================== + +class BoxExtent : public Extent +{ +public: + + BoxExtent(void); + BoxExtent( AxialBox const & newBox ); + BoxExtent(const Vector &newMin, const Vector &newMax, const Sphere & s); + + virtual ~BoxExtent(void); + + static Extent *create(Iff &iff); //lint !e1511 // hides non-virtual base member + + static void install(); + static void remove(); + + static void *operator new(size_t size); + static void operator delete(void *pointer); + + // ---------- + // Virtual Extent interface + + virtual void load ( Iff & iff ); + virtual void write ( Iff & iff ) const; + + virtual bool contains ( Vector const & vector ) const; + virtual bool contains ( Vector const & begin, Vector const & end ) const; + + virtual Range rangedIntersect(Line3d const & line) const; + virtual Range rangedIntersect(Ray3d const & ray) const; + virtual Range rangedIntersect(Segment3d const & seg) const; + + virtual void drawDebugShapes ( DebugShapeRenderer * renderer ) const; + + virtual BaseExtent * clone ( void ) const; + virtual void copy ( BaseExtent const * source ); + virtual void transform ( BaseExtent const * parent, Transform const & tform, float scale ); + virtual AxialBox getBoundingBox ( void ) const; + virtual Sphere getBoundingSphere ( void ) const; + + // ---------- + + void setMin (const Vector &vector); + void setMax (const Vector &vector); + void set (const Vector &newMin, const Vector &newMax, const Vector &newCenter, real newRadius); + void setNegative (); + void updateMinAndMax (const Vector &vector); + void calculateCenterAndRadius (void); + + Vector const & getMin (void) const; + Vector const & getMax (void) const; + + real getWidth (void) const; // x + real getHeight (void) const; // y + real getLength (void) const; // z + + void grow (const BoxExtent & other); + void grow (const Vector & v, bool recompute = true); + void getCornerVectors (Vector * vs) const; + + real getRight ( void ) const; + real getTop ( void ) const; + real getFront ( void ) const; + + real getLeft ( void ) const; + real getBottom ( void ) const; + real getBack ( void ) const; + + // ---------- + + AxialBox const & getBox ( void ) const; + void setBox ( AxialBox const & box ); + + // ---------- + + typedef AxialBox ShapeType; + + ShapeType const & getShape ( void ) const; + + // ---------- + +protected: + + virtual bool realIntersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, real * time) const; + + // ---------- + // Save/Load methods + + void loadBox ( Iff & iff ); + + void load_0000 ( Iff & iff ); + void load_0001 ( Iff & iff ); + + void writeBox ( Iff & iff ) const; + +private: + + AxialBox m_box; + + BoxExtent(const BoxExtent &); + BoxExtent &operator =(const BoxExtent &); +}; + +// ====================================================================== + +inline Vector const & BoxExtent::getMin (void) const +{ + return m_box.getMin(); +} + +inline Vector const & BoxExtent::getMax (void) const +{ + return m_box.getMax(); +} + +inline real BoxExtent::getWidth (void) const +{ + return m_box.getWidth(); +} + +inline real BoxExtent::getHeight (void) const +{ + return m_box.getHeight(); +} + +inline real BoxExtent::getLength (void) const +{ + return m_box.getDepth(); +} + +// ---------------------------------------------------------------------- +/** + * Set the minimum extents of the box extent. + */ +inline void BoxExtent::setMin (const Vector &vector) +{ + m_box.setMin(vector); +} + +// ---------------------------------------------------------------------- +/** + * Set the minimum extents of the box extent. + */ +inline void BoxExtent::setMax (const Vector &vector) +{ + m_box.setMax(vector); +} + +// ---------------------------------------------------------------------- +/** + * Set all fields of the box extent. + */ +inline void BoxExtent::set (const Vector &newMin, const Vector &newMax, const Vector &newCenter, real newRadius) +{ + setMin(newMin); + setMax(newMax); + setSphere(Sphere(newCenter, newRadius)); +} + +/** ---------------------------------------------------------------------- +* +* Obtain an array of Vectors representing the 8 corners of the BoxExtent. +* Vector 0 is the min vector, Vector 7 is the max vector. +* +* @param vs a pointer to an array of 8 Vectors. Vector 0 is the min vector, Vector 7 is the max vector. +*/ + +inline void BoxExtent::getCornerVectors (Vector *vs) const +{ + Vector const & min = m_box.getMin(); + Vector const & max = m_box.getMax(); + + vs[0].x = min.x; + vs[0].y = min.y; + vs[0].z = min.z; + + vs[1].x = max.x; + vs[1].y = min.y; + vs[1].z = min.z; + + vs[2].x = max.x; + vs[2].y = max.y; + vs[2].z = min.z; + + vs[3].x = min.x; + vs[3].y = max.y; + vs[3].z = min.z; + + vs[4].x = max.x; + vs[4].y = min.y; + vs[4].z = max.z; + + vs[5].x = min.x; + vs[5].y = min.y; + vs[5].z = max.z; + + vs[6].x = min.x; + vs[6].y = max.y; + vs[6].z = max.z; + + vs[7].x = max.x; + vs[7].y = max.y; + vs[7].z = max.z; + +} + +// ---------------------------------------------------------------------- + +inline AxialBox const & BoxExtent::getBox ( void ) const +{ + return m_box; +} + +// ---------------------------------------------------------------------- + +inline BoxExtent::ShapeType const & BoxExtent::getShape ( void ) const +{ + return getBox(); +} + +// ====================================================================== +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/extent/CollisionDetect.cpp b/engine/shared/library/sharedCollision/src/shared/extent/CollisionDetect.cpp new file mode 100644 index 00000000..1602025b --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/CollisionDetect.cpp @@ -0,0 +1,688 @@ +// ====================================================================== +// +// CollisionDetect.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/CollisionDetect.h" + +#include "sharedCollision/CollisionProperty.h" +#include "sharedCollision/CollisionUtils.h" +#include "sharedCollision/CollisionWorld.h" +#include "sharedCollision/ComponentExtent.h" +#include "sharedCollision/Containment3d.h" +#include "sharedCollision/DetailExtent.h" +#include "sharedCollision/SimpleExtent.h" +#include "sharedCollision/ConfigSharedCollision.h" +#include "sharedCollision/Intersect1d.h" +#include "sharedCollision/Intersect3d.h" + +#include "sharedObject/Object.h" +#include "sharedObject/Appearance.h" +#include "sharedObject/AppearanceTemplate.h" + +#include "sharedMath/Range.h" +#include "sharedMath/ShapeUtils.h" +#include "sharedMath/Cylinder.h" +#include "sharedMath/MultiShape.h" + +//#include +#include + +static int gs_testCounter; + +// ---------------------------------------------------------------------- + +void CollisionDetect::resetTestCounter ( void ) +{ + gs_testCounter = 0; +} + +int CollisionDetect::getTestCounter ( void ) +{ + return gs_testCounter; +} + +// ---------------------------------------------------------------------- + +BaseExtent const * getCollisionExtent_p ( Object const * obj ) +{ + if(obj == NULL) return NULL; + + CollisionProperty const * collision = obj->getCollisionProperty(); + + if(!collision) return NULL; + + return collision->getExtent_p(); +} + +// ---------------------------------------------------------------------- + +DetectResult CollisionDetect::testObjects ( Object const * objA, Object const * objB ) +{ + if(objA == NULL) return false; + if(objB == NULL) return false; + + // ---------- + + CollisionProperty const * collA = objA->getCollisionProperty(); + CollisionProperty const * collB = objB->getCollisionProperty(); + + if(!collA->isCollidable()) return false; + if(!collB->isCollidable()) return false; + + if(!collA->canCollideWith(collB)) return false; + + // ---------- + + BaseExtent const * extentA = getCollisionExtent_p(objA); + BaseExtent const * extentB = getCollisionExtent_p(objB); + + return testExtents(extentA,extentB); +} + +DetectResult CollisionDetect::testObjects ( Object const * objA, Vector const & velocity, Object const * objB ) +{ + if(objA == NULL) return false; + if(objB == NULL) return false; + + // ---------- + + CollisionProperty const * collA = objA->getCollisionProperty(); + CollisionProperty const * collB = objB->getCollisionProperty(); + + if(!collA->isCollidable()) return false; + if(!collB->isCollidable()) return false; + + if(!collA->canCollideWith(collB)) return false; + + // ---------- + + BaseExtent const * extentA = getCollisionExtent_p(objA); + BaseExtent const * extentB = getCollisionExtent_p(objB); + + return testExtents(extentA,velocity,extentB); +} + +DetectResult CollisionDetect::testObjectsAsCylinders ( Object const * objA, Object const * objB ) +{ + if(objA == NULL) return false; + if(objB == NULL) return false; + + // ---------- + + CollisionProperty const * collA = objA->getCollisionProperty(); + CollisionProperty const * collB = objB->getCollisionProperty(); + + if(!collA->isCollidable()) return false; + if(!collB->isCollidable()) return false; + + if(!collA->canCollideWith(collB)) return false; + + // ---------- + + BaseExtent const * extentA = getCollisionExtent_p(objA); + BaseExtent const * extentB = getCollisionExtent_p(objB); + + return testExtentsAsCylinders(extentA,extentB); +} + +// ---------------------------------------------------------------------- + +DetectResult CollisionDetect::testObjectExtent ( Object const * objA, BaseExtent const * extentB ) +{ + if(objA == NULL) return false; + + CollisionProperty const * collA = objA->getCollisionProperty(); + + if(!collA || !collA->isCollidable()) return false; + + // ---------- + + BaseExtent const * extentA = getCollisionExtent_p(objA); + + return testExtents(extentA,extentB); +} + +// ---------------------------------------------------------------------- + +DetectResult CollisionDetect::testSphereExtent ( Sphere const & sphereA, BaseExtent const * extentB ) +{ + MultiShape shapeA(sphereA); + + SimpleExtent extentA(shapeA); + + DetectResult result = testExtents(&extentA,extentB); + + // don't return a DetectResult that contains a pointer to a temporary + + result.extentA = NULL; + + return result; +} + +// ---------------------------------------------------------------------- + +DetectResult CollisionDetect::testSphereExtent ( Sphere const & sphereA, Vector const & velocity, BaseExtent const * extentB ) +{ + MultiShape shapeA(sphereA); + + SimpleExtent extentA(shapeA); + + DetectResult result = testExtents(&extentA,velocity,extentB); + + // don't return a DetectResult that contains a pointer to a temporary + + result.extentA = NULL; + + return result; +} + +// ---------------------------------------------------------------------- + +DetectResult CollisionDetect::testSphereObject ( Sphere const & sphereA, Object const * objB ) +{ + if(objB == NULL) return false; + + CollisionProperty const * collB = objB->getCollisionProperty(); + + if(!collB || !collB->isCollidable()) return false; + + // ---------- + + BaseExtent const * extentB = getCollisionExtent_p(objB); + + return testSphereExtent(sphereA,extentB); +} + +// ---------------------------------------------------------------------- + +DetectResult CollisionDetect::testSphereObject ( Sphere const & sphereA, Vector const & velocity, Object const * objB ) +{ + if(objB == NULL) return false; + + CollisionProperty const * collB = objB->getCollisionProperty(); + + if(!collB || !collB->isCollidable()) return false; + + // ---------- + + BaseExtent const * extentB = getCollisionExtent_p(objB); + + return testSphereExtent(sphereA,velocity,extentB); +} + +// ---------------------------------------------------------------------- + +DetectResult CollisionDetect::testCapsuleExtent ( Capsule const & capsuleA, BaseExtent const * extentB ) +{ + DetectResult result = testSphereExtent(capsuleA.getSphereA(),capsuleA.getDelta(),extentB); + + if(result.collided) + { + result.collisionTime = Range::inf; + } + + return result; +} + +// ---------------------------------------------------------------------- + +DetectResult CollisionDetect::testCapsuleObject ( Capsule const & capsuleA, Object const * objB ) +{ + DetectResult result = testSphereObject(capsuleA.getSphereA(),capsuleA.getDelta(),objB); + + if(result.collided) + { + result.collisionTime = Range::inf; + } + + return result; +} + +// ---------------------------------------------------------------------- + +DetectResult CollisionDetect::testCapsuleObjectAgainstAllTypes(Capsule const & capsule_w, Object const * object) +{ + DetectResult detectResult(false); + + if (object == 0) + return detectResult; + + BaseExtent const * extentToTest_l = 0; + + { + // testSphereObject method expanded out + CollisionProperty const * collisionProperty = object->getCollisionProperty(); + + if ((collisionProperty == 0) || (!collisionProperty->isCollidable())) + return detectResult; + + extentToTest_l = collisionProperty->getExtent_l(); + } + + if (extentToTest_l != 0) + { + // in the original call to testSphereObject we were getting the world extent + // instead I'm transforming the capsule into the objects local space which + // is less work on large extents + + Vector const capsuleBegin_l(object->rotateTranslate_w2o(capsule_w.getPointA())); + Vector const capsuleEnd_l(object->rotateTranslate_w2o(capsule_w.getPointB())); + Vector const capsuleVelocity_l(capsuleEnd_l - capsuleBegin_l); + Sphere const sphere_l(capsuleBegin_l, capsule_w.getRadius()); + + // unrolling the original call to CollisionDetect::testSphereExtent + // return testSphereExtent(sphere_l, capsuleVelocity_l, extentToTest_l); + + MultiShape const sphereMultiShape(sphere_l); + SimpleExtent const sphereExtent_l(sphereMultiShape); + + // unrolling the original call to CollisionDetect::testExtents below + // DetectResult result = testExtents(&extentA,velocity,extentB); + + { + // the extentA and extentB are used here so that we can more + // closely mirror the body of CollisionDetect::testExtents + BaseExtent const * const extentA = &sphereExtent_l; + BaseExtent const * const extentB = extentToTest_l; + Vector const & velocity = capsuleVelocity_l; + + // typeA is always guaranteed to be ET_Simple; + ExtentType const typeB = extentB->getType(); + + if(typeB == ET_Simple) + { + detectResult = testSimpleSimple(extentA, velocity, extentB); + } + else if(typeB == ET_Component) + { + detectResult = testAnyComponent(extentA, velocity, extentB); + } + else if(typeB == ET_Detail) + { + detectResult = testAnyDetail(extentA, velocity, extentB); + } + else if(typeB == ET_Mesh) + { + // TODO: this actually needs to test against the buckets and triangles + // of the CollisionMesh. Until then, AI won't be able to get in close + // to the larger objects. This won't allow docking, etc on these objects + SimpleExtent meshBoundingBoxExtent_l(MultiShape(extentB->getBoundingBox())); + detectResult = testSimpleSimple(extentA, velocity, &meshBoundingBoxExtent_l); + } + } + } + + // don't return a DetectResult that contains a pointer to a temporary + // from CollisionDetect::testSphereExtent + detectResult.extentA = 0; + + if(detectResult.collided) + { + detectResult.collisionTime = Range::inf; + } + + return detectResult; +} + +// ---------------------------------------------------------------------- + +DetectResult CollisionDetect::testExtents ( BaseExtent const * extentA, BaseExtent const * extentB ) +{ + if(extentA == NULL) return false; + if(extentB == NULL) return false; + + ExtentType typeA = extentA->getType(); + ExtentType typeB = extentB->getType(); + + // ---------- + + if((typeA == ET_Simple) && (typeB == ET_Simple)) { return testSimpleSimple(extentA,extentB); } + else if(typeA == ET_Component) { return testComponentAny(extentA,extentB); } + else if(typeA == ET_Detail) { return testDetailAny (extentA,extentB); } + else if(typeB == ET_Component) { return testAnyComponent(extentA,extentB); } + else if(typeB == ET_Detail) { return testAnyDetail (extentA,extentB); } + else { return false; } +} + +// ---------------------------------------------------------------------- + +DetectResult CollisionDetect::testExtents ( BaseExtent const * extentA, Vector const & velocity, BaseExtent const * extentB ) +{ + if(extentA == NULL) return false; + if(extentB == NULL) return false; + + ExtentType typeA = extentA->getType(); + ExtentType typeB = extentB->getType(); + + // ---------- + + if((typeA == ET_Simple) && (typeB == ET_Simple)) { return testSimpleSimple(extentA,velocity,extentB); } + else if(typeA == ET_Component) { return testComponentAny(extentA,velocity,extentB); } + else if(typeA == ET_Detail) { return testDetailAny (extentA,velocity,extentB); } + else if(typeB == ET_Component) { return testAnyComponent(extentA,velocity,extentB); } + else if(typeB == ET_Detail) { return testAnyDetail (extentA,velocity,extentB); } + else { return false; } +} + +// ---------------------------------------------------------------------- + +DetectResult CollisionDetect::testExtentsAsCylinders ( BaseExtent const * extentA, BaseExtent const * extentB ) +{ + if(extentA == NULL) return false; + if(extentB == NULL) return false; + + SimpleExtent const * simpleExtentA = safe_cast(extentA); + SimpleExtent const * simpleExtentB = safe_cast(extentB); + + gs_testCounter++; + + MultiShape shapeA = simpleExtentA->getShape(); + MultiShape shapeB = simpleExtentB->getShape(); + + // Stretch the cylinders vertically so that players can't squeeze under creatures + + shapeA.setExtentY( shapeA.getExtentY() * 5.0f ); + shapeB.setExtentY( shapeB.getExtentY() * 5.0f ); + + // ---------- + + ContainmentResult result = Containment3d::Test(shapeA.getCylinder(),shapeB.getCylinder()); + + // ---------- + + if(result != CR_Outside) + { + return DetectResult(true,extentA,extentB); + } + else + { + return false; + } +} + +// ---------------------------------------------------------------------- + +DetectResult CollisionDetect::testSimpleSimple ( BaseExtent const * extentA, BaseExtent const * extentB ) +{ + SimpleExtent const * simpleExtentA = safe_cast(extentA); + SimpleExtent const * simpleExtentB = safe_cast(extentB); + + if(!simpleExtentA) return false; + if(!simpleExtentB) return false; + + gs_testCounter++; + + MultiShape const & shapeA = simpleExtentA->getShape(); + MultiShape const & shapeB = simpleExtentB->getShape(); + + // ---------- + + ContainmentResult result = Containment3d::Test(shapeA,shapeB); + + // ---------- + + if(result != CR_Outside) + { + return DetectResult(true,extentA,extentB); + } + else + { + return false; + } +} + +// ---------------------------------------------------------------------- + +DetectResult CollisionDetect::testComponentAny ( BaseExtent const * extentA, BaseExtent const * extentB ) +{ + ComponentExtent const * component = safe_cast< ComponentExtent const * >(extentA); + + if(!component) return false; + + NOT_NULL(component); + + for(int i = 0; i < component->getExtentCount(); i++) + { + BaseExtent const * newExtentA = component->getExtent(i); + + DetectResult result = CollisionDetect::testExtents(newExtentA,extentB); + + if(result.collided) + { + return result; + } + } + + return false; +} + +// ---------- + +DetectResult CollisionDetect::testAnyComponent ( BaseExtent const * extentA, BaseExtent const * extentB ) +{ + // Lint complains if I use safe_cast? + + ComponentExtent const * component = safe_cast< ComponentExtent const * >(extentB); + + if(!component) return false; + + NOT_NULL(component); + + for(int i = 0; i < component->getExtentCount(); i++) + { + BaseExtent const * newExtentB = component->getExtent(i); + + DetectResult result = CollisionDetect::testExtents(extentA,newExtentB); + + if(result.collided) + { + return result; + } + } + + return false; +} + +// ---------------------------------------------------------------------- + +DetectResult CollisionDetect::testDetailAny ( BaseExtent const * extentA, BaseExtent const * extentB ) +{ + DetailExtent const * detail = safe_cast< DetailExtent const * >(extentA); + + if(!detail) return false; + + NOT_NULL(detail); + + DetectResult result; + + for(int i = 0; i < detail->getExtentCount(); i++) + { + BaseExtent const * newExtentA = detail->getExtent(i); + + result = CollisionDetect::testExtents(newExtentA,extentB); + + if(!result.collided) + { + return false; + } + } + + return result; +} + +// ---------- + +DetectResult CollisionDetect::testAnyDetail ( BaseExtent const * extentA, BaseExtent const * extentB ) +{ + DetailExtent const * detail = safe_cast< DetailExtent const * >(extentB); + + if(!detail) return false; + + NOT_NULL(detail); + + DetectResult result = false; + + for(int i = 0; i < detail->getExtentCount(); i++) + { + BaseExtent const * newExtentB = detail->getExtent(i); + + result = CollisionDetect::testExtents(extentA,newExtentB); + + if(!result.collided) + { + return false; + } + } + + return result; +} + +// ---------------------------------------------------------------------- + +DetectResult CollisionDetect::testSimpleSimple ( BaseExtent const * extentA, Vector const & velocity, BaseExtent const * extentB ) +{ + SimpleExtent const * simpleExtentA = safe_cast(extentA); + SimpleExtent const * simpleExtentB = safe_cast(extentB); + + if(!simpleExtentA) return false; + if(!simpleExtentB) return false; + + gs_testCounter++; + + MultiShape const & shapeA = simpleExtentA->getShape(); + MultiShape const & shapeB = simpleExtentB->getShape(); + + // ---------- + + Range range = Intersect3d::Intersect(shapeA,velocity,shapeB); + + Range clipRange = Intersect1d::IntersectRanges( range, Range::unit ); + + // ---------- + + if(!clipRange.isEmpty()) + { + + return DetectResult(true,clipRange,extentA,extentB); + } + else + { + return false; + } +} + +// ---------------------------------------------------------------------- + +DetectResult CollisionDetect::testComponentAny ( BaseExtent const * extentA, Vector const & velocity, BaseExtent const * extentB ) +{ + ComponentExtent const * component = safe_cast< ComponentExtent const * >(extentA); + + if(!component) return false; + + NOT_NULL(component); + + DetectResult minResult; + + for(int i = 0; i < component->getExtentCount(); i++) + { + BaseExtent const * newExtentA = component->getExtent(i); + + DetectResult result = CollisionDetect::testExtents(newExtentA,velocity,extentB); + + if(result.collisionTime.getMin() < minResult.collisionTime.getMin()) + { + minResult = result; + } + } + + return minResult; +} + +// ---------- + +DetectResult CollisionDetect::testAnyComponent ( BaseExtent const * extentA, Vector const & velocity, BaseExtent const * extentB ) +{ + // Lint complains if I use safe_cast? + + ComponentExtent const * component = safe_cast< ComponentExtent const * >(extentB); + + if(!component) return false; + + NOT_NULL(component); + + DetectResult minResult; + + for(int i = 0; i < component->getExtentCount(); i++) + { + BaseExtent const * newExtentB = component->getExtent(i); + + DetectResult result = CollisionDetect::testExtents(extentA,velocity,newExtentB); + + if(result.collisionTime.getMin() < minResult.collisionTime.getMin() ) + { + minResult = result; + } + } + + return minResult; +} + +// ---------------------------------------------------------------------- + +DetectResult CollisionDetect::testDetailAny ( BaseExtent const * extentA, Vector const & velocity, BaseExtent const * extentB ) +{ + DetailExtent const * detail = safe_cast< DetailExtent const * >(extentA); + + if(!detail) return false; + + NOT_NULL(detail); + + DetectResult result; + + for(int i = 0; i < detail->getExtentCount(); i++) + { + BaseExtent const * newExtentA = detail->getExtent(i); + + result = CollisionDetect::testExtents(newExtentA,velocity,extentB); + + if(!result.collided) + { + return false; + } + } + + return result; +} + +// ---------- + +DetectResult CollisionDetect::testAnyDetail ( BaseExtent const * extentA, Vector const & velocity, BaseExtent const * extentB ) +{ + DetailExtent const * detail = safe_cast< DetailExtent const * >(extentB); + + if(!detail) return false; + + NOT_NULL(detail); + + DetectResult result; + + for(int i = 0; i < detail->getExtentCount(); i++) + { + BaseExtent const * newExtentB = detail->getExtent(i); + + result = CollisionDetect::testExtents(extentA,velocity,newExtentB); + + if(!result.collided) + { + return false; + } + } + + return result; +} + +// ---------------------------------------------------------------------- diff --git a/engine/shared/library/sharedCollision/src/shared/extent/CollisionDetect.h b/engine/shared/library/sharedCollision/src/shared/extent/CollisionDetect.h new file mode 100644 index 00000000..8bc78848 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/CollisionDetect.h @@ -0,0 +1,113 @@ +// ====================================================================== +// +// CollisionDetect.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_CollisionDetect_H +#define INCLUDED_CollisionDetect_H + +#include "sharedMath/Range.h" + +class BaseExtent; +class Object; +class Vector; +class Sphere; +class Capsule; + +// ====================================================================== + +class DetectResult +{ +public: + + DetectResult() + : collided(false), + collisionTime(Range::empty), + extentA(NULL), + extentB(NULL) + { + } + + DetectResult ( bool result ) + : collided(result), + collisionTime( result ? Range::inf : Range::empty ), + extentA(NULL), + extentB(NULL) + { + } + + DetectResult ( bool result, BaseExtent const * a, BaseExtent const * b ) + : collided(result), + collisionTime( result ? Range::inf : Range::empty ), + extentA(a), + extentB(b) + { + } + + DetectResult ( bool result, Range time, BaseExtent const * a, BaseExtent const * b ) + : collided(result), + collisionTime(time), + extentA(a), + extentB(b) + { + } + + // ---------- + + bool collided; + Range collisionTime; + BaseExtent const * extentA; + BaseExtent const * extentB; +}; + +// ---------- + +class CollisionDetect +{ +public: + + // Tests that use velocity _must_ return a collision time range clamped to (0,1) + + static DetectResult testObjects ( Object const * objA, Object const * objB ); + static DetectResult testObjects ( Object const * objA, Vector const & velocity, Object const * objB ); + static DetectResult testObjectsAsCylinders ( Object const * objA, Object const * objB ); + static DetectResult testObjectExtent ( Object const * objA, BaseExtent const * extentB ); + + static DetectResult testExtents ( BaseExtent const * extentA, BaseExtent const * extentB ); + static DetectResult testExtentsAsCylinders ( BaseExtent const * extentA, BaseExtent const * extentB ); + static DetectResult testExtents ( BaseExtent const * extentA, Vector const & velocity, BaseExtent const * extentB ); + + static DetectResult testSphereExtent ( Sphere const & sphereA, BaseExtent const * extentB ); + static DetectResult testSphereExtent ( Sphere const & sphereA, Vector const & velocity, BaseExtent const * extentB ); + static DetectResult testSphereObject ( Sphere const & sphereA_w, Object const * objB ); + static DetectResult testSphereObject ( Sphere const & sphereA_w, Vector const & velocity, Object const * objB ); + + static DetectResult testCapsuleExtent ( Capsule const & capsuleA, BaseExtent const * extentB ); + static DetectResult testCapsuleObject ( Capsule const & capsuleA_w, Object const * objB ); + + static DetectResult testCapsuleObjectAgainstAllTypes(Capsule const & capsule_w, Object const * objB); + + static void resetTestCounter ( void ); + static int getTestCounter ( void ); + +protected: + + static DetectResult testSimpleSimple ( BaseExtent const * extentA, BaseExtent const * extentB ); + static DetectResult testComponentAny ( BaseExtent const * extentA, BaseExtent const * extentB ); + static DetectResult testAnyComponent ( BaseExtent const * extentA, BaseExtent const * extentB ); + static DetectResult testDetailAny ( BaseExtent const * extentA, BaseExtent const * extentB ); + static DetectResult testAnyDetail ( BaseExtent const * extentA, BaseExtent const * extentB ); + + static DetectResult testSimpleSimple ( BaseExtent const * extentA, Vector const & velocity, BaseExtent const * extentB ); + static DetectResult testComponentAny ( BaseExtent const * extentA, Vector const & velocity, BaseExtent const * extentB ); + static DetectResult testAnyComponent ( BaseExtent const * extentA, Vector const & velocity, BaseExtent const * extentB ); + static DetectResult testDetailAny ( BaseExtent const * extentA, Vector const & velocity, BaseExtent const * extentB ); + static DetectResult testAnyDetail ( BaseExtent const * extentA, Vector const & velocity, BaseExtent const * extentB ); +}; + +// ====================================================================== + +#endif + diff --git a/engine/shared/library/sharedCollision/src/shared/extent/ComponentExtent.cpp b/engine/shared/library/sharedCollision/src/shared/extent/ComponentExtent.cpp new file mode 100644 index 00000000..e64f9424 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/ComponentExtent.cpp @@ -0,0 +1,221 @@ +// ====================================================================== +// +// ComponentExtent.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/ComponentExtent.h" + +#include "sharedCollision/ExtentList.h" +#include "sharedFile/Iff.h" +#include "sharedMath/Range.h" + +const Tag TAG_CMPT = TAG(C,M,P,T); + +// ---------------------------------------------------------------------- + +ComponentExtent::ComponentExtent() +: CompositeExtent(ET_Component) +{ +} + +ComponentExtent::~ComponentExtent() +{ +} + +// ---------------------------------------------------------------------- + +void ComponentExtent::install ( void ) +{ + ExtentList::assignBinding(TAG_CMPT,ComponentExtent::build); +} + +void ComponentExtent::remove ( void ) +{ +} + +Extent * ComponentExtent::build ( Iff & iff ) +{ + ComponentExtent * extent = new ComponentExtent(); + + extent->load(iff); + + return extent; +} + +// ---------------------------------------------------------------------- +// Virtual BaseExtent interface + +void ComponentExtent::load ( Iff & iff ) +{ + iff.enterForm(TAG_CMPT); + + switch (iff.getCurrentName()) + { + case TAG_0000: + load_0000(iff); + break; + + default: + FATAL (true, ("ComponentExtent::load - unsupported format")); + break; + } + + iff.exitForm(TAG_CMPT); + +} + +// ---------- + +void ComponentExtent::write ( Iff & iff ) const +{ + iff.insertForm(TAG_CMPT); + + iff.insertForm(TAG_0000); + + CompositeExtent::write(iff); + + iff.exitForm(TAG_0000); + + iff.exitForm(TAG_CMPT); +} + +// ---------------------------------------------------------------------- + +bool ComponentExtent::contains ( Vector const & vector ) const +{ + for(int i = 0; i < getExtentCount(); i++) + { + if(getExtent(i)->contains(vector)) return true; + } + + return false; +} + +// this doesn't handle cumulative containment +bool ComponentExtent::contains ( Vector const & begin, Vector const & end ) const +{ + for(int i = 0; i < getExtentCount(); i++) + { + if(getExtent(i)->contains(begin,end)) return true; + } + + return false; +} + +// ---------------------------------------------------------------------- + +bool ComponentExtent::realIntersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, real * time) const +{ + float bestTime = REAL_MAX; + bool hit = false; + + int const numberOfExtents = getExtentCount(); + for (int i = 0; i < numberOfExtents; ++i) + { + Vector normal; + float t; + Vector * const normalPtr = (surfaceNormal != 0) ? &normal : 0; + BaseExtent const * const extent = getExtent(i); + if (extent->intersect(begin, end, normalPtr, &t)) + { + if (t < bestTime) + { + if (surfaceNormal) + *surfaceNormal = normal; + + if (time) + *time = t; + + bestTime = t; + } + + hit = true; + } + } + + return hit; +} + +// ---------------------------------------------------------------------- + +Range ComponentExtent::rangedIntersect( Line3d const & line ) const +{ + Range range = Range::empty; + + for(int i = 0; i < getExtentCount(); i++) + { + Range temp = getExtent(i)->rangedIntersect(line); + + range = Range::enclose(range,temp); + } + + return range; +} + +Range ComponentExtent::rangedIntersect( Ray3d const & ray ) const +{ + Range range = Range::empty; + + for(int i = 0; i < getExtentCount(); i++) + { + Range temp = getExtent(i)->rangedIntersect(ray); + + range = Range::enclose(range,temp); + } + + return range; +} + +Range ComponentExtent::rangedIntersect( Segment3d const & seg ) const +{ + Range range = Range::empty; + + for(int i = 0; i < getExtentCount(); i++) + { + Range temp = getExtent(i)->rangedIntersect(seg); + + range = Range::enclose(range,temp); + } + + return range; +} + +// ---------------------------------------------------------------------- + +BaseExtent * ComponentExtent::clone ( void ) const +{ + ComponentExtent * newExtent = new ComponentExtent(); + + cloneChildren( newExtent ); + + return newExtent; +} + +// ---------------------------------------------------------------------- + + + + + + + + + + + + +// ====================================================================== + +void ComponentExtent::load_0000 ( Iff & iff ) +{ + iff.enterForm(TAG_0000); + + CompositeExtent::load(iff); + + iff.exitForm(TAG_0000); +} + +// ---------------------------------------------------------------------- diff --git a/engine/shared/library/sharedCollision/src/shared/extent/ComponentExtent.h b/engine/shared/library/sharedCollision/src/shared/extent/ComponentExtent.h new file mode 100644 index 00000000..fb124bfa --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/ComponentExtent.h @@ -0,0 +1,62 @@ +// ====================================================================== +// +// ComponentExtent.h +// Austin Appleby +// +// copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_ComponentExtent_H +#define INCLUDED_ComponentExtent_H + +// ====================================================================== + +#include "sharedCollision/CompositeExtent.h" + +class Iff; + +// ====================================================================== + +class ComponentExtent : public CompositeExtent +{ +public: + + ComponentExtent(); + virtual ~ComponentExtent(); + + static void install ( void ); + static void remove ( void ); + + static Extent * build ( Iff & iff ); + + // ---------- + // Virtual Extent interface + + virtual void load ( Iff & iff ); + virtual void write ( Iff & iff ) const; + + virtual bool contains ( Vector const & vector ) const; + virtual bool contains ( Vector const & begin, Vector const & end ) const; + + virtual Range rangedIntersect(Line3d const & line) const; + virtual Range rangedIntersect(Ray3d const & ray) const; + virtual Range rangedIntersect(Segment3d const & seg) const; + + virtual BaseExtent * clone ( void ) const; + +protected: + + virtual bool realIntersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, real * time) const; + + void load_0000 ( Iff & iff ); + +private: + + ComponentExtent(const ComponentExtent &); + ComponentExtent &operator =(const ComponentExtent &); +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/extent/CompositeExtent.cpp b/engine/shared/library/sharedCollision/src/shared/extent/CompositeExtent.cpp new file mode 100644 index 00000000..d7a6d91a --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/CompositeExtent.cpp @@ -0,0 +1,225 @@ +// ====================================================================== +// +// CompositeExtent.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/CompositeExtent.h" + +#include "sharedCollision/Containment3d.h" +#include "sharedCollision/ExtentList.h" +#include "sharedFile/Iff.h" + +#include + +const Tag TAG_CPST = TAG(C,P,S,T); + +// ---------------------------------------------------------------------- + +CompositeExtent::CompositeExtent( ExtentType const & type ) +: Extent(type), + m_extents( new ExtentVec() ), + m_box() +{ +} + +CompositeExtent::~CompositeExtent() +{ + for( uint i = 0; i < m_extents->size(); i++ ) + { + delete m_extents->at(i); + + m_extents->at(i) = NULL; + } + + delete m_extents; + m_extents = NULL; + +} + +// ---------------------------------------------------------------------- + +void CompositeExtent::load ( Iff & iff ) +{ + iff.enterForm(TAG_CPST); + + switch (iff.getCurrentName()) + { + case TAG_0000: + load_0000(iff); + break; + + default: + FATAL (true, ("CompositeExtent::load - unsupported format")); + break; + } + + iff.exitForm(TAG_CPST); + +} + +// ---------- + +void CompositeExtent::write ( Iff & iff ) const +{ + iff.insertForm(TAG_CPST); + + iff.insertForm(TAG_0000); + + for(int i = 0; i < getExtentCount(); i++) + { + getExtent(i)->write(iff); + } + + iff.exitForm(TAG_0000); + + iff.exitForm(TAG_CPST); +} + +// ---------------------------------------------------------------------- + +int CompositeExtent::getExtentCount ( void ) const +{ + return static_cast(m_extents->size()); +} + +// ---------- + +BaseExtent * CompositeExtent::getExtent ( int whichExtent ) +{ + return m_extents->at( static_cast(whichExtent) ); +} + +// ---------- + +BaseExtent const * CompositeExtent::getExtent ( int whichExtent ) const +{ + return m_extents->at( static_cast(whichExtent) ); +} + +// ---------- + +void CompositeExtent::attachExtent ( BaseExtent * newExtent ) +{ + m_extents->push_back( newExtent ); + + updateBounds(); +} + +// ---------------------------------------------------------------------- + +void CompositeExtent::drawDebugShapes ( DebugShapeRenderer * renderer ) const +{ + UNREF(renderer); + +#ifdef _DEBUG + + if(renderer == NULL) return; + + for(int i = 0; i < getExtentCount(); i++) + { + getExtent(i)->drawDebugShapes(renderer); + } + +#endif +} + +// ---------- + +void CompositeExtent::cloneChildren ( CompositeExtent * destExtent ) const +{ + for(int i = 0; i < getExtentCount(); i++) + { + destExtent->attachExtent( getExtent(i)->clone() ); + } +} + +// ---------- + +void CompositeExtent::copy ( BaseExtent const * source ) +{ + CompositeExtent const * compositeSource = safe_cast< CompositeExtent const * >(source); + + DEBUG_FATAL( getExtentCount() != compositeSource->getExtentCount(), ("CompositeExtent::copy - Source extent has a different number of children\n")); + + for(int i = 0; i < compositeSource->getExtentCount(); i++) + { + getExtent(i)->copy( compositeSource->getExtent(i) ); + } + + updateBounds(); +} + +// ---------- + +void CompositeExtent::transform ( BaseExtent const * source, Transform const & tform, float scale ) +{ + CompositeExtent const * compositeSource = safe_cast< CompositeExtent const * >(source); + + DEBUG_FATAL( getExtentCount() != compositeSource->getExtentCount(), ("CompositeExtent::copy - Source extent has a different number of children\n")); + + for(int i = 0; i < compositeSource->getExtentCount(); i++) + { + getExtent(i)->transform( compositeSource->getExtent(i), tform, scale ); + } + + updateBounds(); +} + +// ---------- + +AxialBox CompositeExtent::getBoundingBox ( void ) const +{ + return m_box; +} + +Sphere CompositeExtent::getBoundingSphere ( void ) const +{ + return m_sphere; +} + +void CompositeExtent::updateBounds ( void ) +{ + if(getExtentCount() == 0) + { + m_sphere = Sphere::zero; + m_box = AxialBox(); + } + else + { + Sphere sphere = getExtent(0)->getBoundingSphere(); + AxialBox box = getExtent(0)->getBoundingBox(); + + for(int i = 1; i < getExtentCount(); i++) + { + sphere = Containment3d::EncloseSphere( sphere, getExtent(i)->getBoundingSphere() ); + box = Containment3d::EncloseABox( box, getExtent(i)->getBoundingBox() ); + } + + m_sphere = sphere; + m_box = box; + } +} + +// ---------------------------------------------------------------------- + +void CompositeExtent::load_0000 ( Iff & iff ) +{ + iff.enterForm(TAG_0000); + + while(!iff.atEndOfForm()) + { + Extent * pExtent = ExtentList::create(iff); + + attachExtent(pExtent); + } + + iff.exitForm(TAG_0000); + + updateBounds(); +} + +// ====================================================================== + diff --git a/engine/shared/library/sharedCollision/src/shared/extent/CompositeExtent.h b/engine/shared/library/sharedCollision/src/shared/extent/CompositeExtent.h new file mode 100644 index 00000000..82d0d35f --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/CompositeExtent.h @@ -0,0 +1,74 @@ +// ====================================================================== +// +// CompositeExtent.h +// Austin Appleby +// +// copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_CompositeExtent_H +#define INCLUDED_CompositeExtent_H + +// ====================================================================== + +#include "sharedCollision/Extent.h" + +#include "sharedMath/AxialBox.h" + +class Iff; + +// ====================================================================== + +class CompositeExtent : public Extent +{ +public: + + CompositeExtent( ExtentType const & type ); + virtual ~CompositeExtent(); + + // ---------- + // Virtual Extent interface + + virtual void load ( Iff & iff ); + virtual void write ( Iff & iff ) const; + + virtual void drawDebugShapes ( DebugShapeRenderer * renderer ) const; + + virtual void copy ( BaseExtent const * source ); + virtual void transform ( BaseExtent const * parent, Transform const & tform, float scale ); + virtual AxialBox getBoundingBox ( void ) const; + virtual Sphere getBoundingSphere ( void ) const; + + // ---------- + // CompositeExtent interface + + virtual int getExtentCount ( void ) const; + virtual BaseExtent * getExtent ( int whichExtent ); + virtual BaseExtent const * getExtent ( int whichExtent ) const; + + virtual void attachExtent ( BaseExtent * newExtent ); + +protected: + + void updateBounds ( void ); + void cloneChildren ( CompositeExtent * dest ) const; + void load_0000 ( Iff & iff ); + + // ---------- + + typedef stdvector< BaseExtent * >::fwd ExtentVec; + + ExtentVec * m_extents; + + AxialBox m_box; + +private: + + CompositeExtent(const CompositeExtent &); + CompositeExtent &operator =(const CompositeExtent &); +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/extent/CylinderExtent.cpp b/engine/shared/library/sharedCollision/src/shared/extent/CylinderExtent.cpp new file mode 100644 index 00000000..04d069c9 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/CylinderExtent.cpp @@ -0,0 +1,275 @@ +// ====================================================================== +// +// CylinderExtent.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/CylinderExtent.h" + +#include "sharedCollision/ExtentList.h" +#include "sharedCollision/Containment3d.h" +#include "sharedCollision/Intersect1d.h" +#include "sharedCollision/Intersect3d.h" +#include "sharedCollision/Intersect2d.h" +#include "sharedFile/Iff.h" +#include "sharedMath/Circle.h" +#include "sharedMath/DebugShapeRenderer.h" +#include "sharedMath/Line3d.h" +#include "sharedMath/Plane3d.h" +#include "sharedMath/Plane.h" +#include "sharedMath/Range.h" +#include "sharedMath/Ray3d.h" +#include "sharedMath/ShapeUtils.h" +#include "sharedMath/AxialBox.h" +#include "sharedMath/Segment3d.h" + +const Tag TAG_XCYL = TAG(X,C,Y,L); +const Tag TAG_CYLN = TAG(C,Y,L,N); + +// ====================================================================== + +void CylinderExtent::install ( void ) +{ + ExtentList::assignBinding(TAG_XCYL,CylinderExtent::build); +} + +void CylinderExtent::remove ( void ) +{ +} + +Extent * CylinderExtent::build ( Iff & iff ) +{ + CylinderExtent * extent = new CylinderExtent(); + + extent->load(iff); + + return extent; +} + +// ---------------------------------------------------------------------- + +CylinderExtent::CylinderExtent() +: Extent(ET_Cylinder), + m_cylinder() +{ +} + +CylinderExtent::CylinderExtent( Cylinder const & cylinder ) +: Extent(ET_Cylinder), + m_cylinder(cylinder) +{ +} + +CylinderExtent::~CylinderExtent() +{ +} + +// ---------------------------------------------------------------------- + +bool CylinderExtent::contains ( Vector const & point ) const +{ + return Containment::isContainment( Containment3d::Test(point,m_cylinder) ); +} + +bool CylinderExtent::contains ( Vector const & begin, Vector const & end ) const +{ + return contains(begin) && contains(end); +} + +// ---------------------------------------------------------------------- + +bool CylinderExtent::realIntersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, float * time ) const +{ + Vector direction(end - begin); + if (direction.normalize()) + { + Ray3d const ray(begin, direction); + bool const getSurfaceNormal = (surfaceNormal != 0); + Intersect3d::ResultData data(getSurfaceNormal); + if (Intersect3d::intersectRayCylinderWithData(ray, m_cylinder, &data)) + { + float const magnitude = (end - begin).magnitude(); + if (data.m_length <= magnitude) + { + if (surfaceNormal) + *surfaceNormal = data.m_surfaceNormal; + + if (time) + *time = data.m_length / magnitude; + + return true; + } + } + } + + return false; +} + +// ---------------------------------------------------------------------- + +Range CylinderExtent::rangedIntersect ( Line3d const & line ) const +{ + return Intersect3d::Intersect(line,m_cylinder); +} + +Range CylinderExtent::rangedIntersect ( Ray3d const & ray ) const +{ + return Intersect3d::Intersect(ray,m_cylinder); +} + +Range CylinderExtent::rangedIntersect ( Segment3d const & seg ) const +{ + return Intersect3d::Intersect(seg,m_cylinder); +} + +// ---------------------------------------------------------------------- + +Cylinder const & CylinderExtent::getCylinder ( void ) const +{ + return m_cylinder; +} + +// ---------- + +void CylinderExtent::setCylinder ( Cylinder const & newCylinder ) +{ + m_cylinder = newCylinder; +} + +// ---------------------------------------------------------------------- + +void CylinderExtent::drawDebugShapes ( DebugShapeRenderer * renderer ) const +{ + UNREF(renderer); + +#ifdef _DEBUG + + if(renderer == NULL) return; + + renderer->setColor( VectorArgb::solidGreen ); + renderer->drawCylinder(m_cylinder); + +#endif +} + +// ---------------------------------------------------------------------- + +void CylinderExtent::load ( Iff & iff ) +{ + iff.enterForm(TAG_XCYL); + + switch (iff.getCurrentName()) + { + case TAG_0000: + load_0000(iff); + break; + + default: + FATAL (true, ("CylinderExtent::load - unsupported format")); + break; + } + + iff.exitForm(TAG_XCYL); + +} + +// ---------- + +void CylinderExtent::write ( Iff & iff ) const +{ + iff.insertForm(TAG_XCYL); + + iff.insertForm(TAG_0000); + + writeCylinder(iff); + + iff.exitForm(TAG_0000); + + iff.exitForm(TAG_XCYL); +} + +// ---------------------------------------------------------------------- + +void CylinderExtent::loadCylinder ( Iff & iff ) +{ + iff.enterChunk(TAG_CYLN); + + Vector base = iff.read_floatVector(); + real radius = iff.read_float(); + real height = iff.read_float(); + + m_cylinder = Cylinder(base,radius,height); + + iff.exitChunk(TAG_CYLN); +} + +// ---------------------------------------------------------------------- + +void CylinderExtent::load_0000 ( Iff & iff ) +{ + iff.enterForm(TAG_0000); + + loadCylinder(iff); + + iff.exitForm(TAG_0000); +} + +// ---------------------------------------------------------------------- + +void CylinderExtent::writeCylinder ( Iff & iff ) const +{ + iff.insertChunk(TAG_CYLN); + + iff.insertChunkFloatVector( m_cylinder.getBase() ); + iff.insertChunkData( m_cylinder.getRadius() ); + iff.insertChunkData( m_cylinder.getHeight() ); + + iff.exitChunk(TAG_CYLN); +} + +// ---------------------------------------------------------------------- + +BaseExtent * CylinderExtent::clone ( void ) const +{ + return new CylinderExtent( getCylinder() ); +} + +// ---------- + +void CylinderExtent::copy ( BaseExtent const * source ) +{ + if(!source) return; + + CylinderExtent const * cylinderSource = safe_cast(source); + + setCylinder( cylinderSource->getCylinder() ); +} + +// ---------- + +void CylinderExtent::transform ( BaseExtent const * source, Transform const & tform, float scale ) +{ + if(!source) return; + + CylinderExtent const * cylinderSource = safe_cast(source); + + setCylinder( ShapeUtils::transform( cylinderSource->getCylinder(), tform, scale ) ); +} + +// ---------- + +AxialBox CylinderExtent::getBoundingBox ( void ) const +{ + return Containment3d::EncloseABox( m_cylinder ); +} + +// ---------- + +Sphere CylinderExtent::getBoundingSphere ( void ) const +{ + return Containment3d::EncloseSphere( m_cylinder ); +} + +// ---------------------------------------------------------------------- diff --git a/engine/shared/library/sharedCollision/src/shared/extent/CylinderExtent.h b/engine/shared/library/sharedCollision/src/shared/extent/CylinderExtent.h new file mode 100644 index 00000000..21aea910 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/CylinderExtent.h @@ -0,0 +1,100 @@ +// ====================================================================== +// +// CylinderExtent.h +// Austin Appleby +// +// copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_CylinderExtent_H +#define INCLUDED_CylinderExtent_H + +// ====================================================================== + +#include "sharedCollision/Extent.h" +#include "sharedMath/Cylinder.h" + +class Iff; + +// ====================================================================== + +// CylinderExtent MUST be derived from Extent and not BaseExtent because +// ExtentList requires it. + +class CylinderExtent : public Extent +{ +public: + + CylinderExtent(); + CylinderExtent( Cylinder const & cylinder ); + virtual ~CylinderExtent(); + + static void install ( void ); + static void remove ( void ); + + static Extent * build ( Iff & iff ); + + // ---------- + // Virtual Extent interface + + virtual void load ( Iff & iff ); + virtual void write ( Iff & iff ) const; + + virtual bool contains ( Vector const & vector ) const; + virtual bool contains ( Vector const & begin, Vector const & end ) const; + + virtual Range rangedIntersect(Line3d const & line) const; + virtual Range rangedIntersect(Ray3d const & ray) const; + virtual Range rangedIntersect(Segment3d const & seg) const; + + virtual void drawDebugShapes ( DebugShapeRenderer * renderer ) const; + + virtual BaseExtent * clone ( void ) const; + virtual void copy ( BaseExtent const * source ); + virtual void transform ( BaseExtent const * parent, Transform const & tform, float scale ); + virtual AxialBox getBoundingBox ( void ) const; + virtual Sphere getBoundingSphere ( void ) const; + + // ---------- + + Cylinder const & getCylinder ( void ) const; + void setCylinder ( Cylinder const & newCylinder ); + + // ---------- + + typedef Cylinder ShapeType; + + ShapeType const & getShape ( void ) const; + + // ---------- + +protected: + + virtual bool realIntersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, real * time) const; + + void loadCylinder ( Iff & iff ); + void writeCylinder ( Iff & iff ) const; + + void load_0000 ( Iff & iff ); + +private: + + CylinderExtent(const CylinderExtent &); + CylinderExtent &operator =(const CylinderExtent &); + + // ---------- + + Cylinder m_cylinder; +}; + +// ---------------------------------------------------------------------- + +inline CylinderExtent::ShapeType const & CylinderExtent::getShape ( void ) const +{ + return getCylinder(); +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/extent/DetailExtent.cpp b/engine/shared/library/sharedCollision/src/shared/extent/DetailExtent.cpp new file mode 100644 index 00000000..8ceea23a --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/DetailExtent.cpp @@ -0,0 +1,279 @@ +// ====================================================================== +// +// DetailExtent.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/DetailExtent.h" + +#include "sharedCollision/ExtentList.h" +#include "sharedCollision/Intersect3d.h" +#include "sharedFile/Iff.h" +#include "sharedMath/Range.h" +#include "sharedMath/Ray3d.h" + +#include + +const Tag TAG_DTAL = TAG(D,T,A,L); + +// ====================================================================== + +DetailExtent::DetailExtent() +: CompositeExtent(ET_Detail) +{ +} + +DetailExtent::~DetailExtent() +{ +} + +// ---------------------------------------------------------------------- + +void DetailExtent::install ( void ) +{ + ExtentList::assignBinding(TAG_DTAL,DetailExtent::build); +} + +void DetailExtent::remove ( void ) +{ +} + +Extent * DetailExtent::build ( Iff & iff ) +{ + DetailExtent * extent = new DetailExtent(); + + extent->load(iff); + + return extent; +} + +// ---------------------------------------------------------------------- + +void DetailExtent::load ( Iff & iff ) +{ + iff.enterForm(TAG_DTAL); + + switch (iff.getCurrentName()) + { + case TAG_0000: + load_0000(iff); + break; + + default: + FATAL (true, ("DetailExtent::load - unsupported format")); + break; + } + + iff.exitForm(TAG_DTAL); + +} + +// ---------------------------------------------------------------------- + +void DetailExtent::write ( Iff & iff ) const +{ + iff.insertForm(TAG_DTAL); + + iff.insertForm(TAG_0000); + + CompositeExtent::write(iff); + + iff.exitForm(TAG_0000); + + iff.exitForm(TAG_DTAL); +} + +// ---------- + +void DetailExtent::load_0000 ( Iff & iff ) +{ + iff.enterForm(TAG_0000); + + CompositeExtent::load(iff); + + iff.exitForm(TAG_0000); +} + +// ---------------------------------------------------------------------- + +bool DetailExtent::contains ( Vector const & vector ) const +{ + for(int i = 0; i < getExtentCount(); i++) + { + if(!getExtent(i)->contains(vector)) return false; + } + + return true; +} + +bool DetailExtent::contains ( Vector const & begin, Vector const & end ) const +{ + for(int i = 0; i < getExtentCount(); i++) + { + if(!getExtent(i)->contains(begin,end)) return false; + } + + return true; +} + +// ---------------------------------------------------------------------- + +bool DetailExtent::realIntersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, real * time) const +{ + float bestTime = REAL_MAX; + bool hit = false; + + int const numberOfExtents = getExtentCount(); + if (numberOfExtents) + { + //-- Check the bounding box for the entire composite extent + Vector direction(end - begin); + if (direction.normalize()) + { + Ray3d const ray(begin, direction); + + Intersect3d::ResultData * const data = 0; + if (Intersect3d::intersectRayABoxWithData(ray, m_box, data)) + { + //-- If the global bounding intersection succeeds, we need to test the "child" extents + for (int i = 1; i < numberOfExtents; ++i) + { + Vector normal; + float t; + Vector * const normalPtr = (surfaceNormal != 0) ? &normal : 0; + BaseExtent const * const extent = getExtent(i); + if (extent->intersect(begin, end, normalPtr, &t)) + { + if (t < bestTime) + { + if (surfaceNormal) + *surfaceNormal = normal; + + if (time) + *time = t; + + bestTime = t; + } + + hit = true; + } + } + } + } + } + + return hit; +} + +// ---------------------------------------------------------------------- + +Range DetailExtent::rangedIntersect ( Line3d const & line ) const +{ + Range R = Range::empty; + + for(int i = 0; i < getExtentCount(); i++) + { + R = getExtent(i)->rangedIntersect(line); + + if(R.isEmpty()) return R; + } + + return R; +} + +Range DetailExtent::rangedIntersect ( Ray3d const & ray ) const +{ + Range range = Range::empty; + + for(int i = 0; i < getExtentCount(); i++) + { + range = getExtent(i)->rangedIntersect(ray); + + if(range.isEmpty()) return range; + } + + return range; +} + +Range DetailExtent::rangedIntersect ( Segment3d const & seg ) const +{ + Range range = Range::empty; + + for(int i = 0; i < getExtentCount(); i++) + { + range = getExtent(i)->rangedIntersect(seg); + + if(range.isEmpty()) return range; + } + + return range; +} + +// ---------------------------------------------------------------------- + +BaseExtent * DetailExtent::clone ( void ) const +{ + DetailExtent * newExtent = new DetailExtent(); + + cloneChildren( newExtent ); + + return newExtent; +} + +void DetailExtent::updateBounds ( void ) +{ + m_sphere = m_extents->back()->getBoundingSphere(); + m_box = m_extents->back()->getBoundingBox(); +} + +// ---------------------------------------------------------------------- + +int countExtents ( BaseExtent const * extent ) +{ + if(extent == NULL) return 0; + + CompositeExtent const * composite = dynamic_cast(extent); + + if(composite == NULL) return 1; + + int accum = 0; + + for(int i = 0; i < composite->getExtentCount(); i++) + { + accum += countExtents(composite->getExtent(i)); + } + + return accum; +} + +bool DetailExtent::validate ( void ) const +{ + int extentCount = getExtentCount(); + + if(extentCount < 2) + { + DEBUG_WARNING(true,("DetailExtent::validate - Detail extent has less than 2 sub-extents")); + + return false; + } + + int lastCount = -1; + + for(int i = 0; i < extentCount; i++) + { + int nextCount = countExtents(getExtent(i)); + + if(nextCount <= lastCount) + { + DEBUG_WARNING(true,("DetailExtent::validate - Detail extent's sub-extents are in the wrong order")); + + return false; + } + + lastCount = nextCount; + } + + return true; +} diff --git a/engine/shared/library/sharedCollision/src/shared/extent/DetailExtent.h b/engine/shared/library/sharedCollision/src/shared/extent/DetailExtent.h new file mode 100644 index 00000000..cf35086a --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/DetailExtent.h @@ -0,0 +1,64 @@ +// ====================================================================== +// +// DetailExtent.h +// Austin Appleby +// +// copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_DetailExtent_H +#define INCLUDED_DetailExtent_H + +// ====================================================================== + +#include "sharedCollision/CompositeExtent.h" + +// ====================================================================== + +class DetailExtent : public CompositeExtent +{ +public: + + DetailExtent(); + virtual ~DetailExtent(); + + static void install ( void ); + static void remove ( void ); + + static Extent * build ( Iff & iff ); + + // ---------- + // Virtual Extent interface + + virtual void load ( Iff & iff ); + virtual void write ( Iff & iff ) const; + + virtual bool contains ( Vector const & vector ) const; + virtual bool contains ( Vector const & begin, Vector const & end ) const; + + virtual Range rangedIntersect(Line3d const & line) const; + virtual Range rangedIntersect(Ray3d const & ray) const; + virtual Range rangedIntersect(Segment3d const & seg) const; + + virtual BaseExtent * clone ( void ) const; + + virtual bool validate ( void ) const; + +protected: + + virtual bool realIntersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, real * time) const; + + void updateBounds ( void ); + + void load_0000 ( Iff & iff ); + +private: + + DetailExtent(const DetailExtent &); + DetailExtent &operator =(const DetailExtent &); +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/extent/Extent.cpp b/engine/shared/library/sharedCollision/src/shared/extent/Extent.cpp new file mode 100644 index 00000000..24768bf9 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/Extent.cpp @@ -0,0 +1,423 @@ +// ====================================================================== +// +// Extent.cpp +// jeff grills +// +// copyright 1999 Bootprint Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/Extent.h" + +#include "sharedCollision/ExtentList.h" +#include "sharedCollision/Intersect3d.h" +#include "sharedCollision/Containment3d.h" +#include "sharedFile/Iff.h" +#include "sharedMath/DebugShapeRenderer.h" +#include "sharedMath/Range.h" +#include "sharedMath/Ray3d.h" +#include "sharedMath/ShapeUtils.h" +#include "sharedMath/AxialBox.h" + +//----------------------------------------------------------------- + +static const Tag TAG_EXSP = TAG (E,X,S,P); +static const Tag TAG_CNTR = TAG (C,N,T,R); +static const Tag TAG_RADI = TAG (R,A,D,I); +static const Tag TAG_SPHR = TAG (S,P,H,R); + +// ====================================================================== + +void Extent::install ( void ) +{ + ExtentList::assignBinding(TAG_EXSP, Extent::create); +} + +void Extent::remove ( void ) +{ +} + +// ---------------------------------------------------------------------- + +Extent *Extent::create(Iff & iff) +{ + Extent * extent = new Extent(); + + extent->load(iff); + + return extent; +} + +// ====================================================================== +// Create an extent + +Extent::Extent(void) +: BaseExtent(ET_Sphere), + m_sphere(), + m_referenceCount(0) +{ +} + +Extent::Extent( ExtentType const & type ) +: BaseExtent(type), + m_sphere(), + m_referenceCount(0) +{ +} + +Extent::Extent(const Sphere &newSphere) +: BaseExtent(ET_Sphere), + m_sphere(newSphere), + m_referenceCount(0) +{ +} + +Extent::Extent( ExtentType const & type, Sphere const & sphere ) +: BaseExtent(type), + m_sphere(sphere), + m_referenceCount(0) +{ +} + +Extent::Extent(const Vector &newSphereCenter, real newSphereRadius) +: BaseExtent(ET_Sphere), + m_sphere(newSphereCenter, newSphereRadius), + m_referenceCount(0) +{ +} + +// ---------------------------------------------------------------------- +/** + * Destroy an extent. + */ + +Extent::~Extent(void) +{ + DEBUG_FATAL(m_referenceCount > 0, ("referenceCount not zero %d", m_referenceCount)); +} + +// ---------------------------------------------------------------------- + +int Extent::getReferenceCount ( void ) const +{ + return m_referenceCount; +} + +// ---------- + +int Extent::incrementReference ( void ) const +{ + m_referenceCount++; + + return m_referenceCount; +} + +// ---------- + +int Extent::decrementReference ( void ) const +{ + m_referenceCount--; + + return m_referenceCount; +} + +// ---------------------------------------------------------------------- + +void Extent::loadSphere_old (Iff & iff ) +{ + iff.enterChunk(TAG_CNTR); + const Vector center = iff.read_floatVector(); + iff.exitChunk(TAG_CNTR); + + iff.enterChunk(TAG_RADI); + const real radius = iff.read_float(); + iff.exitChunk(TAG_RADI); + + setSphere(Sphere(center, radius)); +} + +// ---------------------------------------------------------------------- + +void Extent::loadSphere( Iff & iff ) +{ + iff.enterChunk(TAG_SPHR); + + Vector center = iff.read_floatVector(); + real radius = iff.read_float(); + + setSphere( Sphere(center,radius) ); + + iff.exitChunk(TAG_SPHR); +} + +// ---------------------------------------------------------------------- + +void Extent::load_0000(Iff & iff) +{ + iff.enterForm(TAG_0000); + + loadSphere_old(iff); + + iff.exitForm(TAG_0000); +} + +// ---------------------------------------------------------------------- + +void Extent::load_0001(Iff & iff) +{ + iff.enterForm(TAG_0001); + + loadSphere(iff); + + iff.exitForm(TAG_0001); +} + +// ---------------------------------------------------------------------- + +void Extent::load(Iff & iff) +{ + iff.enterForm(TAG_EXSP); + + switch (iff.getCurrentName()) + { + case TAG_0000: + load_0000(iff); + break; + + case TAG_0001: + load_0001(iff); + break; + + default: + FATAL (true, ("Extent::load - unsupported format")); + break; + } + + iff.exitForm(TAG_EXSP); +} + +//----------------------------------------------------------------- +/** + * Write a complete Extent object. + */ + +void Extent::write(Iff & iff) const +{ + iff.insertForm(TAG_EXSP); + + iff.insertForm(TAG_0001); + + writeSphere(iff); + + iff.exitForm(TAG_0001); + + iff.exitForm(TAG_EXSP); +} + +// ---------------------------------------------------------------------- + +void Extent::writeSphere( Iff & iff ) const +{ + iff.insertChunk(TAG_SPHR); + + iff.insertChunkFloatVector( m_sphere.getCenter() ); + iff.insertChunkData( m_sphere.getRadius() ); + + iff.exitChunk(TAG_SPHR); +} + +// ---------------------------------------------------------------------- +/** + * Test a line segment against the Extent's bounding sphere. + * + * If the begining point is contained within the sphere, this routine will return true, + * and the parametric time will be 0. + * + * @param begin Beginning of the line segment + * @param end End of the line segment + * @param time [OUT] Parametric time of intersection along the line segment + * @return True if the any part of the line segment is inside the line segment, otherwise false + */ + +bool Extent::testSphereOnly(Vector const & begin, Vector const & end, Vector * surfaceNormal, float * time) const +{ + Vector direction(end - begin); + if (!direction.normalize()) + { + // handle the line segment being very short to avoid a divide-by-zero + if (testSphereOnly(begin)) + { + if (surfaceNormal) + { + Vector normal(begin - m_sphere.getCenter()); + if (!normal.normalize()) + normal = -direction; + + *surfaceNormal = normal; + } + + if (time) + *time = 0.f; + + return true; + } + + if (testSphereOnly(end)) + { + if (surfaceNormal) + { + Vector normal(begin - m_sphere.getCenter()); + if (!normal.normalize()) + normal = -direction; + + *surfaceNormal = normal; + } + + if (time) + *time = 1.f; + + return true; + } + + return false; + } + + Ray3d const ray(begin, direction); + bool const getSurfaceNormal = (surfaceNormal != 0); + Intersect3d::ResultData data(getSurfaceNormal); + if (Intersect3d::intersectRaySphereWithData(ray, m_sphere, &data)) + { + float const magnitude = (end - begin).magnitude(); + if (data.m_length <= magnitude) + { + if (surfaceNormal) + *surfaceNormal = data.m_surfaceNormal; + + if (time) + *time = data.m_length / magnitude; + + return true; + } + } + + return false; +} + +// ---------------------------------------------------------------------- +/** + * Determine if a point is inside the Extent. + * + * This routine will take care of trivial rejection itself, so do not + * call the testSphereOnly() routine before calling this routine. + * + * @param vector Point to test for inclusion in the Extent + */ + +bool Extent::contains(const Vector &vector) const +{ + return testSphereOnly(vector); +} + +// ---------------------------------------------------------------------- +/** + * Determine if a line segment is completely contained within the Extent. + * + * This routine will take care of trivial rejection itself, so do not + * call the testSphereOnly() routine before calling this routine. + * + * The default implementation of this routine is valid for all convex shapes. + * + * @param begin Beginning of the line segment + * @param end End of the line segment + */ + +bool Extent::contains(const Vector &begin, const Vector &end) const +{ + return contains(begin) && contains(end); +} + +// ---------------------------------------------------------------------- + +bool Extent::realIntersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, float * time ) const +{ + return testSphereOnly(begin, end, surfaceNormal, time); +} + +// ---------------------------------------------------------------------- + +Range Extent::rangedIntersect ( Line3d const & line ) const +{ + return Intersect3d::Intersect(line,m_sphere); +} + +Range Extent::rangedIntersect ( Ray3d const & ray ) const +{ + return Intersect3d::Intersect(ray,m_sphere); +} + +Range Extent::rangedIntersect ( Segment3d const & seg ) const +{ + return Intersect3d::Intersect(seg,m_sphere); +} + +// ---------------------------------------------------------------------- + +void Extent::drawDebugShapes ( DebugShapeRenderer * renderer ) const +{ + UNREF(renderer); + +#ifdef _DEBUG + + if(renderer == NULL) return; + + renderer->setColor( VectorArgb::solidYellow ); + renderer->drawSphere(m_sphere); + +#endif +} + +// ---------------------------------------------------------------------- + +BaseExtent * Extent::clone ( void ) const +{ + return new Extent( getSphere() ); +} + +// ---------- + +void Extent::copy ( BaseExtent const * source ) +{ + if(!source) return; + + Extent const * sphereSource = safe_cast< Extent const * >(source); + + setSphere( sphereSource->getSphere() ); +} + +// ---------- + +void Extent::transform ( BaseExtent const * source, Transform const & tform, float scale ) +{ + if(!source) return; + + Extent const * sphereSource = safe_cast< Extent const * >(source); + + setSphere( ShapeUtils::transform( sphereSource->getSphere(), tform, scale ) ); +} + +// ---------- + +AxialBox Extent::getBoundingBox ( void ) const +{ + return Containment3d::EncloseABox(getSphere()); +} + +// ---------- + +Sphere Extent::getBoundingSphere ( void ) const +{ + return getSphere(); +} + +// ====================================================================== diff --git a/engine/shared/library/sharedCollision/src/shared/extent/Extent.h b/engine/shared/library/sharedCollision/src/shared/extent/Extent.h new file mode 100644 index 00000000..9934c18c --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/Extent.h @@ -0,0 +1,174 @@ +// ====================================================================== +// +// Extent.h +// copyright 1999 Bootprint Entertainment +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef EXTENT_H +#define EXTENT_H + +// ====================================================================== + +class VectorArgb; +class Iff; +class DebugShapeRenderer; + +#include "sharedMath/Sphere.h" +#include "sharedCollision/BaseExtent.h" +#include "sharedCollision/CollisionEnums.h" + +// ====================================================================== + +class Extent : public BaseExtent +{ +public: + + Extent(void); + + explicit Extent( ExtentType const & type ); + explicit Extent( Sphere const & newSphere ); + + Extent( ExtentType const & type, Sphere const & sphere ); + Extent( Vector const & newSphereCenter, real newSphereRadius ); + + virtual ~Extent(void); + + static Extent *create(Iff & iff); + static void install(); + static void remove(); + + // ---------- + + int getReferenceCount ( void ) const; + int incrementReference ( void ) const; + int decrementReference ( void ) const; + + // ---------- + + Sphere const & getSphere ( void ) const; + void setSphere (const Sphere &sphere); + + bool testSphereOnly (Vector const & vector) const; + bool testSphereOnly (Vector const & begin, Vector const & end) const; + bool testSphereOnly (Vector const & begin, Vector const & end, real *time) const; + bool testSphereOnly (Vector const & begin, Vector const & end, Vector * surfaceNormal, float * time) const; + + // ---------- + + typedef Sphere ShapeType; + + ShapeType const & getShape ( void ) const; + ExtentType const & getType ( void ) const; + + // ---------- + // Virtual Extent interface + + virtual void load ( Iff & iff ); + virtual void write ( Iff & iff ) const; + + virtual bool contains ( Vector const & vector ) const; + virtual bool contains ( Vector const & begin, Vector const & end ) const; + + virtual Range rangedIntersect(Line3d const & line) const; + virtual Range rangedIntersect(Ray3d const & ray) const; + virtual Range rangedIntersect(Segment3d const & seg) const; + + virtual void drawDebugShapes ( DebugShapeRenderer * renderer ) const; + + virtual BaseExtent * clone ( void ) const; + virtual void copy ( BaseExtent const * source ); + virtual void transform ( BaseExtent const * parent, Transform const & tform, float scale ); + virtual AxialBox getBoundingBox ( void ) const; + virtual Sphere getBoundingSphere ( void ) const; + + // ---------- + +protected: + + virtual bool realIntersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, real * time) const; + + void loadSphere ( Iff & iff ); + void loadSphere_old ( Iff & iff ); + + void load_0000 ( Iff & iff ); + void load_0001 ( Iff & iff ); + + void writeSphere ( Iff & iff ) const; + + // ---------- + + Sphere m_sphere; + +private: + + Extent(const Extent &); + Extent &operator =(const Extent &); + + mutable int m_referenceCount; +}; + +// ====================================================================== +/** + * Set the enclosing sphere of this extent. + */ + +inline void Extent::setSphere(const Sphere &newSphere) +{ + m_sphere = newSphere; +} + +// ---------------------------------------------------------------------- +/** + * Get the enclosing sphere of this extent. + * + * Allowing the sphere to be centered around a point other that the origin + * may allow the radius to be significantly smaller. + */ + +inline const Sphere &Extent::getSphere(void) const +{ + return m_sphere; +} + +// ---------------------------------------------------------------------- +/** + * Test a point against the Extent's bounding sphere. + * + * @param vector Point to test inside the sphere + * @return True if the point is inside the line segment, otherwise false + */ + +inline bool Extent::testSphereOnly(Vector const & vector) const +{ + return m_sphere.contains(vector); +} + +// ---------------------------------------------------------------------- + +inline bool Extent::testSphereOnly(Vector const & begin, Vector const & end) const +{ + real time; + Vector * normal = 0; + return testSphereOnly(begin, end, normal, &time); +} + +// ---------------------------------------------------------------------- + +inline bool Extent::testSphereOnly(Vector const & begin, Vector const & end, real *time) const +{ + Vector * normal = 0; + return testSphereOnly(begin, end, normal, time); +} + +// ---------------------------------------------------------------------- + +inline Extent::ShapeType const & Extent::getShape ( void ) const +{ + return getSphere(); +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/extent/ExtentList.cpp b/engine/shared/library/sharedCollision/src/shared/extent/ExtentList.cpp new file mode 100644 index 00000000..e77715a1 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/ExtentList.cpp @@ -0,0 +1,223 @@ +// ====================================================================== +// +// ExtentList.cpp +// copyright 1999 Bootprint Entertainment +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/ExtentList.h" + +#include "sharedCollision/BoxExtent.h" +#include "sharedCollision/CylinderExtent.h" +#include "sharedCollision/ComponentExtent.h" +#include "sharedCollision/DetailExtent.h" +#include "sharedCollision/MeshExtent.h" + +#include "sharedFoundation/ExitChain.h" +#include "sharedFile/Iff.h" + +#include + +const Tag TAG_NULL = TAG(N,U,L,L); + +// ====================================================================== + +struct ExtentList::BindImpl +{ + typedef std::map Map_t; + Map_t * m_map; +}; + +bool ExtentList::ms_installed; +ExtentList::BindImpl ExtentList::ms_bindImpl; + +// ---------- + +Extent * ExtentList::nullFactory ( Iff & iff ) +{ + Tag name = iff.getCurrentName(); + + DEBUG_FATAL(name != TAG_NULL,("ExtentList::nullFactory - Got an iff whose current tag isn't TAG_NULL")); + UNREF(name); + + IGNORE_RETURN( iff.goForward() ); + + return NULL; +} + +// ====================================================================== +// Install the ExtentList system + +void ExtentList::install() +{ + DEBUG_FATAL(ms_installed, ("already installed")); + ms_installed = true; + + // ---------- + // create static data + + ms_bindImpl.m_map = new BindImpl::Map_t; + + // ---------- + // add exit chain entry + + ExitChain::add(ExtentList::remove, "ExtentList::remove", 0, false); + + // ---------- + // install dependencies + + Extent::install(); + BoxExtent::install(); + CylinderExtent::install(); + ComponentExtent::install(); + DetailExtent::install(); + MeshExtent::install(); + + assignBinding(TAG_NULL,ExtentList::nullFactory); +} + +// ---------------------------------------------------------------------- +/** + * Remove the ExtentList system. + */ + +void ExtentList::remove(void) +{ + DEBUG_FATAL(!ms_installed, ("not installed")); + + delete ms_bindImpl.m_map; + ms_bindImpl.m_map = 0; + + ms_installed = false; +} + +// ---------------------------------------------------------------------- +/** + * Assign a new CreateFunction to a Tag. + * + * If the tag already exists in the binding list, the CreateFunction will + * be changed to the new function. If the tag is not in the binding list, + * the Tag/CreateFunction pair will be added. + * + * If the binding list is full and an attempt is made to add a new pair, + * this routine will call Fatal in debug builds. + * + * @param tag Tag to bind to a CreateFunction + * @param createFunction Function to call to create an extent with this Tag + */ + +void ExtentList::assignBinding(Tag tag, CreateFunction createFunction) +{ + DEBUG_FATAL(!createFunction, ("createFunction may not be NULL")); + + (*ms_bindImpl.m_map) [tag] = createFunction; +} + +// ---------------------------------------------------------------------- +/** + * Remove a Tag/CreateFunction binding. + * + * No action is taken if the tag is not in the list. + * + * @param tag Tag for the binding to remove + */ + +void ExtentList::removeBinding(Tag tag) +{ + IGNORE_RETURN (ms_bindImpl.m_map->erase (tag)); +} + +// ---------------------------------------------------------------------- +/** + * Fetch an Extent. + * + * This routine may be passed NULL. + * + * This routine will increase the reference count of the specified extent. + * + * @param extent Extent to add a reference to + */ + +const Extent *ExtentList::fetch(const Extent *extent) +{ + DEBUG_FATAL(!ms_installed, ("not installed")); + + if (extent) + IGNORE_RETURN( extent->incrementReference() ); + + return extent; +} + +// ---------------------------------------------------------------------- +/** + * Load an Extent from an Iff. + * + * The reference count of this Extent will be 1. + * + * @param iff Data file containing the Extent + */ + +Extent * ExtentList::create(Iff & iff) +{ + DEBUG_FATAL(!ms_installed, ("not installed")); + + // handle no extents + if (iff.atEndOfForm()) + return NULL; + + const Tag tag = iff.getCurrentName(); + + const BindImpl::Map_t::const_iterator it = ms_bindImpl.m_map->find (tag); + + if (it == ms_bindImpl.m_map->end ()) //lint !e1702 //operator is both + { + DEBUG_FATAL(true, ("binding not found")); + return 0; //lint !e527 // unreachable + } + + Extent *extent = (*it).second(iff); //lint !e1702 //operator is both + + return extent; +} + +const Extent *ExtentList::fetch(Iff & iff) +{ + Extent *extent = create(iff); //lint !e1702 //operator is both + + // fetch the extent + if(extent) + { + return fetch(extent); + } + else + { + return extent; + } +} + +// ---------------------------------------------------------------------- +/** + * Release an Extent. + * + * This routine may be passed NULL. + * + * This routine will decrement the reference count of the Extent. When + * the reference count becomes 0, the Extent will be deleted. + * + * @param extent Extent that is no longer used + */ + +void ExtentList::release(const Extent *extent) +{ + DEBUG_FATAL(!ms_installed, ("not installed")); + + if (extent && extent->decrementReference() <= 0) + { + DEBUG_FATAL(extent->getReferenceCount() < 0, ("user count negative")); + delete const_cast(extent); + } +} + +// ====================================================================== diff --git a/engine/shared/library/sharedCollision/src/shared/extent/ExtentList.h b/engine/shared/library/sharedCollision/src/shared/extent/ExtentList.h new file mode 100644 index 00000000..86f6c243 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/ExtentList.h @@ -0,0 +1,57 @@ +// ====================================================================== +// +// ExtentList.h +// jeff grills +// +// copyright 1999 Bootprint Entertainment +// +// ====================================================================== + +#ifndef EXTENT_LIST_H +#define EXTENT_LIST_H + +// This is an abstract factory for extents + +// ====================================================================== + +class Extent; +class Iff; + +#include "sharedFoundation/Tag.h" + +// ====================================================================== + +class ExtentList +{ +public: + + typedef Extent *(*CreateFunction)(Iff &iff); + +private: + + static bool ms_installed; + + //-- the private implementation of the list of Tag->CreateFunction bindings + struct BindImpl; + static BindImpl ms_bindImpl; + +public: + + static void install(void); + static void remove(void); + + static void assignBinding(Tag tag, CreateFunction createFunction); + static void removeBinding(Tag tag); + + static Extent * nullFactory( Iff & iff ); + + static const Extent *fetch(const Extent *extent); + static const Extent *fetch(Iff &iff); + static Extent * create(Iff &iff); + + static void release(const Extent *extent); +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/extent/MeshExtent.cpp b/engine/shared/library/sharedCollision/src/shared/extent/MeshExtent.cpp new file mode 100644 index 00000000..2384b251 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/MeshExtent.cpp @@ -0,0 +1,275 @@ +// ====================================================================== +// +// MeshExtent.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/MeshExtent.h" + +#include "sharedCollision/ConfigSharedCollision.h" +#include "sharedCollision/Containment3d.h" +#include "sharedCollision/ExtentList.h" +#include "sharedCollision/SimpleCollisionMesh.h" + +#include "sharedFile/Iff.h" + +#include "sharedMath/Range.h" +#include "sharedMath/Transform.h" +#include "sharedMath/IndexedTriangleList.h" +#include "sharedMath/DebugShapeRenderer.h" + +const Tag TAG_CMSH = TAG(C,M,S,H); + +typedef stdvector::fwd VertexList; + +// ---------------------------------------------------------------------- + +void MeshExtent::install ( void ) +{ + ExtentList::assignBinding(TAG_CMSH,MeshExtent::build); +} + +void MeshExtent::remove ( void ) +{ +} + +Extent * MeshExtent::build( Iff & iff ) +{ + MeshExtent * extent = new MeshExtent(); + + extent->load(iff); + + return extent; +} + +// ---------------------------------------------------------------------- + +SimpleCollisionMesh * MeshExtent::createMesh( IndexedTriangleList * tris ) +{ + if(tris) + { + return new SimpleCollisionMesh(tris); + } + else + { + return new SimpleCollisionMesh(); + } +} + +// ---------------------------------------------------------------------- + +MeshExtent::MeshExtent() +: Extent(ET_Mesh), + m_mesh(NULL), + m_box() +{ + m_mesh = createMesh(NULL); +} + +MeshExtent::MeshExtent( IndexedTriangleList * tris ) +: Extent(ET_Mesh), + m_mesh(NULL), + m_box() +{ + m_mesh = createMesh(tris); +} + +MeshExtent::MeshExtent( SimpleCollisionMesh * mesh ) +: Extent(ET_Mesh), + m_mesh(mesh), + m_box() +{ +} + +MeshExtent::~MeshExtent() +{ + delete m_mesh; + m_mesh = NULL; +} + +// ---------------------------------------------------------------------- + +void MeshExtent::load ( Iff & iff ) +{ + iff.enterForm(TAG_CMSH); + + switch (iff.getCurrentName()) + { + case TAG_0000: + load_0000(iff); + break; + + default: + FATAL (true, ("MeshExtent::load - unsupported format")); + break; + } + + iff.exitForm(TAG_CMSH); + + updateBounds(); +} + +// ---------- + +void MeshExtent::write ( Iff & iff ) const +{ + iff.insertForm(TAG_CMSH); + + iff.insertForm(TAG_0000); + + m_mesh->write(iff); + + iff.exitForm(TAG_0000); + + iff.exitForm(TAG_CMSH); +} + +// ---------------------------------------------------------------------- + +bool MeshExtent::contains ( Vector const & vector ) const +{ + return m_box.contains(vector); +} + +bool MeshExtent::contains ( Vector const & begin, Vector const & end ) const +{ + return contains(begin) && contains(end); +} + +// ---------------------------------------------------------------------- + +bool MeshExtent::realIntersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, float * time ) const +{ + return m_mesh->intersect(begin, end, surfaceNormal, time); +} + +// ---------------------------------------------------------------------- + +Range MeshExtent::rangedIntersect( Line3d const & line ) const +{ + return m_mesh->intersectRange(line); +} + +Range MeshExtent::rangedIntersect( Ray3d const & ray ) const +{ + return m_mesh->intersectRange(ray); +} + +Range MeshExtent::rangedIntersect( Segment3d const & seg ) const +{ + return m_mesh->intersectRange(seg); +} + +void MeshExtent::drawDebugShapes ( DebugShapeRenderer * renderer ) const +{ + UNREF(renderer); + +#ifdef _DEBUG + + if(renderer == NULL) return; + + renderer->setColor( VectorArgb::solidWhite ); + m_mesh->drawDebugShapes(renderer); + +#endif +} + +// ---------------------------------------------------------------------- + +BaseExtent * MeshExtent::clone ( void ) const +{ + return new MeshExtent( m_mesh->clone() ); +} + +void MeshExtent::copy( BaseExtent const * source ) +{ + MeshExtent const * meshSource = safe_cast< MeshExtent const * >(source); + + m_mesh->copy( meshSource->getMesh() ); + + m_sphere = meshSource->m_sphere; + m_box = meshSource->m_box; +} + +void MeshExtent::transform( BaseExtent const * source, Transform const & tform, float scale ) +{ + MeshExtent const * meshSource = safe_cast< MeshExtent const * >(source); + + CollisionMesh const * mesh = meshSource->getMesh(); + + m_mesh->transform( mesh, tform, scale ); + + updateBounds(); +} + +// ---------- + +void MeshExtent::updateBounds ( void ) +{ + m_mesh->calcBounds(); + + VertexList const & verts = m_mesh->getTris()->getVertices(); + + m_sphere = Containment3d::EncloseSphere( verts ); + m_box = Containment3d::EncloseABox( verts ); +} + +// ---------- + +AxialBox MeshExtent::getBoundingBox ( void ) const +{ + return m_box; + +} + +// ---------- + +Sphere MeshExtent::getBoundingSphere ( void ) const +{ + return m_sphere; +} + +// ---------------------------------------------------------------------- + +void MeshExtent::attachMesh( IndexedTriangleList * tris ) +{ + m_mesh->attachTris(tris); + + updateBounds(); +} + +CollisionMesh * MeshExtent::getMesh ( void ) +{ + return m_mesh; +} + +CollisionMesh const * MeshExtent::getMesh ( void ) const +{ + return m_mesh; +} + + + + + + + + + + + + +// ====================================================================== + +void MeshExtent::load_0000 ( Iff & iff ) +{ + iff.enterForm(TAG_0000); + + m_mesh->load(iff); + + iff.exitForm(TAG_0000); +} + +// ---------------------------------------------------------------------- diff --git a/engine/shared/library/sharedCollision/src/shared/extent/MeshExtent.h b/engine/shared/library/sharedCollision/src/shared/extent/MeshExtent.h new file mode 100644 index 00000000..a9731daf --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/MeshExtent.h @@ -0,0 +1,97 @@ +// ====================================================================== +// +// MeshExtent.h +// Austin Appleby +// +// copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_MeshExtent_H +#define INCLUDED_MeshExtent_H + +// ====================================================================== + +#include "sharedCollision/Extent.h" + +#include "sharedMath/AxialBox.h" + +class CollisionMesh; +class Iff; +class IndexedTriangleList; +class SimpleCollisionMesh; + +// ====================================================================== + +// Grr, MeshExtent has to derive from Extent because of the ExtentList +// stuff + +class MeshExtent : public Extent +{ +public: + + MeshExtent(); + MeshExtent( IndexedTriangleList * tris ); + MeshExtent( SimpleCollisionMesh * mesh ); + virtual ~MeshExtent(); + + static void install ( void ); + static void remove ( void ); + + static Extent * build ( Iff & iff ); + + // ---------- + // Virtual Extent interface + + virtual void load ( Iff & iff ); + virtual void write ( Iff & iff ) const; + + virtual bool contains ( Vector const & vector ) const; + virtual bool contains ( Vector const & begin, Vector const & end ) const; + + virtual Range rangedIntersect(Line3d const & line) const; + virtual Range rangedIntersect(Ray3d const & ray) const; + virtual Range rangedIntersect(Segment3d const & seg) const; + + virtual void drawDebugShapes ( DebugShapeRenderer * renderer ) const; + + virtual BaseExtent * clone ( void ) const; + virtual void copy ( BaseExtent const * source ); + virtual void transform ( BaseExtent const * parent, Transform const & tform, float scale ); + + virtual AxialBox getBoundingBox ( void ) const; + virtual Sphere getBoundingSphere ( void ) const; + + // ---------- + + void attachMesh ( IndexedTriangleList * mesh ); + + CollisionMesh * getMesh ( void ); + CollisionMesh const * getMesh ( void ) const; + +protected: + + virtual bool realIntersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, real * time) const; + + + void updateBounds ( void ); + + void load_0000 ( Iff & iff ); + + static SimpleCollisionMesh * createMesh ( IndexedTriangleList * tris ); + + // ---------- + + SimpleCollisionMesh * m_mesh; + + AxialBox m_box; + +private: + + MeshExtent(const MeshExtent &); + MeshExtent &operator =(const MeshExtent &); +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/extent/OrientedCylinderExtent.cpp b/engine/shared/library/sharedCollision/src/shared/extent/OrientedCylinderExtent.cpp new file mode 100644 index 00000000..234db209 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/OrientedCylinderExtent.cpp @@ -0,0 +1,230 @@ +// ====================================================================== +// +// OrientedCylinderExtent.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/OrientedCylinderExtent.h" + +#include "sharedCollision/ExtentList.h" +#include "sharedCollision/Containment3d.h" +#include "sharedFile/Iff.h" +#include "sharedMath/DebugShapeRenderer.h" +#include "sharedMath/ShapeUtils.h" + +const Tag TAG_XOCL = TAG(X,O,C,L); +const Tag TAG_OCYL = TAG(O,C,Y,L); + +// ====================================================================== + +void OrientedCylinderExtent::install ( void ) +{ + ExtentList::assignBinding(TAG_XOCL,OrientedCylinderExtent::build); +} + +void OrientedCylinderExtent::remove ( void ) +{ +} + +Extent * OrientedCylinderExtent::build ( Iff & iff ) +{ + OrientedCylinderExtent * extent = new OrientedCylinderExtent(); + + extent->load(iff); + + return extent; +} + +// ---------------------------------------------------------------------- + +OrientedCylinderExtent::OrientedCylinderExtent() +: Extent(ET_Cylinder), + m_cylinder() +{ + DEBUG_WARNING(true, ("OrientedCylinderExtent::OrientedCylinderExtent: this class does not implement the necessary intersect functions.")); +} + +OrientedCylinderExtent::OrientedCylinderExtent( OrientedCylinder const & cylinder ) +: Extent(ET_Cylinder), + m_cylinder(cylinder) +{ + DEBUG_WARNING(true, ("OrientedCylinderExtent::OrientedCylinderExtent: this class does not implement the necessary intersect functions.")); +} + +OrientedCylinderExtent::~OrientedCylinderExtent() +{ +} + +// ---------------------------------------------------------------------- +//@todo - Implement these! + +bool OrientedCylinderExtent::contains ( Vector const & point ) const +{ + UNREF(point); + + return false; +} + +bool OrientedCylinderExtent::contains ( Vector const & begin, Vector const & end ) const +{ + UNREF(begin); + UNREF(end); + + return false; +} + +bool OrientedCylinderExtent::realIntersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, real * time) const +{ + UNREF(begin); + UNREF(end); + UNREF(surfaceNormal); + UNREF(time); + return false; +} + +// ---------------------------------------------------------------------- + +OrientedCylinder const & OrientedCylinderExtent::getCylinder ( void ) const +{ + return m_cylinder; +} + +// ---------- + +void OrientedCylinderExtent::setCylinder ( OrientedCylinder const & newCylinder ) +{ + m_cylinder = newCylinder; +} + +// ---------------------------------------------------------------------- + +void OrientedCylinderExtent::drawDebugShapes ( DebugShapeRenderer * renderer ) const +{ + UNREF(renderer); + +#ifdef _DEBUG + + if(renderer == NULL) return; + + renderer->setColor( VectorArgb::solidGreen ); + renderer->draw(m_cylinder); + +#endif +} + +// ---------------------------------------------------------------------- + +void OrientedCylinderExtent::load ( Iff & iff ) +{ + iff.enterForm(TAG_XOCL); + + switch (iff.getCurrentName()) + { + case TAG_0000: + load_0000(iff); + break; + + default: + FATAL (true, ("OrientedCylinderExtent::load - unsupported format")); + break; + } + + iff.exitForm(TAG_XOCL); + +} + +// ---------- + +void OrientedCylinderExtent::write ( Iff & iff ) const +{ + iff.insertForm(TAG_XOCL); + + iff.insertForm(TAG_0000); + + writeCylinder(iff); + + iff.exitForm(TAG_0000); + + iff.exitForm(TAG_XOCL); +} + +// ---------------------------------------------------------------------- + +void OrientedCylinderExtent::loadCylinder ( Iff & iff ) +{ + iff.enterChunk(TAG_OCYL); + + Vector base = iff.read_floatVector(); + Vector axis = iff.read_floatVector(); + real radius = iff.read_float(); + real height = iff.read_float(); + + m_cylinder = OrientedCylinder(base,axis,radius,height); + + iff.exitChunk(TAG_OCYL); +} + +// ---------------------------------------------------------------------- + +void OrientedCylinderExtent::load_0000 ( Iff & iff ) +{ + iff.enterForm(TAG_0000); + + loadCylinder(iff); + + iff.exitForm(TAG_0000); +} + +// ---------------------------------------------------------------------- + +void OrientedCylinderExtent::writeCylinder ( Iff & iff ) const +{ + iff.insertChunk(TAG_OCYL); + + iff.insertChunkFloatVector( m_cylinder.getBase() ); + iff.insertChunkFloatVector( m_cylinder.getAxis() ); + iff.insertChunkData( m_cylinder.getRadius() ); + iff.insertChunkData( m_cylinder.getHeight() ); + + iff.exitChunk(TAG_OCYL); +} + +// ---------------------------------------------------------------------- + +BaseExtent * OrientedCylinderExtent::clone ( void ) const +{ + return new OrientedCylinderExtent( getCylinder() ); +} + +// ---------- + +void OrientedCylinderExtent::copy ( BaseExtent const * source ) +{ + if(!source) return; + + OrientedCylinderExtent const * cylinderSource = safe_cast(source); + + setCylinder( cylinderSource->getCylinder() ); +} + +// ---------- + +void OrientedCylinderExtent::transform ( BaseExtent const * source, Transform const & tform, float scale ) +{ + if(!source) return; + + OrientedCylinderExtent const * cylinderSource = safe_cast(source); + + setCylinder( ShapeUtils::transform( cylinderSource->getCylinder(), tform, scale ) ); +} + +// ---------- + +Sphere OrientedCylinderExtent::getBoundingSphere ( void ) const +{ + return Containment3d::EncloseSphere( m_cylinder ); +} + +// ---------------------------------------------------------------------- diff --git a/engine/shared/library/sharedCollision/src/shared/extent/OrientedCylinderExtent.h b/engine/shared/library/sharedCollision/src/shared/extent/OrientedCylinderExtent.h new file mode 100644 index 00000000..31d4db0d --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/OrientedCylinderExtent.h @@ -0,0 +1,96 @@ +// ====================================================================== +// +// OrientedCylinderExtent.h +// Austin Appleby +// +// copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_OrientedCylinderExtent_H +#define INCLUDED_OrientedCylinderExtent_H + +// ====================================================================== + +#include "sharedCollision/Extent.h" +#include "sharedMath/OrientedCylinder.h" + +class Iff; + +// ====================================================================== + +// OrientedCylinderExtent MUST be derived from Extent and not BaseExtent because +// ExtentList requires it. + +class OrientedCylinderExtent : public Extent +{ +public: + + OrientedCylinderExtent(); + OrientedCylinderExtent( OrientedCylinder const & cylinder ); + OrientedCylinderExtent( Cylinder const & cylinder, Transform const & tform ); + virtual ~OrientedCylinderExtent(); + + static void install ( void ); + static void remove ( void ); + + static Extent * build ( Iff & iff ); + + // ---------- + // Virtual Extent interface + + virtual void load ( Iff & iff ); + virtual void write ( Iff & iff ) const; + + virtual bool contains ( Vector const & vector ) const; + virtual bool contains ( Vector const & begin, Vector const & end ) const; + + virtual void drawDebugShapes ( DebugShapeRenderer * renderer ) const; + + virtual BaseExtent * clone ( void ) const; + virtual void copy ( BaseExtent const * source ); + virtual void transform ( BaseExtent const * parent, Transform const & tform, float scale ); + virtual Sphere getBoundingSphere ( void ) const; + + // ---------- + + OrientedCylinder const & getCylinder ( void ) const; + void setCylinder ( OrientedCylinder const & newCylinder ); + + // ---------- + + typedef OrientedCylinder ShapeType; + + ShapeType const & getShape ( void ) const; + + // ---------- + +protected: + + void loadCylinder ( Iff & iff ); + void writeCylinder ( Iff & iff ) const; + + void load_0000 ( Iff & iff ); + +private: + + virtual bool realIntersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, real * time) const; + + OrientedCylinderExtent(const OrientedCylinderExtent &); + OrientedCylinderExtent &operator =(const OrientedCylinderExtent &); + + // ---------- + + OrientedCylinder m_cylinder; +}; + +// ---------------------------------------------------------------------- + +inline OrientedCylinderExtent::ShapeType const & OrientedCylinderExtent::getShape ( void ) const +{ + return getCylinder(); +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedCollision/src/shared/extent/SimpleExtent.cpp b/engine/shared/library/sharedCollision/src/shared/extent/SimpleExtent.cpp new file mode 100644 index 00000000..5e744c37 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/SimpleExtent.cpp @@ -0,0 +1,277 @@ +// ====================================================================== +// +// SimpleExtent.cpp +// Austin Appleby +// +// copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h" +#include "sharedCollision/SimpleExtent.h" + +#include "sharedCollision/Intersect3d.h" +#include "sharedCollision/Containment3d.h" +#include "sharedFile/Iff.h" +#include "sharedFoundation/ExitChain.h" +#include "sharedFoundation/MemoryBlockManager.h" +#include "sharedMath/AxialBox.h" +#include "sharedMath/Cylinder.h" +#include "sharedMath/DebugShapeRenderer.h" +#include "sharedMath/Ray3d.h" +#include "sharedMath/ShapeUtils.h" +#include "sharedMath/Sphere.h" + +const Tag TAG_XSMP = TAG(X,S,M,P); +const Tag TAG_MSHP = TAG(M,S,H,P); + +// ====================================================================== + +MEMORY_BLOCK_MANAGER_IMPLEMENTATION_WITH_INSTALL(SimpleExtent, true, 0, 0, 0); + +// ====================================================================== + +SimpleExtent::SimpleExtent(void) +: BaseExtent(ET_Simple), + m_shape() +{ +} + +SimpleExtent::SimpleExtent( MultiShape const & newShape ) +: BaseExtent(ET_Simple), + m_shape(newShape) +{ +} + +SimpleExtent::~SimpleExtent(void) +{ +} + +// ---------------------------------------------------------------------- +// Virtual Extent interface + +void SimpleExtent::load ( Iff & iff ) +{ + iff.enterForm(TAG_XSMP); + + switch(iff.getCurrentName()) + { + case TAG_0000: + load_0000(iff); + break; + + default: + FATAL(true,("SimpleExtent::load - unsupported format\n")); + break; + } + + iff.exitForm(TAG_XSMP); +} + +void SimpleExtent::write ( Iff & iff ) const +{ + iff.insertForm(TAG_XSMP); + + iff.insertForm(TAG_0000); + + writeShape(iff); + + iff.exitForm(TAG_0000); + + iff.exitForm(TAG_XSMP); +} + +// ---------------------------------------------------------------------- + +bool SimpleExtent::contains ( Vector const & vector ) const +{ + return Containment::isContainment( Containment3d::Test(vector,getShape()) ); +} + +bool SimpleExtent::contains ( Vector const & begin, Vector const & end ) const +{ + return contains(begin) && contains(end); +} + +// ---------------------------------------------------------------------- + +bool SimpleExtent::realIntersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, real * time) const +{ + Vector direction(end - begin); + if (direction.normalize()) + { + Ray3d const ray(begin, direction); + bool const getSurfaceNormal = (surfaceNormal != 0); + Intersect3d::ResultData data(getSurfaceNormal); + if (Intersect3d::intersectRayShapeWithData(ray, getShape(), &data)) + { + float const magnitude = (end - begin).magnitude(); + if (data.m_length <= magnitude) + { + if (surfaceNormal) + *surfaceNormal = data.m_surfaceNormal; + + if (time) + *time = data.m_length / magnitude; + + return true; + } + } + } + return(false); +} + +// ---------------------------------------------------------------------- + +Range SimpleExtent::rangedIntersect ( Line3d const & line ) const +{ + return Intersect3d::Intersect( line, getShape() ); +} + +Range SimpleExtent::rangedIntersect ( Ray3d const & ray ) const +{ + return Intersect3d::Intersect( ray, getShape() ); +} + +Range SimpleExtent::rangedIntersect ( Segment3d const & seg ) const +{ + return Intersect3d::Intersect( seg, getShape() ); +} + +// ---------------------------------------------------------------------- + +void SimpleExtent::drawDebugShapes ( DebugShapeRenderer * renderer ) const +{ + UNREF(renderer); + +#ifdef _DEBUG + + if(renderer == NULL) return; + + switch( getShape().getShapeType() ) + { + case MultiShape::MST_Sphere : + renderer->setColor( VectorArgb::solidYellow ); + renderer->drawSphere( getShape().getSphere() ); + break; + + case MultiShape::MST_Cylinder : + renderer->setColor( VectorArgb::solidGreen ); + renderer->drawCylinder( getShape().getCylinder() ); + break; + + case MultiShape::MST_AxialBox : + renderer->setColor( VectorArgb::solidBlue ); + renderer->drawBox( getShape().getAxialBox() ); + break; + + case MultiShape::MST_OrientedCylinder: + case MultiShape::MST_YawedBox: + case MultiShape::MST_OrientedBox: + case MultiShape::MST_Count: + case MultiShape::MST_Invalid: + default: + break; + } + +#endif +} + +BaseExtent * SimpleExtent::clone ( void ) const +{ + return new SimpleExtent(m_shape); +} + +void SimpleExtent::copy ( BaseExtent const * source ) +{ + SimpleExtent const * simpleSource = safe_cast< SimpleExtent const * >(source); + + setShape( simpleSource->getShape() ); +} + +void SimpleExtent::transform ( BaseExtent const * source, Transform const & tform, float scale ) +{ + SimpleExtent const * simpleSource = safe_cast< SimpleExtent const * >(source); + + setShape( ShapeUtils::transform( simpleSource->getShape(), tform, scale ) ); +} + +AxialBox SimpleExtent::getBoundingBox ( void ) const +{ + return getShape().getBoundingBox(); +} + +Sphere SimpleExtent::getBoundingSphere ( void ) const +{ + return getShape().getBoundingSphere(); +} + +Vector SimpleExtent::getCenter ( void ) const +{ + return getShape().getCenter(); +} + +float SimpleExtent::getRadius ( void ) const +{ + return getShape().calcAvoidanceRadius(); +} + +// ---------------------------------------------------------------------- + +void SimpleExtent::load_0000 ( Iff & iff ) +{ + iff.enterForm(TAG_0000); + + loadShape(iff); + + iff.exitForm(TAG_0000); +} + +// ---------- + +void SimpleExtent::loadShape ( Iff & iff ) +{ + iff.enterForm(TAG_MSHP); + + MultiShape::BaseType baseType = static_cast(iff.read_int32()); + MultiShape::ShapeType shapeType = static_cast(iff.read_int32()); + + Vector center = iff.read_floatVector(); + + Vector axisX = iff.read_floatVector(); + Vector axisY = iff.read_floatVector(); + Vector axisZ = iff.read_floatVector(); + + float extentX = iff.read_float(); + float extentY = iff.read_float(); + float extentZ = iff.read_float(); + + iff.exitForm(TAG_MSHP); + + m_shape = MultiShape( baseType, shapeType, center, axisX, axisY, axisZ, extentX, extentY, extentZ ); +} + +// ---------- + +void SimpleExtent::writeShape ( Iff & iff ) const +{ + iff.insertChunk(TAG_MSHP); + + iff.insertChunkData( getShape().getBaseType() ); + iff.insertChunkData( getShape().getShapeType() ); + + iff.insertChunkData( getShape().getCenter() ); + + iff.insertChunkData( getShape().getAxisX() ); + iff.insertChunkData( getShape().getAxisY() ); + iff.insertChunkData( getShape().getAxisZ() ); + + iff.insertChunkData( getShape().getExtentX() ); + iff.insertChunkData( getShape().getExtentY() ); + iff.insertChunkData( getShape().getExtentZ() ); + + iff.exitChunk(TAG_MSHP); +} + +// ---------------------------------------------------------------------- + diff --git a/engine/shared/library/sharedCollision/src/shared/extent/SimpleExtent.h b/engine/shared/library/sharedCollision/src/shared/extent/SimpleExtent.h new file mode 100644 index 00000000..0f8a25f9 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/shared/extent/SimpleExtent.h @@ -0,0 +1,95 @@ +// ====================================================================== +// +// SimpleExtent.h +// Austin Appleby +// +// copyright 2002 Sony Online Entertainment +// +// SimpleExtent is a very simple extent class that's nothing but a single +// MultiShape. +// +// ====================================================================== + +#ifndef INCLUDED_SimpleExtent_H +#define INCLUDED_SimpleExtent_H + +#include "sharedCollision/BaseExtent.h" +#include "sharedMath/MultiShape.h" +#include "sharedFoundation/MemoryBlockManagerMacros.h" + +class Iff; +class DebugShapeRenderer; + +// ====================================================================== + +class SimpleExtent : public BaseExtent +{ + MEMORY_BLOCK_MANAGER_INTERFACE_WITH_INSTALL; + +public: + + SimpleExtent(void); + SimpleExtent( MultiShape const & newShape ); + virtual ~SimpleExtent(void); + + // ---------- + // Virtual Extent interface + + virtual void load ( Iff & iff ); + virtual void write ( Iff & iff ) const; + + virtual bool contains ( Vector const & vector ) const; + virtual bool contains ( Vector const & begin, Vector const & end ) const; + + virtual Range rangedIntersect(Line3d const & line) const; + virtual Range rangedIntersect(Ray3d const & ray) const; + virtual Range rangedIntersect(Segment3d const & seg) const; + + virtual void drawDebugShapes ( DebugShapeRenderer * renderer ) const; + + virtual BaseExtent * clone ( void ) const; + virtual void copy ( BaseExtent const * source ); + virtual void transform ( BaseExtent const * parent, Transform const & tform, float scale ); + virtual AxialBox getBoundingBox ( void ) const; + virtual Sphere getBoundingSphere ( void ) const; + virtual Vector getCenter ( void ) const; + virtual float getRadius ( void ) const; + + // ---------- + + MultiShape const & getShape ( void ) const; + void setShape ( MultiShape const & newShape ); + +protected: + + virtual bool realIntersect(Vector const & begin, Vector const & end, Vector * surfaceNormal, real * time) const; + + void load_0000 ( Iff & iff ); + void loadShape ( Iff & iff ); + void writeShape ( Iff & iff ) const; + + // ---------- + + MultiShape m_shape; + +private: + + SimpleExtent(const SimpleExtent &); + SimpleExtent &operator =(const SimpleExtent &); +}; + +// ---------------------------------------------------------------------- + +inline MultiShape const & SimpleExtent::getShape ( void ) const +{ + return m_shape; +} + +inline void SimpleExtent::setShape ( MultiShape const & newShape ) +{ + m_shape = newShape; +} + +// ====================================================================== + +#endif // #ifndef INCLUDED_SimpleExtent_H diff --git a/engine/shared/library/sharedCollision/src/win32/FirstSharedCollision.cpp b/engine/shared/library/sharedCollision/src/win32/FirstSharedCollision.cpp new file mode 100644 index 00000000..175da0d0 --- /dev/null +++ b/engine/shared/library/sharedCollision/src/win32/FirstSharedCollision.cpp @@ -0,0 +1,8 @@ +// ====================================================================== +// +// FirstSharedCollision.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedCollision/FirstSharedCollision.h"