Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Renderer/DebugRenderer.cpp
9906 views
1
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3
// SPDX-License-Identifier: MIT
4
5
#include <Jolt/Jolt.h>
6
7
#ifdef JPH_DEBUG_RENDERER
8
9
#include <Jolt/Renderer/DebugRenderer.h>
10
#include <Jolt/Core/Profiler.h>
11
#include <Jolt/Geometry/OrientedBox.h>
12
13
JPH_NAMESPACE_BEGIN
14
15
DebugRenderer *DebugRenderer::sInstance = nullptr;
16
17
// Number of LOD levels to create
18
static const int sMaxLevel = 4;
19
20
// Distance for each LOD level, these are tweaked for an object of approx. size 1. Use the lod scale to scale these distances.
21
static const float sLODDistanceForLevel[] = { 5.0f, 10.0f, 40.0f, cLargeFloat };
22
23
DebugRenderer::Triangle::Triangle(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, ColorArg inColor)
24
{
25
// Set position
26
inV1.StoreFloat3(&mV[0].mPosition);
27
inV2.StoreFloat3(&mV[1].mPosition);
28
inV3.StoreFloat3(&mV[2].mPosition);
29
30
// Set color
31
mV[0].mColor = mV[1].mColor = mV[2].mColor = inColor;
32
33
// Calculate normal
34
Vec3 normal = (inV2 - inV1).Cross(inV3 - inV1);
35
float normal_len = normal.Length();
36
if (normal_len > 0.0f)
37
normal /= normal_len;
38
Float3 normal3;
39
normal.StoreFloat3(&normal3);
40
mV[0].mNormal = mV[1].mNormal = mV[2].mNormal = normal3;
41
42
// Reset UV's
43
mV[0].mUV = mV[1].mUV = mV[2].mUV = { 0, 0 };
44
}
45
46
DebugRenderer::Triangle::Triangle(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, ColorArg inColor, Vec3Arg inUVOrigin, Vec3Arg inUVDirection)
47
{
48
// Set position
49
inV1.StoreFloat3(&mV[0].mPosition);
50
inV2.StoreFloat3(&mV[1].mPosition);
51
inV3.StoreFloat3(&mV[2].mPosition);
52
53
// Set color
54
mV[0].mColor = mV[1].mColor = mV[2].mColor = inColor;
55
56
// Calculate normal
57
Vec3 normal = (inV2 - inV1).Cross(inV3 - inV1).Normalized();
58
Float3 normal3;
59
normal.StoreFloat3(&normal3);
60
mV[0].mNormal = mV[1].mNormal = mV[2].mNormal = normal3;
61
62
// Set UV's
63
Vec3 uv1 = inV1 - inUVOrigin;
64
Vec3 uv2 = inV2 - inUVOrigin;
65
Vec3 uv3 = inV3 - inUVOrigin;
66
Vec3 axis2 = normal.Cross(inUVDirection);
67
mV[0].mUV = { inUVDirection.Dot(uv1), axis2.Dot(uv1) };
68
mV[1].mUV = { inUVDirection.Dot(uv2), axis2.Dot(uv2) };
69
mV[2].mUV = { inUVDirection.Dot(uv3), axis2.Dot(uv3) };
70
}
71
72
DebugRenderer::DebugRenderer()
73
{
74
// Store singleton
75
JPH_ASSERT(sInstance == nullptr);
76
sInstance = this;
77
}
78
79
DebugRenderer::~DebugRenderer()
80
{
81
JPH_ASSERT(sInstance == this);
82
sInstance = nullptr;
83
}
84
85
void DebugRenderer::DrawWireBox(const AABox &inBox, ColorArg inColor)
86
{
87
JPH_PROFILE_FUNCTION();
88
89
// 8 vertices
90
RVec3 v1(Real(inBox.mMin.GetX()), Real(inBox.mMin.GetY()), Real(inBox.mMin.GetZ()));
91
RVec3 v2(Real(inBox.mMin.GetX()), Real(inBox.mMin.GetY()), Real(inBox.mMax.GetZ()));
92
RVec3 v3(Real(inBox.mMin.GetX()), Real(inBox.mMax.GetY()), Real(inBox.mMin.GetZ()));
93
RVec3 v4(Real(inBox.mMin.GetX()), Real(inBox.mMax.GetY()), Real(inBox.mMax.GetZ()));
94
RVec3 v5(Real(inBox.mMax.GetX()), Real(inBox.mMin.GetY()), Real(inBox.mMin.GetZ()));
95
RVec3 v6(Real(inBox.mMax.GetX()), Real(inBox.mMin.GetY()), Real(inBox.mMax.GetZ()));
96
RVec3 v7(Real(inBox.mMax.GetX()), Real(inBox.mMax.GetY()), Real(inBox.mMin.GetZ()));
97
RVec3 v8(Real(inBox.mMax.GetX()), Real(inBox.mMax.GetY()), Real(inBox.mMax.GetZ()));
98
99
// 12 edges
100
DrawLine(v1, v2, inColor);
101
DrawLine(v1, v3, inColor);
102
DrawLine(v1, v5, inColor);
103
DrawLine(v2, v4, inColor);
104
DrawLine(v2, v6, inColor);
105
DrawLine(v3, v4, inColor);
106
DrawLine(v3, v7, inColor);
107
DrawLine(v4, v8, inColor);
108
DrawLine(v5, v6, inColor);
109
DrawLine(v5, v7, inColor);
110
DrawLine(v6, v8, inColor);
111
DrawLine(v7, v8, inColor);
112
}
113
114
void DebugRenderer::DrawWireBox(const OrientedBox &inBox, ColorArg inColor)
115
{
116
JPH_PROFILE_FUNCTION();
117
118
// 8 vertices
119
RVec3 v1(inBox.mOrientation * Vec3(-inBox.mHalfExtents.GetX(), -inBox.mHalfExtents.GetY(), -inBox.mHalfExtents.GetZ()));
120
RVec3 v2(inBox.mOrientation * Vec3(-inBox.mHalfExtents.GetX(), -inBox.mHalfExtents.GetY(), inBox.mHalfExtents.GetZ()));
121
RVec3 v3(inBox.mOrientation * Vec3(-inBox.mHalfExtents.GetX(), inBox.mHalfExtents.GetY(), -inBox.mHalfExtents.GetZ()));
122
RVec3 v4(inBox.mOrientation * Vec3(-inBox.mHalfExtents.GetX(), inBox.mHalfExtents.GetY(), inBox.mHalfExtents.GetZ()));
123
RVec3 v5(inBox.mOrientation * Vec3(inBox.mHalfExtents.GetX(), -inBox.mHalfExtents.GetY(), -inBox.mHalfExtents.GetZ()));
124
RVec3 v6(inBox.mOrientation * Vec3(inBox.mHalfExtents.GetX(), -inBox.mHalfExtents.GetY(), inBox.mHalfExtents.GetZ()));
125
RVec3 v7(inBox.mOrientation * Vec3(inBox.mHalfExtents.GetX(), inBox.mHalfExtents.GetY(), -inBox.mHalfExtents.GetZ()));
126
RVec3 v8(inBox.mOrientation * Vec3(inBox.mHalfExtents.GetX(), inBox.mHalfExtents.GetY(), inBox.mHalfExtents.GetZ()));
127
128
// 12 edges
129
DrawLine(v1, v2, inColor);
130
DrawLine(v1, v3, inColor);
131
DrawLine(v1, v5, inColor);
132
DrawLine(v2, v4, inColor);
133
DrawLine(v2, v6, inColor);
134
DrawLine(v3, v4, inColor);
135
DrawLine(v3, v7, inColor);
136
DrawLine(v4, v8, inColor);
137
DrawLine(v5, v6, inColor);
138
DrawLine(v5, v7, inColor);
139
DrawLine(v6, v8, inColor);
140
DrawLine(v7, v8, inColor);
141
}
142
143
void DebugRenderer::DrawWireBox(RMat44Arg inMatrix, const AABox &inBox, ColorArg inColor)
144
{
145
JPH_PROFILE_FUNCTION();
146
147
// 8 vertices
148
RVec3 v1 = inMatrix * Vec3(inBox.mMin.GetX(), inBox.mMin.GetY(), inBox.mMin.GetZ());
149
RVec3 v2 = inMatrix * Vec3(inBox.mMin.GetX(), inBox.mMin.GetY(), inBox.mMax.GetZ());
150
RVec3 v3 = inMatrix * Vec3(inBox.mMin.GetX(), inBox.mMax.GetY(), inBox.mMin.GetZ());
151
RVec3 v4 = inMatrix * Vec3(inBox.mMin.GetX(), inBox.mMax.GetY(), inBox.mMax.GetZ());
152
RVec3 v5 = inMatrix * Vec3(inBox.mMax.GetX(), inBox.mMin.GetY(), inBox.mMin.GetZ());
153
RVec3 v6 = inMatrix * Vec3(inBox.mMax.GetX(), inBox.mMin.GetY(), inBox.mMax.GetZ());
154
RVec3 v7 = inMatrix * Vec3(inBox.mMax.GetX(), inBox.mMax.GetY(), inBox.mMin.GetZ());
155
RVec3 v8 = inMatrix * Vec3(inBox.mMax.GetX(), inBox.mMax.GetY(), inBox.mMax.GetZ());
156
157
// 12 edges
158
DrawLine(v1, v2, inColor);
159
DrawLine(v1, v3, inColor);
160
DrawLine(v1, v5, inColor);
161
DrawLine(v2, v4, inColor);
162
DrawLine(v2, v6, inColor);
163
DrawLine(v3, v4, inColor);
164
DrawLine(v3, v7, inColor);
165
DrawLine(v4, v8, inColor);
166
DrawLine(v5, v6, inColor);
167
DrawLine(v5, v7, inColor);
168
DrawLine(v6, v8, inColor);
169
DrawLine(v7, v8, inColor);
170
}
171
172
void DebugRenderer::DrawMarker(RVec3Arg inPosition, ColorArg inColor, float inSize)
173
{
174
JPH_PROFILE_FUNCTION();
175
176
Vec3 dx(inSize, 0, 0);
177
Vec3 dy(0, inSize, 0);
178
Vec3 dz(0, 0, inSize);
179
DrawLine(inPosition - dy, inPosition + dy, inColor);
180
DrawLine(inPosition - dx, inPosition + dx, inColor);
181
DrawLine(inPosition - dz, inPosition + dz, inColor);
182
}
183
184
void DebugRenderer::DrawArrow(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor, float inSize)
185
{
186
JPH_PROFILE_FUNCTION();
187
188
// Draw base line
189
DrawLine(inFrom, inTo, inColor);
190
191
if (inSize > 0.0f)
192
{
193
// Draw arrow head
194
Vec3 dir = Vec3(inTo - inFrom);
195
float len = dir.Length();
196
if (len != 0.0f)
197
dir = dir * (inSize / len);
198
else
199
dir = Vec3(inSize, 0, 0);
200
Vec3 perp = inSize * dir.GetNormalizedPerpendicular();
201
DrawLine(inTo - dir + perp, inTo, inColor);
202
DrawLine(inTo - dir - perp, inTo, inColor);
203
}
204
}
205
206
void DebugRenderer::DrawCoordinateSystem(RMat44Arg inTransform, float inSize)
207
{
208
JPH_PROFILE_FUNCTION();
209
210
DrawArrow(inTransform.GetTranslation(), inTransform * Vec3(inSize, 0, 0), Color::sRed, 0.1f * inSize);
211
DrawArrow(inTransform.GetTranslation(), inTransform * Vec3(0, inSize, 0), Color::sGreen, 0.1f * inSize);
212
DrawArrow(inTransform.GetTranslation(), inTransform * Vec3(0, 0, inSize), Color::sBlue, 0.1f * inSize);
213
}
214
215
void DebugRenderer::DrawPlane(RVec3Arg inPoint, Vec3Arg inNormal, ColorArg inColor, float inSize)
216
{
217
// Create orthogonal basis
218
Vec3 perp1 = inNormal.Cross(Vec3::sAxisY()).NormalizedOr(Vec3::sAxisX());
219
Vec3 perp2 = perp1.Cross(inNormal).Normalized();
220
perp1 = inNormal.Cross(perp2);
221
222
// Calculate corners
223
RVec3 corner1 = inPoint + inSize * (perp1 + perp2);
224
RVec3 corner2 = inPoint + inSize * (perp1 - perp2);
225
RVec3 corner3 = inPoint + inSize * (-perp1 - perp2);
226
RVec3 corner4 = inPoint + inSize * (-perp1 + perp2);
227
228
// Draw cross
229
DrawLine(corner1, corner3, inColor);
230
DrawLine(corner2, corner4, inColor);
231
232
// Draw square
233
DrawLine(corner1, corner2, inColor);
234
DrawLine(corner2, corner3, inColor);
235
DrawLine(corner3, corner4, inColor);
236
DrawLine(corner4, corner1, inColor);
237
238
// Draw normal
239
DrawArrow(inPoint, inPoint + inSize * inNormal, inColor, 0.1f * inSize);
240
}
241
242
void DebugRenderer::DrawWireTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor)
243
{
244
JPH_PROFILE_FUNCTION();
245
246
DrawLine(inV1, inV2, inColor);
247
DrawLine(inV2, inV3, inColor);
248
DrawLine(inV3, inV1, inColor);
249
}
250
251
void DebugRenderer::DrawWireSphere(RVec3Arg inCenter, float inRadius, ColorArg inColor, int inLevel)
252
{
253
RMat44 matrix = RMat44::sTranslation(inCenter) * Mat44::sScale(inRadius);
254
255
DrawWireUnitSphere(matrix, inColor, inLevel);
256
}
257
258
void DebugRenderer::DrawWireUnitSphere(RMat44Arg inMatrix, ColorArg inColor, int inLevel)
259
{
260
JPH_PROFILE_FUNCTION();
261
262
DrawWireUnitSphereRecursive(inMatrix, inColor, Vec3::sAxisX(), Vec3::sAxisY(), Vec3::sAxisZ(), inLevel);
263
DrawWireUnitSphereRecursive(inMatrix, inColor, -Vec3::sAxisX(), Vec3::sAxisY(), Vec3::sAxisZ(), inLevel);
264
DrawWireUnitSphereRecursive(inMatrix, inColor, Vec3::sAxisX(), -Vec3::sAxisY(), Vec3::sAxisZ(), inLevel);
265
DrawWireUnitSphereRecursive(inMatrix, inColor, -Vec3::sAxisX(), -Vec3::sAxisY(), Vec3::sAxisZ(), inLevel);
266
DrawWireUnitSphereRecursive(inMatrix, inColor, Vec3::sAxisX(), Vec3::sAxisY(), -Vec3::sAxisZ(), inLevel);
267
DrawWireUnitSphereRecursive(inMatrix, inColor, -Vec3::sAxisX(), Vec3::sAxisY(), -Vec3::sAxisZ(), inLevel);
268
DrawWireUnitSphereRecursive(inMatrix, inColor, Vec3::sAxisX(), -Vec3::sAxisY(), -Vec3::sAxisZ(), inLevel);
269
DrawWireUnitSphereRecursive(inMatrix, inColor, -Vec3::sAxisX(), -Vec3::sAxisY(), -Vec3::sAxisZ(), inLevel);
270
}
271
272
void DebugRenderer::DrawWireUnitSphereRecursive(RMat44Arg inMatrix, ColorArg inColor, Vec3Arg inDir1, Vec3Arg inDir2, Vec3Arg inDir3, int inLevel)
273
{
274
if (inLevel == 0)
275
{
276
RVec3 d1 = inMatrix * inDir1;
277
RVec3 d2 = inMatrix * inDir2;
278
RVec3 d3 = inMatrix * inDir3;
279
280
DrawLine(d1, d2, inColor);
281
DrawLine(d2, d3, inColor);
282
DrawLine(d3, d1, inColor);
283
}
284
else
285
{
286
Vec3 center1 = (inDir1 + inDir2).Normalized();
287
Vec3 center2 = (inDir2 + inDir3).Normalized();
288
Vec3 center3 = (inDir3 + inDir1).Normalized();
289
290
DrawWireUnitSphereRecursive(inMatrix, inColor, inDir1, center1, center3, inLevel - 1);
291
DrawWireUnitSphereRecursive(inMatrix, inColor, center1, center2, center3, inLevel - 1);
292
DrawWireUnitSphereRecursive(inMatrix, inColor, center1, inDir2, center2, inLevel - 1);
293
DrawWireUnitSphereRecursive(inMatrix, inColor, center3, center2, inDir3, inLevel - 1);
294
}
295
}
296
297
void DebugRenderer::Create8thSphereRecursive(Array<uint32> &ioIndices, Array<Vertex> &ioVertices, Vec3Arg inDir1, uint32 &ioIdx1, Vec3Arg inDir2, uint32 &ioIdx2, Vec3Arg inDir3, uint32 &ioIdx3, const Float2 &inUV, SupportFunction inGetSupport, int inLevel)
298
{
299
if (inLevel == 0)
300
{
301
if (ioIdx1 == 0xffffffff)
302
{
303
ioIdx1 = (uint32)ioVertices.size();
304
Float3 position, normal;
305
inGetSupport(inDir1).StoreFloat3(&position);
306
inDir1.StoreFloat3(&normal);
307
ioVertices.push_back({ position, normal, inUV, Color::sWhite });
308
}
309
310
if (ioIdx2 == 0xffffffff)
311
{
312
ioIdx2 = (uint32)ioVertices.size();
313
Float3 position, normal;
314
inGetSupport(inDir2).StoreFloat3(&position);
315
inDir2.StoreFloat3(&normal);
316
ioVertices.push_back({ position, normal, inUV, Color::sWhite });
317
}
318
319
if (ioIdx3 == 0xffffffff)
320
{
321
ioIdx3 = (uint32)ioVertices.size();
322
Float3 position, normal;
323
inGetSupport(inDir3).StoreFloat3(&position);
324
inDir3.StoreFloat3(&normal);
325
ioVertices.push_back({ position, normal, inUV, Color::sWhite });
326
}
327
328
ioIndices.push_back(ioIdx1);
329
ioIndices.push_back(ioIdx2);
330
ioIndices.push_back(ioIdx3);
331
}
332
else
333
{
334
Vec3 center1 = (inDir1 + inDir2).Normalized();
335
Vec3 center2 = (inDir2 + inDir3).Normalized();
336
Vec3 center3 = (inDir3 + inDir1).Normalized();
337
338
uint32 idx1 = 0xffffffff;
339
uint32 idx2 = 0xffffffff;
340
uint32 idx3 = 0xffffffff;
341
342
Create8thSphereRecursive(ioIndices, ioVertices, inDir1, ioIdx1, center1, idx1, center3, idx3, inUV, inGetSupport, inLevel - 1);
343
Create8thSphereRecursive(ioIndices, ioVertices, center1, idx1, center2, idx2, center3, idx3, inUV, inGetSupport, inLevel - 1);
344
Create8thSphereRecursive(ioIndices, ioVertices, center1, idx1, inDir2, ioIdx2, center2, idx2, inUV, inGetSupport, inLevel - 1);
345
Create8thSphereRecursive(ioIndices, ioVertices, center3, idx3, center2, idx2, inDir3, ioIdx3, inUV, inGetSupport, inLevel - 1);
346
}
347
}
348
349
void DebugRenderer::Create8thSphere(Array<uint32> &ioIndices, Array<Vertex> &ioVertices, Vec3Arg inDir1, Vec3Arg inDir2, Vec3Arg inDir3, const Float2 &inUV, SupportFunction inGetSupport, int inLevel)
350
{
351
uint32 idx1 = 0xffffffff;
352
uint32 idx2 = 0xffffffff;
353
uint32 idx3 = 0xffffffff;
354
355
Create8thSphereRecursive(ioIndices, ioVertices, inDir1, idx1, inDir2, idx2, inDir3, idx3, inUV, inGetSupport, inLevel);
356
}
357
358
DebugRenderer::Batch DebugRenderer::CreateCylinder(float inTop, float inBottom, float inTopRadius, float inBottomRadius, int inLevel)
359
{
360
Array<Vertex> cylinder_vertices;
361
Array<uint32> cylinder_indices;
362
363
for (int q = 0; q < 4; ++q)
364
{
365
Float2 uv = (q & 1) == 0? Float2(0.25f, 0.75f) : Float2(0.25f, 0.25f);
366
367
uint32 center_start_idx = (uint32)cylinder_vertices.size();
368
369
Float3 nt(0.0f, 1.0f, 0.0f);
370
Float3 nb(0.0f, -1.0f, 0.0f);
371
cylinder_vertices.push_back({ Float3(0.0f, inTop, 0.0f), nt, uv, Color::sWhite });
372
cylinder_vertices.push_back({ Float3(0.0f, inBottom, 0.0f), nb, uv, Color::sWhite });
373
374
uint32 vtx_start_idx = (uint32)cylinder_vertices.size();
375
376
int num_parts = 1 << inLevel;
377
for (int i = 0; i <= num_parts; ++i)
378
{
379
// Calculate top and bottom vertex
380
float angle = 0.5f * JPH_PI * (float(q) + float(i) / num_parts);
381
float s = Sin(angle);
382
float c = Cos(angle);
383
Float3 vt(inTopRadius * s, inTop, inTopRadius * c);
384
Float3 vb(inBottomRadius * s, inBottom, inBottomRadius * c);
385
386
// Calculate normal
387
Vec3 edge = Vec3(vt) - Vec3(vb);
388
Float3 n;
389
edge.Cross(Vec3(s, 0, c).Cross(edge)).Normalized().StoreFloat3(&n);
390
391
cylinder_vertices.push_back({ vt, nt, uv, Color::sWhite });
392
cylinder_vertices.push_back({ vb, nb, uv, Color::sWhite });
393
cylinder_vertices.push_back({ vt, n, uv, Color::sWhite });
394
cylinder_vertices.push_back({ vb, n, uv, Color::sWhite });
395
}
396
397
for (int i = 0; i < num_parts; ++i)
398
{
399
uint32 start = vtx_start_idx + 4 * i;
400
401
// Top
402
cylinder_indices.push_back(center_start_idx);
403
cylinder_indices.push_back(start);
404
cylinder_indices.push_back(start + 4);
405
406
// Bottom
407
cylinder_indices.push_back(center_start_idx + 1);
408
cylinder_indices.push_back(start + 5);
409
cylinder_indices.push_back(start + 1);
410
411
// Side
412
cylinder_indices.push_back(start + 2);
413
cylinder_indices.push_back(start + 3);
414
cylinder_indices.push_back(start + 7);
415
416
cylinder_indices.push_back(start + 2);
417
cylinder_indices.push_back(start + 7);
418
cylinder_indices.push_back(start + 6);
419
}
420
}
421
422
return CreateTriangleBatch(cylinder_vertices, cylinder_indices);
423
}
424
425
void DebugRenderer::CreateQuad(Array<uint32> &ioIndices, Array<Vertex> &ioVertices, Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, Vec3Arg inV4)
426
{
427
// Make room
428
uint32 start_idx = uint32(ioVertices.size());
429
ioVertices.resize(start_idx + 4);
430
Vertex *vertices = &ioVertices[start_idx];
431
432
// Set position
433
inV1.StoreFloat3(&vertices[0].mPosition);
434
inV2.StoreFloat3(&vertices[1].mPosition);
435
inV3.StoreFloat3(&vertices[2].mPosition);
436
inV4.StoreFloat3(&vertices[3].mPosition);
437
438
// Set color
439
vertices[0].mColor = vertices[1].mColor = vertices[2].mColor = vertices[3].mColor = Color::sWhite;
440
441
// Calculate normal
442
Vec3 normal = (inV2 - inV1).Cross(inV3 - inV1).Normalized();
443
Float3 normal3;
444
normal.StoreFloat3(&normal3);
445
vertices[0].mNormal = vertices[1].mNormal = vertices[2].mNormal = vertices[3].mNormal = normal3;
446
447
// Set UV's
448
vertices[0].mUV = { 0, 0 };
449
vertices[1].mUV = { 2, 0 };
450
vertices[2].mUV = { 2, 2 };
451
vertices[3].mUV = { 0, 2 };
452
453
// Set indices
454
ioIndices.push_back(start_idx);
455
ioIndices.push_back(start_idx + 1);
456
ioIndices.push_back(start_idx + 2);
457
458
ioIndices.push_back(start_idx);
459
ioIndices.push_back(start_idx + 2);
460
ioIndices.push_back(start_idx + 3);
461
}
462
463
void DebugRenderer::Initialize()
464
{
465
// Box
466
{
467
Array<Vertex> box_vertices;
468
Array<uint32> box_indices;
469
470
// Get corner points
471
Vec3 v0 = Vec3(-1, 1, -1);
472
Vec3 v1 = Vec3( 1, 1, -1);
473
Vec3 v2 = Vec3( 1, 1, 1);
474
Vec3 v3 = Vec3(-1, 1, 1);
475
Vec3 v4 = Vec3(-1, -1, -1);
476
Vec3 v5 = Vec3( 1, -1, -1);
477
Vec3 v6 = Vec3( 1, -1, 1);
478
Vec3 v7 = Vec3(-1, -1, 1);
479
480
// Top
481
CreateQuad(box_indices, box_vertices, v0, v3, v2, v1);
482
483
// Bottom
484
CreateQuad(box_indices, box_vertices, v4, v5, v6, v7);
485
486
// Left
487
CreateQuad(box_indices, box_vertices, v0, v4, v7, v3);
488
489
// Right
490
CreateQuad(box_indices, box_vertices, v2, v6, v5, v1);
491
492
// Front
493
CreateQuad(box_indices, box_vertices, v3, v7, v6, v2);
494
495
// Back
496
CreateQuad(box_indices, box_vertices, v0, v1, v5, v4);
497
498
mBox = new Geometry(CreateTriangleBatch(box_vertices, box_indices), AABox(Vec3(-1, -1, -1), Vec3(1, 1, 1)));
499
}
500
501
// Support function that returns a unit sphere
502
auto sphere_support = [](Vec3Arg inDirection) { return inDirection; };
503
504
// Construct geometries
505
mSphere = new Geometry(AABox(Vec3(-1, -1, -1), Vec3(1, 1, 1)));
506
mCapsuleBottom = new Geometry(AABox(Vec3(-1, -1, -1), Vec3(1, 0, 1)));
507
mCapsuleTop = new Geometry(AABox(Vec3(-1, 0, -1), Vec3(1, 1, 1)));
508
mCapsuleMid = new Geometry(AABox(Vec3(-1, -1, -1), Vec3(1, 1, 1)));
509
mOpenCone = new Geometry(AABox(Vec3(-1, 0, -1), Vec3(1, 1, 1)));
510
mCylinder = new Geometry(AABox(Vec3(-1, -1, -1), Vec3(1, 1, 1)));
511
512
// Iterate over levels
513
for (int level = sMaxLevel; level >= 1; --level)
514
{
515
// Determine at which distance this level should be active
516
float distance = sLODDistanceForLevel[sMaxLevel - level];
517
518
// Sphere
519
mSphere->mLODs.push_back({ CreateTriangleBatchForConvex(sphere_support, level), distance });
520
521
// Capsule bottom half sphere
522
{
523
Array<Vertex> capsule_bottom_vertices;
524
Array<uint32> capsule_bottom_indices;
525
Create8thSphere(capsule_bottom_indices, capsule_bottom_vertices, -Vec3::sAxisX(), -Vec3::sAxisY(), Vec3::sAxisZ(), Float2(0.25f, 0.25f), sphere_support, level);
526
Create8thSphere(capsule_bottom_indices, capsule_bottom_vertices, -Vec3::sAxisY(), Vec3::sAxisX(), Vec3::sAxisZ(), Float2(0.25f, 0.75f), sphere_support, level);
527
Create8thSphere(capsule_bottom_indices, capsule_bottom_vertices, Vec3::sAxisX(), -Vec3::sAxisY(), -Vec3::sAxisZ(), Float2(0.25f, 0.25f), sphere_support, level);
528
Create8thSphere(capsule_bottom_indices, capsule_bottom_vertices, -Vec3::sAxisY(), -Vec3::sAxisX(), -Vec3::sAxisZ(), Float2(0.25f, 0.75f), sphere_support, level);
529
mCapsuleBottom->mLODs.push_back({ CreateTriangleBatch(capsule_bottom_vertices, capsule_bottom_indices), distance });
530
}
531
532
// Capsule top half sphere
533
{
534
Array<Vertex> capsule_top_vertices;
535
Array<uint32> capsule_top_indices;
536
Create8thSphere(capsule_top_indices, capsule_top_vertices, Vec3::sAxisX(), Vec3::sAxisY(), Vec3::sAxisZ(), Float2(0.25f, 0.75f), sphere_support, level);
537
Create8thSphere(capsule_top_indices, capsule_top_vertices, Vec3::sAxisY(), -Vec3::sAxisX(), Vec3::sAxisZ(), Float2(0.25f, 0.25f), sphere_support, level);
538
Create8thSphere(capsule_top_indices, capsule_top_vertices, Vec3::sAxisY(), Vec3::sAxisX(), -Vec3::sAxisZ(), Float2(0.25f, 0.25f), sphere_support, level);
539
Create8thSphere(capsule_top_indices, capsule_top_vertices, -Vec3::sAxisX(), Vec3::sAxisY(), -Vec3::sAxisZ(), Float2(0.25f, 0.75f), sphere_support, level);
540
mCapsuleTop->mLODs.push_back({ CreateTriangleBatch(capsule_top_vertices, capsule_top_indices), distance });
541
}
542
543
// Capsule middle part
544
{
545
Array<Vertex> capsule_mid_vertices;
546
Array<uint32> capsule_mid_indices;
547
for (int q = 0; q < 4; ++q)
548
{
549
Float2 uv = (q & 1) == 0? Float2(0.25f, 0.25f) : Float2(0.25f, 0.75f);
550
551
uint32 start_idx = (uint32)capsule_mid_vertices.size();
552
553
int num_parts = 1 << level;
554
for (int i = 0; i <= num_parts; ++i)
555
{
556
float angle = 0.5f * JPH_PI * (float(q) + float(i) / num_parts);
557
float s = Sin(angle);
558
float c = Cos(angle);
559
Float3 vt(s, 1.0f, c);
560
Float3 vb(s, -1.0f, c);
561
Float3 n(s, 0, c);
562
563
capsule_mid_vertices.push_back({ vt, n, uv, Color::sWhite });
564
capsule_mid_vertices.push_back({ vb, n, uv, Color::sWhite });
565
}
566
567
for (int i = 0; i < num_parts; ++i)
568
{
569
uint32 start = start_idx + 2 * i;
570
571
capsule_mid_indices.push_back(start);
572
capsule_mid_indices.push_back(start + 1);
573
capsule_mid_indices.push_back(start + 3);
574
575
capsule_mid_indices.push_back(start);
576
capsule_mid_indices.push_back(start + 3);
577
capsule_mid_indices.push_back(start + 2);
578
}
579
}
580
mCapsuleMid->mLODs.push_back({ CreateTriangleBatch(capsule_mid_vertices, capsule_mid_indices), distance });
581
}
582
583
// Open cone
584
{
585
Array<Vertex> open_cone_vertices;
586
Array<uint32> open_cone_indices;
587
for (int q = 0; q < 4; ++q)
588
{
589
Float2 uv = (q & 1) == 0? Float2(0.25f, 0.25f) : Float2(0.25f, 0.75f);
590
591
uint32 start_idx = (uint32)open_cone_vertices.size();
592
593
int num_parts = 2 << level;
594
Float3 vt(0, 0, 0);
595
for (int i = 0; i <= num_parts; ++i)
596
{
597
// Calculate bottom vertex
598
float angle = 0.5f * JPH_PI * (float(q) + float(i) / num_parts);
599
float s = Sin(angle);
600
float c = Cos(angle);
601
Float3 vb(s, 1.0f, c);
602
603
// Calculate normal
604
// perpendicular = Y cross vb (perpendicular to the plane in which 0, y and vb exists)
605
// normal = perpendicular cross vb (normal to the edge 0 vb)
606
Vec3 normal = Vec3(s, -Square(s) - Square(c), c).Normalized();
607
Float3 n; normal.StoreFloat3(&n);
608
609
open_cone_vertices.push_back({ vt, n, uv, Color::sWhite });
610
open_cone_vertices.push_back({ vb, n, uv, Color::sWhite });
611
}
612
613
for (int i = 0; i < num_parts; ++i)
614
{
615
uint32 start = start_idx + 2 * i;
616
617
open_cone_indices.push_back(start);
618
open_cone_indices.push_back(start + 1);
619
open_cone_indices.push_back(start + 3);
620
}
621
}
622
mOpenCone->mLODs.push_back({ CreateTriangleBatch(open_cone_vertices, open_cone_indices), distance });
623
}
624
625
// Cylinder
626
mCylinder->mLODs.push_back({ CreateCylinder(1.0f, -1.0f, 1.0f, 1.0f, level), distance });
627
}
628
}
629
630
AABox DebugRenderer::sCalculateBounds(const Vertex *inVertices, int inVertexCount)
631
{
632
AABox bounds;
633
for (const Vertex *v = inVertices, *v_end = inVertices + inVertexCount; v < v_end; ++v)
634
bounds.Encapsulate(Vec3(v->mPosition));
635
return bounds;
636
}
637
638
DebugRenderer::Batch DebugRenderer::CreateTriangleBatch(const VertexList &inVertices, const IndexedTriangleNoMaterialList &inTriangles)
639
{
640
JPH_PROFILE_FUNCTION();
641
642
Array<Vertex> vertices;
643
644
// Create render vertices
645
vertices.resize(inVertices.size());
646
for (size_t v = 0; v < inVertices.size(); ++v)
647
{
648
vertices[v].mPosition = inVertices[v];
649
vertices[v].mNormal = Float3(0, 0, 0);
650
vertices[v].mUV = Float2(0, 0);
651
vertices[v].mColor = Color::sWhite;
652
}
653
654
// Calculate normals
655
for (size_t i = 0; i < inTriangles.size(); ++i)
656
{
657
const IndexedTriangleNoMaterial &tri = inTriangles[i];
658
659
// Calculate normal of face
660
Vec3 vtx[3];
661
for (int j = 0; j < 3; ++j)
662
vtx[j] = Vec3::sLoadFloat3Unsafe(vertices[tri.mIdx[j]].mPosition);
663
Vec3 normal = ((vtx[1] - vtx[0]).Cross(vtx[2] - vtx[0])).Normalized();
664
665
// Add normal to all vertices in face
666
for (int j = 0; j < 3; ++j)
667
(Vec3::sLoadFloat3Unsafe(vertices[tri.mIdx[j]].mNormal) + normal).StoreFloat3(&vertices[tri.mIdx[j]].mNormal);
668
}
669
670
// Renormalize vertex normals
671
for (size_t i = 0; i < vertices.size(); ++i)
672
Vec3::sLoadFloat3Unsafe(vertices[i].mNormal).Normalized().StoreFloat3(&vertices[i].mNormal);
673
674
return CreateTriangleBatch(&vertices[0], (int)vertices.size(), &inTriangles[0].mIdx[0], (int)(3 * inTriangles.size()));
675
}
676
677
DebugRenderer::Batch DebugRenderer::CreateTriangleBatchForConvex(SupportFunction inGetSupport, int inLevel, AABox *outBounds)
678
{
679
JPH_PROFILE_FUNCTION();
680
681
Array<Vertex> vertices;
682
Array<uint32> indices;
683
Create8thSphere(indices, vertices, Vec3::sAxisX(), Vec3::sAxisY(), Vec3::sAxisZ(), Float2(0.25f, 0.25f), inGetSupport, inLevel);
684
Create8thSphere(indices, vertices, Vec3::sAxisY(), -Vec3::sAxisX(), Vec3::sAxisZ(), Float2(0.25f, 0.75f), inGetSupport, inLevel);
685
Create8thSphere(indices, vertices, -Vec3::sAxisY(), Vec3::sAxisX(), Vec3::sAxisZ(), Float2(0.25f, 0.75f), inGetSupport, inLevel);
686
Create8thSphere(indices, vertices, -Vec3::sAxisX(), -Vec3::sAxisY(), Vec3::sAxisZ(), Float2(0.25f, 0.25f), inGetSupport, inLevel);
687
Create8thSphere(indices, vertices, Vec3::sAxisY(), Vec3::sAxisX(), -Vec3::sAxisZ(), Float2(0.25f, 0.75f), inGetSupport, inLevel);
688
Create8thSphere(indices, vertices, -Vec3::sAxisX(), Vec3::sAxisY(), -Vec3::sAxisZ(), Float2(0.25f, 0.25f), inGetSupport, inLevel);
689
Create8thSphere(indices, vertices, Vec3::sAxisX(), -Vec3::sAxisY(), -Vec3::sAxisZ(), Float2(0.25f, 0.25f), inGetSupport, inLevel);
690
Create8thSphere(indices, vertices, -Vec3::sAxisY(), -Vec3::sAxisX(), -Vec3::sAxisZ(), Float2(0.25f, 0.75f), inGetSupport, inLevel);
691
692
if (outBounds != nullptr)
693
*outBounds = sCalculateBounds(&vertices[0], (int)vertices.size());
694
695
return CreateTriangleBatch(vertices, indices);
696
}
697
698
DebugRenderer::GeometryRef DebugRenderer::CreateTriangleGeometryForConvex(SupportFunction inGetSupport)
699
{
700
GeometryRef geometry;
701
702
// Iterate over levels
703
for (int level = sMaxLevel; level >= 1; --level)
704
{
705
// Determine at which distance this level should be active
706
float distance = sLODDistanceForLevel[sMaxLevel - level];
707
708
// Create triangle batch and only calculate bounds for highest LOD level
709
AABox bounds;
710
Batch batch = CreateTriangleBatchForConvex(inGetSupport, level, geometry == nullptr? &bounds : nullptr);
711
712
// Construct geometry in the first iteration
713
if (geometry == nullptr)
714
geometry = new Geometry(bounds);
715
716
// Add the LOD
717
geometry->mLODs.push_back({ batch, distance });
718
}
719
720
return geometry;
721
}
722
723
void DebugRenderer::DrawBox(const AABox &inBox, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode)
724
{
725
JPH_PROFILE_FUNCTION();
726
727
RMat44 m = RMat44::sScale(Vec3::sMax(inBox.GetExtent(), Vec3::sReplicate(1.0e-6f))); // Prevent div by zero when one of the edges has length 0
728
m.SetTranslation(RVec3(inBox.GetCenter()));
729
DrawGeometry(m, inColor, mBox, ECullMode::CullBackFace, inCastShadow, inDrawMode);
730
}
731
732
void DebugRenderer::DrawBox(RMat44Arg inMatrix, const AABox &inBox, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode)
733
{
734
JPH_PROFILE_FUNCTION();
735
736
Mat44 m = Mat44::sScale(Vec3::sMax(inBox.GetExtent(), Vec3::sReplicate(1.0e-6f))); // Prevent div by zero when one of the edges has length 0
737
m.SetTranslation(inBox.GetCenter());
738
DrawGeometry(inMatrix * m, inColor, mBox, ECullMode::CullBackFace, inCastShadow, inDrawMode);
739
}
740
741
void DebugRenderer::DrawSphere(RVec3Arg inCenter, float inRadius, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode)
742
{
743
JPH_PROFILE_FUNCTION();
744
745
RMat44 matrix = RMat44::sTranslation(inCenter) * Mat44::sScale(inRadius);
746
747
DrawUnitSphere(matrix, inColor, inCastShadow, inDrawMode);
748
}
749
750
void DebugRenderer::DrawUnitSphere(RMat44Arg inMatrix, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode)
751
{
752
JPH_PROFILE_FUNCTION();
753
754
DrawGeometry(inMatrix, inColor, mSphere, ECullMode::CullBackFace, inCastShadow, inDrawMode);
755
}
756
757
void DebugRenderer::DrawCapsule(RMat44Arg inMatrix, float inHalfHeightOfCylinder, float inRadius, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode)
758
{
759
JPH_PROFILE_FUNCTION();
760
761
Mat44 scale_matrix = Mat44::sScale(inRadius);
762
763
// Calculate world space bounding box
764
AABox local_bounds(Vec3(-inRadius, -inHalfHeightOfCylinder - inRadius, -inRadius), Vec3(inRadius, inHalfHeightOfCylinder + inRadius, inRadius));
765
AABox world_bounds = local_bounds.Transformed(inMatrix);
766
767
float radius_sq = Square(inRadius);
768
769
// Draw bottom half sphere
770
RMat44 bottom_matrix = inMatrix * Mat44::sTranslation(Vec3(0, -inHalfHeightOfCylinder, 0)) * scale_matrix;
771
DrawGeometry(bottom_matrix, world_bounds, radius_sq, inColor, mCapsuleBottom, ECullMode::CullBackFace, inCastShadow, inDrawMode);
772
773
// Draw top half sphere
774
RMat44 top_matrix = inMatrix * Mat44::sTranslation(Vec3(0, inHalfHeightOfCylinder, 0)) * scale_matrix;
775
DrawGeometry(top_matrix, world_bounds, radius_sq, inColor, mCapsuleTop, ECullMode::CullBackFace, inCastShadow, inDrawMode);
776
777
// Draw middle part
778
DrawGeometry(inMatrix * Mat44::sScale(Vec3(inRadius, inHalfHeightOfCylinder, inRadius)), world_bounds, radius_sq, inColor, mCapsuleMid, ECullMode::CullBackFace, inCastShadow, inDrawMode);
779
}
780
781
void DebugRenderer::DrawCylinder(RMat44Arg inMatrix, float inHalfHeight, float inRadius, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode)
782
{
783
JPH_PROFILE_FUNCTION();
784
785
Mat44 local_transform(Vec4(inRadius, 0, 0, 0), Vec4(0, inHalfHeight, 0, 0), Vec4(0, 0, inRadius, 0), Vec4(0, 0, 0, 1));
786
RMat44 transform = inMatrix * local_transform;
787
788
DrawGeometry(transform, mCylinder->mBounds.Transformed(transform), Square(inRadius), inColor, mCylinder, ECullMode::CullBackFace, inCastShadow, inDrawMode);
789
}
790
791
void DebugRenderer::DrawOpenCone(RVec3Arg inTop, Vec3Arg inAxis, Vec3Arg inPerpendicular, float inHalfAngle, float inLength, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode)
792
{
793
JPH_PROFILE_FUNCTION();
794
795
JPH_ASSERT(inAxis.IsNormalized(1.0e-4f));
796
JPH_ASSERT(inPerpendicular.IsNormalized(1.0e-4f));
797
JPH_ASSERT(abs(inPerpendicular.Dot(inAxis)) < 1.0e-4f);
798
799
Vec3 axis = Sign(inHalfAngle) * inLength * inAxis;
800
float scale = inLength * Tan(abs(inHalfAngle));
801
if (scale != 0.0f)
802
{
803
Vec3 perp1 = scale * inPerpendicular;
804
Vec3 perp2 = scale * inAxis.Cross(inPerpendicular);
805
RMat44 transform(Vec4(perp1, 0), Vec4(axis, 0), Vec4(perp2, 0), inTop);
806
DrawGeometry(transform, inColor, mOpenCone, ECullMode::Off, inCastShadow, inDrawMode);
807
}
808
}
809
810
DebugRenderer::Geometry *DebugRenderer::CreateSwingLimitGeometry(int inNumSegments, const Vec3 *inVertices)
811
{
812
// Allocate space for vertices
813
int num_vertices = 2 * inNumSegments;
814
Vertex *vertices_start = (Vertex *)JPH_STACK_ALLOC(num_vertices * sizeof(Vertex));
815
Vertex *vertices = vertices_start;
816
817
for (int i = 0; i < inNumSegments; ++i)
818
{
819
// Get output vertices
820
Vertex &top = *(vertices++);
821
Vertex &bottom = *(vertices++);
822
823
// Get local position
824
const Vec3 &pos = inVertices[i];
825
826
// Get local normal
827
const Vec3 &prev_pos = inVertices[(i + inNumSegments - 1) % inNumSegments];
828
const Vec3 &next_pos = inVertices[(i + 1) % inNumSegments];
829
Vec3 normal = 0.5f * (next_pos.Cross(pos).NormalizedOr(Vec3::sZero()) + pos.Cross(prev_pos).NormalizedOr(Vec3::sZero()));
830
831
// Store top vertex
832
top.mPosition = { 0, 0, 0 };
833
normal.StoreFloat3(&top.mNormal);
834
top.mColor = Color::sWhite;
835
top.mUV = { 0, 0 };
836
837
// Store bottom vertex
838
pos.StoreFloat3(&bottom.mPosition);
839
normal.StoreFloat3(&bottom.mNormal);
840
bottom.mColor = Color::sWhite;
841
bottom.mUV = { 0, 0 };
842
}
843
844
// Allocate space for indices
845
int num_indices = 3 * inNumSegments;
846
uint32 *indices_start = (uint32 *)JPH_STACK_ALLOC(num_indices * sizeof(uint32));
847
uint32 *indices = indices_start;
848
849
// Calculate indices
850
for (int i = 0; i < inNumSegments; ++i)
851
{
852
int first = 2 * i;
853
int second = (first + 3) % num_vertices;
854
int third = first + 1;
855
856
// Triangle
857
*indices++ = first;
858
*indices++ = second;
859
*indices++ = third;
860
}
861
862
// Convert to triangle batch
863
return new Geometry(CreateTriangleBatch(vertices_start, num_vertices, indices_start, num_indices), sCalculateBounds(vertices_start, num_vertices));
864
}
865
866
void DebugRenderer::DrawSwingConeLimits(RMat44Arg inMatrix, float inSwingYHalfAngle, float inSwingZHalfAngle, float inEdgeLength, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode)
867
{
868
JPH_PROFILE_FUNCTION();
869
870
// Assert sane input
871
JPH_ASSERT(inSwingYHalfAngle >= 0.0f && inSwingYHalfAngle <= JPH_PI);
872
JPH_ASSERT(inSwingZHalfAngle >= 0.0f && inSwingZHalfAngle <= JPH_PI);
873
JPH_ASSERT(inEdgeLength > 0.0f);
874
875
// Check cache
876
SwingConeLimits limits { inSwingYHalfAngle, inSwingZHalfAngle };
877
GeometryRef &geometry = mSwingConeLimits[limits];
878
if (geometry == nullptr)
879
{
880
SwingConeBatches::iterator it = mPrevSwingConeLimits.find(limits);
881
if (it != mPrevSwingConeLimits.end())
882
geometry = it->second;
883
}
884
if (geometry == nullptr)
885
{
886
// Number of segments to draw the cone with
887
const int num_segments = 64;
888
int half_num_segments = num_segments / 2;
889
890
// The y and z values of the quaternion are limited to an ellipse, e1 and e2 are the radii of this ellipse
891
float e1 = Sin(0.5f * inSwingZHalfAngle);
892
float e2 = Sin(0.5f * inSwingYHalfAngle);
893
894
// Check if the limits will draw something
895
if ((e1 <= 0.0f && e2 <= 0.0f) || (e2 >= 1.0f && e1 >= 1.0f))
896
return;
897
898
// Calculate squared values
899
float e1_sq = Square(e1);
900
float e2_sq = Square(e2);
901
902
// Calculate local space vertices for shape
903
Vec3 ls_vertices[num_segments];
904
int tgt_vertex = 0;
905
for (int side_iter = 0; side_iter < 2; ++side_iter)
906
for (int segment_iter = 0; segment_iter < half_num_segments; ++segment_iter)
907
{
908
float y, z;
909
if (e2_sq > e1_sq)
910
{
911
// Trace the y value of the quaternion
912
y = e2 - 2.0f * segment_iter * e2 / half_num_segments;
913
914
// Calculate the corresponding z value of the quaternion
915
float z_sq = e1_sq - e1_sq / e2_sq * Square(y);
916
z = z_sq <= 0.0f? 0.0f : sqrt(z_sq);
917
}
918
else
919
{
920
// Trace the z value of the quaternion
921
z = -e1 + 2.0f * segment_iter * e1 / half_num_segments;
922
923
// Calculate the corresponding y value of the quaternion
924
float y_sq = e2_sq - e2_sq / e1_sq * Square(z);
925
y = y_sq <= 0.0f? 0.0f : sqrt(y_sq);
926
}
927
928
// If we're tracing the opposite side, flip the values
929
if (side_iter == 1)
930
{
931
z = -z;
932
y = -y;
933
}
934
935
// Create quaternion
936
Vec3 q_xyz(0, y, z);
937
float w = sqrt(1.0f - q_xyz.LengthSq());
938
Quat q(Vec4(q_xyz, w));
939
940
// Store vertex
941
ls_vertices[tgt_vertex++] = q.RotateAxisX();
942
}
943
944
geometry = CreateSwingLimitGeometry(num_segments, ls_vertices);
945
}
946
947
DrawGeometry(inMatrix * Mat44::sScale(inEdgeLength), inColor, geometry, ECullMode::Off, inCastShadow, inDrawMode);
948
}
949
950
void DebugRenderer::DrawSwingPyramidLimits(RMat44Arg inMatrix, float inMinSwingYAngle, float inMaxSwingYAngle, float inMinSwingZAngle, float inMaxSwingZAngle, float inEdgeLength, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode)
951
{
952
JPH_PROFILE_FUNCTION();
953
954
// Assert sane input
955
JPH_ASSERT(inMinSwingYAngle <= inMaxSwingYAngle && inMinSwingZAngle <= inMaxSwingZAngle);
956
JPH_ASSERT(inEdgeLength > 0.0f);
957
958
// Check cache
959
SwingPyramidLimits limits { inMinSwingYAngle, inMaxSwingYAngle, inMinSwingZAngle, inMaxSwingZAngle };
960
GeometryRef &geometry = mSwingPyramidLimits[limits];
961
if (geometry == nullptr)
962
{
963
SwingPyramidBatches::iterator it = mPrevSwingPyramidLimits.find(limits);
964
if (it != mPrevSwingPyramidLimits.end())
965
geometry = it->second;
966
}
967
if (geometry == nullptr)
968
{
969
// Number of segments to draw the cone with
970
const int num_segments = 64;
971
int quarter_num_segments = num_segments / 4;
972
973
// Note that this is q = Quat::sRotation(Vec3::sAxisZ(), z) * Quat::sRotation(Vec3::sAxisY(), y) with q.x set to zero so we don't introduce twist
974
// This matches the calculation in SwingTwistConstraintPart::ClampSwingTwist
975
auto get_axis = [](float inY, float inZ) {
976
float hy = 0.5f * inY;
977
float hz = 0.5f * inZ;
978
float cos_hy = Cos(hy);
979
float cos_hz = Cos(hz);
980
return Quat(0, Sin(hy) * cos_hz, cos_hy * Sin(hz), cos_hy * cos_hz).Normalized().RotateAxisX();
981
};
982
983
// Calculate local space vertices for shape
984
Vec3 ls_vertices[num_segments];
985
int tgt_vertex = 0;
986
for (int segment_iter = 0; segment_iter < quarter_num_segments; ++segment_iter)
987
ls_vertices[tgt_vertex++] = get_axis(inMinSwingYAngle, inMaxSwingZAngle - (inMaxSwingZAngle - inMinSwingZAngle) * segment_iter / quarter_num_segments);
988
for (int segment_iter = 0; segment_iter < quarter_num_segments; ++segment_iter)
989
ls_vertices[tgt_vertex++] = get_axis(inMinSwingYAngle + (inMaxSwingYAngle - inMinSwingYAngle) * segment_iter / quarter_num_segments, inMinSwingZAngle);
990
for (int segment_iter = 0; segment_iter < quarter_num_segments; ++segment_iter)
991
ls_vertices[tgt_vertex++] = get_axis(inMaxSwingYAngle, inMinSwingZAngle + (inMaxSwingZAngle - inMinSwingZAngle) * segment_iter / quarter_num_segments);
992
for (int segment_iter = 0; segment_iter < quarter_num_segments; ++segment_iter)
993
ls_vertices[tgt_vertex++] = get_axis(inMaxSwingYAngle - (inMaxSwingYAngle - inMinSwingYAngle) * segment_iter / quarter_num_segments, inMaxSwingZAngle);
994
995
geometry = CreateSwingLimitGeometry(num_segments, ls_vertices);
996
}
997
998
DrawGeometry(inMatrix * Mat44::sScale(inEdgeLength), inColor, geometry, ECullMode::Off, inCastShadow, inDrawMode);
999
}
1000
1001
void DebugRenderer::DrawPie(RVec3Arg inCenter, float inRadius, Vec3Arg inNormal, Vec3Arg inAxis, float inMinAngle, float inMaxAngle, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode)
1002
{
1003
if (inMinAngle >= inMaxAngle)
1004
return;
1005
1006
JPH_PROFILE_FUNCTION();
1007
1008
JPH_ASSERT(inAxis.IsNormalized(1.0e-4f));
1009
JPH_ASSERT(inNormal.IsNormalized(1.0e-4f));
1010
JPH_ASSERT(abs(inNormal.Dot(inAxis)) < 1.0e-4f);
1011
1012
// Pies have a unique batch based on the difference between min and max angle
1013
float delta_angle = inMaxAngle - inMinAngle;
1014
GeometryRef &geometry = mPieLimits[delta_angle];
1015
if (geometry == nullptr)
1016
{
1017
PieBatces::iterator it = mPrevPieLimits.find(delta_angle);
1018
if (it != mPrevPieLimits.end())
1019
geometry = it->second;
1020
}
1021
if (geometry == nullptr)
1022
{
1023
int num_parts = (int)ceil(64.0f * delta_angle / (2.0f * JPH_PI));
1024
1025
Float3 normal = { 0, 1, 0 };
1026
Float3 center = { 0, 0, 0 };
1027
1028
// Allocate space for vertices
1029
int num_vertices = num_parts + 2;
1030
Vertex *vertices_start = (Vertex *)JPH_STACK_ALLOC(num_vertices * sizeof(Vertex));
1031
Vertex *vertices = vertices_start;
1032
1033
// Center of circle
1034
*vertices++ = { center, normal, { 0, 0 }, Color::sWhite };
1035
1036
// Outer edge of pie
1037
for (int i = 0; i <= num_parts; ++i)
1038
{
1039
float angle = float(i) / float(num_parts) * delta_angle;
1040
1041
Float3 pos = { Cos(angle), 0, Sin(angle) };
1042
*vertices++ = { pos, normal, { 0, 0 }, Color::sWhite };
1043
}
1044
1045
// Allocate space for indices
1046
int num_indices = num_parts * 3;
1047
uint32 *indices_start = (uint32 *)JPH_STACK_ALLOC(num_indices * sizeof(uint32));
1048
uint32 *indices = indices_start;
1049
1050
for (int i = 0; i < num_parts; ++i)
1051
{
1052
*indices++ = 0;
1053
*indices++ = i + 1;
1054
*indices++ = i + 2;
1055
}
1056
1057
// Convert to triangle batch
1058
geometry = new Geometry(CreateTriangleBatch(vertices_start, num_vertices, indices_start, num_indices), sCalculateBounds(vertices_start, num_vertices));
1059
}
1060
1061
// Construct matrix that transforms pie into world space
1062
RMat44 matrix = RMat44(Vec4(inRadius * inAxis, 0), Vec4(inRadius * inNormal, 0), Vec4(inRadius * inNormal.Cross(inAxis), 0), inCenter) * Mat44::sRotationY(-inMinAngle);
1063
1064
DrawGeometry(matrix, inColor, geometry, ECullMode::Off, inCastShadow, inDrawMode);
1065
}
1066
1067
void DebugRenderer::DrawTaperedCylinder(RMat44Arg inMatrix, float inTop, float inBottom, float inTopRadius, float inBottomRadius, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode)
1068
{
1069
TaperedCylinder tapered_cylinder { inTop, inBottom, inTopRadius, inBottomRadius };
1070
1071
GeometryRef &geometry = mTaperedCylinders[tapered_cylinder];
1072
if (geometry == nullptr)
1073
{
1074
TaperedCylinderBatces::iterator it = mPrevTaperedCylinders.find(tapered_cylinder);
1075
if (it != mPrevTaperedCylinders.end())
1076
geometry = it->second;
1077
}
1078
if (geometry == nullptr)
1079
{
1080
float max_radius = max(inTopRadius, inBottomRadius);
1081
geometry = new Geometry(AABox(Vec3(-max_radius, inBottom, -max_radius), Vec3(max_radius, inTop, max_radius)));
1082
1083
for (int level = sMaxLevel; level >= 1; --level)
1084
geometry->mLODs.push_back({ CreateCylinder(inTop, inBottom, inTopRadius, inBottomRadius, level), sLODDistanceForLevel[sMaxLevel - level] });
1085
}
1086
1087
DrawGeometry(inMatrix, inColor, geometry, ECullMode::CullBackFace, inCastShadow, inDrawMode);
1088
}
1089
1090
void DebugRenderer::NextFrame()
1091
{
1092
mPrevSwingConeLimits.clear();
1093
std::swap(mSwingConeLimits, mPrevSwingConeLimits);
1094
1095
mPrevSwingPyramidLimits.clear();
1096
std::swap(mSwingPyramidLimits, mPrevSwingPyramidLimits);
1097
1098
mPrevPieLimits.clear();
1099
std::swap(mPieLimits, mPrevPieLimits);
1100
1101
mPrevTaperedCylinders.clear();
1102
std::swap(mTaperedCylinders, mPrevTaperedCylinders);
1103
}
1104
1105
JPH_NAMESPACE_END
1106
1107
#endif // JPH_DEBUG_RENDERER
1108
1109