1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "BsPhysXMesh.h"
4#include "RTTI/BsPhysXMeshRTTI.h"
5#include "Mesh/BsMeshData.h"
6#include "RenderAPI/BsVertexDataDesc.h"
7#include "BsPhysX.h"
8#include "Math/BsAABox.h"
9#include "foundation/PxAllocatorCallback.h"
10#include "geometry/PxTriangleMesh.h"
11#include "geometry/PxConvexMesh.h"
12#include "cooking/PxConvexMeshDesc.h"
13#include "extensions/PxDefaultStreams.h"
14
15using namespace physx;
16
17namespace bs
18{
19 /**
20 * Attempts to cook a convex mesh from the provided mesh data. Assumes the mesh data is not null and contains vertex
21 * positions as well as face indices. If the method returns true the resulting convex mesh will be output in the @p
22 * data buffer, and its size in @p size. The data buffer will be allocated used the generic allocator and is up to the
23 * caller to free it.
24 */
25 bool cookConvex(PxCooking* cooking, const SPtr<MeshData>& meshData, UINT8** data, UINT32& size)
26 {
27 SPtr<VertexDataDesc> vertexDesc = meshData->getVertexDesc();
28
29 // Try to create hull from points
30 PxConvexMeshDesc convexDesc;
31 convexDesc.points.count = meshData->getNumVertices();
32 convexDesc.points.stride = vertexDesc->getVertexStride();
33 convexDesc.points.data = meshData->getElementData(VES_POSITION);
34 convexDesc.flags |= PxConvexFlag::eCOMPUTE_CONVEX;
35
36 PxDefaultMemoryOutputStream output;
37 if (cooking->cookConvexMesh(convexDesc, output))
38 {
39 size = output.getSize();
40 *data = (UINT8*)bs_alloc(size);
41
42 memcpy(*data, output.getData(), size);
43 return true;
44 }
45
46 // Try inflating the convex mesh
47 convexDesc.flags |= PxConvexFlag::eINFLATE_CONVEX;
48 if (cooking->cookConvexMesh(convexDesc, output))
49 {
50 size = output.getSize();
51 *data = (UINT8*)bs_alloc(size);
52
53 memcpy(*data, output.getData(), size);
54 return true;
55 }
56
57 // Nothing works, just compute an AABB
58 AABox box;
59
60 auto vertIter = meshData->getVec3DataIter(VES_POSITION);
61 do
62 {
63 box.merge(vertIter.getValue());
64 }
65 while (vertIter.moveNext());
66
67 Vector3 aabbVerts[8];
68 aabbVerts[0] = box.getCorner(AABox::FAR_LEFT_BOTTOM);
69 aabbVerts[1] = box.getCorner(AABox::FAR_RIGHT_BOTTOM);
70 aabbVerts[2] = box.getCorner(AABox::FAR_RIGHT_TOP);
71 aabbVerts[3] = box.getCorner(AABox::FAR_LEFT_TOP);
72
73 aabbVerts[4] = box.getCorner(AABox::NEAR_LEFT_BOTTOM);
74 aabbVerts[5] = box.getCorner(AABox::NEAR_RIGHT_BOTTOM);
75 aabbVerts[6] = box.getCorner(AABox::NEAR_RIGHT_TOP);
76 aabbVerts[7] = box.getCorner(AABox::NEAR_LEFT_TOP);
77
78 convexDesc.points.count = 8;
79 convexDesc.points.stride = sizeof(Vector3);
80 convexDesc.points.data = &aabbVerts[0];
81 convexDesc.flags &= ~PxConvexFlag::eINFLATE_CONVEX;
82
83 if (cooking->cookConvexMesh(convexDesc, output))
84 {
85 size = output.getSize();
86 *data = (UINT8*)bs_alloc(size);
87
88 memcpy(*data, output.getData(), size);
89 return true;
90 }
91
92 return false;
93 }
94
95 /**
96 * Attempts to cook a triangle or convex mesh from the provided mesh data. Will log a warning and return false if it is
97 * unable to cook the mesh. If the method returns true the resulting convex mesh will be output in the @p data buffer,
98 * and its size in @p size. The data buffer will be allocated used the generic allocator and is up to the caller to
99 * free it.
100 */
101 bool cookMesh(const SPtr<MeshData>& meshData, PhysicsMeshType type, UINT8** data, UINT32& size)
102 {
103 if (meshData == nullptr)
104 return false;
105
106 PxCooking* cooking = gPhysX().getCooking();
107 if (cooking == nullptr)
108 {
109 LOGWRN("Attempting to cook a physics mesh but cooking is not enabled globally.");
110 return false;
111 }
112
113 SPtr<VertexDataDesc> vertexDesc = meshData->getVertexDesc();
114 if (!vertexDesc->hasElement(VES_POSITION))
115 {
116 LOGWRN("Provided PhysicsMesh mesh data has no vertex positions.");
117 return false;
118 }
119
120 if (type == PhysicsMeshType::Convex)
121 {
122 if(!cookConvex(cooking, meshData, data, size))
123 {
124 LOGWRN("Failed cooking a convex mesh. Perpahs it is too complex? Maximum number of convex vertices is 256.");
125 return false;
126 }
127 }
128 else
129 {
130 PxTriangleMeshDesc meshDesc;
131 meshDesc.points.count = meshData->getNumVertices();
132 meshDesc.points.stride = vertexDesc->getVertexStride();
133 meshDesc.points.data = meshData->getElementData(VES_POSITION);
134
135 meshDesc.triangles.count = meshData->getNumIndices() / 3;
136 meshDesc.flags |= PxMeshFlag::eFLIPNORMALS;
137
138 IndexType indexType = meshData->getIndexType();
139 if (indexType == IT_32BIT)
140 {
141 meshDesc.triangles.stride = 3 * sizeof(PxU32);
142 meshDesc.triangles.data = meshData->getIndices32();
143 }
144 else
145 {
146 meshDesc.triangles.stride = 3 * sizeof(PxU16);
147 meshDesc.triangles.data = meshData->getIndices16();
148 meshDesc.flags |= PxMeshFlag::e16_BIT_INDICES;
149 }
150
151 PxDefaultMemoryOutputStream output;
152 if (!cooking->cookTriangleMesh(meshDesc, output))
153 return false;
154
155 size = output.getSize();
156 *data = (UINT8*)bs_alloc(size);
157
158 memcpy(*data, output.getData(), size);
159 }
160
161 return true;
162 }
163
164 PhysXMesh::PhysXMesh(const SPtr<MeshData>& meshData, PhysicsMeshType type)
165 :PhysicsMesh(meshData, type)
166 { }
167
168 void PhysXMesh::initialize()
169 {
170 if(mInternal == nullptr) // Could be not-null if we're deserializing
171 mInternal = bs_shared_ptr_new<FPhysXMesh>(mInitMeshData, mType);
172
173 PhysicsMesh::initialize();
174 }
175
176 void PhysXMesh::destroy()
177 {
178 mInternal = nullptr;
179
180 PhysicsMesh::destroy();
181 }
182
183 FPhysXMesh::FPhysXMesh()
184 :FPhysicsMesh(nullptr, PhysicsMeshType::Convex)
185 {
186
187 }
188
189 FPhysXMesh::FPhysXMesh(const SPtr<MeshData>& meshData, PhysicsMeshType type)
190 :FPhysicsMesh(meshData, type)
191 {
192 // Perform cooking if needed
193 if (meshData != nullptr)
194 cookMesh(meshData, mType, &mCookedData, mCookedDataSize);
195
196 initialize();
197 }
198
199 FPhysXMesh::~FPhysXMesh()
200 {
201 if (mCookedData != nullptr)
202 {
203 bs_free(mCookedData);
204
205 mCookedData = nullptr;
206 mCookedDataSize = 0;
207 }
208
209 if (mTriangleMesh != nullptr)
210 {
211 mTriangleMesh->release();
212 mTriangleMesh = nullptr;
213 }
214
215 if (mConvexMesh != nullptr)
216 {
217 mConvexMesh->release();
218 mConvexMesh = nullptr;
219 }
220 }
221
222 void FPhysXMesh::initialize()
223 {
224 if (mCookedData != nullptr && mCookedDataSize > 0)
225 {
226 PxPhysics* physx = gPhysX().getPhysX();
227
228 PxDefaultMemoryInputData input(mCookedData, mCookedDataSize);
229 if (mType == PhysicsMeshType::Convex)
230 mConvexMesh = physx->createConvexMesh(input);
231 else
232 mTriangleMesh = physx->createTriangleMesh(input);
233 }
234 }
235
236 SPtr<MeshData> FPhysXMesh::getMeshData() const
237 {
238 SPtr<VertexDataDesc> vertexDesc = VertexDataDesc::create();
239 vertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
240
241 if (mConvexMesh == nullptr && mTriangleMesh == nullptr)
242 return MeshData::create(0, 0, vertexDesc);
243
244 UINT32 numVertices = 0;
245 UINT32 numIndices = 0;
246
247 if(mConvexMesh != nullptr)
248 {
249 numVertices = mConvexMesh->getNbVertices();
250
251 UINT32 numPolygons = mConvexMesh->getNbPolygons();
252 for (UINT32 i = 0; i < numPolygons; i++)
253 {
254 PxHullPolygon face;
255 bool status = mConvexMesh->getPolygonData(i, face);
256 assert(status);
257
258 numIndices += (face.mNbVerts - 2) * 3;
259 }
260 }
261 else // Triangle
262 {
263 numVertices = mTriangleMesh->getNbVertices();
264 numIndices = mTriangleMesh->getNbTriangles() * 3;
265 }
266
267 SPtr<MeshData> meshData = MeshData::create(numVertices, numIndices, vertexDesc);
268
269 auto posIter = meshData->getVec3DataIter(VES_POSITION);
270 UINT32* outIndices = meshData->getIndices32();
271
272 if (mConvexMesh != nullptr)
273 {
274 const PxVec3* convexVertices = mConvexMesh->getVertices();
275 const UINT8* convexIndices = mConvexMesh->getIndexBuffer();
276
277 for (UINT32 i = 0; i < numVertices; i++)
278 posIter.addValue(fromPxVector(convexVertices[i]));
279
280 UINT32 numPolygons = mConvexMesh->getNbPolygons();
281 for (UINT32 i = 0; i < numPolygons; i++)
282 {
283 PxHullPolygon face;
284 bool status = mConvexMesh->getPolygonData(i, face);
285 assert(status);
286
287 const PxU8* faceIndices = convexIndices + face.mIndexBase;
288 for (UINT32 j = 2; j < face.mNbVerts; j++)
289 {
290 *outIndices++ = faceIndices[0];
291 *outIndices++ = faceIndices[j];
292 *outIndices++ = faceIndices[j - 1];
293 }
294 }
295 }
296 else
297 {
298 const PxVec3* vertices = mTriangleMesh->getVertices();
299 for (UINT32 i = 0; i < numVertices; i++)
300 posIter.addValue(fromPxVector(vertices[i]));
301
302 if(mTriangleMesh->getTriangleMeshFlags() & PxTriangleMeshFlag::e16_BIT_INDICES)
303 {
304 const UINT16* indices = (const UINT16*)mTriangleMesh->getTriangles();
305
306 UINT32 numTriangles = numIndices / 3;
307 for (UINT32 i = 0; i < numTriangles; i++)
308 {
309 // Flip triangles as PhysX keeps them opposite to what framework expects
310 outIndices[i * 3 + 0] = (UINT32)indices[i * 3 + 0];
311 outIndices[i * 3 + 1] = (UINT32)indices[i * 3 + 2];
312 outIndices[i * 3 + 2] = (UINT32)indices[i * 3 + 1];
313 }
314 }
315 else
316 {
317 const UINT32* indices = (const UINT32*)mTriangleMesh->getTriangles();
318
319 UINT32 numTriangles = numIndices / 3;
320 for (UINT32 i = 0; i < numTriangles; i++)
321 {
322 // Flip triangles as PhysX keeps them opposite to what framework expects
323 outIndices[i * 3 + 0] = indices[i * 3 + 0];
324 outIndices[i * 3 + 1] = indices[i * 3 + 2];
325 outIndices[i * 3 + 2] = indices[i * 3 + 1];
326 }
327 }
328 }
329
330 return meshData;
331 }
332
333 RTTITypeBase* FPhysXMesh::getRTTIStatic()
334 {
335 return FPhysXMeshRTTI::instance();
336 }
337
338 RTTITypeBase* FPhysXMesh::getRTTI() const
339 {
340 return getRTTIStatic();
341 }
342}