1 #include "heightfield.hpp"
2 #include "mtphysics.hpp"
3 
4 #include <osg/Object>
5 
6 #include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
7 #include <BulletCollision/CollisionDispatch/btCollisionObject.h>
8 
9 #include <LinearMath/btTransform.h>
10 
11 #include <type_traits>
12 
13 #if BT_BULLET_VERSION < 310
14 // Older Bullet versions only support `btScalar` heightfields.
15 // Our heightfield data is `float`.
16 //
17 // These functions handle conversion from `float` to `double` when
18 // `btScalar` is `double` (`BT_USE_DOUBLE_PRECISION`).
19 namespace
20 {
21     template <class T>
makeHeights(const T * heights,float sqrtVerts)22     auto makeHeights(const T* heights, float sqrtVerts)
23         -> std::enable_if_t<std::is_same<btScalar, T>::value, std::vector<btScalar>>
24     {
25         return {};
26     }
27 
28     template <class T>
makeHeights(const T * heights,float sqrtVerts)29     auto makeHeights(const T* heights, float sqrtVerts)
30         -> std::enable_if_t<!std::is_same<btScalar, T>::value, std::vector<btScalar>>
31     {
32         return std::vector<btScalar>(heights, heights + static_cast<std::ptrdiff_t>(sqrtVerts * sqrtVerts));
33     }
34 
35     template <class T>
getHeights(const T * floatHeights,const std::vector<btScalar> &)36     auto getHeights(const T* floatHeights, const std::vector<btScalar>&)
37         -> std::enable_if_t<std::is_same<btScalar, T>::value, const btScalar*>
38     {
39         return floatHeights;
40     }
41 
42     template <class T>
getHeights(const T *,const std::vector<btScalar> & btScalarHeights)43     auto getHeights(const T*, const std::vector<btScalar>& btScalarHeights)
44         -> std::enable_if_t<!std::is_same<btScalar, T>::value, const btScalar*>
45     {
46         return btScalarHeights.data();
47     }
48 }
49 #endif
50 
51 namespace MWPhysics
52 {
HeightField()53     HeightField::HeightField() {}
54 
HeightField(const HeightField &,const osg::CopyOp &)55     HeightField::HeightField(const HeightField&, const osg::CopyOp&) {}
56 
HeightField(const float * heights,int x,int y,float triSize,float sqrtVerts,float minH,float maxH,const osg::Object * holdObject,PhysicsTaskScheduler * scheduler)57     HeightField::HeightField(const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject, PhysicsTaskScheduler* scheduler)
58         : mHoldObject(holdObject)
59 #if BT_BULLET_VERSION < 310
60         , mHeights(makeHeights(heights, sqrtVerts))
61 #endif
62         , mTaskScheduler(scheduler)
63     {
64 #if BT_BULLET_VERSION < 310
65         mShape = std::make_unique<btHeightfieldTerrainShape>(
66             sqrtVerts, sqrtVerts,
67             getHeights(heights, mHeights),
68             1,
69             minH, maxH, 2,
70             PHY_FLOAT, false
71         );
72 #else
73         mShape = std::make_unique<btHeightfieldTerrainShape>(
74             sqrtVerts, sqrtVerts, heights, minH, maxH, 2, false);
75 #endif
76         mShape->setUseDiamondSubdivision(true);
77         mShape->setLocalScaling(btVector3(triSize, triSize, 1));
78 
79 #if BT_BULLET_VERSION >= 289
80         // Accelerates some collision tests.
81         //
82         // Note: The accelerator data structure in Bullet is only used
83         // in some operations. This could be improved, see:
84         // https://github.com/bulletphysics/bullet3/issues/3276
85         mShape->buildAccelerator();
86 #endif
87 
88         btTransform transform(btQuaternion::getIdentity(),
89                                 btVector3((x+0.5f) * triSize * (sqrtVerts-1),
90                                           (y+0.5f) * triSize * (sqrtVerts-1),
91                                           (maxH+minH)*0.5f));
92 
93         mCollisionObject = std::make_unique<btCollisionObject>();
94         mCollisionObject->setCollisionShape(mShape.get());
95         mCollisionObject->setWorldTransform(transform);
96         mTaskScheduler->addCollisionObject(mCollisionObject.get(), CollisionType_HeightMap, CollisionType_Actor|CollisionType_Projectile);
97     }
98 
~HeightField()99     HeightField::~HeightField()
100     {
101         mTaskScheduler->removeCollisionObject(mCollisionObject.get());
102     }
103 
getCollisionObject()104     btCollisionObject* HeightField::getCollisionObject()
105     {
106         return mCollisionObject.get();
107     }
108 
getCollisionObject() const109     const btCollisionObject* HeightField::getCollisionObject() const
110     {
111         return mCollisionObject.get();
112     }
113 
getShape() const114     const btHeightfieldTerrainShape* HeightField::getShape() const
115     {
116         return mShape.get();
117     }
118 }
119