Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/tests/scene/test_arraymesh.h
10277 views
1
/**************************************************************************/
2
/* test_arraymesh.h */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#pragma once
32
33
#include "scene/resources/3d/primitive_meshes.h"
34
#include "scene/resources/mesh.h"
35
36
#include "tests/test_macros.h"
37
38
namespace TestArrayMesh {
39
40
TEST_CASE("[SceneTree][ArrayMesh] Adding and modifying blendshapes.") {
41
Ref<ArrayMesh> mesh;
42
mesh.instantiate();
43
StringName name_a{ "ShapeA" };
44
StringName name_b{ "ShapeB" };
45
46
SUBCASE("Adding a blend shape to the mesh before a surface is added.") {
47
mesh->add_blend_shape(name_a);
48
mesh->add_blend_shape(name_b);
49
50
CHECK(mesh->get_blend_shape_name(0) == name_a);
51
CHECK(mesh->get_blend_shape_name(1) == name_b);
52
}
53
54
SUBCASE("Add same blend shape multiple times appends name with number.") {
55
mesh->add_blend_shape(name_a);
56
mesh->add_blend_shape(name_a);
57
mesh->add_blend_shape(name_a);
58
59
CHECK(mesh->get_blend_shape_name(0) == "ShapeA");
60
bool all_different = (static_cast<String>(mesh->get_blend_shape_name(0)) != static_cast<String>(mesh->get_blend_shape_name(1))) &&
61
(static_cast<String>(mesh->get_blend_shape_name(1)) != static_cast<String>(mesh->get_blend_shape_name(2))) &&
62
(static_cast<String>(mesh->get_blend_shape_name(0)) != static_cast<String>(mesh->get_blend_shape_name(2)));
63
bool all_have_name = static_cast<String>(mesh->get_blend_shape_name(1)).contains("ShapeA") &&
64
static_cast<String>(mesh->get_blend_shape_name(2)).contains("ShapeA");
65
CHECK((all_different && all_have_name));
66
}
67
68
SUBCASE("ArrayMesh keeps correct count of number of blend shapes") {
69
mesh->add_blend_shape(name_a);
70
mesh->add_blend_shape(name_a);
71
mesh->add_blend_shape(name_b);
72
mesh->add_blend_shape(name_b);
73
mesh->add_blend_shape(name_b);
74
75
REQUIRE(mesh->get_blend_shape_count() == 5);
76
}
77
78
SUBCASE("Adding blend shape after surface is added causes error") {
79
Ref<CylinderMesh> cylinder;
80
cylinder.instantiate();
81
Array cylinder_array;
82
cylinder_array.resize(Mesh::ARRAY_MAX);
83
cylinder->create_mesh_array(cylinder_array, 3.f, 3.f, 5.f);
84
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array);
85
86
ERR_PRINT_OFF
87
mesh->add_blend_shape(name_a);
88
ERR_PRINT_ON
89
CHECK(mesh->get_blend_shape_count() == 0);
90
}
91
92
SUBCASE("Adding blend shapes once all surfaces have been removed is allowed") {
93
Ref<CylinderMesh> cylinder;
94
cylinder.instantiate();
95
Array cylinder_array;
96
cylinder_array.resize(Mesh::ARRAY_MAX);
97
cylinder->create_mesh_array(cylinder_array, 3.f, 3.f, 5.f);
98
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array);
99
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array);
100
101
mesh->surface_remove(0);
102
ERR_PRINT_OFF
103
mesh->add_blend_shape(name_a);
104
ERR_PRINT_ON
105
CHECK(mesh->get_blend_shape_count() == 0);
106
107
mesh->surface_remove(0);
108
mesh->add_blend_shape(name_a);
109
CHECK(mesh->get_blend_shape_count() == 1);
110
}
111
112
SUBCASE("Change blend shape name after adding.") {
113
mesh->add_blend_shape(name_a);
114
mesh->set_blend_shape_name(0, name_b);
115
116
CHECK(mesh->get_blend_shape_name(0) == name_b);
117
}
118
119
SUBCASE("Change blend shape name to the name of one already there, should append number to end") {
120
mesh->add_blend_shape(name_a);
121
mesh->add_blend_shape(name_b);
122
mesh->set_blend_shape_name(0, name_b);
123
124
String name_string = mesh->get_blend_shape_name(0);
125
CHECK(name_string.contains("ShapeB"));
126
CHECK(name_string.length() > static_cast<String>(name_b).size());
127
}
128
129
SUBCASE("Clear all blend shapes before surface has been added.") {
130
mesh->add_blend_shape(name_a);
131
mesh->add_blend_shape(name_b);
132
CHECK(mesh->get_blend_shape_count() == 2);
133
134
mesh->clear_blend_shapes();
135
CHECK(mesh->get_blend_shape_count() == 0);
136
}
137
138
SUBCASE("Clearing all blend shapes once all surfaces have been removed is allowed") {
139
mesh->add_blend_shape(name_a);
140
mesh->add_blend_shape(name_b);
141
Ref<CylinderMesh> cylinder;
142
cylinder.instantiate();
143
Array cylinder_array;
144
cylinder_array.resize(Mesh::ARRAY_MAX);
145
cylinder->create_mesh_array(cylinder_array, 3.f, 3.f, 5.f);
146
Array blend_shape;
147
blend_shape.resize(Mesh::ARRAY_MAX);
148
blend_shape[Mesh::ARRAY_VERTEX] = cylinder_array[Mesh::ARRAY_VERTEX];
149
blend_shape[Mesh::ARRAY_NORMAL] = cylinder_array[Mesh::ARRAY_NORMAL];
150
blend_shape[Mesh::ARRAY_TANGENT] = cylinder_array[Mesh::ARRAY_TANGENT];
151
Array blend_shapes = { blend_shape, blend_shape };
152
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array, blend_shapes);
153
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array, blend_shapes);
154
155
mesh->surface_remove(0);
156
ERR_PRINT_OFF
157
mesh->clear_blend_shapes();
158
ERR_PRINT_ON
159
CHECK(mesh->get_blend_shape_count() == 2);
160
161
mesh->surface_remove(0);
162
mesh->clear_blend_shapes();
163
CHECK(mesh->get_blend_shape_count() == 0);
164
}
165
166
SUBCASE("Can't add surface with incorrect number of blend shapes.") {
167
mesh->add_blend_shape(name_a);
168
mesh->add_blend_shape(name_b);
169
Array cylinder_array;
170
ERR_PRINT_OFF
171
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array);
172
ERR_PRINT_ON
173
CHECK(mesh->get_surface_count() == 0);
174
}
175
176
SUBCASE("Can't clear blend shapes after surface had been added.") {
177
mesh->add_blend_shape(name_a);
178
mesh->add_blend_shape(name_b);
179
Ref<CylinderMesh> cylinder;
180
cylinder.instantiate();
181
Array cylinder_array;
182
cylinder_array.resize(Mesh::ARRAY_MAX);
183
cylinder->create_mesh_array(cylinder_array, 3.f, 3.f, 5.f);
184
Array blend_shape;
185
blend_shape.resize(Mesh::ARRAY_MAX);
186
blend_shape[Mesh::ARRAY_VERTEX] = cylinder_array[Mesh::ARRAY_VERTEX];
187
blend_shape[Mesh::ARRAY_NORMAL] = cylinder_array[Mesh::ARRAY_NORMAL];
188
blend_shape[Mesh::ARRAY_TANGENT] = cylinder_array[Mesh::ARRAY_TANGENT];
189
Array blend_shapes = { blend_shape, blend_shape };
190
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array, blend_shapes);
191
192
ERR_PRINT_OFF
193
mesh->clear_blend_shapes();
194
ERR_PRINT_ON
195
CHECK(mesh->get_blend_shape_count() == 2);
196
}
197
198
SUBCASE("Set the blend shape mode of ArrayMesh and underlying mesh RID.") {
199
mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_RELATIVE);
200
CHECK(mesh->get_blend_shape_mode() == Mesh::BLEND_SHAPE_MODE_RELATIVE);
201
}
202
}
203
204
TEST_CASE("[SceneTree][ArrayMesh] Surface metadata tests.") {
205
Ref<ArrayMesh> mesh;
206
mesh.instantiate();
207
Ref<CylinderMesh> cylinder;
208
cylinder.instantiate();
209
Array cylinder_array;
210
cylinder_array.resize(Mesh::ARRAY_MAX);
211
cylinder->create_mesh_array(cylinder_array, 3.f, 3.f, 5.f);
212
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array);
213
214
Ref<BoxMesh> box;
215
box.instantiate();
216
Array box_array;
217
box_array.resize(Mesh::ARRAY_MAX);
218
box->create_mesh_array(box_array, Vector3(2.f, 1.2f, 1.6f));
219
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, box_array);
220
221
SUBCASE("Add 2 surfaces and count the number of surfaces in the mesh.") {
222
REQUIRE(mesh->get_surface_count() == 2);
223
}
224
225
SUBCASE("Get the surface array from mesh.") {
226
REQUIRE(mesh->surface_get_arrays(0)[0] == cylinder_array[0]);
227
REQUIRE(mesh->surface_get_arrays(1)[0] == box_array[0]);
228
}
229
230
SUBCASE("Get the array length of a particular surface.") {
231
CHECK(mesh->surface_get_array_len(0) == static_cast<Vector<Vector3>>(cylinder_array[RenderingServer::ARRAY_VERTEX]).size());
232
CHECK(mesh->surface_get_array_len(1) == static_cast<Vector<Vector3>>(box_array[RenderingServer::ARRAY_VERTEX]).size());
233
}
234
235
SUBCASE("Get the index array length of a particular surface.") {
236
CHECK(mesh->surface_get_array_index_len(0) == static_cast<Vector<Vector3>>(cylinder_array[RenderingServer::ARRAY_INDEX]).size());
237
CHECK(mesh->surface_get_array_index_len(1) == static_cast<Vector<Vector3>>(box_array[RenderingServer::ARRAY_INDEX]).size());
238
}
239
240
SUBCASE("Get correct primitive type") {
241
CHECK(mesh->surface_get_primitive_type(0) == Mesh::PRIMITIVE_TRIANGLES);
242
CHECK(mesh->surface_get_primitive_type(1) == Mesh::PRIMITIVE_TRIANGLES);
243
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLE_STRIP, box_array);
244
CHECK(mesh->surface_get_primitive_type(2) == Mesh::PRIMITIVE_TRIANGLE_STRIP);
245
}
246
247
SUBCASE("Returns correct format for the mesh") {
248
int format = RS::ARRAY_FORMAT_BLEND_SHAPE_MASK | RS::ARRAY_FORMAT_TEX_UV | RS::ARRAY_FORMAT_INDEX;
249
CHECK((mesh->surface_get_format(0) & format) != 0);
250
CHECK((mesh->surface_get_format(1) & format) != 0);
251
}
252
253
SUBCASE("Set a surface name and retrieve it by name.") {
254
mesh->surface_set_name(0, "surf1");
255
CHECK(mesh->surface_find_by_name("surf1") == 0);
256
CHECK(mesh->surface_get_name(0) == "surf1");
257
}
258
259
SUBCASE("Set material to two different surfaces.") {
260
Ref<Material> mat;
261
mat.instantiate();
262
mesh->surface_set_material(0, mat);
263
CHECK(mesh->surface_get_material(0) == mat);
264
mesh->surface_set_material(1, mat);
265
CHECK(mesh->surface_get_material(1) == mat);
266
}
267
268
SUBCASE("Set same material multiple times doesn't change material of surface.") {
269
Ref<Material> mat;
270
mat.instantiate();
271
mesh->surface_set_material(0, mat);
272
mesh->surface_set_material(0, mat);
273
mesh->surface_set_material(0, mat);
274
CHECK(mesh->surface_get_material(0) == mat);
275
}
276
277
SUBCASE("Set material of surface then change to different material.") {
278
Ref<Material> mat1;
279
mat1.instantiate();
280
Ref<Material> mat2;
281
mat2.instantiate();
282
mesh->surface_set_material(1, mat1);
283
CHECK(mesh->surface_get_material(1) == mat1);
284
mesh->surface_set_material(1, mat2);
285
CHECK(mesh->surface_get_material(1) == mat2);
286
}
287
288
SUBCASE("Get the LOD of the mesh.") {
289
Dictionary lod;
290
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array, TypedArray<Array>(), lod);
291
CHECK(mesh->surface_get_lods(2) == lod);
292
}
293
294
SUBCASE("Get the blend shape arrays from the mesh.") {
295
TypedArray<Array> blend;
296
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array, blend);
297
CHECK(mesh->surface_get_blend_shape_arrays(2) == blend);
298
}
299
}
300
301
TEST_CASE("[SceneTree][ArrayMesh] Get/Set mesh metadata and actions") {
302
Ref<ArrayMesh> mesh;
303
mesh.instantiate();
304
Ref<CylinderMesh> cylinder;
305
cylinder.instantiate();
306
Array cylinder_array;
307
cylinder_array.resize(Mesh::ARRAY_MAX);
308
constexpr float cylinder_radius = 3.f;
309
constexpr float cylinder_height = 5.f;
310
cylinder->create_mesh_array(cylinder_array, cylinder_radius, cylinder_radius, cylinder_height);
311
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array);
312
313
Ref<BoxMesh> box;
314
box.instantiate();
315
Array box_array;
316
box_array.resize(Mesh::ARRAY_MAX);
317
const Vector3 box_size = Vector3(2.f, 1.2f, 1.6f);
318
box->create_mesh_array(box_array, box_size);
319
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, box_array);
320
321
SUBCASE("Set the shadow mesh.") {
322
Ref<ArrayMesh> shadow;
323
shadow.instantiate();
324
mesh->set_shadow_mesh(shadow);
325
CHECK(mesh->get_shadow_mesh() == shadow);
326
}
327
328
SUBCASE("Set the shadow mesh multiple times.") {
329
Ref<ArrayMesh> shadow;
330
shadow.instantiate();
331
mesh->set_shadow_mesh(shadow);
332
mesh->set_shadow_mesh(shadow);
333
mesh->set_shadow_mesh(shadow);
334
mesh->set_shadow_mesh(shadow);
335
CHECK(mesh->get_shadow_mesh() == shadow);
336
}
337
338
SUBCASE("Set the same shadow mesh on multiple meshes.") {
339
Ref<ArrayMesh> shadow;
340
shadow.instantiate();
341
Ref<ArrayMesh> mesh2;
342
mesh2.instantiate();
343
mesh->set_shadow_mesh(shadow);
344
mesh2->set_shadow_mesh(shadow);
345
346
CHECK(mesh->get_shadow_mesh() == shadow);
347
CHECK(mesh2->get_shadow_mesh() == shadow);
348
}
349
350
SUBCASE("Set the shadow mesh and then change it.") {
351
Ref<ArrayMesh> shadow;
352
shadow.instantiate();
353
mesh->set_shadow_mesh(shadow);
354
CHECK(mesh->get_shadow_mesh() == shadow);
355
Ref<ArrayMesh> shadow2;
356
shadow2.instantiate();
357
mesh->set_shadow_mesh(shadow2);
358
CHECK(mesh->get_shadow_mesh() == shadow2);
359
}
360
361
SUBCASE("Set custom AABB.") {
362
AABB bound;
363
mesh->set_custom_aabb(bound);
364
CHECK(mesh->get_custom_aabb() == bound);
365
}
366
367
SUBCASE("Set custom AABB multiple times.") {
368
AABB bound;
369
mesh->set_custom_aabb(bound);
370
mesh->set_custom_aabb(bound);
371
mesh->set_custom_aabb(bound);
372
mesh->set_custom_aabb(bound);
373
CHECK(mesh->get_custom_aabb() == bound);
374
}
375
376
SUBCASE("Set custom AABB then change to another AABB.") {
377
AABB bound;
378
AABB bound2;
379
mesh->set_custom_aabb(bound);
380
CHECK(mesh->get_custom_aabb() == bound);
381
mesh->set_custom_aabb(bound2);
382
CHECK(mesh->get_custom_aabb() == bound2);
383
}
384
385
SUBCASE("Clear all surfaces should leave zero count.") {
386
mesh->clear_surfaces();
387
CHECK(mesh->get_surface_count() == 0);
388
}
389
390
SUBCASE("Able to get correct mesh RID.") {
391
RID rid = mesh->get_rid();
392
CHECK(RS::get_singleton()->mesh_get_surface_count(rid) == 2);
393
}
394
395
SUBCASE("Create surface from raw SurfaceData data.") {
396
RID mesh_rid = mesh->get_rid();
397
RS::SurfaceData surface_data = RS::get_singleton()->mesh_get_surface(mesh_rid, 0);
398
Ref<ArrayMesh> mesh2;
399
mesh2.instantiate();
400
mesh2->add_surface(surface_data.format, Mesh::PRIMITIVE_TRIANGLES, surface_data.vertex_data, surface_data.attribute_data,
401
surface_data.skin_data, surface_data.vertex_count, surface_data.index_data, surface_data.index_count, surface_data.aabb);
402
CHECK(mesh2->get_surface_count() == 1);
403
CHECK(mesh2->surface_get_primitive_type(0) == Mesh::PRIMITIVE_TRIANGLES);
404
CHECK((mesh2->surface_get_format(0) & surface_data.format) != 0);
405
CHECK(mesh2->get_aabb().is_equal_approx(surface_data.aabb));
406
}
407
408
SUBCASE("Removing a surface decreases surface count.") {
409
REQUIRE(mesh->get_surface_count() == 2);
410
mesh->surface_remove(0);
411
CHECK(mesh->get_surface_count() == 1);
412
mesh->surface_remove(0);
413
CHECK(mesh->get_surface_count() == 0);
414
}
415
416
SUBCASE("Remove the first surface and check the mesh's AABB.") {
417
REQUIRE(mesh->get_surface_count() >= 1);
418
mesh->surface_remove(0);
419
const AABB box_aabb = AABB(-box_size / 2, box_size);
420
CHECK(mesh->get_aabb().is_equal_approx(box_aabb));
421
}
422
423
SUBCASE("Remove the last surface and check the mesh's AABB.") {
424
REQUIRE(mesh->get_surface_count() >= 1);
425
mesh->surface_remove(mesh->get_surface_count() - 1);
426
const AABB cylinder_aabb = AABB(Vector3(-cylinder_radius, -cylinder_height / 2, -cylinder_radius),
427
Vector3(2 * cylinder_radius, cylinder_height, 2 * cylinder_radius));
428
CHECK(mesh->get_aabb().is_equal_approx(cylinder_aabb));
429
}
430
431
SUBCASE("Remove all surfaces and check the mesh's AABB.") {
432
while (mesh->get_surface_count()) {
433
mesh->surface_remove(0);
434
}
435
CHECK(mesh->get_aabb() == AABB());
436
}
437
438
SUBCASE("Removing a non-existent surface causes error.") {
439
ERR_PRINT_OFF
440
mesh->surface_remove(42);
441
ERR_PRINT_ON
442
CHECK(mesh->get_surface_count() == 2);
443
}
444
}
445
446
} // namespace TestArrayMesh
447
448