Path: blob/master/modules/jolt_physics/shapes/jolt_height_map_shape_3d.cpp
10278 views
/**************************************************************************/1/* jolt_height_map_shape_3d.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "jolt_height_map_shape_3d.h"3132#include "../jolt_project_settings.h"33#include "../misc/jolt_type_conversions.h"3435#include "Jolt/Physics/Collision/Shape/HeightFieldShape.h"36#include "Jolt/Physics/Collision/Shape/MeshShape.h"3738namespace {3940bool _is_vertex_hole(const JPH::VertexList &p_vertices, int p_index) {41const float height = p_vertices[(size_t)p_index].y;42return height == FLT_MAX || Math::is_nan(height);43}4445bool _is_triangle_hole(const JPH::VertexList &p_vertices, int p_index0, int p_index1, int p_index2) {46return _is_vertex_hole(p_vertices, p_index0) || _is_vertex_hole(p_vertices, p_index1) || _is_vertex_hole(p_vertices, p_index2);47}4849} // namespace5051JPH::ShapeRefC JoltHeightMapShape3D::_build() const {52const int height_count = (int)heights.size();53if (unlikely(height_count == 0)) {54return nullptr;55}5657ERR_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()));58ERR_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()));5960if (width != depth) {61return JoltShape3D::with_double_sided(_build_mesh(), true);62}6364const int block_size = 2; // Default of JPH::HeightFieldShapeSettings::mBlockSize65const int block_count = width / block_size;6667if (block_count < 2) {68return JoltShape3D::with_double_sided(_build_mesh(), true);69}7071return JoltShape3D::with_double_sided(_build_height_field(), true);72}7374JPH::ShapeRefC JoltHeightMapShape3D::_build_height_field() const {75const int quad_count_x = width - 1;76const int quad_count_y = depth - 1;7778const float offset_x = (float)-quad_count_x / 2.0f;79const float offset_y = (float)-quad_count_y / 2.0f;8081// Jolt triangulates the height map differently from how Godot Physics does it, so we mirror the shape along the82// Z-axis to get the desired triangulation and reverse the rows to undo the mirroring.8384LocalVector<float> heights_rev;85heights_rev.resize(heights.size());8687const real_t *heights_ptr = heights.ptr();88float *heights_rev_ptr = heights_rev.ptr();8990for (int z = 0; z < depth; ++z) {91const int z_rev = (depth - 1) - z;9293const real_t *row = heights_ptr + ptrdiff_t(z * width);94float *row_rev = heights_rev_ptr + ptrdiff_t(z_rev * width);9596for (int x = 0; x < width; ++x) {97const real_t height = row[x];9899// Godot has undocumented (accidental?) support for holes by passing NaN as the height value, whereas Jolt100// uses `FLT_MAX` instead, so we translate any NaN to `FLT_MAX` in order to be drop-in compatible.101row_rev[x] = Math::is_nan(height) ? FLT_MAX : (float)height;102}103}104105JPH::HeightFieldShapeSettings shape_settings(heights_rev.ptr(), JPH::Vec3(offset_x, 0, offset_y), JPH::Vec3::sOne(), (JPH::uint32)width);106107shape_settings.mBitsPerSample = shape_settings.CalculateBitsPerSampleForError(0.0f);108shape_settings.mActiveEdgeCosThresholdAngle = JoltProjectSettings::active_edge_threshold_cos;109110const JPH::ShapeSettings::ShapeResult shape_result = shape_settings.Create();111ERR_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()));112113return with_scale(shape_result.Get(), Vector3(1, 1, -1));114}115116JPH::ShapeRefC JoltHeightMapShape3D::_build_mesh() const {117const int height_count = (int)heights.size();118119const int quad_count_x = width - 1;120const int quad_count_z = depth - 1;121122const int quad_count = quad_count_x * quad_count_z;123const int triangle_count = quad_count * 2;124125JPH::VertexList vertices;126vertices.reserve((size_t)height_count);127128JPH::IndexedTriangleList indices;129indices.reserve((size_t)triangle_count);130131const float offset_x = (float)-quad_count_x / 2.0f;132const float offset_z = (float)-quad_count_z / 2.0f;133134for (int z = 0; z < depth; ++z) {135for (int x = 0; x < width; ++x) {136const float vertex_x = offset_x + (float)x;137const float vertex_y = (float)heights[z * width + x];138const float vertex_z = offset_z + (float)z;139140vertices.emplace_back(vertex_x, vertex_y, vertex_z);141}142}143144for (int z = 0; z < quad_count_z; ++z) {145for (int x = 0; x < quad_count_x; ++x) {146const int index_lower_right = z * width + x;147const int index_lower_left = z * width + (x + 1);148const int index_upper_right = (z + 1) * width + x;149const int index_upper_left = (z + 1) * width + (x + 1);150151if (!_is_triangle_hole(vertices, index_lower_right, index_upper_right, index_lower_left)) {152indices.emplace_back(index_lower_right, index_upper_right, index_lower_left);153}154155if (!_is_triangle_hole(vertices, index_lower_left, index_upper_right, index_upper_left)) {156indices.emplace_back(index_lower_left, index_upper_right, index_upper_left);157}158}159}160161JPH::MeshShapeSettings shape_settings(std::move(vertices), std::move(indices));162shape_settings.mActiveEdgeCosThresholdAngle = JoltProjectSettings::active_edge_threshold_cos;163164const JPH::ShapeSettings::ShapeResult shape_result = shape_settings.Create();165ERR_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()));166167return shape_result.Get();168}169170AABB JoltHeightMapShape3D::_calculate_aabb() const {171AABB result;172173const int quad_count_x = width - 1;174const int quad_count_z = depth - 1;175176const float offset_x = (float)-quad_count_x / 2.0f;177const float offset_z = (float)-quad_count_z / 2.0f;178179for (int z = 0; z < depth; ++z) {180for (int x = 0; x < width; ++x) {181const Vector3 vertex(offset_x + (float)x, (float)heights[z * width + x], offset_z + (float)z);182183if (x == 0 && z == 0) {184result.position = vertex;185} else {186result.expand_to(vertex);187}188}189}190191return result;192}193194Variant JoltHeightMapShape3D::get_data() const {195Dictionary data;196data["width"] = width;197data["depth"] = depth;198data["heights"] = heights;199return data;200}201202void JoltHeightMapShape3D::set_data(const Variant &p_data) {203ERR_FAIL_COND(p_data.get_type() != Variant::DICTIONARY);204205const Dictionary data = p_data;206207const Variant maybe_heights = data.get("heights", Variant());208209#ifdef REAL_T_IS_DOUBLE210ERR_FAIL_COND(maybe_heights.get_type() != Variant::PACKED_FLOAT64_ARRAY);211#else212ERR_FAIL_COND(maybe_heights.get_type() != Variant::PACKED_FLOAT32_ARRAY);213#endif214215const Variant maybe_width = data.get("width", Variant());216ERR_FAIL_COND(maybe_width.get_type() != Variant::INT);217218const Variant maybe_depth = data.get("depth", Variant());219ERR_FAIL_COND(maybe_depth.get_type() != Variant::INT);220221heights = maybe_heights;222width = maybe_width;223depth = maybe_depth;224225aabb = _calculate_aabb();226227destroy();228}229230String JoltHeightMapShape3D::to_string() const {231return vformat("{height_count=%d width=%d depth=%d}", heights.size(), width, depth);232}233234235