jolt-ruby 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitmodules +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +228 -0
- data/Rakefile +180 -0
- data/examples/character_virtual.rb +35 -0
- data/examples/falling_sphere.rb +25 -0
- data/examples/stagecraft_binding.rb +29 -0
- data/ext/jolt_ruby/CMakeLists.txt +42 -0
- data/ext/jolt_ruby/character_helper.cpp +44 -0
- data/ext/jolt_ruby/constraint_helper.cpp +154 -0
- data/ext/jolt_ruby/extconf.rb +56 -0
- data/ext/jolt_ruby/helper.cpp +225 -0
- data/ext/jolt_ruby/helper.h +103 -0
- data/ext/joltc/.github/FUNDING.yml +1 -0
- data/ext/joltc/.github/workflows/build.yml +298 -0
- data/ext/joltc/.gitignore +272 -0
- data/ext/joltc/CMakeLists.txt +431 -0
- data/ext/joltc/LICENSE +21 -0
- data/ext/joltc/README.md +10 -0
- data/ext/joltc/build/cmake_vs2026_arm64.bat +3 -0
- data/ext/joltc/build/cmake_vs2026_clang.bat +10 -0
- data/ext/joltc/build/cmake_vs2026_x64.bat +3 -0
- data/ext/joltc/build/cmake_vs2026_x64_double.bat +3 -0
- data/ext/joltc/include/joltc.h +3166 -0
- data/ext/joltc/samples/01_HelloWorld/main.cpp +170 -0
- data/ext/joltc/samples/CMakeLists.txt +35 -0
- data/ext/joltc/src/joltc.c +4 -0
- data/ext/joltc/src/joltc.cpp +11812 -0
- data/ext/joltc/src/joltc_assert.cpp +271 -0
- data/ext/joltc/tests/CMakeLists.txt +77 -0
- data/ext/joltc/tests/test_character.cpp +149 -0
- data/ext/joltc/tests/test_collision.cpp +146 -0
- data/ext/joltc/tests/test_constraints.cpp +353 -0
- data/ext/joltc/tests/test_core.cpp +94 -0
- data/ext/joltc/tests/test_math.cpp +585 -0
- data/ext/joltc/tests/test_physics_system.cpp +465 -0
- data/ext/joltc/tests/test_shapes.cpp +789 -0
- data/ext/joltc/tests/test_skeleton.cpp +370 -0
- data/ext/joltc/tests/test_vehicle.cpp +319 -0
- data/generator/generate.rb +237 -0
- data/generator/layout_probe.cpp +489 -0
- data/generator/verify_layout.rb +32 -0
- data/lib/jolt/body.rb +147 -0
- data/lib/jolt/body_collection.rb +115 -0
- data/lib/jolt/body_dynamics.rb +93 -0
- data/lib/jolt/character_virtual.rb +162 -0
- data/lib/jolt/constraint.rb +136 -0
- data/lib/jolt/constraint_collection.rb +123 -0
- data/lib/jolt/contact_events.rb +19 -0
- data/lib/jolt/conversions.rb +76 -0
- data/lib/jolt/errors.rb +12 -0
- data/lib/jolt/fixed_stepper.rb +37 -0
- data/lib/jolt/hit.rb +5 -0
- data/lib/jolt/layers.rb +182 -0
- data/lib/jolt/native/character_functions.rb +16 -0
- data/lib/jolt/native/constraint_functions.rb +27 -0
- data/lib/jolt/native/core_functions.rb +17 -0
- data/lib/jolt/native/generated.rb +1995 -0
- data/lib/jolt/native/platform.rb +51 -0
- data/lib/jolt/native/query_functions.rb +43 -0
- data/lib/jolt/native/types.rb +28 -0
- data/lib/jolt/native.rb +94 -0
- data/lib/jolt/shape.rb +90 -0
- data/lib/jolt/shape_builders.rb +155 -0
- data/lib/jolt/system.rb +238 -0
- data/lib/jolt/system_characters.rb +25 -0
- data/lib/jolt/system_constraints.rb +36 -0
- data/lib/jolt/system_contacts.rb +57 -0
- data/lib/jolt/system_queries.rb +111 -0
- data/lib/jolt/transform.rb +5 -0
- data/lib/jolt/version.rb +5 -0
- data/lib/jolt.rb +83 -0
- data/script/smoke_gem.rb +29 -0
- data/script/verify_release.rb +36 -0
- metadata +147 -0
|
@@ -0,0 +1,789 @@
|
|
|
1
|
+
// Copyright (c) Amer Koleci and Contributors.
|
|
2
|
+
// Licensed under the MIT License (MIT). See LICENSE in the repository root for more information.
|
|
3
|
+
|
|
4
|
+
#include <gtest/gtest.h>
|
|
5
|
+
#include <cmath>
|
|
6
|
+
#include "joltc.h"
|
|
7
|
+
|
|
8
|
+
class ShapesTest : public ::testing::Test {
|
|
9
|
+
protected:
|
|
10
|
+
void SetUp() override {
|
|
11
|
+
ASSERT_TRUE(JPH_Init());
|
|
12
|
+
}
|
|
13
|
+
void TearDown() override {
|
|
14
|
+
JPH_Shutdown();
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// SphereShape Tests
|
|
20
|
+
// ============================================================================
|
|
21
|
+
|
|
22
|
+
TEST_F(ShapesTest, SphereShape_Create) {
|
|
23
|
+
JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
|
|
24
|
+
ASSERT_NE(shape, nullptr);
|
|
25
|
+
|
|
26
|
+
float radius = JPH_SphereShape_GetRadius(shape);
|
|
27
|
+
EXPECT_FLOAT_EQ(radius, 1.0f);
|
|
28
|
+
|
|
29
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
TEST_F(ShapesTest, SphereShape_CreateWithSettings) {
|
|
33
|
+
JPH_SphereShapeSettings* settings = JPH_SphereShapeSettings_Create(2.0f);
|
|
34
|
+
ASSERT_NE(settings, nullptr);
|
|
35
|
+
|
|
36
|
+
float radius = JPH_SphereShapeSettings_GetRadius(settings);
|
|
37
|
+
EXPECT_FLOAT_EQ(radius, 2.0f);
|
|
38
|
+
|
|
39
|
+
JPH_SphereShape* shape = JPH_SphereShapeSettings_CreateShape(settings);
|
|
40
|
+
ASSERT_NE(shape, nullptr);
|
|
41
|
+
|
|
42
|
+
radius = JPH_SphereShape_GetRadius(shape);
|
|
43
|
+
EXPECT_FLOAT_EQ(radius, 2.0f);
|
|
44
|
+
|
|
45
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
46
|
+
JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
TEST_F(ShapesTest, SphereShapeSettings_SetRadius) {
|
|
50
|
+
JPH_SphereShapeSettings* settings = JPH_SphereShapeSettings_Create(1.0f);
|
|
51
|
+
ASSERT_NE(settings, nullptr);
|
|
52
|
+
|
|
53
|
+
JPH_SphereShapeSettings_SetRadius(settings, 3.0f);
|
|
54
|
+
float radius = JPH_SphereShapeSettings_GetRadius(settings);
|
|
55
|
+
EXPECT_FLOAT_EQ(radius, 3.0f);
|
|
56
|
+
|
|
57
|
+
JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
TEST_F(ShapesTest, SphereShape_GetType) {
|
|
61
|
+
JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
|
|
62
|
+
ASSERT_NE(shape, nullptr);
|
|
63
|
+
|
|
64
|
+
JPH_ShapeType type = JPH_Shape_GetType((const JPH_Shape*)shape);
|
|
65
|
+
EXPECT_EQ(type, JPH_ShapeType_Convex);
|
|
66
|
+
|
|
67
|
+
JPH_ShapeSubType subType = JPH_Shape_GetSubType((const JPH_Shape*)shape);
|
|
68
|
+
EXPECT_EQ(subType, JPH_ShapeSubType_Sphere);
|
|
69
|
+
|
|
70
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
TEST_F(ShapesTest, SphereShape_GetVolume) {
|
|
74
|
+
JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
|
|
75
|
+
ASSERT_NE(shape, nullptr);
|
|
76
|
+
|
|
77
|
+
float volume = JPH_Shape_GetVolume((const JPH_Shape*)shape);
|
|
78
|
+
// Volume of sphere = (4/3) * PI * r^3
|
|
79
|
+
float expectedVolume = (4.0f / 3.0f) * JPH_M_PI * 1.0f * 1.0f * 1.0f;
|
|
80
|
+
EXPECT_NEAR(volume, expectedVolume, 0.001f);
|
|
81
|
+
|
|
82
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ============================================================================
|
|
86
|
+
// BoxShape Tests
|
|
87
|
+
// ============================================================================
|
|
88
|
+
|
|
89
|
+
TEST_F(ShapesTest, BoxShape_Create) {
|
|
90
|
+
JPH_Vec3 halfExtent = {1.0f, 2.0f, 3.0f};
|
|
91
|
+
JPH_BoxShape* shape = JPH_BoxShape_Create(&halfExtent, JPH_DEFAULT_CONVEX_RADIUS);
|
|
92
|
+
ASSERT_NE(shape, nullptr);
|
|
93
|
+
|
|
94
|
+
JPH_Vec3 result;
|
|
95
|
+
JPH_BoxShape_GetHalfExtent(shape, &result);
|
|
96
|
+
EXPECT_FLOAT_EQ(result.x, 1.0f);
|
|
97
|
+
EXPECT_FLOAT_EQ(result.y, 2.0f);
|
|
98
|
+
EXPECT_FLOAT_EQ(result.z, 3.0f);
|
|
99
|
+
|
|
100
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
TEST_F(ShapesTest, BoxShape_CreateWithSettings) {
|
|
104
|
+
JPH_Vec3 halfExtent = {2.0f, 2.0f, 2.0f};
|
|
105
|
+
JPH_BoxShapeSettings* settings = JPH_BoxShapeSettings_Create(&halfExtent, JPH_DEFAULT_CONVEX_RADIUS);
|
|
106
|
+
ASSERT_NE(settings, nullptr);
|
|
107
|
+
|
|
108
|
+
JPH_BoxShape* shape = JPH_BoxShapeSettings_CreateShape(settings);
|
|
109
|
+
ASSERT_NE(shape, nullptr);
|
|
110
|
+
|
|
111
|
+
JPH_Vec3 result;
|
|
112
|
+
JPH_BoxShape_GetHalfExtent(shape, &result);
|
|
113
|
+
EXPECT_FLOAT_EQ(result.x, 2.0f);
|
|
114
|
+
EXPECT_FLOAT_EQ(result.y, 2.0f);
|
|
115
|
+
EXPECT_FLOAT_EQ(result.z, 2.0f);
|
|
116
|
+
|
|
117
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
118
|
+
JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
TEST_F(ShapesTest, BoxShape_GetConvexRadius) {
|
|
122
|
+
JPH_Vec3 halfExtent = {1.0f, 1.0f, 1.0f};
|
|
123
|
+
JPH_BoxShape* shape = JPH_BoxShape_Create(&halfExtent, 0.1f);
|
|
124
|
+
ASSERT_NE(shape, nullptr);
|
|
125
|
+
|
|
126
|
+
float convexRadius = JPH_BoxShape_GetConvexRadius(shape);
|
|
127
|
+
EXPECT_FLOAT_EQ(convexRadius, 0.1f);
|
|
128
|
+
|
|
129
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
TEST_F(ShapesTest, BoxShape_GetType) {
|
|
133
|
+
JPH_Vec3 halfExtent = {1.0f, 1.0f, 1.0f};
|
|
134
|
+
JPH_BoxShape* shape = JPH_BoxShape_Create(&halfExtent, JPH_DEFAULT_CONVEX_RADIUS);
|
|
135
|
+
ASSERT_NE(shape, nullptr);
|
|
136
|
+
|
|
137
|
+
JPH_ShapeType type = JPH_Shape_GetType((const JPH_Shape*)shape);
|
|
138
|
+
EXPECT_EQ(type, JPH_ShapeType_Convex);
|
|
139
|
+
|
|
140
|
+
JPH_ShapeSubType subType = JPH_Shape_GetSubType((const JPH_Shape*)shape);
|
|
141
|
+
EXPECT_EQ(subType, JPH_ShapeSubType_Box);
|
|
142
|
+
|
|
143
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ============================================================================
|
|
147
|
+
// CapsuleShape Tests
|
|
148
|
+
// ============================================================================
|
|
149
|
+
|
|
150
|
+
TEST_F(ShapesTest, CapsuleShape_Create) {
|
|
151
|
+
JPH_CapsuleShape* shape = JPH_CapsuleShape_Create(1.0f, 0.5f);
|
|
152
|
+
ASSERT_NE(shape, nullptr);
|
|
153
|
+
|
|
154
|
+
float halfHeight = JPH_CapsuleShape_GetHalfHeightOfCylinder(shape);
|
|
155
|
+
float radius = JPH_CapsuleShape_GetRadius(shape);
|
|
156
|
+
EXPECT_FLOAT_EQ(halfHeight, 1.0f);
|
|
157
|
+
EXPECT_FLOAT_EQ(radius, 0.5f);
|
|
158
|
+
|
|
159
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
TEST_F(ShapesTest, CapsuleShape_CreateWithSettings) {
|
|
163
|
+
JPH_CapsuleShapeSettings* settings = JPH_CapsuleShapeSettings_Create(2.0f, 1.0f);
|
|
164
|
+
ASSERT_NE(settings, nullptr);
|
|
165
|
+
|
|
166
|
+
JPH_CapsuleShape* shape = JPH_CapsuleShapeSettings_CreateShape(settings);
|
|
167
|
+
ASSERT_NE(shape, nullptr);
|
|
168
|
+
|
|
169
|
+
float halfHeight = JPH_CapsuleShape_GetHalfHeightOfCylinder(shape);
|
|
170
|
+
float radius = JPH_CapsuleShape_GetRadius(shape);
|
|
171
|
+
EXPECT_FLOAT_EQ(halfHeight, 2.0f);
|
|
172
|
+
EXPECT_FLOAT_EQ(radius, 1.0f);
|
|
173
|
+
|
|
174
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
175
|
+
JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
TEST_F(ShapesTest, CapsuleShape_GetType) {
|
|
179
|
+
JPH_CapsuleShape* shape = JPH_CapsuleShape_Create(1.0f, 0.5f);
|
|
180
|
+
ASSERT_NE(shape, nullptr);
|
|
181
|
+
|
|
182
|
+
JPH_ShapeSubType subType = JPH_Shape_GetSubType((const JPH_Shape*)shape);
|
|
183
|
+
EXPECT_EQ(subType, JPH_ShapeSubType_Capsule);
|
|
184
|
+
|
|
185
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ============================================================================
|
|
189
|
+
// CylinderShape Tests
|
|
190
|
+
// ============================================================================
|
|
191
|
+
|
|
192
|
+
TEST_F(ShapesTest, CylinderShape_Create) {
|
|
193
|
+
JPH_CylinderShape* shape = JPH_CylinderShape_Create(1.0f, 0.5f);
|
|
194
|
+
ASSERT_NE(shape, nullptr);
|
|
195
|
+
|
|
196
|
+
float halfHeight = JPH_CylinderShape_GetHalfHeight(shape);
|
|
197
|
+
float radius = JPH_CylinderShape_GetRadius(shape);
|
|
198
|
+
EXPECT_FLOAT_EQ(halfHeight, 1.0f);
|
|
199
|
+
EXPECT_FLOAT_EQ(radius, 0.5f);
|
|
200
|
+
|
|
201
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
TEST_F(ShapesTest, CylinderShape_CreateWithSettings) {
|
|
205
|
+
JPH_CylinderShapeSettings* settings = JPH_CylinderShapeSettings_Create(2.0f, 1.0f, JPH_DEFAULT_CONVEX_RADIUS);
|
|
206
|
+
ASSERT_NE(settings, nullptr);
|
|
207
|
+
|
|
208
|
+
JPH_CylinderShape* shape = JPH_CylinderShapeSettings_CreateShape(settings);
|
|
209
|
+
ASSERT_NE(shape, nullptr);
|
|
210
|
+
|
|
211
|
+
float halfHeight = JPH_CylinderShape_GetHalfHeight(shape);
|
|
212
|
+
float radius = JPH_CylinderShape_GetRadius(shape);
|
|
213
|
+
EXPECT_FLOAT_EQ(halfHeight, 2.0f);
|
|
214
|
+
EXPECT_FLOAT_EQ(radius, 1.0f);
|
|
215
|
+
|
|
216
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
217
|
+
JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
TEST_F(ShapesTest, CylinderShape_GetType) {
|
|
221
|
+
JPH_CylinderShape* shape = JPH_CylinderShape_Create(1.0f, 0.5f);
|
|
222
|
+
ASSERT_NE(shape, nullptr);
|
|
223
|
+
|
|
224
|
+
JPH_ShapeSubType subType = JPH_Shape_GetSubType((const JPH_Shape*)shape);
|
|
225
|
+
EXPECT_EQ(subType, JPH_ShapeSubType_Cylinder);
|
|
226
|
+
|
|
227
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ============================================================================
|
|
231
|
+
// TriangleShape Tests
|
|
232
|
+
// ============================================================================
|
|
233
|
+
|
|
234
|
+
TEST_F(ShapesTest, TriangleShape_Create) {
|
|
235
|
+
JPH_Vec3 v1 = {0.0f, 0.0f, 0.0f};
|
|
236
|
+
JPH_Vec3 v2 = {1.0f, 0.0f, 0.0f};
|
|
237
|
+
JPH_Vec3 v3 = {0.0f, 1.0f, 0.0f};
|
|
238
|
+
|
|
239
|
+
JPH_TriangleShape* shape = JPH_TriangleShape_Create(&v1, &v2, &v3, 0.0f);
|
|
240
|
+
ASSERT_NE(shape, nullptr);
|
|
241
|
+
|
|
242
|
+
JPH_Vec3 result;
|
|
243
|
+
JPH_TriangleShape_GetVertex1(shape, &result);
|
|
244
|
+
EXPECT_FLOAT_EQ(result.x, 0.0f);
|
|
245
|
+
EXPECT_FLOAT_EQ(result.y, 0.0f);
|
|
246
|
+
EXPECT_FLOAT_EQ(result.z, 0.0f);
|
|
247
|
+
|
|
248
|
+
JPH_TriangleShape_GetVertex2(shape, &result);
|
|
249
|
+
EXPECT_FLOAT_EQ(result.x, 1.0f);
|
|
250
|
+
EXPECT_FLOAT_EQ(result.y, 0.0f);
|
|
251
|
+
EXPECT_FLOAT_EQ(result.z, 0.0f);
|
|
252
|
+
|
|
253
|
+
JPH_TriangleShape_GetVertex3(shape, &result);
|
|
254
|
+
EXPECT_FLOAT_EQ(result.x, 0.0f);
|
|
255
|
+
EXPECT_FLOAT_EQ(result.y, 1.0f);
|
|
256
|
+
EXPECT_FLOAT_EQ(result.z, 0.0f);
|
|
257
|
+
|
|
258
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
TEST_F(ShapesTest, TriangleShape_GetType) {
|
|
262
|
+
JPH_Vec3 v1 = {0.0f, 0.0f, 0.0f};
|
|
263
|
+
JPH_Vec3 v2 = {1.0f, 0.0f, 0.0f};
|
|
264
|
+
JPH_Vec3 v3 = {0.0f, 1.0f, 0.0f};
|
|
265
|
+
|
|
266
|
+
JPH_TriangleShape* shape = JPH_TriangleShape_Create(&v1, &v2, &v3, 0.0f);
|
|
267
|
+
ASSERT_NE(shape, nullptr);
|
|
268
|
+
|
|
269
|
+
JPH_ShapeSubType subType = JPH_Shape_GetSubType((const JPH_Shape*)shape);
|
|
270
|
+
EXPECT_EQ(subType, JPH_ShapeSubType_Triangle);
|
|
271
|
+
|
|
272
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// ============================================================================
|
|
276
|
+
// TaperedCapsuleShape Tests
|
|
277
|
+
// ============================================================================
|
|
278
|
+
|
|
279
|
+
TEST_F(ShapesTest, TaperedCapsuleShape_Create) {
|
|
280
|
+
JPH_TaperedCapsuleShapeSettings* settings = JPH_TaperedCapsuleShapeSettings_Create(1.0f, 0.5f, 0.3f);
|
|
281
|
+
ASSERT_NE(settings, nullptr);
|
|
282
|
+
|
|
283
|
+
JPH_TaperedCapsuleShape* shape = JPH_TaperedCapsuleShapeSettings_CreateShape(settings);
|
|
284
|
+
ASSERT_NE(shape, nullptr);
|
|
285
|
+
|
|
286
|
+
float topRadius = JPH_TaperedCapsuleShape_GetTopRadius(shape);
|
|
287
|
+
float bottomRadius = JPH_TaperedCapsuleShape_GetBottomRadius(shape);
|
|
288
|
+
float halfHeight = JPH_TaperedCapsuleShape_GetHalfHeight(shape);
|
|
289
|
+
|
|
290
|
+
EXPECT_FLOAT_EQ(topRadius, 0.5f);
|
|
291
|
+
EXPECT_FLOAT_EQ(bottomRadius, 0.3f);
|
|
292
|
+
EXPECT_FLOAT_EQ(halfHeight, 1.0f);
|
|
293
|
+
|
|
294
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
295
|
+
JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
TEST_F(ShapesTest, TaperedCapsuleShape_GetType) {
|
|
299
|
+
JPH_TaperedCapsuleShapeSettings* settings = JPH_TaperedCapsuleShapeSettings_Create(1.0f, 0.5f, 0.3f);
|
|
300
|
+
ASSERT_NE(settings, nullptr);
|
|
301
|
+
|
|
302
|
+
JPH_TaperedCapsuleShape* shape = JPH_TaperedCapsuleShapeSettings_CreateShape(settings);
|
|
303
|
+
ASSERT_NE(shape, nullptr);
|
|
304
|
+
|
|
305
|
+
JPH_ShapeSubType subType = JPH_Shape_GetSubType((const JPH_Shape*)shape);
|
|
306
|
+
EXPECT_EQ(subType, JPH_ShapeSubType_TaperedCapsule);
|
|
307
|
+
|
|
308
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
309
|
+
JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// ============================================================================
|
|
313
|
+
// ConvexHullShape Tests
|
|
314
|
+
// ============================================================================
|
|
315
|
+
|
|
316
|
+
TEST_F(ShapesTest, ConvexHullShape_Create) {
|
|
317
|
+
// Create a simple tetrahedron
|
|
318
|
+
JPH_Vec3 points[] = {
|
|
319
|
+
{0.0f, 0.0f, 0.0f},
|
|
320
|
+
{1.0f, 0.0f, 0.0f},
|
|
321
|
+
{0.5f, 1.0f, 0.0f},
|
|
322
|
+
{0.5f, 0.5f, 1.0f}
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
JPH_ConvexHullShapeSettings* settings = JPH_ConvexHullShapeSettings_Create(points, 4, JPH_DEFAULT_CONVEX_RADIUS);
|
|
326
|
+
ASSERT_NE(settings, nullptr);
|
|
327
|
+
|
|
328
|
+
JPH_ConvexHullShape* shape = JPH_ConvexHullShapeSettings_CreateShape(settings);
|
|
329
|
+
ASSERT_NE(shape, nullptr);
|
|
330
|
+
|
|
331
|
+
uint32_t numPoints = JPH_ConvexHullShape_GetNumPoints(shape);
|
|
332
|
+
EXPECT_EQ(numPoints, 4u);
|
|
333
|
+
|
|
334
|
+
uint32_t numFaces = JPH_ConvexHullShape_GetNumFaces(shape);
|
|
335
|
+
EXPECT_EQ(numFaces, 4u); // Tetrahedron has 4 faces
|
|
336
|
+
|
|
337
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
338
|
+
JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
TEST_F(ShapesTest, ConvexHullShape_GetPoint) {
|
|
342
|
+
JPH_Vec3 points[] = {
|
|
343
|
+
{0.0f, 0.0f, 0.0f},
|
|
344
|
+
{1.0f, 0.0f, 0.0f},
|
|
345
|
+
{0.5f, 1.0f, 0.0f},
|
|
346
|
+
{0.5f, 0.5f, 1.0f}
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
JPH_ConvexHullShapeSettings* settings = JPH_ConvexHullShapeSettings_Create(points, 4, JPH_DEFAULT_CONVEX_RADIUS);
|
|
350
|
+
JPH_ConvexHullShape* shape = JPH_ConvexHullShapeSettings_CreateShape(settings);
|
|
351
|
+
ASSERT_NE(shape, nullptr);
|
|
352
|
+
|
|
353
|
+
JPH_Vec3 result;
|
|
354
|
+
JPH_ConvexHullShape_GetPoint(shape, 0, &result);
|
|
355
|
+
// Points may be reordered, so just check they're valid
|
|
356
|
+
EXPECT_FALSE(std::isnan(result.x));
|
|
357
|
+
EXPECT_FALSE(std::isnan(result.y));
|
|
358
|
+
EXPECT_FALSE(std::isnan(result.z));
|
|
359
|
+
|
|
360
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
361
|
+
JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// ============================================================================
|
|
365
|
+
// PlaneShape Tests
|
|
366
|
+
// ============================================================================
|
|
367
|
+
|
|
368
|
+
TEST_F(ShapesTest, PlaneShape_Create) {
|
|
369
|
+
JPH_Plane plane = {{0.0f, 1.0f, 0.0f}, 0.0f}; // XZ plane at origin
|
|
370
|
+
JPH_PlaneShape* shape = JPH_PlaneShape_Create(&plane, nullptr, 1000.0f);
|
|
371
|
+
ASSERT_NE(shape, nullptr);
|
|
372
|
+
|
|
373
|
+
JPH_Plane result;
|
|
374
|
+
JPH_PlaneShape_GetPlane(shape, &result);
|
|
375
|
+
EXPECT_FLOAT_EQ(result.normal.x, 0.0f);
|
|
376
|
+
EXPECT_FLOAT_EQ(result.normal.y, 1.0f);
|
|
377
|
+
EXPECT_FLOAT_EQ(result.normal.z, 0.0f);
|
|
378
|
+
EXPECT_FLOAT_EQ(result.distance, 0.0f);
|
|
379
|
+
|
|
380
|
+
float halfExtent = JPH_PlaneShape_GetHalfExtent(shape);
|
|
381
|
+
EXPECT_FLOAT_EQ(halfExtent, 1000.0f);
|
|
382
|
+
|
|
383
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// ============================================================================
|
|
387
|
+
// Shape Common API Tests
|
|
388
|
+
// ============================================================================
|
|
389
|
+
|
|
390
|
+
TEST_F(ShapesTest, Shape_UserData) {
|
|
391
|
+
JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
|
|
392
|
+
ASSERT_NE(shape, nullptr);
|
|
393
|
+
|
|
394
|
+
JPH_Shape_SetUserData((JPH_Shape*)shape, 12345);
|
|
395
|
+
uint64_t userData = JPH_Shape_GetUserData((const JPH_Shape*)shape);
|
|
396
|
+
EXPECT_EQ(userData, 12345u);
|
|
397
|
+
|
|
398
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
TEST_F(ShapesTest, Shape_GetLocalBounds) {
|
|
402
|
+
JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
|
|
403
|
+
ASSERT_NE(shape, nullptr);
|
|
404
|
+
|
|
405
|
+
JPH_AABox bounds;
|
|
406
|
+
JPH_Shape_GetLocalBounds((const JPH_Shape*)shape, &bounds);
|
|
407
|
+
|
|
408
|
+
// Sphere of radius 1 should have bounds from (-1,-1,-1) to (1,1,1)
|
|
409
|
+
EXPECT_FLOAT_EQ(bounds.min.x, -1.0f);
|
|
410
|
+
EXPECT_FLOAT_EQ(bounds.min.y, -1.0f);
|
|
411
|
+
EXPECT_FLOAT_EQ(bounds.min.z, -1.0f);
|
|
412
|
+
EXPECT_FLOAT_EQ(bounds.max.x, 1.0f);
|
|
413
|
+
EXPECT_FLOAT_EQ(bounds.max.y, 1.0f);
|
|
414
|
+
EXPECT_FLOAT_EQ(bounds.max.z, 1.0f);
|
|
415
|
+
|
|
416
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
TEST_F(ShapesTest, Shape_GetCenterOfMass) {
|
|
420
|
+
JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
|
|
421
|
+
ASSERT_NE(shape, nullptr);
|
|
422
|
+
|
|
423
|
+
JPH_Vec3 com;
|
|
424
|
+
JPH_Shape_GetCenterOfMass((const JPH_Shape*)shape, &com);
|
|
425
|
+
|
|
426
|
+
// Center of mass for sphere is at origin
|
|
427
|
+
EXPECT_FLOAT_EQ(com.x, 0.0f);
|
|
428
|
+
EXPECT_FLOAT_EQ(com.y, 0.0f);
|
|
429
|
+
EXPECT_FLOAT_EQ(com.z, 0.0f);
|
|
430
|
+
|
|
431
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
TEST_F(ShapesTest, Shape_GetInnerRadius) {
|
|
435
|
+
JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
|
|
436
|
+
ASSERT_NE(shape, nullptr);
|
|
437
|
+
|
|
438
|
+
float innerRadius = JPH_Shape_GetInnerRadius((const JPH_Shape*)shape);
|
|
439
|
+
EXPECT_FLOAT_EQ(innerRadius, 1.0f);
|
|
440
|
+
|
|
441
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
TEST_F(ShapesTest, Shape_GetMassProperties) {
|
|
445
|
+
JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
|
|
446
|
+
ASSERT_NE(shape, nullptr);
|
|
447
|
+
|
|
448
|
+
JPH_MassProperties massProps;
|
|
449
|
+
JPH_Shape_GetMassProperties((const JPH_Shape*)shape, &massProps);
|
|
450
|
+
|
|
451
|
+
// Mass should be positive (depends on density)
|
|
452
|
+
EXPECT_GT(massProps.mass, 0.0f);
|
|
453
|
+
|
|
454
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
TEST_F(ShapesTest, Shape_MustBeStatic) {
|
|
458
|
+
// Regular sphere doesn't need to be static
|
|
459
|
+
JPH_SphereShape* sphere = JPH_SphereShape_Create(1.0f);
|
|
460
|
+
EXPECT_FALSE(JPH_Shape_MustBeStatic((const JPH_Shape*)sphere));
|
|
461
|
+
JPH_Shape_Destroy((JPH_Shape*)sphere);
|
|
462
|
+
|
|
463
|
+
// Plane must be static
|
|
464
|
+
JPH_Plane plane = {{0.0f, 1.0f, 0.0f}, 0.0f};
|
|
465
|
+
JPH_PlaneShape* planeShape = JPH_PlaneShape_Create(&plane, nullptr, 1000.0f);
|
|
466
|
+
EXPECT_TRUE(JPH_Shape_MustBeStatic((const JPH_Shape*)planeShape));
|
|
467
|
+
JPH_Shape_Destroy((JPH_Shape*)planeShape);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// ============================================================================
|
|
471
|
+
// Convex Shape Density Tests
|
|
472
|
+
// ============================================================================
|
|
473
|
+
|
|
474
|
+
TEST_F(ShapesTest, ConvexShape_Density) {
|
|
475
|
+
JPH_SphereShapeSettings* settings = JPH_SphereShapeSettings_Create(1.0f);
|
|
476
|
+
ASSERT_NE(settings, nullptr);
|
|
477
|
+
|
|
478
|
+
// Get default density
|
|
479
|
+
float defaultDensity = JPH_ConvexShapeSettings_GetDensity((const JPH_ConvexShapeSettings*)settings);
|
|
480
|
+
EXPECT_GT(defaultDensity, 0.0f);
|
|
481
|
+
|
|
482
|
+
// Set new density
|
|
483
|
+
JPH_ConvexShapeSettings_SetDensity((JPH_ConvexShapeSettings*)settings, 2000.0f);
|
|
484
|
+
float newDensity = JPH_ConvexShapeSettings_GetDensity((const JPH_ConvexShapeSettings*)settings);
|
|
485
|
+
EXPECT_FLOAT_EQ(newDensity, 2000.0f);
|
|
486
|
+
|
|
487
|
+
JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
TEST_F(ShapesTest, ConvexShape_SetDensity) {
|
|
491
|
+
JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
|
|
492
|
+
ASSERT_NE(shape, nullptr);
|
|
493
|
+
|
|
494
|
+
// Get default density
|
|
495
|
+
float defaultDensity = JPH_ConvexShape_GetDensity((const JPH_ConvexShape*)shape);
|
|
496
|
+
EXPECT_GT(defaultDensity, 0.0f);
|
|
497
|
+
|
|
498
|
+
// Set new density
|
|
499
|
+
JPH_ConvexShape_SetDensity((JPH_ConvexShape*)shape, 5000.0f);
|
|
500
|
+
float newDensity = JPH_ConvexShape_GetDensity((const JPH_ConvexShape*)shape);
|
|
501
|
+
EXPECT_FLOAT_EQ(newDensity, 5000.0f);
|
|
502
|
+
|
|
503
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// ============================================================================
|
|
507
|
+
// ShapeSettings UserData Tests
|
|
508
|
+
// ============================================================================
|
|
509
|
+
|
|
510
|
+
TEST_F(ShapesTest, ShapeSettings_UserData) {
|
|
511
|
+
JPH_SphereShapeSettings* settings = JPH_SphereShapeSettings_Create(1.0f);
|
|
512
|
+
ASSERT_NE(settings, nullptr);
|
|
513
|
+
|
|
514
|
+
JPH_ShapeSettings_SetUserData((JPH_ShapeSettings*)settings, 99999);
|
|
515
|
+
uint64_t userData = JPH_ShapeSettings_GetUserData((const JPH_ShapeSettings*)settings);
|
|
516
|
+
EXPECT_EQ(userData, 99999u);
|
|
517
|
+
|
|
518
|
+
JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// ============================================================================
|
|
522
|
+
// Decorated Shape Tests
|
|
523
|
+
// ============================================================================
|
|
524
|
+
|
|
525
|
+
TEST_F(ShapesTest, RotatedTranslatedShape_Create) {
|
|
526
|
+
JPH_SphereShape* innerShape = JPH_SphereShape_Create(1.0f);
|
|
527
|
+
ASSERT_NE(innerShape, nullptr);
|
|
528
|
+
|
|
529
|
+
JPH_Vec3 position = {1.0f, 2.0f, 3.0f};
|
|
530
|
+
JPH_Quat rotation = {0.0f, 0.0f, 0.0f, 1.0f}; // Identity
|
|
531
|
+
|
|
532
|
+
JPH_RotatedTranslatedShape* shape = JPH_RotatedTranslatedShape_Create(&position, &rotation, (const JPH_Shape*)innerShape);
|
|
533
|
+
ASSERT_NE(shape, nullptr);
|
|
534
|
+
|
|
535
|
+
JPH_Vec3 resultPos;
|
|
536
|
+
JPH_RotatedTranslatedShape_GetPosition(shape, &resultPos);
|
|
537
|
+
EXPECT_FLOAT_EQ(resultPos.x, 1.0f);
|
|
538
|
+
EXPECT_FLOAT_EQ(resultPos.y, 2.0f);
|
|
539
|
+
EXPECT_FLOAT_EQ(resultPos.z, 3.0f);
|
|
540
|
+
|
|
541
|
+
JPH_Quat resultRot;
|
|
542
|
+
JPH_RotatedTranslatedShape_GetRotation(shape, &resultRot);
|
|
543
|
+
EXPECT_FLOAT_EQ(resultRot.w, 1.0f);
|
|
544
|
+
|
|
545
|
+
// Get inner shape
|
|
546
|
+
const JPH_Shape* inner = JPH_DecoratedShape_GetInnerShape((const JPH_DecoratedShape*)shape);
|
|
547
|
+
EXPECT_EQ(inner, (const JPH_Shape*)innerShape);
|
|
548
|
+
|
|
549
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
550
|
+
// Note: innerShape is owned by the decorated shape and will be destroyed with it
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
TEST_F(ShapesTest, ScaledShape_Create) {
|
|
554
|
+
JPH_SphereShape* innerShape = JPH_SphereShape_Create(1.0f);
|
|
555
|
+
ASSERT_NE(innerShape, nullptr);
|
|
556
|
+
|
|
557
|
+
JPH_Vec3 scale = {2.0f, 2.0f, 2.0f};
|
|
558
|
+
JPH_ScaledShape* shape = JPH_ScaledShape_Create((const JPH_Shape*)innerShape, &scale);
|
|
559
|
+
ASSERT_NE(shape, nullptr);
|
|
560
|
+
|
|
561
|
+
JPH_Vec3 resultScale;
|
|
562
|
+
JPH_ScaledShape_GetScale(shape, &resultScale);
|
|
563
|
+
EXPECT_FLOAT_EQ(resultScale.x, 2.0f);
|
|
564
|
+
EXPECT_FLOAT_EQ(resultScale.y, 2.0f);
|
|
565
|
+
EXPECT_FLOAT_EQ(resultScale.z, 2.0f);
|
|
566
|
+
|
|
567
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
TEST_F(ShapesTest, OffsetCenterOfMassShape_Create) {
|
|
571
|
+
JPH_SphereShape* innerShape = JPH_SphereShape_Create(1.0f);
|
|
572
|
+
ASSERT_NE(innerShape, nullptr);
|
|
573
|
+
|
|
574
|
+
JPH_Vec3 offset = {0.0f, 1.0f, 0.0f};
|
|
575
|
+
JPH_OffsetCenterOfMassShape* shape = JPH_OffsetCenterOfMassShape_Create(&offset, (const JPH_Shape*)innerShape);
|
|
576
|
+
ASSERT_NE(shape, nullptr);
|
|
577
|
+
|
|
578
|
+
JPH_Vec3 resultOffset;
|
|
579
|
+
JPH_OffsetCenterOfMassShape_GetOffset(shape, &resultOffset);
|
|
580
|
+
EXPECT_FLOAT_EQ(resultOffset.x, 0.0f);
|
|
581
|
+
EXPECT_FLOAT_EQ(resultOffset.y, 1.0f);
|
|
582
|
+
EXPECT_FLOAT_EQ(resultOffset.z, 0.0f);
|
|
583
|
+
|
|
584
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// ============================================================================
|
|
588
|
+
// Compound Shape Tests
|
|
589
|
+
// ============================================================================
|
|
590
|
+
|
|
591
|
+
TEST_F(ShapesTest, StaticCompoundShape_Create) {
|
|
592
|
+
JPH_StaticCompoundShapeSettings* settings = JPH_StaticCompoundShapeSettings_Create();
|
|
593
|
+
ASSERT_NE(settings, nullptr);
|
|
594
|
+
|
|
595
|
+
// Create two sphere shapes
|
|
596
|
+
JPH_SphereShape* sphere1 = JPH_SphereShape_Create(1.0f);
|
|
597
|
+
JPH_SphereShape* sphere2 = JPH_SphereShape_Create(0.5f);
|
|
598
|
+
|
|
599
|
+
JPH_Vec3 pos1 = {-1.0f, 0.0f, 0.0f};
|
|
600
|
+
JPH_Vec3 pos2 = {1.0f, 0.0f, 0.0f};
|
|
601
|
+
JPH_Quat rot = {0.0f, 0.0f, 0.0f, 1.0f};
|
|
602
|
+
|
|
603
|
+
JPH_CompoundShapeSettings_AddShape2((JPH_CompoundShapeSettings*)settings, &pos1, &rot, (const JPH_Shape*)sphere1, 0);
|
|
604
|
+
JPH_CompoundShapeSettings_AddShape2((JPH_CompoundShapeSettings*)settings, &pos2, &rot, (const JPH_Shape*)sphere2, 0);
|
|
605
|
+
|
|
606
|
+
JPH_StaticCompoundShape* shape = JPH_StaticCompoundShape_Create(settings);
|
|
607
|
+
ASSERT_NE(shape, nullptr);
|
|
608
|
+
|
|
609
|
+
uint32_t numSubShapes = JPH_CompoundShape_GetNumSubShapes((const JPH_CompoundShape*)shape);
|
|
610
|
+
EXPECT_EQ(numSubShapes, 2u);
|
|
611
|
+
|
|
612
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
613
|
+
JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
TEST_F(ShapesTest, MutableCompoundShape_Create) {
|
|
617
|
+
JPH_MutableCompoundShapeSettings* settings = JPH_MutableCompoundShapeSettings_Create();
|
|
618
|
+
ASSERT_NE(settings, nullptr);
|
|
619
|
+
|
|
620
|
+
JPH_MutableCompoundShape* shape = JPH_MutableCompoundShape_Create(settings);
|
|
621
|
+
ASSERT_NE(shape, nullptr);
|
|
622
|
+
|
|
623
|
+
// Add a sphere
|
|
624
|
+
JPH_SphereShape* sphere = JPH_SphereShape_Create(1.0f);
|
|
625
|
+
JPH_Vec3 pos = {0.0f, 0.0f, 0.0f};
|
|
626
|
+
JPH_Quat rot = {0.0f, 0.0f, 0.0f, 1.0f};
|
|
627
|
+
|
|
628
|
+
uint32_t index = JPH_MutableCompoundShape_AddShape(shape, &pos, &rot, (const JPH_Shape*)sphere, 0, UINT32_MAX);
|
|
629
|
+
EXPECT_EQ(index, 0u);
|
|
630
|
+
|
|
631
|
+
uint32_t numSubShapes = JPH_CompoundShape_GetNumSubShapes((const JPH_CompoundShape*)shape);
|
|
632
|
+
EXPECT_EQ(numSubShapes, 1u);
|
|
633
|
+
|
|
634
|
+
// Remove the shape
|
|
635
|
+
JPH_MutableCompoundShape_RemoveShape(shape, 0);
|
|
636
|
+
numSubShapes = JPH_CompoundShape_GetNumSubShapes((const JPH_CompoundShape*)shape);
|
|
637
|
+
EXPECT_EQ(numSubShapes, 0u);
|
|
638
|
+
|
|
639
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
640
|
+
JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// ============================================================================
|
|
644
|
+
// EmptyShape Tests
|
|
645
|
+
// ============================================================================
|
|
646
|
+
|
|
647
|
+
TEST_F(ShapesTest, EmptyShape_Create) {
|
|
648
|
+
JPH_Vec3 centerOfMass = {0.0f, 0.0f, 0.0f};
|
|
649
|
+
JPH_EmptyShapeSettings* settings = JPH_EmptyShapeSettings_Create(¢erOfMass);
|
|
650
|
+
ASSERT_NE(settings, nullptr);
|
|
651
|
+
|
|
652
|
+
JPH_EmptyShape* shape = JPH_EmptyShapeSettings_CreateShape(settings);
|
|
653
|
+
ASSERT_NE(shape, nullptr);
|
|
654
|
+
|
|
655
|
+
// Empty shape should have zero volume
|
|
656
|
+
float volume = JPH_Shape_GetVolume((const JPH_Shape*)shape);
|
|
657
|
+
EXPECT_FLOAT_EQ(volume, 0.0f);
|
|
658
|
+
|
|
659
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
660
|
+
JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// ============================================================================
|
|
664
|
+
// Shape RayCast Tests
|
|
665
|
+
// ============================================================================
|
|
666
|
+
|
|
667
|
+
TEST_F(ShapesTest, Shape_CastRay) {
|
|
668
|
+
JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
|
|
669
|
+
ASSERT_NE(shape, nullptr);
|
|
670
|
+
|
|
671
|
+
// Cast ray from outside the sphere toward its center
|
|
672
|
+
JPH_Vec3 origin = {-5.0f, 0.0f, 0.0f};
|
|
673
|
+
JPH_Vec3 direction = {10.0f, 0.0f, 0.0f}; // Direction, not normalized
|
|
674
|
+
JPH_RayCastResult hit;
|
|
675
|
+
|
|
676
|
+
bool hasHit = JPH_Shape_CastRay((const JPH_Shape*)shape, &origin, &direction, &hit);
|
|
677
|
+
EXPECT_TRUE(hasHit);
|
|
678
|
+
|
|
679
|
+
// The hit should be at fraction ~0.4 (when ray hits sphere at x=-1)
|
|
680
|
+
// fraction = (5-1)/10 = 0.4
|
|
681
|
+
EXPECT_NEAR(hit.fraction, 0.4f, 0.01f);
|
|
682
|
+
|
|
683
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
TEST_F(ShapesTest, Shape_CastRay_Miss) {
|
|
687
|
+
JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
|
|
688
|
+
ASSERT_NE(shape, nullptr);
|
|
689
|
+
|
|
690
|
+
// Cast ray that misses the sphere
|
|
691
|
+
JPH_Vec3 origin = {0.0f, 5.0f, 0.0f};
|
|
692
|
+
JPH_Vec3 direction = {1.0f, 0.0f, 0.0f}; // Parallel to X axis, above sphere
|
|
693
|
+
JPH_RayCastResult hit;
|
|
694
|
+
|
|
695
|
+
bool hasHit = JPH_Shape_CastRay((const JPH_Shape*)shape, &origin, &direction, &hit);
|
|
696
|
+
EXPECT_FALSE(hasHit);
|
|
697
|
+
|
|
698
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// ============================================================================
|
|
702
|
+
// Shape CollidePoint Tests
|
|
703
|
+
// ============================================================================
|
|
704
|
+
|
|
705
|
+
TEST_F(ShapesTest, Shape_CollidePoint_Inside) {
|
|
706
|
+
JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
|
|
707
|
+
ASSERT_NE(shape, nullptr);
|
|
708
|
+
|
|
709
|
+
// Point inside sphere
|
|
710
|
+
JPH_Vec3 point = {0.0f, 0.0f, 0.0f};
|
|
711
|
+
bool isInside = JPH_Shape_CollidePoint((const JPH_Shape*)shape, &point, nullptr);
|
|
712
|
+
EXPECT_TRUE(isInside);
|
|
713
|
+
|
|
714
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
TEST_F(ShapesTest, Shape_CollidePoint_Outside) {
|
|
718
|
+
JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
|
|
719
|
+
ASSERT_NE(shape, nullptr);
|
|
720
|
+
|
|
721
|
+
// Point outside sphere
|
|
722
|
+
JPH_Vec3 point = {5.0f, 0.0f, 0.0f};
|
|
723
|
+
bool isInside = JPH_Shape_CollidePoint((const JPH_Shape*)shape, &point, nullptr);
|
|
724
|
+
EXPECT_FALSE(isInside);
|
|
725
|
+
|
|
726
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// ============================================================================
|
|
730
|
+
// TaperedCylinderShape Tests
|
|
731
|
+
// ============================================================================
|
|
732
|
+
|
|
733
|
+
TEST_F(ShapesTest, TaperedCylinderShape_Create) {
|
|
734
|
+
JPH_TaperedCylinderShapeSettings* settings = JPH_TaperedCylinderShapeSettings_Create(
|
|
735
|
+
1.0f, // halfHeight
|
|
736
|
+
0.5f, // topRadius
|
|
737
|
+
1.0f, // bottomRadius
|
|
738
|
+
JPH_DEFAULT_CONVEX_RADIUS,
|
|
739
|
+
nullptr
|
|
740
|
+
);
|
|
741
|
+
ASSERT_NE(settings, nullptr);
|
|
742
|
+
|
|
743
|
+
JPH_TaperedCylinderShape* shape = JPH_TaperedCylinderShapeSettings_CreateShape(settings);
|
|
744
|
+
ASSERT_NE(shape, nullptr);
|
|
745
|
+
|
|
746
|
+
float topRadius = JPH_TaperedCylinderShape_GetTopRadius(shape);
|
|
747
|
+
float bottomRadius = JPH_TaperedCylinderShape_GetBottomRadius(shape);
|
|
748
|
+
float halfHeight = JPH_TaperedCylinderShape_GetHalfHeight(shape);
|
|
749
|
+
|
|
750
|
+
EXPECT_FLOAT_EQ(topRadius, 0.5f);
|
|
751
|
+
EXPECT_FLOAT_EQ(bottomRadius, 1.0f);
|
|
752
|
+
EXPECT_FLOAT_EQ(halfHeight, 1.0f);
|
|
753
|
+
|
|
754
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
755
|
+
JPH_ShapeSettings_Destroy((JPH_ShapeSettings*)settings);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// ============================================================================
|
|
759
|
+
// Shape Scale Validation Tests
|
|
760
|
+
// ============================================================================
|
|
761
|
+
|
|
762
|
+
TEST_F(ShapesTest, Shape_IsValidScale) {
|
|
763
|
+
JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
|
|
764
|
+
ASSERT_NE(shape, nullptr);
|
|
765
|
+
|
|
766
|
+
// Uniform scale is valid for sphere
|
|
767
|
+
JPH_Vec3 uniformScale = {2.0f, 2.0f, 2.0f};
|
|
768
|
+
EXPECT_TRUE(JPH_Shape_IsValidScale((const JPH_Shape*)shape, &uniformScale));
|
|
769
|
+
|
|
770
|
+
// Non-uniform scale is NOT valid for sphere (spheres only support uniform scale)
|
|
771
|
+
JPH_Vec3 nonUniformScale = {2.0f, 1.0f, 1.0f};
|
|
772
|
+
EXPECT_FALSE(JPH_Shape_IsValidScale((const JPH_Shape*)shape, &nonUniformScale));
|
|
773
|
+
|
|
774
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
TEST_F(ShapesTest, Shape_MakeScaleValid) {
|
|
778
|
+
JPH_SphereShape* shape = JPH_SphereShape_Create(1.0f);
|
|
779
|
+
ASSERT_NE(shape, nullptr);
|
|
780
|
+
|
|
781
|
+
JPH_Vec3 nonUniformScale = {2.0f, 1.0f, 1.0f};
|
|
782
|
+
JPH_Vec3 result;
|
|
783
|
+
JPH_Shape_MakeScaleValid((const JPH_Shape*)shape, &nonUniformScale, &result);
|
|
784
|
+
|
|
785
|
+
// Result should be a valid (uniform) scale
|
|
786
|
+
EXPECT_TRUE(JPH_Shape_IsValidScale((const JPH_Shape*)shape, &result));
|
|
787
|
+
|
|
788
|
+
JPH_Shape_Destroy((JPH_Shape*)shape);
|
|
789
|
+
}
|