OpenGothic
Open source reimplementation of Gothic I and II
Loading...
Searching...
No Matches
collisionworld.cpp
Go to the documentation of this file.
1#include "collisionworld.h"
2#include "physicvbo.h"
3
4#include "physics/physics.h"
5#include "dynamicworld.h"
7
8CollisionWorld::CollisionBody::CollisionBody(btRigidBody::btRigidBodyConstructionInfo& inf, CollisionWorld* owner)
9 :btRigidBody(inf), owner(owner) {
10 }
11
13 auto flags = this->getCollisionFlags();
14
15 if((flags & btCollisionObject::CF_STATIC_OBJECT)==0) {
16 owner->removeRigidBody(this);
17 auto& rigid = owner->rigid;
18 for(size_t i=0; i<rigid.size(); ++i) {
19 if(rigid[i]==this) {
20 rigid[i] = rigid.back();
21 rigid.pop_back();
22 break;
23 }
24 }
25 } else {
26 owner->removeCollisionObject(this);
27 }
28
29 owner->touchAabbs();
30 }
31
32struct CollisionWorld::Broadphase : btDbvtBroadphase {
33 struct BroadphaseRayTester : btDbvt::ICollide {
34 btBroadphaseRayCallback& m_rayCallback;
35 BroadphaseRayTester(btBroadphaseRayCallback& orgCallback) : m_rayCallback(orgCallback) {}
36 void Process(const btDbvtNode* leaf) {
37 btDbvtProxy* proxy = reinterpret_cast<btDbvtProxy*>(leaf->data);
38 m_rayCallback.process(proxy);
39 }
40 };
41
43 m_deferedcollide = true;
44 rayTestStk.reserve(btDbvt::DOUBLE_STACKSIZE);
45 }
46
47 void rayTest(const btVector3& rayFrom, const btVector3& rayTo, btBroadphaseRayCallback& rayCallback,
48 const btVector3& aabbMin, const btVector3& aabbMax) {
49 BroadphaseRayTester callback(rayCallback);
50 btAlignedObjectArray<const btDbvtNode*>* stack = &rayTestStk;
51
52 m_sets[0].rayTestInternal(m_sets[0].m_root,
53 rayFrom,
54 rayTo,
55 rayCallback.m_rayDirectionInverse,
56 rayCallback.m_signs,
57 rayCallback.m_lambda_max,
58 aabbMin,
59 aabbMax,
60 *stack,
61 callback);
62
63 m_sets[1].rayTestInternal(m_sets[1].m_root,
64 rayFrom,
65 rayTo,
66 rayCallback.m_rayDirectionInverse,
67 rayCallback.m_signs,
68 rayCallback.m_lambda_max,
69 aabbMin,
70 aabbMax,
71 *stack,
72 callback);
73 }
74
75 btAlignedObjectArray<const btDbvtNode*> rayTestStk;
76 };
77
80 // collision configuration contains default setup for memory, collision setup
81 conf .reset(new btDefaultCollisionConfiguration());
82 // use the default collision dispatcher. For parallel processing you can use a diffent dispatcher (see Extras/BulletMultiThreaded)
83 //auto disp = new btCollisionDispatcherMt(conf.get());
84 disp .reset(new btCollisionDispatcher(conf.get()));
85 // disp->setDispatcherFlags(btCollisionDispatcher::CD_DISABLE_CONTACTPOOL_DYNAMIC_ALLOCATION); // may crash with OOM, when runout of pool
86 broad .reset(new Broadphase());
87 solver.reset(new btSequentialImpulseConstraintSolver());
88 }
89 std::unique_ptr<btCollisionConfiguration> conf;
90 std::unique_ptr<btCollisionDispatcher> disp;
91 std::unique_ptr<btSequentialImpulseConstraintSolver> solver;
92 std::unique_ptr<btBroadphaseInterface> broad;
93 };
94
98
99float CollisionWorld::toMeters(const float v) {
100 return v * 0.01f;
101 }
102
103btVector3 CollisionWorld::toMeters(const Tempest::Vec3& v) {
104 return {v.x*0.01f, v.y*0.01f, v.z*0.01f};
105 }
106
107const Tempest::Vec3 CollisionWorld::toCentimeters(const btVector3& v) {
108 return {v.x()*100.f, v.y()*100.f, v.z()*100.f};
109 }
110
111CollisionWorld::CollisionWorld(ContructInfo ci)
112 :btDiscreteDynamicsWorld(ci.disp.get(), ci.broad.get(), ci.solver.get(), ci.conf.get()) {
113 disp = std::move(ci.disp);
114 broad = std::move(ci.broad);
115 solver = std::move(ci.solver);
116 conf = std::move(ci.conf);
117
118 setForceUpdateAllAabbs(false);
119 gravity = btVector3(0,-DynamicWorld::gravityMS,0);
120 setGravity(gravity);
121 }
122
124 if(aabbChanged>0) {
125 btDiscreteDynamicsWorld::updateAabbs();
126 aabbChanged = 0;
127 return;
128 }
129 }
130
132 aabbChanged++;
133 }
134
135bool CollisionWorld::hasCollision(btRigidBody& it, Tempest::Vec3& normal, Interactive*& vob) {
136 struct rCallBack : public btCollisionWorld::ContactResultCallback {
137 int count = 0;
138 Tempest::Vec3 norm = {};
139 btCollisionObject* src = nullptr;
140 Interactive* vob = nullptr;
141
142 explicit rCallBack(btCollisionObject* src):src(src){
143 m_collisionFilterMask = btBroadphaseProxy::DefaultFilter | btBroadphaseProxy::StaticFilter;
144 }
145
146 bool needsCollision(btBroadphaseProxy* proxy0) const override {
147 auto obj=reinterpret_cast<btCollisionObject*>(proxy0->m_clientObject);
148 if(obj->getUserIndex()!=DynamicWorld::C_Water &&
149 obj->getUserIndex()!=DynamicWorld::C_Ghost &&
150 obj->getUserIndex()!=DynamicWorld::C_Item)
151 return ContactResultCallback::needsCollision(proxy0);
152 return false;
153 }
154
155 btScalar addSingleResult(btManifoldPoint& p,
156 const btCollisionObjectWrapper* proxy0, int, int,
157 const btCollisionObjectWrapper* proxy1, int, int) override {
158 norm.x+=p.m_normalWorldOnB.x();
159 norm.y+=p.m_normalWorldOnB.y();
160 norm.z+=p.m_normalWorldOnB.z();
161 ++count;
162 auto obj = proxy1->getCollisionObject();
163 if(obj->getUserIndex()==DynamicWorld::C_Object) {
164 vob = reinterpret_cast<Interactive*>(obj->getUserPointer());
165 }
166 return 0;
167 }
168
169 void normalize() {
170 norm /= norm.length();
171 }
172 };
173
174 rCallBack callback{&it};
175
176 updateAabbs();
177 contactTest(&it, callback);
178
179 if(callback.count>0){
180 callback.normalize();
181 normal = callback.norm;
182 vob = callback.vob;
183 }
184 return callback.count>0;
185 }
186
187std::unique_ptr<CollisionWorld::CollisionBody> CollisionWorld::addCollisionBody(btCollisionShape& shape, const Tempest::Matrix4x4& tr, float friction) {
188 btRigidBody::btRigidBodyConstructionInfo rigidBodyCI(
189 0, // mass, in kg. 0 -> Static object, will never move.
190 nullptr,
191 &shape,
192 btVector3(0,0,0)
193 );
194
195 std::unique_ptr<CollisionBody> obj(new CollisionBody(rigidBodyCI,this));
196 obj->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT);
197
198 btTransform trans;
199 trans.setFromOpenGLMatrix(tr.data());
200 trans.getOrigin()*=0.01f;
201
202 obj->setWorldTransform(trans);
203 obj->setFriction(friction);
204
205 this->addCollisionObject(obj.get());
206 this->updateSingleAabb(obj.get());
207 return obj;
208 }
209
210std::unique_ptr<CollisionWorld::DynamicBody> CollisionWorld::addDynamicBody(btCollisionShape& shape, const Tempest::Matrix4x4& tr,
211 float friction, float mass) {
212 if(mass<=0)
213 mass = 1;
214 btVector3 localInertia = {};
215 shape.calculateLocalInertia(mass, localInertia);
216
217 btRigidBody::btRigidBodyConstructionInfo rigidBodyCI(
218 mass,
219 nullptr,
220 &shape,
221 localInertia
222 );
223
224 std::unique_ptr<DynamicBody> obj(new DynamicBody(rigidBodyCI,this));
225 // obj->setFlags(btCollisionObject::CF_NO_CONTACT_RESPONSE);
226 // obj->setCollisionFlags(btCollisionObject::CO_RIGID_BODY);
227
228 btTransform trans;
229 trans.setFromOpenGLMatrix(tr.data());
230 trans.getOrigin()*=0.01f;
231
232 obj->setWorldTransform(trans);
233 obj->setFriction(friction);
234 obj->setActivationState(ACTIVE_TAG);
235
236 obj->setCcdSweptSphereRadius(0.1f);
237 obj->setCcdMotionThreshold(0.005f);
238
239 this->addRigidBody(obj.get());
240 this->updateSingleAabb(obj.get());
241 rigid.push_back(obj.get());
242 return obj;
243 }
244
245void CollisionWorld::rayCast(const Tempest::Vec3& b, const Tempest::Vec3& e, btCollisionWorld::RayResultCallback& cb) {
246 btVector3 s = toMeters(b), f = toMeters(e);
247 if(s==f)
248 return;
249 this->rayTest(s,f,cb);
250 }
251
252void CollisionWorld::tick(uint64_t dt) {
253 static bool dynamic = true;
254 const float dtF = float(dt);
255
256 if(dynamic) {
257 if(rigid.size()>0)
258 this->stepSimulation(dtF/1000.f, 2);
259
260 if(hitItem) {
261 const int numManifolds = getDispatcher()->getNumManifolds();
262 for(int i=0; i<numManifolds; ++i) {
263 btPersistentManifold* contactManifold = getDispatcher()->getManifoldByIndexInternal(i);
264 const btCollisionObject* a = contactManifold->getBody0();
265 const btCollisionObject* b = contactManifold->getBody1();
266
267 for(auto obj:{a,b}) {
268 if(obj->getUserIndex()!=DynamicWorld::C_Item)
269 continue;
270 const int numContacts = contactManifold->getNumContacts();
271 if(numContacts==0)
272 continue;
273
274 btManifoldPoint& pt = contactManifold->getContactPoint(0);
275 auto impulse = pt.getAppliedImpulse();
276 auto mass = reinterpret_cast<const DynamicBody*>(obj)->mass;
277 if(impulse/mass<0.9f)
278 continue;
279
280 auto land = (obj==a ? b : a);
281 if(land->getUserIndex()!=DynamicWorld::C_Landscape)
282 continue;
283
284 auto matId = zenkit::MaterialGroup::STONE;
285 if(auto shape = land->getCollisionShape()) {
286 auto s = reinterpret_cast<const btMultimaterialTriangleMeshShape*>(shape);
287 auto mt = reinterpret_cast<const PhysicVbo*>(s->getMeshInterface());
288
289 int part = (obj==a ? pt.m_partId1 : pt.m_partId0);
290 matId = mt->materialId(size_t(part));
291 }
292
293 if(auto ptr = reinterpret_cast<::Item*>(obj->getUserPointer())) {
294 hitItem(*ptr,matId,impulse,mass);
295 }
296 }
297 }
298 }
299 } else {
300 // fake 'just fall' implementation
301 for(auto& i:rigid) {
302 i->setLinearVelocity(i->getLinearVelocity()+gravity*dtF);
303
304 uint64_t cnt = uint64_t(i->getLinearVelocity().length());
305 bool active = cnt>0;
306 for(uint64_t r=1; active && r<=(cnt*dt) && r<150; ++r)
307 active &= tick(1.f/float(cnt*dt),*i);
308
309 if(active) {
310 i->setDeactivationTime(0);
311 i->setActivationState(ACTIVE_TAG);
312 } else {
313 i->setLinearVelocity(btVector3(0,0,0));
314 i->setActivationState(WANTS_DEACTIVATION);
315 i->setDeactivationTime(i->getDeactivationTime()+float(dt)/1000.f);
316 }
317 }
318 }
319
320 for(auto i:rigid)
321 if(auto ptr = reinterpret_cast<::Item*>(i->getUserPointer())) {
322 auto t = i->getWorldTransform();
323 t.getOrigin()*=100.f;
324 Tempest::Matrix4x4 mt;
325 t.getOpenGLMatrix(reinterpret_cast<btScalar*>(&mt));
326 ptr->setObjMatrix(mt);
327 }
328 for(size_t i=0; i<rigid.size(); ++i) {
329 auto it = rigid[i];
330 if((it->wantsSleeping() && (it->getDeactivationTime()>3.f || !it->isActive())) ||
331 (it->getWorldTransform().getOrigin().y()<bbox[0].y()-100)) {
332 if(auto ptr = reinterpret_cast<::Item*>(it->getUserPointer()))
333 ptr->setPhysicsDisable();
334 }
335 }
336 }
337
338void CollisionWorld::setBBox(const btVector3& min, const btVector3& max) {
339 bbox[0] = min;
340 bbox[1] = max;
341 }
342
343void CollisionWorld::setItemHitCallback(std::function<void (Item&, zenkit::MaterialGroup, float, float)> f) {
344 hitItem = f;
345 }
346
347bool CollisionWorld::tick(float step, btRigidBody& body) {
348 Tempest::Vec3 norm;
349
350 btTransform prev = body.getWorldTransform();
351 auto trans = prev;
352 auto& at = trans.getOrigin();
353
354 at+=body.getLinearVelocity()*step;
355 body.setWorldTransform(trans);
356
357 Interactive* vob = nullptr;
358 if(hasCollision(body,norm,vob)) {
359 body.setWorldTransform(prev);
360 return false;
361 }
362 updateSingleAabb(&body);
363 return true;
364 }
365
366void CollisionWorld::saveKinematicState(btScalar /*timeStep*/) {
367 // assume no CF_KINEMATIC_OBJECT in this game
368 }
369
std::unique_ptr< CollisionBody > addCollisionBody(btCollisionShape &shape, const Tempest::Matrix4x4 &tr, float friction)
void tick(uint64_t dt)
static const Tempest::Vec3 toCentimeters(const btVector3 &v)
void setItemHitCallback(std::function< void(Item &itm, zenkit::MaterialGroup mat, float impulse, float mass)> f)
void rayCast(const Tempest::Vec3 &b, const Tempest::Vec3 &e, RayResultCallback &cb)
bool hasCollision(const btCollisionObject &it, Tempest::Vec3 &normal)
static float toMeters(const float v)
void setBBox(const btVector3 &min, const btVector3 &max)
std::unique_ptr< DynamicBody > addDynamicBody(btCollisionShape &shape, const Tempest::Matrix4x4 &tr, float friction, float mass)
void updateAabbs() override
static constexpr float gravityMS
Definition item.h:14
zenkit::MaterialGroup materialId(size_t segment) const
Definition physicvbo.cpp:74
BroadphaseRayTester(btBroadphaseRayCallback &orgCallback)
btAlignedObjectArray< const btDbvtNode * > rayTestStk
void rayTest(const btVector3 &rayFrom, const btVector3 &rayTo, btBroadphaseRayCallback &rayCallback, const btVector3 &aabbMin, const btVector3 &aabbMax)
std::unique_ptr< btSequentialImpulseConstraintSolver > solver
std::unique_ptr< btCollisionConfiguration > conf
std::unique_ptr< btCollisionDispatcher > disp
std::unique_ptr< btBroadphaseInterface > broad