jolt_height_map_shape_3d.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  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. #include "jolt_height_map_shape_3d.h"
  31. #include "../jolt_project_settings.h"
  32. #include "../misc/jolt_type_conversions.h"
  33. #include "Jolt/Physics/Collision/Shape/HeightFieldShape.h"
  34. #include "Jolt/Physics/Collision/Shape/MeshShape.h"
  35. namespace {
  36. bool _is_vertex_hole(const JPH::VertexList &p_vertices, int p_index) {
  37. const float height = p_vertices[(size_t)p_index].y;
  38. return height == FLT_MAX || Math::is_nan(height);
  39. }
  40. bool _is_triangle_hole(const JPH::VertexList &p_vertices, int p_index0, int p_index1, int p_index2) {
  41. return _is_vertex_hole(p_vertices, p_index0) || _is_vertex_hole(p_vertices, p_index1) || _is_vertex_hole(p_vertices, p_index2);
  42. }
  43. } // namespace
  44. JPH::ShapeRefC JoltHeightMapShape3D::_build() const {
  45. const int height_count = (int)heights.size();
  46. if (unlikely(height_count == 0)) {
  47. return nullptr;
  48. }
  49. 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()));
  50. 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()));
  51. if (width != depth) {
  52. return JoltShape3D::with_double_sided(_build_mesh(), true);
  53. }
  54. const int block_size = 2; // Default of JPH::HeightFieldShapeSettings::mBlockSize
  55. const int block_count = width / block_size;
  56. if (block_count < 2) {
  57. return JoltShape3D::with_double_sided(_build_mesh(), true);
  58. }
  59. return JoltShape3D::with_double_sided(_build_height_field(), true);
  60. }
  61. JPH::ShapeRefC JoltHeightMapShape3D::_build_height_field() const {
  62. const int quad_count_x = width - 1;
  63. const int quad_count_y = depth - 1;
  64. const float offset_x = (float)-quad_count_x / 2.0f;
  65. const float offset_y = (float)-quad_count_y / 2.0f;
  66. // Jolt triangulates the height map differently from how Godot Physics does it, so we mirror the shape along the
  67. // Z-axis to get the desired triangulation and reverse the rows to undo the mirroring.
  68. LocalVector<float> heights_rev;
  69. heights_rev.resize(heights.size());
  70. const real_t *heights_ptr = heights.ptr();
  71. float *heights_rev_ptr = heights_rev.ptr();
  72. for (int z = 0; z < depth; ++z) {
  73. const int z_rev = (depth - 1) - z;
  74. const real_t *row = heights_ptr + ptrdiff_t(z * width);
  75. float *row_rev = heights_rev_ptr + ptrdiff_t(z_rev * width);
  76. for (int x = 0; x < width; ++x) {
  77. const real_t height = row[x];
  78. // Godot has undocumented (accidental?) support for holes by passing NaN as the height value, whereas Jolt
  79. // uses `FLT_MAX` instead, so we translate any NaN to `FLT_MAX` in order to be drop-in compatible.
  80. row_rev[x] = Math::is_nan(height) ? FLT_MAX : (float)height;
  81. }
  82. }
  83. JPH::HeightFieldShapeSettings shape_settings(heights_rev.ptr(), JPH::Vec3(offset_x, 0, offset_y), JPH::Vec3::sReplicate(1.0f), (JPH::uint32)width);
  84. shape_settings.mBitsPerSample = shape_settings.CalculateBitsPerSampleForError(0.0f);
  85. shape_settings.mActiveEdgeCosThresholdAngle = JoltProjectSettings::get_active_edge_threshold();
  86. const JPH::ShapeSettings::ShapeResult shape_result = shape_settings.Create();
  87. 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()));
  88. return with_scale(shape_result.Get(), Vector3(1, 1, -1));
  89. }
  90. JPH::ShapeRefC JoltHeightMapShape3D::_build_mesh() const {
  91. const int height_count = (int)heights.size();
  92. const int quad_count_x = width - 1;
  93. const int quad_count_z = depth - 1;
  94. const int quad_count = quad_count_x * quad_count_z;
  95. const int triangle_count = quad_count * 2;
  96. JPH::VertexList vertices;
  97. vertices.reserve((size_t)height_count);
  98. JPH::IndexedTriangleList indices;
  99. indices.reserve((size_t)triangle_count);
  100. const float offset_x = (float)-quad_count_x / 2.0f;
  101. const float offset_z = (float)-quad_count_z / 2.0f;
  102. for (int z = 0; z < depth; ++z) {
  103. for (int x = 0; x < width; ++x) {
  104. const float vertex_x = offset_x + (float)x;
  105. const float vertex_y = (float)heights[z * width + x];
  106. const float vertex_z = offset_z + (float)z;
  107. vertices.emplace_back(vertex_x, vertex_y, vertex_z);
  108. }
  109. }
  110. for (int z = 0; z < quad_count_z; ++z) {
  111. for (int x = 0; x < quad_count_x; ++x) {
  112. const int index_lower_right = z * width + x;
  113. const int index_lower_left = z * width + (x + 1);
  114. const int index_upper_right = (z + 1) * width + x;
  115. const int index_upper_left = (z + 1) * width + (x + 1);
  116. if (!_is_triangle_hole(vertices, index_lower_right, index_upper_right, index_lower_left)) {
  117. indices.emplace_back(index_lower_right, index_upper_right, index_lower_left);
  118. }
  119. if (!_is_triangle_hole(vertices, index_lower_left, index_upper_right, index_upper_left)) {
  120. indices.emplace_back(index_lower_left, index_upper_right, index_upper_left);
  121. }
  122. }
  123. }
  124. JPH::MeshShapeSettings shape_settings(std::move(vertices), std::move(indices));
  125. shape_settings.mActiveEdgeCosThresholdAngle = JoltProjectSettings::get_active_edge_threshold();
  126. const JPH::ShapeSettings::ShapeResult shape_result = shape_settings.Create();
  127. 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()));
  128. return shape_result.Get();
  129. }
  130. AABB JoltHeightMapShape3D::_calculate_aabb() const {
  131. AABB result;
  132. const int quad_count_x = width - 1;
  133. const int quad_count_z = depth - 1;
  134. const float offset_x = (float)-quad_count_x / 2.0f;
  135. const float offset_z = (float)-quad_count_z / 2.0f;
  136. for (int z = 0; z < depth; ++z) {
  137. for (int x = 0; x < width; ++x) {
  138. const Vector3 vertex(offset_x + (float)x, (float)heights[z * width + x], offset_z + (float)z);
  139. if (x == 0 && z == 0) {
  140. result.position = vertex;
  141. } else {
  142. result.expand_to(vertex);
  143. }
  144. }
  145. }
  146. return result;
  147. }
  148. Variant JoltHeightMapShape3D::get_data() const {
  149. Dictionary data;
  150. data["width"] = width;
  151. data["depth"] = depth;
  152. data["heights"] = heights;
  153. return data;
  154. }
  155. void JoltHeightMapShape3D::set_data(const Variant &p_data) {
  156. ERR_FAIL_COND(p_data.get_type() != Variant::DICTIONARY);
  157. const Dictionary data = p_data;
  158. const Variant maybe_heights = data.get("heights", Variant());
  159. #ifdef REAL_T_IS_DOUBLE
  160. ERR_FAIL_COND(maybe_heights.get_type() != Variant::PACKED_FLOAT64_ARRAY);
  161. #else
  162. ERR_FAIL_COND(maybe_heights.get_type() != Variant::PACKED_FLOAT32_ARRAY);
  163. #endif
  164. const Variant maybe_width = data.get("width", Variant());
  165. ERR_FAIL_COND(maybe_width.get_type() != Variant::INT);
  166. const Variant maybe_depth = data.get("depth", Variant());
  167. ERR_FAIL_COND(maybe_depth.get_type() != Variant::INT);
  168. heights = maybe_heights;
  169. width = maybe_width;
  170. depth = maybe_depth;
  171. aabb = _calculate_aabb();
  172. destroy();
  173. }
  174. String JoltHeightMapShape3D::to_string() const {
  175. return vformat("{height_count=%d width=%d depth=%d}", heights.size(), width, depth);
  176. }