1 //
2 // Copyright (c) 2008-2017 the Urho3D project.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22 
23 #include "../Precompiled.h"
24 
25 #include "../Core/Context.h"
26 #include "../Core/Profiler.h"
27 #include "../Graphics/CustomGeometry.h"
28 #include "../Graphics/DebugRenderer.h"
29 #include "../Graphics/DrawableEvents.h"
30 #include "../Graphics/Geometry.h"
31 #include "../Graphics/IndexBuffer.h"
32 #include "../Graphics/Model.h"
33 #include "../Graphics/Terrain.h"
34 #include "../Graphics/VertexBuffer.h"
35 #include "../IO/Log.h"
36 #include "../Physics/CollisionShape.h"
37 #include "../Physics/PhysicsUtils.h"
38 #include "../Physics/PhysicsWorld.h"
39 #include "../Physics/RigidBody.h"
40 #include "../Resource/ResourceCache.h"
41 #include "../Resource/ResourceEvents.h"
42 #include "../Scene/Scene.h"
43 
44 #include <Bullet/BulletCollision/CollisionDispatch/btInternalEdgeUtility.h>
45 #include <Bullet/BulletCollision/CollisionShapes/btBoxShape.h>
46 #include <Bullet/BulletCollision/CollisionShapes/btCapsuleShape.h>
47 #include <Bullet/BulletCollision/CollisionShapes/btCompoundShape.h>
48 #include <Bullet/BulletCollision/CollisionShapes/btConeShape.h>
49 #include <Bullet/BulletCollision/CollisionShapes/btConvexHullShape.h>
50 #include <Bullet/BulletCollision/CollisionShapes/btCylinderShape.h>
51 #include <Bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
52 #include <Bullet/BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h>
53 #include <Bullet/BulletCollision/CollisionShapes/btSphereShape.h>
54 #include <Bullet/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h>
55 #include <Bullet/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
56 #include <Bullet/BulletCollision/CollisionShapes/btStaticPlaneShape.h>
57 #include <StanHull/hull.h>
58 
59 namespace Urho3D
60 {
61 
62 static const float DEFAULT_COLLISION_MARGIN = 0.04f;
63 static const unsigned QUANTIZE_MAX_TRIANGLES = 1000000;
64 
65 static const btVector3 WHITE(1.0f, 1.0f, 1.0f);
66 static const btVector3 GREEN(0.0f, 1.0f, 0.0f);
67 
68 static const char* typeNames[] =
69 {
70     "Box",
71     "Sphere",
72     "StaticPlane",
73     "Cylinder",
74     "Capsule",
75     "Cone",
76     "TriangleMesh",
77     "ConvexHull",
78     "Terrain",
79     0
80 };
81 
82 extern const char* PHYSICS_CATEGORY;
83 
84 class TriangleMeshInterface : public btTriangleIndexVertexArray
85 {
86 public:
TriangleMeshInterface(Model * model,unsigned lodLevel)87     TriangleMeshInterface(Model* model, unsigned lodLevel) :
88         btTriangleIndexVertexArray()
89     {
90         unsigned numGeometries = model->GetNumGeometries();
91         unsigned totalTriangles = 0;
92 
93         for (unsigned i = 0; i < numGeometries; ++i)
94         {
95             Geometry* geometry = model->GetGeometry(i, lodLevel);
96             if (!geometry)
97             {
98                 URHO3D_LOGWARNING("Skipping null geometry for triangle mesh collision");
99                 continue;
100             }
101 
102             SharedArrayPtr<unsigned char> vertexData;
103             SharedArrayPtr<unsigned char> indexData;
104             unsigned vertexSize;
105             unsigned indexSize;
106             const PODVector<VertexElement>* elements;
107 
108             geometry->GetRawDataShared(vertexData, vertexSize, indexData, indexSize, elements);
109             if (!vertexData || !indexData || !elements || VertexBuffer::GetElementOffset(*elements, TYPE_VECTOR3, SEM_POSITION) != 0)
110             {
111                 URHO3D_LOGWARNING("Skipping geometry with no or unsuitable CPU-side geometry data for triangle mesh collision");
112                 continue;
113             }
114 
115             // Keep shared pointers to the vertex/index data so that if it's unloaded or changes size, we don't crash
116             dataArrays_.Push(vertexData);
117             dataArrays_.Push(indexData);
118 
119             unsigned indexStart = geometry->GetIndexStart();
120             unsigned indexCount = geometry->GetIndexCount();
121 
122             btIndexedMesh meshIndex;
123             meshIndex.m_numTriangles = indexCount / 3;
124             meshIndex.m_triangleIndexBase = &indexData[indexStart * indexSize];
125             meshIndex.m_triangleIndexStride = 3 * indexSize;
126             meshIndex.m_numVertices = 0;
127             meshIndex.m_vertexBase = vertexData;
128             meshIndex.m_vertexStride = vertexSize;
129             meshIndex.m_indexType = (indexSize == sizeof(unsigned short)) ? PHY_SHORT : PHY_INTEGER;
130             meshIndex.m_vertexType = PHY_FLOAT;
131             m_indexedMeshes.push_back(meshIndex);
132 
133             totalTriangles += meshIndex.m_numTriangles;
134         }
135 
136         // Bullet will not work properly with quantized AABB compression, if the triangle count is too large. Use a conservative
137         // threshold value
138         useQuantize_ = totalTriangles <= QUANTIZE_MAX_TRIANGLES;
139     }
140 
TriangleMeshInterface(CustomGeometry * custom)141     TriangleMeshInterface(CustomGeometry* custom) :
142         btTriangleIndexVertexArray()
143     {
144         const Vector<PODVector<CustomGeometryVertex> >& srcVertices = custom->GetVertices();
145         unsigned totalVertexCount = 0;
146         unsigned totalTriangles = 0;
147 
148         for (unsigned i = 0; i < srcVertices.Size(); ++i)
149             totalVertexCount += srcVertices[i].Size();
150 
151         if (totalVertexCount)
152         {
153             // CustomGeometry vertex data is unindexed, so build index data here
154             SharedArrayPtr<unsigned char> vertexData(new unsigned char[totalVertexCount * sizeof(Vector3)]);
155             SharedArrayPtr<unsigned char> indexData(new unsigned char[totalVertexCount * sizeof(unsigned)]);
156             dataArrays_.Push(vertexData);
157             dataArrays_.Push(indexData);
158 
159             Vector3* destVertex = reinterpret_cast<Vector3*>(&vertexData[0]);
160             unsigned* destIndex = reinterpret_cast<unsigned*>(&indexData[0]);
161             unsigned k = 0;
162 
163             for (unsigned i = 0; i < srcVertices.Size(); ++i)
164             {
165                 for (unsigned j = 0; j < srcVertices[i].Size(); ++j)
166                 {
167                     *destVertex++ = srcVertices[i][j].position_;
168                     *destIndex++ = k++;
169                 }
170             }
171 
172             btIndexedMesh meshIndex;
173             meshIndex.m_numTriangles = totalVertexCount / 3;
174             meshIndex.m_triangleIndexBase = indexData;
175             meshIndex.m_triangleIndexStride = 3 * sizeof(unsigned);
176             meshIndex.m_numVertices = totalVertexCount;
177             meshIndex.m_vertexBase = vertexData;
178             meshIndex.m_vertexStride = sizeof(Vector3);
179             meshIndex.m_indexType = PHY_INTEGER;
180             meshIndex.m_vertexType = PHY_FLOAT;
181             m_indexedMeshes.push_back(meshIndex);
182 
183             totalTriangles += meshIndex.m_numTriangles;
184         }
185 
186         useQuantize_ = totalTriangles <= QUANTIZE_MAX_TRIANGLES;
187     }
188 
189     /// OK to use quantization flag.
190     bool useQuantize_;
191 
192 private:
193     /// Shared vertex/index data used in the collision
194     Vector<SharedArrayPtr<unsigned char> > dataArrays_;
195 };
196 
TriangleMeshData(Model * model,unsigned lodLevel)197 TriangleMeshData::TriangleMeshData(Model* model, unsigned lodLevel)
198 {
199     meshInterface_ = new TriangleMeshInterface(model, lodLevel);
200     shape_ = new btBvhTriangleMeshShape(meshInterface_.Get(), meshInterface_->useQuantize_, true);
201 
202     infoMap_ = new btTriangleInfoMap();
203     btGenerateInternalEdgeInfo(shape_.Get(), infoMap_.Get());
204 }
205 
TriangleMeshData(CustomGeometry * custom)206 TriangleMeshData::TriangleMeshData(CustomGeometry* custom)
207 {
208     meshInterface_ = new TriangleMeshInterface(custom);
209     shape_ = new btBvhTriangleMeshShape(meshInterface_.Get(), meshInterface_->useQuantize_, true);
210 
211     infoMap_ = new btTriangleInfoMap();
212     btGenerateInternalEdgeInfo(shape_.Get(), infoMap_.Get());
213 }
214 
~TriangleMeshData()215 TriangleMeshData::~TriangleMeshData()
216 {
217 }
218 
ConvexData(Model * model,unsigned lodLevel)219 ConvexData::ConvexData(Model* model, unsigned lodLevel)
220 {
221     PODVector<Vector3> vertices;
222     unsigned numGeometries = model->GetNumGeometries();
223 
224     for (unsigned i = 0; i < numGeometries; ++i)
225     {
226         Geometry* geometry = model->GetGeometry(i, lodLevel);
227         if (!geometry)
228         {
229             URHO3D_LOGWARNING("Skipping null geometry for convex hull collision");
230             continue;
231         };
232 
233         const unsigned char* vertexData;
234         const unsigned char* indexData;
235         unsigned vertexSize;
236         unsigned indexSize;
237         const PODVector<VertexElement>* elements;
238 
239         geometry->GetRawData(vertexData, vertexSize, indexData, indexSize, elements);
240         if (!vertexData || VertexBuffer::GetElementOffset(*elements, TYPE_VECTOR3, SEM_POSITION) != 0)
241         {
242             URHO3D_LOGWARNING("Skipping geometry with no or unsuitable CPU-side geometry data for convex hull collision");
243             continue;
244         }
245 
246         unsigned vertexStart = geometry->GetVertexStart();
247         unsigned vertexCount = geometry->GetVertexCount();
248 
249         // Copy vertex data
250         for (unsigned j = 0; j < vertexCount; ++j)
251         {
252             const Vector3& v = *((const Vector3*)(&vertexData[(vertexStart + j) * vertexSize]));
253             vertices.Push(v);
254         }
255     }
256 
257     BuildHull(vertices);
258 }
259 
ConvexData(CustomGeometry * custom)260 ConvexData::ConvexData(CustomGeometry* custom)
261 {
262     const Vector<PODVector<CustomGeometryVertex> >& srcVertices = custom->GetVertices();
263     PODVector<Vector3> vertices;
264 
265     for (unsigned i = 0; i < srcVertices.Size(); ++i)
266     {
267         for (unsigned j = 0; j < srcVertices[i].Size(); ++j)
268             vertices.Push(srcVertices[i][j].position_);
269     }
270 
271     BuildHull(vertices);
272 }
273 
BuildHull(const PODVector<Vector3> & vertices)274 void ConvexData::BuildHull(const PODVector<Vector3>& vertices)
275 {
276     if (vertices.Size())
277     {
278         // Build the convex hull from the raw geometry
279         StanHull::HullDesc desc;
280         desc.SetHullFlag(StanHull::QF_TRIANGLES);
281         desc.mVcount = vertices.Size();
282         desc.mVertices = vertices[0].Data();
283         desc.mVertexStride = 3 * sizeof(float);
284         desc.mSkinWidth = 0.0f;
285 
286         StanHull::HullLibrary lib;
287         StanHull::HullResult result;
288         lib.CreateConvexHull(desc, result);
289 
290         vertexCount_ = result.mNumOutputVertices;
291         vertexData_ = new Vector3[vertexCount_];
292 
293         indexCount_ = result.mNumIndices;
294         indexData_ = new unsigned[indexCount_];
295 
296         // Copy vertex data & index data
297         memcpy(vertexData_.Get(), result.mOutputVertices, vertexCount_ * sizeof(Vector3));
298         memcpy(indexData_.Get(), result.mIndices, indexCount_ * sizeof(unsigned));
299 
300         lib.ReleaseResult(result);
301     }
302     else
303     {
304         vertexCount_ = 0;
305         indexCount_ = 0;
306     }
307 }
308 
~ConvexData()309 ConvexData::~ConvexData()
310 {
311 }
312 
HeightfieldData(Terrain * terrain,unsigned lodLevel)313 HeightfieldData::HeightfieldData(Terrain* terrain, unsigned lodLevel) :
314     heightData_(terrain->GetHeightData()),
315     spacing_(terrain->GetSpacing()),
316     size_(terrain->GetNumVertices()),
317     minHeight_(0.0f),
318     maxHeight_(0.0f)
319 {
320     if (heightData_)
321     {
322         if (lodLevel > 0)
323         {
324             IntVector2 lodSize = size_;
325             Vector3 lodSpacing = spacing_;
326             unsigned skip = 1;
327 
328             for (unsigned i = 0; i < lodLevel; ++i)
329             {
330                 skip *= 2;
331                 lodSpacing.x_ *= 2.0f;
332                 lodSpacing.z_ *= 2.0f;
333                 int rX = lodSize.x_ & 1;
334                 int rY = lodSize.y_ & 1;
335                 lodSize.x_ >>= 1;
336                 lodSize.y_ >>= 1;
337                 lodSize.x_ += rX;
338                 lodSize.y_ += rY;
339                 if (lodSize.x_ <= 2 || lodSize.y_ <= 2)
340                     break;
341             }
342 
343             SharedArrayPtr<float> lodHeightData(new float[lodSize.x_ * lodSize.y_]);
344             for (int y = 0, dY = 0; y < size_.y_ && dY < lodSize.y_; y += skip, ++dY)
345             {
346                 for (int x = 0, dX = 0; x < size_.x_ && dX < lodSize.x_; x += skip, ++dX)
347                     lodHeightData[dY * lodSize.x_ + dX] = heightData_[y * size_.x_ + x];
348             }
349 
350             size_ = lodSize;
351             spacing_ = lodSpacing;
352             heightData_ = lodHeightData;
353         }
354 
355         unsigned points = (unsigned)(size_.x_ * size_.y_);
356         float* data = heightData_.Get();
357 
358         minHeight_ = maxHeight_ = data[0];
359         for (unsigned i = 1; i < points; ++i)
360         {
361             minHeight_ = Min(minHeight_, data[i]);
362             maxHeight_ = Max(maxHeight_, data[i]);
363         }
364     }
365 }
366 
~HeightfieldData()367 HeightfieldData::~HeightfieldData()
368 {
369 }
370 
HasDynamicBuffers(Model * model,unsigned lodLevel)371 bool HasDynamicBuffers(Model* model, unsigned lodLevel)
372 {
373     unsigned numGeometries = model->GetNumGeometries();
374 
375     for (unsigned i = 0; i < numGeometries; ++i)
376     {
377         Geometry* geometry = model->GetGeometry(i, lodLevel);
378         if (!geometry)
379             continue;
380         unsigned numVertexBuffers = geometry->GetNumVertexBuffers();
381         for (unsigned j = 0; j < numVertexBuffers; ++j)
382         {
383             VertexBuffer* buffer = geometry->GetVertexBuffer(j);
384             if (!buffer)
385                 continue;
386             if (buffer->IsDynamic())
387                 return true;
388         }
389         IndexBuffer* buffer = geometry->GetIndexBuffer();
390         if (buffer && buffer->IsDynamic())
391             return true;
392     }
393 
394     return false;
395 }
396 
CollisionShape(Context * context)397 CollisionShape::CollisionShape(Context* context) :
398     Component(context),
399     shapeType_(SHAPE_BOX),
400     position_(Vector3::ZERO),
401     rotation_(Quaternion::IDENTITY),
402     size_(Vector3::ONE),
403     cachedWorldScale_(Vector3::ONE),
404     lodLevel_(0),
405     customGeometryID_(0),
406     margin_(DEFAULT_COLLISION_MARGIN),
407     recreateShape_(true),
408     retryCreation_(false)
409 {
410 }
411 
~CollisionShape()412 CollisionShape::~CollisionShape()
413 {
414     ReleaseShape();
415 
416     if (physicsWorld_)
417         physicsWorld_->RemoveCollisionShape(this);
418 }
419 
RegisterObject(Context * context)420 void CollisionShape::RegisterObject(Context* context)
421 {
422     context->RegisterFactory<CollisionShape>(PHYSICS_CATEGORY);
423 
424     URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
425     URHO3D_ENUM_ATTRIBUTE("Shape Type", shapeType_, typeNames, SHAPE_BOX, AM_DEFAULT);
426     URHO3D_ATTRIBUTE("Size", Vector3, size_, Vector3::ONE, AM_DEFAULT);
427     URHO3D_ACCESSOR_ATTRIBUTE("Offset Position", GetPosition, SetPosition, Vector3, Vector3::ZERO, AM_DEFAULT);
428     URHO3D_ACCESSOR_ATTRIBUTE("Offset Rotation", GetRotation, SetRotation, Quaternion, Quaternion::IDENTITY, AM_DEFAULT);
429     URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Model", GetModelAttr, SetModelAttr, ResourceRef, ResourceRef(Model::GetTypeStatic()), AM_DEFAULT);
430     URHO3D_ATTRIBUTE("LOD Level", int, lodLevel_, 0, AM_DEFAULT);
431     URHO3D_ATTRIBUTE("Collision Margin", float, margin_, DEFAULT_COLLISION_MARGIN, AM_DEFAULT);
432     URHO3D_ATTRIBUTE("CustomGeometry ComponentID", unsigned, customGeometryID_, 0, AM_DEFAULT | AM_COMPONENTID);
433 }
434 
OnSetAttribute(const AttributeInfo & attr,const Variant & src)435 void CollisionShape::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
436 {
437     Serializable::OnSetAttribute(attr, src);
438 
439     // Change of any non-accessor attribute requires recreation of the collision shape
440     if (!attr.accessor_)
441         recreateShape_ = true;
442 }
443 
ApplyAttributes()444 void CollisionShape::ApplyAttributes()
445 {
446     if (recreateShape_)
447     {
448         UpdateShape();
449         NotifyRigidBody();
450     }
451 }
452 
OnSetEnabled()453 void CollisionShape::OnSetEnabled()
454 {
455     NotifyRigidBody();
456 }
457 
DrawDebugGeometry(DebugRenderer * debug,bool depthTest)458 void CollisionShape::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
459 {
460     if (debug && physicsWorld_ && shape_ && node_ && IsEnabledEffective())
461     {
462         // Use the rigid body's world transform if possible, as it may be different from the rendering transform
463         Matrix3x4 worldTransform;
464         RigidBody* body = GetComponent<RigidBody>();
465         bool bodyActive = false;
466         if (body)
467         {
468             worldTransform = Matrix3x4(body->GetPosition(), body->GetRotation(), node_->GetWorldScale());
469             bodyActive = body->IsActive();
470         }
471         else
472             worldTransform = node_->GetWorldTransform();
473 
474         // Special case code for convex hull: bypass Bullet's own rendering to draw triangles correctly, not just edges
475         if (shapeType_ == SHAPE_CONVEXHULL)
476         {
477             ConvexData *convexData = static_cast<ConvexData*>(GetGeometryData());
478             RigidBody* body = GetComponent<RigidBody>();
479             Color color = bodyActive ? Color::WHITE : Color::GREEN;
480             Matrix3x4 shapeTransform(worldTransform * position_, worldTransform.Rotation() * rotation_, worldTransform.Scale());
481 
482             if (convexData)
483             {
484                 for (unsigned i = 0; i < convexData->indexCount_; i += 3)
485                 {
486                     Vector3 a = shapeTransform * convexData->vertexData_[convexData->indexData_[i + 0]];
487                     Vector3 b = shapeTransform * convexData->vertexData_[convexData->indexData_[i + 1]];
488                     Vector3 c = shapeTransform * convexData->vertexData_[convexData->indexData_[i + 2]];
489                     debug->AddLine(a, b, color, depthTest);
490                     debug->AddLine(b, c, color, depthTest);
491                     debug->AddLine(a, c, color, depthTest);
492                 }
493              }
494         }
495         else
496         {
497             physicsWorld_->SetDebugRenderer(debug);
498             physicsWorld_->SetDebugDepthTest(depthTest);
499 
500             Vector3 position = position_;
501             // For terrains, undo the height centering performed automatically by Bullet
502             if (shapeType_ == SHAPE_TERRAIN && geometry_)
503             {
504                 HeightfieldData* heightfield = static_cast<HeightfieldData*>(geometry_.Get());
505                 position.y_ += (heightfield->minHeight_ + heightfield->maxHeight_) * 0.5f;
506             }
507 
508             Vector3 worldPosition(worldTransform * position);
509             Quaternion worldRotation(worldTransform.Rotation() * rotation_);
510 
511             btDiscreteDynamicsWorld* world = physicsWorld_->GetWorld();
512             world->debugDrawObject(btTransform(ToBtQuaternion(worldRotation), ToBtVector3(worldPosition)), shape_.Get(), bodyActive ?
513                 WHITE : GREEN);
514 
515             physicsWorld_->SetDebugRenderer(0);
516         }
517     }
518 }
519 
SetBox(const Vector3 & size,const Vector3 & position,const Quaternion & rotation)520 void CollisionShape::SetBox(const Vector3& size, const Vector3& position, const Quaternion& rotation)
521 {
522     if (model_)
523         UnsubscribeFromEvent(model_, E_RELOADFINISHED);
524 
525     shapeType_ = SHAPE_BOX;
526     size_ = size;
527     position_ = position;
528     rotation_ = rotation;
529     model_.Reset();
530     customGeometryID_ = 0;
531 
532     UpdateShape();
533     NotifyRigidBody();
534     MarkNetworkUpdate();
535 }
536 
SetSphere(float diameter,const Vector3 & position,const Quaternion & rotation)537 void CollisionShape::SetSphere(float diameter, const Vector3& position, const Quaternion& rotation)
538 {
539     if (model_)
540         UnsubscribeFromEvent(model_, E_RELOADFINISHED);
541 
542     shapeType_ = SHAPE_SPHERE;
543     size_ = Vector3(diameter, diameter, diameter);
544     position_ = position;
545     rotation_ = rotation;
546     model_.Reset();
547     customGeometryID_ = 0;
548 
549     UpdateShape();
550     NotifyRigidBody();
551     MarkNetworkUpdate();
552 }
553 
SetStaticPlane(const Vector3 & position,const Quaternion & rotation)554 void CollisionShape::SetStaticPlane(const Vector3& position, const Quaternion& rotation)
555 {
556     if (model_)
557         UnsubscribeFromEvent(model_, E_RELOADFINISHED);
558 
559     shapeType_ = SHAPE_STATICPLANE;
560     position_ = position;
561     rotation_ = rotation;
562     model_.Reset();
563     customGeometryID_ = 0;
564 
565     UpdateShape();
566     NotifyRigidBody();
567     MarkNetworkUpdate();
568 }
569 
SetCylinder(float diameter,float height,const Vector3 & position,const Quaternion & rotation)570 void CollisionShape::SetCylinder(float diameter, float height, const Vector3& position, const Quaternion& rotation)
571 {
572     if (model_)
573         UnsubscribeFromEvent(model_, E_RELOADFINISHED);
574 
575     shapeType_ = SHAPE_CYLINDER;
576     size_ = Vector3(diameter, height, diameter);
577     position_ = position;
578     rotation_ = rotation;
579     model_.Reset();
580     customGeometryID_ = 0;
581 
582     UpdateShape();
583     NotifyRigidBody();
584     MarkNetworkUpdate();
585 }
586 
SetCapsule(float diameter,float height,const Vector3 & position,const Quaternion & rotation)587 void CollisionShape::SetCapsule(float diameter, float height, const Vector3& position, const Quaternion& rotation)
588 {
589     if (model_)
590         UnsubscribeFromEvent(model_, E_RELOADFINISHED);
591 
592     shapeType_ = SHAPE_CAPSULE;
593     size_ = Vector3(diameter, height, diameter);
594     position_ = position;
595     rotation_ = rotation;
596     model_.Reset();
597     customGeometryID_ = 0;
598 
599     UpdateShape();
600     NotifyRigidBody();
601     MarkNetworkUpdate();
602 }
603 
SetCone(float diameter,float height,const Vector3 & position,const Quaternion & rotation)604 void CollisionShape::SetCone(float diameter, float height, const Vector3& position, const Quaternion& rotation)
605 {
606     if (model_)
607         UnsubscribeFromEvent(model_, E_RELOADFINISHED);
608 
609     shapeType_ = SHAPE_CONE;
610     size_ = Vector3(diameter, height, diameter);
611     position_ = position;
612     rotation_ = rotation;
613     model_.Reset();
614     customGeometryID_ = 0;
615 
616     UpdateShape();
617     NotifyRigidBody();
618     MarkNetworkUpdate();
619 }
620 
SetTriangleMesh(Model * model,unsigned lodLevel,const Vector3 & scale,const Vector3 & position,const Quaternion & rotation)621 void CollisionShape::SetTriangleMesh(Model* model, unsigned lodLevel, const Vector3& scale, const Vector3& position,
622     const Quaternion& rotation)
623 {
624     if (!model)
625     {
626         URHO3D_LOGERROR("Null model, can not set triangle mesh");
627         return;
628     }
629 
630     if (model_)
631         UnsubscribeFromEvent(model_, E_RELOADFINISHED);
632 
633     shapeType_ = SHAPE_TRIANGLEMESH;
634     model_ = model;
635     lodLevel_ = lodLevel;
636     size_ = scale;
637     position_ = position;
638     rotation_ = rotation;
639     customGeometryID_ = 0;
640 
641     UpdateShape();
642     NotifyRigidBody();
643     MarkNetworkUpdate();
644 }
645 
SetCustomTriangleMesh(CustomGeometry * custom,const Vector3 & scale,const Vector3 & position,const Quaternion & rotation)646 void CollisionShape::SetCustomTriangleMesh(CustomGeometry* custom, const Vector3& scale, const Vector3& position,
647     const Quaternion& rotation)
648 {
649     if (!custom)
650     {
651         URHO3D_LOGERROR("Null custom geometry, can not set triangle mesh");
652         return;
653     }
654     if (custom->GetScene() != GetScene())
655     {
656         URHO3D_LOGERROR("Custom geometry is not in the same scene as the collision shape, can not set triangle mesh");
657         return;
658     }
659 
660     if (model_)
661         UnsubscribeFromEvent(model_, E_RELOADFINISHED);
662 
663     shapeType_ = SHAPE_TRIANGLEMESH;
664     model_.Reset();
665     lodLevel_ = 0;
666     size_ = scale;
667     position_ = position;
668     rotation_ = rotation;
669     customGeometryID_ = custom->GetID();
670 
671     UpdateShape();
672     NotifyRigidBody();
673     MarkNetworkUpdate();
674 }
675 
SetConvexHull(Model * model,unsigned lodLevel,const Vector3 & scale,const Vector3 & position,const Quaternion & rotation)676 void CollisionShape::SetConvexHull(Model* model, unsigned lodLevel, const Vector3& scale, const Vector3& position,
677     const Quaternion& rotation)
678 {
679     if (!model)
680     {
681         URHO3D_LOGERROR("Null model, can not set convex hull");
682         return;
683     }
684 
685     if (model_)
686         UnsubscribeFromEvent(model_, E_RELOADFINISHED);
687 
688     shapeType_ = SHAPE_CONVEXHULL;
689     model_ = model;
690     lodLevel_ = lodLevel;
691     size_ = scale;
692     position_ = position;
693     rotation_ = rotation;
694     customGeometryID_ = 0;
695 
696     UpdateShape();
697     NotifyRigidBody();
698     MarkNetworkUpdate();
699 }
700 
SetCustomConvexHull(CustomGeometry * custom,const Vector3 & scale,const Vector3 & position,const Quaternion & rotation)701 void CollisionShape::SetCustomConvexHull(CustomGeometry* custom, const Vector3& scale, const Vector3& position,
702     const Quaternion& rotation)
703 {
704     if (!custom)
705     {
706         URHO3D_LOGERROR("Null custom geometry, can not set convex hull");
707         return;
708     }
709     if (custom->GetScene() != GetScene())
710     {
711         URHO3D_LOGERROR("Custom geometry is not in the same scene as the collision shape, can not set convex hull");
712         return;
713     }
714 
715     if (model_)
716         UnsubscribeFromEvent(model_, E_RELOADFINISHED);
717 
718     shapeType_ = SHAPE_CONVEXHULL;
719     model_.Reset();
720     lodLevel_ = 0;
721     size_ = scale;
722     position_ = position;
723     rotation_ = rotation;
724     customGeometryID_ = custom->GetID();
725 
726     UpdateShape();
727     NotifyRigidBody();
728     MarkNetworkUpdate();
729 }
730 
SetTerrain(unsigned lodLevel)731 void CollisionShape::SetTerrain(unsigned lodLevel)
732 {
733     Terrain* terrain = GetComponent<Terrain>();
734     if (!terrain)
735     {
736         URHO3D_LOGERROR("No terrain component, can not set terrain shape");
737         return;
738     }
739 
740     if (model_)
741         UnsubscribeFromEvent(model_, E_RELOADFINISHED);
742 
743     shapeType_ = SHAPE_TERRAIN;
744     lodLevel_ = lodLevel;
745 
746     UpdateShape();
747     NotifyRigidBody();
748     MarkNetworkUpdate();
749 }
750 
SetShapeType(ShapeType type)751 void CollisionShape::SetShapeType(ShapeType type)
752 {
753     if (type != shapeType_)
754     {
755         shapeType_ = type;
756         UpdateShape();
757         NotifyRigidBody();
758         MarkNetworkUpdate();
759     }
760 }
761 
SetSize(const Vector3 & size)762 void CollisionShape::SetSize(const Vector3& size)
763 {
764     if (size != size_)
765     {
766         size_ = size;
767         UpdateShape();
768         NotifyRigidBody();
769         MarkNetworkUpdate();
770     }
771 }
772 
SetPosition(const Vector3 & position)773 void CollisionShape::SetPosition(const Vector3& position)
774 {
775     if (position != position_)
776     {
777         position_ = position;
778         NotifyRigidBody();
779         MarkNetworkUpdate();
780     }
781 }
782 
SetRotation(const Quaternion & rotation)783 void CollisionShape::SetRotation(const Quaternion& rotation)
784 {
785     if (rotation != rotation_)
786     {
787         rotation_ = rotation;
788         NotifyRigidBody();
789         MarkNetworkUpdate();
790     }
791 }
792 
SetTransform(const Vector3 & position,const Quaternion & rotation)793 void CollisionShape::SetTransform(const Vector3& position, const Quaternion& rotation)
794 {
795     if (position != position_ || rotation != rotation_)
796     {
797         position_ = position;
798         rotation_ = rotation;
799         NotifyRigidBody();
800         MarkNetworkUpdate();
801     }
802 }
803 
SetMargin(float margin)804 void CollisionShape::SetMargin(float margin)
805 {
806     margin = Max(margin, 0.0f);
807 
808     if (margin != margin_)
809     {
810         if (shape_)
811             shape_->setMargin(margin);
812         margin_ = margin;
813         MarkNetworkUpdate();
814     }
815 }
816 
SetModel(Model * model)817 void CollisionShape::SetModel(Model* model)
818 {
819     if (model != model_)
820     {
821         if (model_)
822             UnsubscribeFromEvent(model_, E_RELOADFINISHED);
823 
824         model_ = model;
825         if (shapeType_ >= SHAPE_TRIANGLEMESH)
826         {
827             UpdateShape();
828             NotifyRigidBody();
829         }
830         MarkNetworkUpdate();
831     }
832 }
833 
SetLodLevel(unsigned lodLevel)834 void CollisionShape::SetLodLevel(unsigned lodLevel)
835 {
836     if (lodLevel != lodLevel_)
837     {
838         lodLevel_ = lodLevel;
839         if (shapeType_ >= SHAPE_TRIANGLEMESH)
840         {
841             UpdateShape();
842             NotifyRigidBody();
843         }
844         MarkNetworkUpdate();
845     }
846 }
847 
GetWorldBoundingBox() const848 BoundingBox CollisionShape::GetWorldBoundingBox() const
849 {
850     if (shape_ && node_)
851     {
852         // Use the rigid body's world transform if possible, as it may be different from the rendering transform
853         RigidBody* body = GetComponent<RigidBody>();
854         Matrix3x4 worldTransform = body ? Matrix3x4(body->GetPosition(), body->GetRotation(), node_->GetWorldScale()) :
855             node_->GetWorldTransform();
856 
857         Vector3 worldPosition(worldTransform * position_);
858         Quaternion worldRotation(worldTransform.Rotation() * rotation_);
859         btTransform shapeWorldTransform(ToBtQuaternion(worldRotation), ToBtVector3(worldPosition));
860         btVector3 aabbMin, aabbMax;
861         shape_->getAabb(shapeWorldTransform, aabbMin, aabbMax);
862 
863         return BoundingBox(ToVector3(aabbMin), ToVector3(aabbMax));
864     }
865     else
866         return BoundingBox();
867 }
868 
NotifyRigidBody(bool updateMass)869 void CollisionShape::NotifyRigidBody(bool updateMass)
870 {
871     btCompoundShape* compound = GetParentCompoundShape();
872     if (node_ && shape_ && compound)
873     {
874         // Remove the shape first to ensure it is not added twice
875         compound->removeChildShape(shape_.Get());
876 
877         if (IsEnabledEffective())
878         {
879             // Then add with updated offset
880             Vector3 position = position_;
881             // For terrains, undo the height centering performed automatically by Bullet
882             if (shapeType_ == SHAPE_TERRAIN && geometry_)
883             {
884                 HeightfieldData* heightfield = static_cast<HeightfieldData*>(geometry_.Get());
885                 position.y_ += (heightfield->minHeight_ + heightfield->maxHeight_) * 0.5f;
886             }
887 
888             btTransform offset;
889             offset.setOrigin(ToBtVector3(node_->GetWorldScale() * position));
890             offset.setRotation(ToBtQuaternion(rotation_));
891             compound->addChildShape(offset, shape_.Get());
892         }
893 
894         // Finally tell the rigid body to update its mass
895         if (updateMass)
896             rigidBody_->UpdateMass();
897     }
898 }
899 
SetModelAttr(const ResourceRef & value)900 void CollisionShape::SetModelAttr(const ResourceRef& value)
901 {
902     ResourceCache* cache = GetSubsystem<ResourceCache>();
903     model_ = cache->GetResource<Model>(value.name_);
904     recreateShape_ = true;
905     MarkNetworkUpdate();
906 }
907 
GetModelAttr() const908 ResourceRef CollisionShape::GetModelAttr() const
909 {
910     return GetResourceRef(model_, Model::GetTypeStatic());
911 }
912 
ReleaseShape()913 void CollisionShape::ReleaseShape()
914 {
915     btCompoundShape* compound = GetParentCompoundShape();
916     if (shape_ && compound)
917     {
918         compound->removeChildShape(shape_.Get());
919         rigidBody_->UpdateMass();
920     }
921 
922     shape_.Reset();
923 
924     geometry_.Reset();
925 
926     if (physicsWorld_)
927         physicsWorld_->CleanupGeometryCache();
928 }
929 
OnNodeSet(Node * node)930 void CollisionShape::OnNodeSet(Node* node)
931 {
932     if (node)
933     {
934         node->AddListener(this);
935         cachedWorldScale_ = node->GetWorldScale();
936 
937         // Terrain collision shape depends on the terrain component's geometry updates. Subscribe to them
938         SubscribeToEvent(node, E_TERRAINCREATED, URHO3D_HANDLER(CollisionShape, HandleTerrainCreated));
939     }
940 }
941 
OnSceneSet(Scene * scene)942 void CollisionShape::OnSceneSet(Scene* scene)
943 {
944     if (scene)
945     {
946         if (scene == node_)
947             URHO3D_LOGWARNING(GetTypeName() + " should not be created to the root scene node");
948 
949         physicsWorld_ = scene->GetOrCreateComponent<PhysicsWorld>();
950         physicsWorld_->AddCollisionShape(this);
951 
952         // Create shape now if necessary (attributes modified before adding to scene)
953         if (retryCreation_)
954         {
955             UpdateShape();
956             NotifyRigidBody();
957         }
958     }
959     else
960     {
961         ReleaseShape();
962 
963         if (physicsWorld_)
964             physicsWorld_->RemoveCollisionShape(this);
965 
966         // Recreate when moved to a scene again
967         retryCreation_ = true;
968     }
969 }
970 
OnMarkedDirty(Node * node)971 void CollisionShape::OnMarkedDirty(Node* node)
972 {
973     Vector3 newWorldScale = node_->GetWorldScale();
974     if (HasWorldScaleChanged(cachedWorldScale_, newWorldScale) && shape_)
975     {
976         // Physics operations are not safe from worker threads
977         Scene* scene = GetScene();
978         if (scene && scene->IsThreadedUpdate())
979         {
980             scene->DelayedMarkedDirty(this);
981             return;
982         }
983 
984         switch (shapeType_)
985         {
986         case SHAPE_BOX:
987         case SHAPE_SPHERE:
988         case SHAPE_CYLINDER:
989         case SHAPE_CAPSULE:
990         case SHAPE_CONE:
991             shape_->setLocalScaling(ToBtVector3(newWorldScale));
992             break;
993 
994         case SHAPE_TRIANGLEMESH:
995         case SHAPE_CONVEXHULL:
996             shape_->setLocalScaling(ToBtVector3(newWorldScale * size_));
997             break;
998 
999         case SHAPE_TERRAIN:
1000             {
1001                 HeightfieldData* heightfield = static_cast<HeightfieldData*>(geometry_.Get());
1002                 shape_->setLocalScaling(ToBtVector3(Vector3(heightfield->spacing_.x_, 1.0f, heightfield->spacing_.z_) *
1003                                                     newWorldScale * size_));
1004             }
1005             break;
1006 
1007         default:
1008             break;
1009         }
1010 
1011         NotifyRigidBody();
1012 
1013         cachedWorldScale_ = newWorldScale;
1014     }
1015 }
1016 
GetParentCompoundShape()1017 btCompoundShape* CollisionShape::GetParentCompoundShape()
1018 {
1019     if (!rigidBody_)
1020         rigidBody_ = GetComponent<RigidBody>();
1021 
1022     return rigidBody_ ? rigidBody_->GetCompoundShape() : 0;
1023 }
1024 
UpdateShape()1025 void CollisionShape::UpdateShape()
1026 {
1027     URHO3D_PROFILE(UpdateCollisionShape);
1028 
1029     ReleaseShape();
1030 
1031     // If no physics world available now mark for retry later
1032     if (!physicsWorld_)
1033     {
1034         retryCreation_ = true;
1035         return;
1036     }
1037 
1038     if (node_)
1039     {
1040         Scene* scene = GetScene();
1041         Vector3 newWorldScale = node_->GetWorldScale();
1042 
1043         switch (shapeType_)
1044         {
1045         case SHAPE_BOX:
1046             shape_ = new btBoxShape(ToBtVector3(size_ * 0.5f));
1047             shape_->setLocalScaling(ToBtVector3(newWorldScale));
1048             break;
1049 
1050         case SHAPE_SPHERE:
1051             shape_ = new btSphereShape(size_.x_ * 0.5f);
1052             shape_->setLocalScaling(ToBtVector3(newWorldScale));
1053             break;
1054 
1055         case SHAPE_STATICPLANE:
1056             shape_ = new btStaticPlaneShape(btVector3(0.0f, 1.0f, 0.0f), 0.0f);
1057             break;
1058 
1059         case SHAPE_CYLINDER:
1060             shape_ = new btCylinderShape(btVector3(size_.x_ * 0.5f, size_.y_ * 0.5f, size_.x_ * 0.5f));
1061             shape_->setLocalScaling(ToBtVector3(newWorldScale));
1062             break;
1063 
1064         case SHAPE_CAPSULE:
1065             shape_ = new btCapsuleShape(size_.x_ * 0.5f, Max(size_.y_ - size_.x_, 0.0f));
1066             shape_->setLocalScaling(ToBtVector3(newWorldScale));
1067             break;
1068 
1069         case SHAPE_CONE:
1070             shape_ = new btConeShape(size_.x_ * 0.5f, size_.y_);
1071             shape_->setLocalScaling(ToBtVector3(newWorldScale));
1072             break;
1073 
1074         case SHAPE_TRIANGLEMESH:
1075             size_ = size_.Abs();
1076             if (customGeometryID_ && scene)
1077             {
1078                 CustomGeometry* custom = dynamic_cast<CustomGeometry*>(scene->GetComponent(customGeometryID_));
1079                 if (custom)
1080                 {
1081                     geometry_ = new TriangleMeshData(custom);
1082                     TriangleMeshData* triMesh = static_cast<TriangleMeshData*>(geometry_.Get());
1083                     shape_ = new btScaledBvhTriangleMeshShape(triMesh->shape_.Get(), ToBtVector3(newWorldScale * size_));
1084                 }
1085                 else
1086                     URHO3D_LOGWARNING("Could not find custom geometry component ID " + String(customGeometryID_) +
1087                                " for triangle mesh shape creation");
1088             }
1089             else if (model_ && model_->GetNumGeometries())
1090             {
1091                 // Check the geometry cache
1092                 Pair<Model*, unsigned> id = MakePair(model_.Get(), lodLevel_);
1093                 HashMap<Pair<Model*, unsigned>, SharedPtr<CollisionGeometryData> >& cache = physicsWorld_->GetTriMeshCache();
1094                 HashMap<Pair<Model*, unsigned>, SharedPtr<CollisionGeometryData> >::Iterator j = cache.Find(id);
1095                 if (j != cache.End())
1096                     geometry_ = j->second_;
1097                 else
1098                 {
1099                     geometry_ = new TriangleMeshData(model_, lodLevel_);
1100                     // Check if model has dynamic buffers, do not cache in that case
1101                     if (!HasDynamicBuffers(model_, lodLevel_))
1102                         cache[id] = geometry_;
1103                 }
1104 
1105                 TriangleMeshData* triMesh = static_cast<TriangleMeshData*>(geometry_.Get());
1106                 shape_ = new btScaledBvhTriangleMeshShape(triMesh->shape_.Get(), ToBtVector3(newWorldScale * size_));
1107                 // Watch for live reloads of the collision model to reload the geometry if necessary
1108                 SubscribeToEvent(model_, E_RELOADFINISHED, URHO3D_HANDLER(CollisionShape, HandleModelReloadFinished));
1109             }
1110             break;
1111 
1112         case SHAPE_CONVEXHULL:
1113             size_ = size_.Abs();
1114             if (customGeometryID_ && scene)
1115             {
1116                 CustomGeometry* custom = dynamic_cast<CustomGeometry*>(scene->GetComponent(customGeometryID_));
1117                 if (custom)
1118                 {
1119                     geometry_ = new ConvexData(custom);
1120                     ConvexData* convex = static_cast<ConvexData*>(geometry_.Get());
1121                     shape_ = new btConvexHullShape((btScalar*)convex->vertexData_.Get(), convex->vertexCount_, sizeof(Vector3));
1122                     shape_->setLocalScaling(ToBtVector3(newWorldScale * size_));
1123                 }
1124                 else
1125                     URHO3D_LOGWARNING("Could not find custom geometry component ID " + String(customGeometryID_) +
1126                                " for convex shape creation");
1127             }
1128             else if (model_ && model_->GetNumGeometries())
1129             {
1130                 // Check the geometry cache
1131                 Pair<Model*, unsigned> id = MakePair(model_.Get(), lodLevel_);
1132                 HashMap<Pair<Model*, unsigned>, SharedPtr<CollisionGeometryData> >& cache = physicsWorld_->GetConvexCache();
1133                 HashMap<Pair<Model*, unsigned>, SharedPtr<CollisionGeometryData> >::Iterator j = cache.Find(id);
1134                 if (j != cache.End())
1135                     geometry_ = j->second_;
1136                 else
1137                 {
1138                     geometry_ = new ConvexData(model_, lodLevel_);
1139                     // Check if model has dynamic buffers, do not cache in that case
1140                     if (!HasDynamicBuffers(model_, lodLevel_))
1141                         cache[id] = geometry_;
1142                 }
1143 
1144                 ConvexData* convex = static_cast<ConvexData*>(geometry_.Get());
1145                 shape_ = new btConvexHullShape((btScalar*)convex->vertexData_.Get(), convex->vertexCount_, sizeof(Vector3));
1146                 shape_->setLocalScaling(ToBtVector3(newWorldScale * size_));
1147                 SubscribeToEvent(model_, E_RELOADFINISHED, URHO3D_HANDLER(CollisionShape, HandleModelReloadFinished));
1148             }
1149             break;
1150 
1151         case SHAPE_TERRAIN:
1152             size_ = size_.Abs();
1153             {
1154                 Terrain* terrain = GetComponent<Terrain>();
1155                 if (terrain && terrain->GetHeightData())
1156                 {
1157                     geometry_ = new HeightfieldData(terrain, lodLevel_);
1158                     HeightfieldData* heightfield = static_cast<HeightfieldData*>(geometry_.Get());
1159 
1160                     shape_ =
1161                         new btHeightfieldTerrainShape(heightfield->size_.x_, heightfield->size_.y_, heightfield->heightData_.Get(),
1162                             1.0f, heightfield->minHeight_, heightfield->maxHeight_, 1, PHY_FLOAT, false);
1163                     shape_->setLocalScaling(
1164                         ToBtVector3(Vector3(heightfield->spacing_.x_, 1.0f, heightfield->spacing_.z_) * newWorldScale * size_));
1165                 }
1166             }
1167             break;
1168 
1169         default:
1170             shape_ = this->UpdateDerivedShape(shapeType_, newWorldScale);
1171             break;
1172         }
1173 
1174         if (shape_)
1175         {
1176             shape_->setUserPointer(this);
1177             shape_->setMargin(margin_);
1178         }
1179 
1180         cachedWorldScale_ = newWorldScale;
1181     }
1182 
1183     if (physicsWorld_)
1184         physicsWorld_->CleanupGeometryCache();
1185 
1186     recreateShape_ = false;
1187     retryCreation_ = false;
1188 }
1189 
UpdateDerivedShape(int shapeType,const Vector3 & newWorldScale)1190 btCollisionShape* CollisionShape::UpdateDerivedShape(int shapeType, const Vector3& newWorldScale)
1191 {
1192     // To be overridden in derived classes.
1193     return 0;
1194 }
1195 
1196 
HandleTerrainCreated(StringHash eventType,VariantMap & eventData)1197 void CollisionShape::HandleTerrainCreated(StringHash eventType, VariantMap& eventData)
1198 {
1199     if (shapeType_ == SHAPE_TERRAIN)
1200     {
1201         UpdateShape();
1202         NotifyRigidBody();
1203     }
1204 }
1205 
HandleModelReloadFinished(StringHash eventType,VariantMap & eventData)1206 void CollisionShape::HandleModelReloadFinished(StringHash eventType, VariantMap& eventData)
1207 {
1208     if (physicsWorld_)
1209         physicsWorld_->RemoveCachedGeometry(model_);
1210     if (shapeType_ == SHAPE_TRIANGLEMESH || shapeType_ == SHAPE_CONVEXHULL)
1211     {
1212         UpdateShape();
1213         NotifyRigidBody();
1214     }
1215 }
1216 
1217 }
1218