Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/jolt_physics/shapes/jolt_height_map_shape_3d.cpp
10278 views
1
/**************************************************************************/
2
/* jolt_height_map_shape_3d.cpp */
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
#include "jolt_height_map_shape_3d.h"
32
33
#include "../jolt_project_settings.h"
34
#include "../misc/jolt_type_conversions.h"
35
36
#include "Jolt/Physics/Collision/Shape/HeightFieldShape.h"
37
#include "Jolt/Physics/Collision/Shape/MeshShape.h"
38
39
namespace {
40
41
bool _is_vertex_hole(const JPH::VertexList &p_vertices, int p_index) {
42
const float height = p_vertices[(size_t)p_index].y;
43
return height == FLT_MAX || Math::is_nan(height);
44
}
45
46
bool _is_triangle_hole(const JPH::VertexList &p_vertices, int p_index0, int p_index1, int p_index2) {
47
return _is_vertex_hole(p_vertices, p_index0) || _is_vertex_hole(p_vertices, p_index1) || _is_vertex_hole(p_vertices, p_index2);
48
}
49
50
} // namespace
51
52
JPH::ShapeRefC JoltHeightMapShape3D::_build() const {
53
const int height_count = (int)heights.size();
54
if (unlikely(height_count == 0)) {
55
return nullptr;
56
}
57
58
ERR_FAIL_COND_V_MSG(height_count != width * depth, nullptr, vformat("Failed to build Jolt Physics height map shape with %s. Height count must be the product of width and depth. This shape belongs to %s.", to_string(), _owners_to_string()));
59
ERR_FAIL_COND_V_MSG(width < 2 || depth < 2, nullptr, vformat("Failed to build Jolt Physics height map shape with %s. The height map must be at least 2x2. This shape belongs to %s.", to_string(), _owners_to_string()));
60
61
if (width != depth) {
62
return JoltShape3D::with_double_sided(_build_mesh(), true);
63
}
64
65
const int block_size = 2; // Default of JPH::HeightFieldShapeSettings::mBlockSize
66
const int block_count = width / block_size;
67
68
if (block_count < 2) {
69
return JoltShape3D::with_double_sided(_build_mesh(), true);
70
}
71
72
return JoltShape3D::with_double_sided(_build_height_field(), true);
73
}
74
75
JPH::ShapeRefC JoltHeightMapShape3D::_build_height_field() const {
76
const int quad_count_x = width - 1;
77
const int quad_count_y = depth - 1;
78
79
const float offset_x = (float)-quad_count_x / 2.0f;
80
const float offset_y = (float)-quad_count_y / 2.0f;
81
82
// Jolt triangulates the height map differently from how Godot Physics does it, so we mirror the shape along the
83
// Z-axis to get the desired triangulation and reverse the rows to undo the mirroring.
84
85
LocalVector<float> heights_rev;
86
heights_rev.resize(heights.size());
87
88
const real_t *heights_ptr = heights.ptr();
89
float *heights_rev_ptr = heights_rev.ptr();
90
91
for (int z = 0; z < depth; ++z) {
92
const int z_rev = (depth - 1) - z;
93
94
const real_t *row = heights_ptr + ptrdiff_t(z * width);
95
float *row_rev = heights_rev_ptr + ptrdiff_t(z_rev * width);
96
97
for (int x = 0; x < width; ++x) {
98
const real_t height = row[x];
99
100
// Godot has undocumented (accidental?) support for holes by passing NaN as the height value, whereas Jolt
101
// uses `FLT_MAX` instead, so we translate any NaN to `FLT_MAX` in order to be drop-in compatible.
102
row_rev[x] = Math::is_nan(height) ? FLT_MAX : (float)height;
103
}
104
}
105
106
JPH::HeightFieldShapeSettings shape_settings(heights_rev.ptr(), JPH::Vec3(offset_x, 0, offset_y), JPH::Vec3::sOne(), (JPH::uint32)width);
107
108
shape_settings.mBitsPerSample = shape_settings.CalculateBitsPerSampleForError(0.0f);
109
shape_settings.mActiveEdgeCosThresholdAngle = JoltProjectSettings::active_edge_threshold_cos;
110
111
const JPH::ShapeSettings::ShapeResult shape_result = shape_settings.Create();
112
ERR_FAIL_COND_V_MSG(shape_result.HasError(), nullptr, vformat("Failed to build Jolt Physics height map shape with %s. It returned the following error: '%s'. This shape belongs to %s.", to_string(), to_godot(shape_result.GetError()), _owners_to_string()));
113
114
return with_scale(shape_result.Get(), Vector3(1, 1, -1));
115
}
116
117
JPH::ShapeRefC JoltHeightMapShape3D::_build_mesh() const {
118
const int height_count = (int)heights.size();
119
120
const int quad_count_x = width - 1;
121
const int quad_count_z = depth - 1;
122
123
const int quad_count = quad_count_x * quad_count_z;
124
const int triangle_count = quad_count * 2;
125
126
JPH::VertexList vertices;
127
vertices.reserve((size_t)height_count);
128
129
JPH::IndexedTriangleList indices;
130
indices.reserve((size_t)triangle_count);
131
132
const float offset_x = (float)-quad_count_x / 2.0f;
133
const float offset_z = (float)-quad_count_z / 2.0f;
134
135
for (int z = 0; z < depth; ++z) {
136
for (int x = 0; x < width; ++x) {
137
const float vertex_x = offset_x + (float)x;
138
const float vertex_y = (float)heights[z * width + x];
139
const float vertex_z = offset_z + (float)z;
140
141
vertices.emplace_back(vertex_x, vertex_y, vertex_z);
142
}
143
}
144
145
for (int z = 0; z < quad_count_z; ++z) {
146
for (int x = 0; x < quad_count_x; ++x) {
147
const int index_lower_right = z * width + x;
148
const int index_lower_left = z * width + (x + 1);
149
const int index_upper_right = (z + 1) * width + x;
150
const int index_upper_left = (z + 1) * width + (x + 1);
151
152
if (!_is_triangle_hole(vertices, index_lower_right, index_upper_right, index_lower_left)) {
153
indices.emplace_back(index_lower_right, index_upper_right, index_lower_left);
154
}
155
156
if (!_is_triangle_hole(vertices, index_lower_left, index_upper_right, index_upper_left)) {
157
indices.emplace_back(index_lower_left, index_upper_right, index_upper_left);
158
}
159
}
160
}
161
162
JPH::MeshShapeSettings shape_settings(std::move(vertices), std::move(indices));
163
shape_settings.mActiveEdgeCosThresholdAngle = JoltProjectSettings::active_edge_threshold_cos;
164
165
const JPH::ShapeSettings::ShapeResult shape_result = shape_settings.Create();
166
ERR_FAIL_COND_V_MSG(shape_result.HasError(), nullptr, vformat("Failed to build Jolt Physics height map shape (as polygon) with %s. It returned the following error: '%s'. This shape belongs to %s.", to_string(), to_godot(shape_result.GetError()), _owners_to_string()));
167
168
return shape_result.Get();
169
}
170
171
AABB JoltHeightMapShape3D::_calculate_aabb() const {
172
AABB result;
173
174
const int quad_count_x = width - 1;
175
const int quad_count_z = depth - 1;
176
177
const float offset_x = (float)-quad_count_x / 2.0f;
178
const float offset_z = (float)-quad_count_z / 2.0f;
179
180
for (int z = 0; z < depth; ++z) {
181
for (int x = 0; x < width; ++x) {
182
const Vector3 vertex(offset_x + (float)x, (float)heights[z * width + x], offset_z + (float)z);
183
184
if (x == 0 && z == 0) {
185
result.position = vertex;
186
} else {
187
result.expand_to(vertex);
188
}
189
}
190
}
191
192
return result;
193
}
194
195
Variant JoltHeightMapShape3D::get_data() const {
196
Dictionary data;
197
data["width"] = width;
198
data["depth"] = depth;
199
data["heights"] = heights;
200
return data;
201
}
202
203
void JoltHeightMapShape3D::set_data(const Variant &p_data) {
204
ERR_FAIL_COND(p_data.get_type() != Variant::DICTIONARY);
205
206
const Dictionary data = p_data;
207
208
const Variant maybe_heights = data.get("heights", Variant());
209
210
#ifdef REAL_T_IS_DOUBLE
211
ERR_FAIL_COND(maybe_heights.get_type() != Variant::PACKED_FLOAT64_ARRAY);
212
#else
213
ERR_FAIL_COND(maybe_heights.get_type() != Variant::PACKED_FLOAT32_ARRAY);
214
#endif
215
216
const Variant maybe_width = data.get("width", Variant());
217
ERR_FAIL_COND(maybe_width.get_type() != Variant::INT);
218
219
const Variant maybe_depth = data.get("depth", Variant());
220
ERR_FAIL_COND(maybe_depth.get_type() != Variant::INT);
221
222
heights = maybe_heights;
223
width = maybe_width;
224
depth = maybe_depth;
225
226
aabb = _calculate_aabb();
227
228
destroy();
229
}
230
231
String JoltHeightMapShape3D::to_string() const {
232
return vformat("{height_count=%d width=%d depth=%d}", heights.size(), width, depth);
233
}
234
235