Split Godot utilities in their own files
parent
c191a1b3ad
commit
f8883966db
|
@ -3,7 +3,7 @@
|
|||
#include "../storage/voxel_buffer_gd.h"
|
||||
#include "../storage/voxel_data_grid.h"
|
||||
#include "../terrain/variable_lod/voxel_lod_terrain.h"
|
||||
#include "../util/godot/funcs.h"
|
||||
#include "../util/godot/mesh.h"
|
||||
#include "../util/island_finder.h"
|
||||
#include "../util/math/conv.h"
|
||||
#include "../util/tasks/async_dependency_tracker.h"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "../storage/voxel_buffer_gd.h"
|
||||
#include "../storage/voxel_metadata_variant.h"
|
||||
#include "../terrain/fixed_lod/voxel_terrain.h"
|
||||
#include "../util/godot/funcs.h"
|
||||
#include "../util/godot/ref_counted.h"
|
||||
#include "../util/math/conv.h"
|
||||
#include "../util/voxel_raycast.h"
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "../../storage/modifiers_gd.h"
|
||||
#include "../../terrain/fixed_lod/voxel_terrain.h"
|
||||
#include "../../terrain/variable_lod/voxel_lod_terrain.h"
|
||||
#include "../../util/godot/funcs.h"
|
||||
#include "../about_window.h"
|
||||
#include "../graph/voxel_graph_node_inspector_wrapper.h"
|
||||
#include "voxel_terrain_editor_task_indicator.h"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "vox_import_funcs.h"
|
||||
//#include "../../storage/voxel_buffer_internal.h"
|
||||
#include "../../util/godot/funcs.h"
|
||||
#include "../../util/godot/mesh.h"
|
||||
|
||||
namespace zylann {
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include "../storage/voxel_data_map.h"
|
||||
#include "../terrain/voxel_mesh_block.h"
|
||||
#include "../util/dstack.h"
|
||||
#include "../util/godot/funcs.h"
|
||||
#include "../util/godot/mesh.h"
|
||||
#include "../util/log.h"
|
||||
#include "../util/profiling.h"
|
||||
#include "generate_distance_normalmap_task.h"
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "../../storage/voxel_buffer_internal.h"
|
||||
#include "../../util/container_funcs.h"
|
||||
#include "../../util/expression_parser.h"
|
||||
#include "../../util/godot/object.h"
|
||||
#include "../../util/log.h"
|
||||
#include "../../util/macros.h"
|
||||
#include "../../util/math/conv.h"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#define VOXEL_GRAPH_NODE_DB_H
|
||||
|
||||
#include "../../util/expression_parser.h"
|
||||
#include "../../util/godot/funcs.h" // For String hash
|
||||
#include "../../util/godot/string.h" // For String hash
|
||||
#include "voxel_generator_graph.h"
|
||||
#include "voxel_graph_compiler.h"
|
||||
#include "voxel_graph_shader_generator.h"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "voxel_mesher_cubes.h"
|
||||
#include "../../storage/voxel_buffer_internal.h"
|
||||
#include "../../util/godot/funcs.h"
|
||||
#include "../../util/math/conv.h"
|
||||
#include "../../util/profiling.h"
|
||||
#include <core/math/geometry_2d.h>
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "../meshers/transvoxel/voxel_mesher_transvoxel.h"
|
||||
#include "../storage/voxel_buffer_gd.h"
|
||||
#include "../util/godot/funcs.h"
|
||||
#include "../util/godot/mesh.h"
|
||||
#include "transvoxel/transvoxel_cell_iterator.h"
|
||||
|
||||
namespace zylann::voxel {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "region_file.h"
|
||||
#include "../../streams/voxel_block_serializer.h"
|
||||
#include "../../util/godot/funcs.h"
|
||||
#include "../../util/godot/string.h"
|
||||
#include "../../util/log.h"
|
||||
#include "../../util/profiling.h"
|
||||
#include "../../util/string_funcs.h"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "voxel_stream_region_files.h"
|
||||
#include "../../engine/voxel_engine.h"
|
||||
#include "../../util/godot/funcs.h"
|
||||
#include "../../util/godot/string.h"
|
||||
#include "../../util/log.h"
|
||||
#include "../../util/math/box3i.h"
|
||||
#include "../../util/profiling.h"
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include "vox_data.h"
|
||||
#include "../../util/godot/funcs.h"
|
||||
#include "../../util/log.h"
|
||||
#include "../../util/profiling.h"
|
||||
#include "../../util/string_funcs.h"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#define VOX_DATA_H
|
||||
|
||||
#include "../../util/fixed_array.h"
|
||||
#include "../../util/godot/funcs.h"
|
||||
#include "../../util/godot/string.h"
|
||||
#include "../../util/math/color8.h"
|
||||
#include "../../util/math/vector3i.h"
|
||||
#include "../../util/memory.h"
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#include "voxel_box_mover.h"
|
||||
#include "../../meshers/blocky/voxel_mesher_blocky.h"
|
||||
#include "../../meshers/cubes/voxel_mesher_cubes.h"
|
||||
#include "../../util/godot/funcs.h"
|
||||
#include "../../util/godot/ref_counted.h"
|
||||
#include "voxel_terrain.h"
|
||||
|
||||
namespace zylann::voxel {
|
||||
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
#ifndef VOXEL_BOX_MOVER_H
|
||||
#define VOXEL_BOX_MOVER_H
|
||||
|
||||
#include "voxel_terrain.h"
|
||||
#include <core/math/aabb.h>
|
||||
#include <core/object/ref_counted.h>
|
||||
|
||||
class Node;
|
||||
|
||||
namespace zylann::voxel {
|
||||
|
||||
class VoxelTerrain;
|
||||
|
||||
// Helper to get simple AABB physics
|
||||
class VoxelBoxMover : public RefCounted {
|
||||
GDCLASS(VoxelBoxMover, RefCounted)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include "../../engine/meshing_dependency.h"
|
||||
#include "../../storage/voxel_data_map.h"
|
||||
#include "../../util/godot/funcs.h"
|
||||
#include "../../util/godot/memory.h"
|
||||
#include "../voxel_data_block_enter_info.h"
|
||||
#include "../voxel_mesh_map.h"
|
||||
#include "../voxel_node.h"
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#include "../../edition/voxel_tool.h"
|
||||
#include "../../engine/save_block_data_task.h"
|
||||
#include "../../util/container_funcs.h"
|
||||
#include "../../util/godot/funcs.h"
|
||||
#include "../../util/godot/multimesh.h"
|
||||
#include "../../util/godot/node.h"
|
||||
#include "../../util/godot/ref_counted.h"
|
||||
#include "../../util/math/conv.h"
|
||||
#include "../../util/profiling.h"
|
||||
#include "../../util/string_funcs.h"
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "../../storage/voxel_buffer_gd.h"
|
||||
#include "../../util/container_funcs.h"
|
||||
#include "../../util/godot/funcs.h"
|
||||
#include "../../util/godot/node.h"
|
||||
#include "../../util/godot/shader.h"
|
||||
#include "../../util/log.h"
|
||||
#include "../../util/math/color.h"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "voxel_mesh_block_vlt.h"
|
||||
#include "../../constants/voxel_string_names.h"
|
||||
#include "../../util/godot/funcs.h"
|
||||
#include "../../util/godot/mesh.h"
|
||||
#include "../../util/profiling.h"
|
||||
#include "../free_mesh_task.h"
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "voxel_mesh_block.h"
|
||||
#include "../constants/voxel_string_names.h"
|
||||
#include "../util/godot/funcs.h"
|
||||
#include "../util/godot/collision_shape.h"
|
||||
#include "../util/macros.h"
|
||||
#include "../util/profiling.h"
|
||||
#include "free_mesh_task.h"
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
#include "collision_shape.h"
|
||||
#include "../math/conv.h"
|
||||
#include "../profiling.h"
|
||||
#include <scene/resources/concave_polygon_shape_3d.h>
|
||||
#include <scene/resources/mesh.h>
|
||||
|
||||
namespace zylann {
|
||||
|
||||
Ref<ConcavePolygonShape3D> create_concave_polygon_shape(Span<const Array> surfaces) {
|
||||
// Faster version of Mesh::create_trimesh_shape()
|
||||
// See https://github.com/Zylann/godot_voxel/issues/54
|
||||
|
||||
ZN_PROFILE_SCOPE();
|
||||
|
||||
PackedVector3Array face_points;
|
||||
int face_points_size = 0;
|
||||
|
||||
//find the correct size for face_points
|
||||
for (unsigned int i = 0; i < surfaces.size(); i++) {
|
||||
const Array &surface_arrays = surfaces[i];
|
||||
if (surface_arrays.size() == 0) {
|
||||
// That surface is empty
|
||||
continue;
|
||||
}
|
||||
// If the surface is not empty then it must have an expected amount of data arrays
|
||||
ERR_CONTINUE(surface_arrays.size() != Mesh::ARRAY_MAX);
|
||||
PackedInt32Array indices = surface_arrays[Mesh::ARRAY_INDEX];
|
||||
face_points_size += indices.size();
|
||||
}
|
||||
face_points.resize(face_points_size);
|
||||
|
||||
if (face_points_size < 3) {
|
||||
return Ref<ConcavePolygonShape3D>();
|
||||
}
|
||||
|
||||
// Deindex surfaces into a single one
|
||||
unsigned int face_points_offset = 0;
|
||||
for (unsigned int i = 0; i < surfaces.size(); i++) {
|
||||
const Array &surface_arrays = surfaces[i];
|
||||
if (surface_arrays.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
PackedVector3Array positions = surface_arrays[Mesh::ARRAY_VERTEX];
|
||||
PackedInt32Array indices = surface_arrays[Mesh::ARRAY_INDEX];
|
||||
|
||||
ERR_FAIL_COND_V(positions.size() < 3, Ref<ConcavePolygonShape3D>());
|
||||
ERR_FAIL_COND_V(indices.size() < 3, Ref<ConcavePolygonShape3D>());
|
||||
ERR_FAIL_COND_V(indices.size() % 3 != 0, Ref<ConcavePolygonShape3D>());
|
||||
|
||||
unsigned int face_points_count = face_points_offset + indices.size();
|
||||
|
||||
{
|
||||
Vector3 *w = face_points.ptrw();
|
||||
const int *index_r = indices.ptr();
|
||||
const Vector3 *position_r = positions.ptr();
|
||||
|
||||
for (unsigned int p = face_points_offset; p < face_points_count; ++p) {
|
||||
const int ii = p - face_points_offset;
|
||||
#ifdef DEBUG_ENABLED
|
||||
CRASH_COND(ii < 0 || ii >= indices.size());
|
||||
#endif
|
||||
const int index = index_r[ii];
|
||||
#ifdef DEBUG_ENABLED
|
||||
CRASH_COND(index < 0 || index >= positions.size());
|
||||
#endif
|
||||
w[p] = position_r[index];
|
||||
}
|
||||
}
|
||||
|
||||
face_points_offset += indices.size();
|
||||
}
|
||||
|
||||
Ref<ConcavePolygonShape3D> shape;
|
||||
{
|
||||
ZN_PROFILE_SCOPE_NAMED("Godot shape");
|
||||
shape.instantiate();
|
||||
shape->set_faces(face_points);
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
Ref<ConcavePolygonShape3D> create_concave_polygon_shape(Span<const Vector3f> positions, Span<const int> indices) {
|
||||
ZN_PROFILE_SCOPE();
|
||||
|
||||
PackedVector3Array face_points;
|
||||
|
||||
if (indices.size() < 3) {
|
||||
return Ref<ConcavePolygonShape3D>();
|
||||
}
|
||||
|
||||
face_points.resize(indices.size());
|
||||
|
||||
ERR_FAIL_COND_V(positions.size() < 3, Ref<ConcavePolygonShape3D>());
|
||||
ERR_FAIL_COND_V(indices.size() < 3, Ref<ConcavePolygonShape3D>());
|
||||
ERR_FAIL_COND_V(indices.size() % 3 != 0, Ref<ConcavePolygonShape3D>());
|
||||
|
||||
// Deindex mesh
|
||||
{
|
||||
Vector3 *w = face_points.ptrw();
|
||||
for (unsigned int ii = 0; ii < indices.size(); ++ii) {
|
||||
const int index = indices[ii];
|
||||
w[ii] = to_vec3(positions[index]);
|
||||
}
|
||||
}
|
||||
|
||||
Ref<ConcavePolygonShape3D> shape;
|
||||
{
|
||||
ZN_PROFILE_SCOPE_NAMED("Godot shape");
|
||||
shape.instantiate();
|
||||
shape->set_faces(face_points);
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
Ref<ConcavePolygonShape3D> create_concave_polygon_shape(const Array surface_arrays, unsigned int index_count) {
|
||||
ZN_PROFILE_SCOPE();
|
||||
|
||||
Ref<ConcavePolygonShape3D> shape;
|
||||
|
||||
if (surface_arrays.size() == 0) {
|
||||
return shape;
|
||||
}
|
||||
ZN_ASSERT(surface_arrays.size() == Mesh::ARRAY_MAX);
|
||||
|
||||
PackedInt32Array indices = surface_arrays[Mesh::ARRAY_INDEX];
|
||||
ERR_FAIL_COND_V(index_count < 0 || index_count > static_cast<unsigned int>(indices.size()), shape);
|
||||
if (indices.size() < 3) {
|
||||
return shape;
|
||||
}
|
||||
|
||||
PackedVector3Array positions = surface_arrays[Mesh::ARRAY_VERTEX];
|
||||
|
||||
ERR_FAIL_COND_V(positions.size() < 3, shape);
|
||||
ERR_FAIL_COND_V(indices.size() < 3, shape);
|
||||
ERR_FAIL_COND_V(indices.size() % 3 != 0, shape);
|
||||
|
||||
PackedVector3Array face_points;
|
||||
face_points.resize(indices.size());
|
||||
|
||||
// Deindex mesh
|
||||
{
|
||||
Vector3 *w = face_points.ptrw();
|
||||
for (unsigned int ii = 0; ii < index_count; ++ii) {
|
||||
const int index = indices[ii];
|
||||
w[ii] = positions[index];
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ZN_PROFILE_SCOPE_NAMED("Godot shape");
|
||||
shape.instantiate();
|
||||
shape->set_faces(face_points);
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
} // namespace zylann
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef ZN_GODOT_COLLISION_SHAPE_H
|
||||
#define ZN_GODOT_COLLISION_SHAPE_H
|
||||
|
||||
#include "../math/vector3f.h"
|
||||
#include "../span.h"
|
||||
#include <core/object/ref_counted.h>
|
||||
|
||||
class ConcavePolygonShape3D;
|
||||
|
||||
namespace zylann {
|
||||
|
||||
// Combines all mesh surface arrays into one collider.
|
||||
Ref<ConcavePolygonShape3D> create_concave_polygon_shape(Span<const Array> surfaces);
|
||||
Ref<ConcavePolygonShape3D> create_concave_polygon_shape(Span<const Vector3f> positions, Span<const int> indices);
|
||||
// Create shape from a sub-region of a mesh surface (starting at 0).
|
||||
Ref<ConcavePolygonShape3D> create_concave_polygon_shape(const Array surface_arrays, unsigned int index_count);
|
||||
|
||||
} // namespace zylann
|
||||
|
||||
#endif // ZN_GODOT_COLLISION_SHAPE_H
|
|
@ -4,321 +4,10 @@
|
|||
|
||||
#include <core/config/engine.h>
|
||||
#include <scene/main/node.h>
|
||||
#include <scene/resources/concave_polygon_shape_3d.h>
|
||||
#include <scene/resources/mesh.h>
|
||||
#include <scene/resources/multimesh.h>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace zylann {
|
||||
|
||||
bool is_surface_triangulated(Array surface) {
|
||||
PackedVector3Array positions = surface[Mesh::ARRAY_VERTEX];
|
||||
PackedInt32Array indices = surface[Mesh::ARRAY_INDEX];
|
||||
return positions.size() >= 3 && indices.size() >= 3;
|
||||
}
|
||||
|
||||
bool is_mesh_empty(Span<const Array> surfaces) {
|
||||
if (surfaces.size() == 0) {
|
||||
return true;
|
||||
}
|
||||
for (unsigned int i = 0; i < surfaces.size(); ++i) {
|
||||
Array surface = surfaces[i];
|
||||
if (is_surface_triangulated(surface)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_mesh_empty(const Mesh &mesh) {
|
||||
if (mesh.get_surface_count() == 0) {
|
||||
return true;
|
||||
}
|
||||
if (mesh.surface_get_array_len(0) == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Ref<ConcavePolygonShape3D> create_concave_polygon_shape(Span<const Array> surfaces) {
|
||||
// Faster version of Mesh::create_trimesh_shape()
|
||||
// See https://github.com/Zylann/godot_voxel/issues/54
|
||||
|
||||
ZN_PROFILE_SCOPE();
|
||||
|
||||
PackedVector3Array face_points;
|
||||
int face_points_size = 0;
|
||||
|
||||
//find the correct size for face_points
|
||||
for (unsigned int i = 0; i < surfaces.size(); i++) {
|
||||
const Array &surface_arrays = surfaces[i];
|
||||
if (surface_arrays.size() == 0) {
|
||||
// That surface is empty
|
||||
continue;
|
||||
}
|
||||
// If the surface is not empty then it must have an expected amount of data arrays
|
||||
ERR_CONTINUE(surface_arrays.size() != Mesh::ARRAY_MAX);
|
||||
PackedInt32Array indices = surface_arrays[Mesh::ARRAY_INDEX];
|
||||
face_points_size += indices.size();
|
||||
}
|
||||
face_points.resize(face_points_size);
|
||||
|
||||
if (face_points_size < 3) {
|
||||
return Ref<ConcavePolygonShape3D>();
|
||||
}
|
||||
|
||||
// Deindex surfaces into a single one
|
||||
unsigned int face_points_offset = 0;
|
||||
for (unsigned int i = 0; i < surfaces.size(); i++) {
|
||||
const Array &surface_arrays = surfaces[i];
|
||||
if (surface_arrays.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
PackedVector3Array positions = surface_arrays[Mesh::ARRAY_VERTEX];
|
||||
PackedInt32Array indices = surface_arrays[Mesh::ARRAY_INDEX];
|
||||
|
||||
ERR_FAIL_COND_V(positions.size() < 3, Ref<ConcavePolygonShape3D>());
|
||||
ERR_FAIL_COND_V(indices.size() < 3, Ref<ConcavePolygonShape3D>());
|
||||
ERR_FAIL_COND_V(indices.size() % 3 != 0, Ref<ConcavePolygonShape3D>());
|
||||
|
||||
unsigned int face_points_count = face_points_offset + indices.size();
|
||||
|
||||
{
|
||||
Vector3 *w = face_points.ptrw();
|
||||
const int *index_r = indices.ptr();
|
||||
const Vector3 *position_r = positions.ptr();
|
||||
|
||||
for (unsigned int p = face_points_offset; p < face_points_count; ++p) {
|
||||
const int ii = p - face_points_offset;
|
||||
#ifdef DEBUG_ENABLED
|
||||
CRASH_COND(ii < 0 || ii >= indices.size());
|
||||
#endif
|
||||
const int index = index_r[ii];
|
||||
#ifdef DEBUG_ENABLED
|
||||
CRASH_COND(index < 0 || index >= positions.size());
|
||||
#endif
|
||||
w[p] = position_r[index];
|
||||
}
|
||||
}
|
||||
|
||||
face_points_offset += indices.size();
|
||||
}
|
||||
|
||||
Ref<ConcavePolygonShape3D> shape;
|
||||
{
|
||||
ZN_PROFILE_SCOPE_NAMED("Godot shape");
|
||||
shape.instantiate();
|
||||
shape->set_faces(face_points);
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
Ref<ConcavePolygonShape3D> create_concave_polygon_shape(Span<const Vector3f> positions, Span<const int> indices) {
|
||||
ZN_PROFILE_SCOPE();
|
||||
|
||||
PackedVector3Array face_points;
|
||||
|
||||
if (indices.size() < 3) {
|
||||
return Ref<ConcavePolygonShape3D>();
|
||||
}
|
||||
|
||||
face_points.resize(indices.size());
|
||||
|
||||
ERR_FAIL_COND_V(positions.size() < 3, Ref<ConcavePolygonShape3D>());
|
||||
ERR_FAIL_COND_V(indices.size() < 3, Ref<ConcavePolygonShape3D>());
|
||||
ERR_FAIL_COND_V(indices.size() % 3 != 0, Ref<ConcavePolygonShape3D>());
|
||||
|
||||
// Deindex mesh
|
||||
{
|
||||
Vector3 *w = face_points.ptrw();
|
||||
for (unsigned int ii = 0; ii < indices.size(); ++ii) {
|
||||
const int index = indices[ii];
|
||||
w[ii] = to_vec3(positions[index]);
|
||||
}
|
||||
}
|
||||
|
||||
Ref<ConcavePolygonShape3D> shape;
|
||||
{
|
||||
ZN_PROFILE_SCOPE_NAMED("Godot shape");
|
||||
shape.instantiate();
|
||||
shape->set_faces(face_points);
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
Ref<ConcavePolygonShape3D> create_concave_polygon_shape(const Array surface_arrays, unsigned int index_count) {
|
||||
ZN_PROFILE_SCOPE();
|
||||
|
||||
Ref<ConcavePolygonShape3D> shape;
|
||||
|
||||
if (surface_arrays.size() == 0) {
|
||||
return shape;
|
||||
}
|
||||
ZN_ASSERT(surface_arrays.size() == Mesh::ARRAY_MAX);
|
||||
|
||||
PackedInt32Array indices = surface_arrays[Mesh::ARRAY_INDEX];
|
||||
ERR_FAIL_COND_V(index_count < 0 || index_count > static_cast<unsigned int>(indices.size()), shape);
|
||||
if (indices.size() < 3) {
|
||||
return shape;
|
||||
}
|
||||
|
||||
PackedVector3Array positions = surface_arrays[Mesh::ARRAY_VERTEX];
|
||||
|
||||
ERR_FAIL_COND_V(positions.size() < 3, shape);
|
||||
ERR_FAIL_COND_V(indices.size() < 3, shape);
|
||||
ERR_FAIL_COND_V(indices.size() % 3 != 0, shape);
|
||||
|
||||
PackedVector3Array face_points;
|
||||
face_points.resize(indices.size());
|
||||
|
||||
// Deindex mesh
|
||||
{
|
||||
Vector3 *w = face_points.ptrw();
|
||||
for (unsigned int ii = 0; ii < index_count; ++ii) {
|
||||
const int index = indices[ii];
|
||||
w[ii] = positions[index];
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ZN_PROFILE_SCOPE_NAMED("Godot shape");
|
||||
shape.instantiate();
|
||||
shape->set_faces(face_points);
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
int get_visible_instance_count(const MultiMesh &mm) {
|
||||
int visible_count = mm.get_visible_instance_count();
|
||||
if (visible_count == -1) {
|
||||
visible_count = mm.get_instance_count();
|
||||
}
|
||||
return visible_count;
|
||||
}
|
||||
|
||||
Array generate_debug_seams_wireframe_surface(const Mesh &src_mesh, int surface_index) {
|
||||
if (src_mesh.surface_get_primitive_type(surface_index) != Mesh::PRIMITIVE_TRIANGLES) {
|
||||
return Array();
|
||||
}
|
||||
Array src_surface = src_mesh.surface_get_arrays(surface_index);
|
||||
if (src_surface.is_empty()) {
|
||||
return Array();
|
||||
}
|
||||
PackedVector3Array src_positions = src_surface[Mesh::ARRAY_VERTEX];
|
||||
PackedVector3Array src_normals = src_surface[Mesh::ARRAY_NORMAL];
|
||||
PackedInt32Array src_indices = src_surface[Mesh::ARRAY_INDEX];
|
||||
if (src_indices.size() < 3) {
|
||||
return Array();
|
||||
}
|
||||
struct Dupe {
|
||||
int dst_index = 0;
|
||||
int count = 0;
|
||||
};
|
||||
|
||||
// Using a map so we can have a comparator with floating error
|
||||
std::map<Vector3, Dupe> vertex_to_dupe;
|
||||
std::unordered_map<int, int> src_index_to_dst_index;
|
||||
std::vector<Vector3> dst_positions;
|
||||
{
|
||||
//const Vector3 *src_positions_read = src_positions.ptr();
|
||||
//const Vector3 *src_normals_read = src_normals.ptr();
|
||||
for (int i = 0; i < src_positions.size(); ++i) {
|
||||
const Vector3 pos = src_positions[i];
|
||||
auto dupe_it = vertex_to_dupe.find(pos);
|
||||
if (dupe_it == vertex_to_dupe.end()) {
|
||||
vertex_to_dupe.insert({ pos, Dupe() });
|
||||
} else {
|
||||
Dupe &dupe = dupe_it->second;
|
||||
if (dupe.count == 0) {
|
||||
dupe.dst_index = dst_positions.size();
|
||||
dst_positions.push_back(pos + src_normals[i] * 0.05);
|
||||
}
|
||||
++dupe.count;
|
||||
src_index_to_dst_index.insert({ i, dupe.dst_index });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> dst_indices;
|
||||
{
|
||||
//PoolIntArray::Read r = src_indices.read();
|
||||
for (int i = 0; i < src_indices.size(); i += 3) {
|
||||
const int vi0 = src_indices[i];
|
||||
const int vi1 = src_indices[i + 1];
|
||||
const int vi2 = src_indices[i + 2];
|
||||
|
||||
auto v0_it = src_index_to_dst_index.find(vi0);
|
||||
auto v1_it = src_index_to_dst_index.find(vi1);
|
||||
auto v2_it = src_index_to_dst_index.find(vi2);
|
||||
|
||||
if (v0_it != src_index_to_dst_index.end() && v1_it != src_index_to_dst_index.end()) {
|
||||
dst_indices.push_back(v0_it->second);
|
||||
dst_indices.push_back(v1_it->second);
|
||||
}
|
||||
if (v1_it != src_index_to_dst_index.end() && v2_it != src_index_to_dst_index.end()) {
|
||||
dst_indices.push_back(v1_it->second);
|
||||
dst_indices.push_back(v2_it->second);
|
||||
}
|
||||
if (v2_it != src_index_to_dst_index.end() && v0_it != src_index_to_dst_index.end()) {
|
||||
dst_indices.push_back(v2_it->second);
|
||||
dst_indices.push_back(v0_it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dst_indices.size() == 0) {
|
||||
return Array();
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(dst_indices.size() % 2 != 0, Array());
|
||||
ERR_FAIL_COND_V(dst_positions.size() < 2, Array());
|
||||
|
||||
PackedVector3Array dst_positions_pv;
|
||||
PackedInt32Array dst_indices_pv;
|
||||
raw_copy_to(dst_positions_pv, dst_positions);
|
||||
raw_copy_to(dst_indices_pv, dst_indices);
|
||||
Array dst_surface;
|
||||
dst_surface.resize(Mesh::ARRAY_MAX);
|
||||
dst_surface[Mesh::ARRAY_VERTEX] = dst_positions_pv;
|
||||
dst_surface[Mesh::ARRAY_INDEX] = dst_indices_pv;
|
||||
return dst_surface;
|
||||
|
||||
// Ref<ArrayMesh> wire_mesh;
|
||||
// wire_mesh.instance();
|
||||
// wire_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, dst_surface);
|
||||
|
||||
// Ref<SpatialMaterial> line_material;
|
||||
// line_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
|
||||
// line_material->set_albedo(Color(1.0, 0.0, 1.0));
|
||||
// wire_mesh->surface_set_material(0, line_material);
|
||||
|
||||
// return wire_mesh;
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void for_each_node_depth_first(Node *parent, F f) {
|
||||
ERR_FAIL_COND(parent == nullptr);
|
||||
f(parent);
|
||||
for (int i = 0; i < parent->get_child_count(); ++i) {
|
||||
for_each_node_depth_first(parent->get_child(i), f);
|
||||
}
|
||||
}
|
||||
|
||||
void set_nodes_owner(Node *root, Node *owner) {
|
||||
for_each_node_depth_first(root, [owner](Node *node) { //
|
||||
node->set_owner(owner);
|
||||
});
|
||||
}
|
||||
|
||||
void set_nodes_owner_except_root(Node *root, Node *owner) {
|
||||
ERR_FAIL_COND(root == nullptr);
|
||||
for (int i = 0; i < root->get_child_count(); ++i) {
|
||||
set_nodes_owner(root->get_child(i), owner);
|
||||
}
|
||||
}
|
||||
|
||||
void copy_to(Vector<Vector3> &dst, const std::vector<Vector3f> &src) {
|
||||
dst.resize(src.size());
|
||||
// resize can fail in case allocation was not possible
|
||||
|
@ -357,62 +46,4 @@ void copy_to(Vector<Vector2> &dst, const std::vector<Vector2f> &src) {
|
|||
#endif
|
||||
}
|
||||
|
||||
PackedStringArray to_godot(const std::vector<std::string_view> &svv) {
|
||||
PackedStringArray psa;
|
||||
psa.resize(svv.size());
|
||||
for (unsigned int i = 0; i < svv.size(); ++i) {
|
||||
psa.write[i] = to_godot(svv[i]);
|
||||
}
|
||||
return psa;
|
||||
}
|
||||
|
||||
PackedStringArray to_godot(const std::vector<std::string> &sv) {
|
||||
PackedStringArray psa;
|
||||
psa.resize(sv.size());
|
||||
for (unsigned int i = 0; i < sv.size(); ++i) {
|
||||
psa.write[i] = to_godot(sv[i]);
|
||||
}
|
||||
return psa;
|
||||
}
|
||||
|
||||
uint64_t get_deep_hash(const Object &obj, uint32_t property_usage, uint64_t hash) {
|
||||
ZN_PROFILE_SCOPE();
|
||||
|
||||
hash = hash_djb2_one_64(obj.get_class_name().hash(), hash);
|
||||
|
||||
List<PropertyInfo> properties;
|
||||
obj.get_property_list(&properties, false);
|
||||
|
||||
// I'd like to use ConstIterator since I only read that list but that isn't possible :shrug:
|
||||
for (List<PropertyInfo>::Iterator it = properties.begin(); it != properties.end(); ++it) {
|
||||
const PropertyInfo property = *it;
|
||||
|
||||
if ((property.usage & property_usage) != 0) {
|
||||
const Variant value = obj.get(property.name);
|
||||
uint64_t value_hash = 0;
|
||||
|
||||
if (value.get_type() == Variant::OBJECT) {
|
||||
const Object *obj_value = value.operator Object *();
|
||||
if (obj_value != nullptr) {
|
||||
value_hash = get_deep_hash(*obj_value, property_usage, hash);
|
||||
}
|
||||
|
||||
} else {
|
||||
value_hash = value.hash();
|
||||
}
|
||||
|
||||
hash = hash_djb2_one_64(value_hash, hash);
|
||||
}
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
} // namespace zylann
|
||||
|
||||
std::stringstream &operator<<(std::stringstream &ss, GodotStringWrapper s) {
|
||||
const CharString cs = s.s.utf8();
|
||||
// String has non-explicit constructors from various types making this ambiguous
|
||||
ss.std::stringstream::operator<<(cs.get_data());
|
||||
return ss;
|
||||
}
|
||||
|
|
|
@ -5,79 +5,10 @@
|
|||
#include "../math/vector3f.h"
|
||||
#include "../span.h"
|
||||
|
||||
#include <core/object/ref_counted.h>
|
||||
|
||||
#include <functional>
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
|
||||
class Mesh;
|
||||
class ConcavePolygonShape3D;
|
||||
class MultiMesh;
|
||||
class Node;
|
||||
#include <core/variant/variant.h>
|
||||
|
||||
namespace zylann {
|
||||
|
||||
bool is_surface_triangulated(Array surface);
|
||||
bool is_mesh_empty(const Mesh &mesh);
|
||||
bool is_mesh_empty(Span<const Array> surfaces);
|
||||
|
||||
// Combines all mesh surface arrays into one collider.
|
||||
Ref<ConcavePolygonShape3D> create_concave_polygon_shape(Span<const Array> surfaces);
|
||||
Ref<ConcavePolygonShape3D> create_concave_polygon_shape(Span<const Vector3f> positions, Span<const int> indices);
|
||||
// Create shape from a sub-region of a mesh surface (starting at 0).
|
||||
Ref<ConcavePolygonShape3D> create_concave_polygon_shape(const Array surface_arrays, unsigned int index_count);
|
||||
|
||||
// This API can be confusing so I made a wrapper
|
||||
int get_visible_instance_count(const MultiMesh &mm);
|
||||
|
||||
// Generates a wireframe-mesh that highlights edges of a triangle-mesh where vertices are not shared
|
||||
Array generate_debug_seams_wireframe_surface(const Mesh &src_mesh, int surface_index);
|
||||
|
||||
// `(ref1 = ref2).is_valid()` does not work because Ref<T> does not implement an `operator=` returning the value.
|
||||
// So instead we can write it as `try_get_as(ref2, ref1)`
|
||||
template <typename From_T, typename To_T>
|
||||
inline bool try_get_as(const Ref<From_T> &from, Ref<To_T> &to) {
|
||||
to = from;
|
||||
return to.is_valid();
|
||||
}
|
||||
|
||||
// Creates a shared_ptr which will always use Godot's allocation functions
|
||||
template <typename T>
|
||||
inline std::shared_ptr<T> gd_make_shared() {
|
||||
// std::make_shared() apparently wont allow us to specify custom new and delete
|
||||
return std::shared_ptr<T>(memnew(T), memdelete<T>);
|
||||
}
|
||||
|
||||
// For use with smart pointers such as std::unique_ptr
|
||||
template <typename T>
|
||||
struct GodotObjectDeleter {
|
||||
inline void operator()(T *obj) {
|
||||
memdelete(obj);
|
||||
}
|
||||
};
|
||||
|
||||
// Specialization of `std::unique_ptr which always uses Godot's `memdelete()` as deleter.
|
||||
template <typename T>
|
||||
using GodotObjectUniquePtr = std::unique_ptr<T, GodotObjectDeleter<T>>;
|
||||
|
||||
// Creates a `GodotObjectUniquePtr<T>` with an object constructed with `memnew()` inside.
|
||||
template <typename T>
|
||||
GodotObjectUniquePtr<T> gd_make_unique() {
|
||||
return GodotObjectUniquePtr<T>(memnew(T));
|
||||
}
|
||||
|
||||
void set_nodes_owner(Node *root, Node *owner);
|
||||
void set_nodes_owner_except_root(Node *root, Node *owner);
|
||||
|
||||
// To allow using Ref<T> as key in Godot's HashMap
|
||||
template <typename T>
|
||||
struct RefHasher {
|
||||
static _FORCE_INLINE_ uint32_t hash(const Ref<T> &v) {
|
||||
return uint32_t(uint64_t(v.ptr())) * (0x9e3779b1L);
|
||||
}
|
||||
};
|
||||
|
||||
// Specialized copy functions for vectors because they use `real_t`, which can be either `float` or `double`
|
||||
void copy_to(Vector<Vector3> &dst, const std::vector<Vector3f> &src);
|
||||
void copy_to(Vector<Vector2> &dst, const std::vector<Vector2f> &src);
|
||||
|
@ -90,18 +21,6 @@ void raw_copy_to(Vector<T> &to, const std::vector<T> &from) {
|
|||
memcpy(to.ptrw(), from.data(), from.size() * sizeof(T));
|
||||
}
|
||||
|
||||
inline String to_godot(const std::string_view sv) {
|
||||
return String::utf8(sv.data(), sv.size());
|
||||
}
|
||||
|
||||
inline Vector2f to_vec2f(Vector2 v) {
|
||||
return Vector2f(v.x, v.y);
|
||||
}
|
||||
|
||||
inline Vector2f to_vec2f(Vector2i v) {
|
||||
return Vector2f(v.x, v.y);
|
||||
}
|
||||
|
||||
inline Vector3 get_forward(const Transform3D &t) {
|
||||
return -t.basis.get_column(Vector3::AXIS_Z);
|
||||
}
|
||||
|
@ -110,21 +29,6 @@ inline Vector3 to_godot(const Vector3f v) {
|
|||
return Vector3(v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
// Turns out these functions are only used in editor for now.
|
||||
// They are generic, but I have to wrap them, otherwise GCC throws warnings-as-errors for them being unused.
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
||||
PackedStringArray to_godot(const std::vector<std::string_view> &svv);
|
||||
PackedStringArray to_godot(const std::vector<std::string> &sv);
|
||||
|
||||
// Gets a hash of a given object from its properties. If properties are objects too, they are recursively parsed.
|
||||
// Note that restricting to editable properties is important to avoid costly properties with objects such as textures or
|
||||
// meshes.
|
||||
uint64_t get_deep_hash(
|
||||
const Object &obj, uint32_t property_usage = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR, uint64_t hash = 0);
|
||||
|
||||
#endif
|
||||
|
||||
// template <typename T>
|
||||
// Span<const T> to_span_const(const Vector<T> &a) {
|
||||
// return Span<const T>(a.ptr(), 0, a.size());
|
||||
|
@ -157,34 +61,4 @@ inline T try_get(const Dictionary &d, String key) {
|
|||
|
||||
} // namespace zylann
|
||||
|
||||
// Needed for `zylann::format()`.
|
||||
// I gave up trying to nicely convert Godot's String here... it has non-explicit `const char*` constructor, that makes
|
||||
// other overloads ambiguous...
|
||||
//std::stringstream &operator<<(std::stringstream &ss, const String &s);
|
||||
struct GodotStringWrapper {
|
||||
GodotStringWrapper(const String &p_s) : s(p_s) {}
|
||||
const String &s;
|
||||
};
|
||||
std::stringstream &operator<<(std::stringstream &ss, GodotStringWrapper s);
|
||||
|
||||
namespace std {
|
||||
|
||||
// For String keys in std::unordered_map
|
||||
template <>
|
||||
struct hash<String> {
|
||||
inline size_t operator()(const String &v) const {
|
||||
return v.hash();
|
||||
}
|
||||
};
|
||||
|
||||
// For Ref<T> keys in std::unordered_map, hashed by pointer, not by content
|
||||
template <typename T>
|
||||
struct hash<Ref<T>> {
|
||||
inline size_t operator()(const Ref<T> &v) const {
|
||||
return std::hash<const T *>{}(v.ptr());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // VOXEL_UTILITY_GODOT_FUNCS_H
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef ZN_GODOT_MEMORY_H
|
||||
#define ZN_GODOT_MEMORY_H
|
||||
|
||||
#include <core/os/memory.h>
|
||||
#include <memory>
|
||||
|
||||
namespace zylann {
|
||||
|
||||
// Creates a shared_ptr which will always use Godot's allocation functions
|
||||
template <typename T>
|
||||
inline std::shared_ptr<T> gd_make_shared() {
|
||||
// std::make_shared() apparently wont allow us to specify custom new and delete
|
||||
return std::shared_ptr<T>(memnew(T), memdelete<T>);
|
||||
}
|
||||
|
||||
// For use with smart pointers such as std::unique_ptr
|
||||
template <typename T>
|
||||
struct GodotObjectDeleter {
|
||||
inline void operator()(T *obj) {
|
||||
memdelete(obj);
|
||||
}
|
||||
};
|
||||
|
||||
// Specialization of `std::unique_ptr which always uses Godot's `memdelete()` as deleter.
|
||||
template <typename T>
|
||||
using GodotObjectUniquePtr = std::unique_ptr<T, GodotObjectDeleter<T>>;
|
||||
|
||||
// Creates a `GodotObjectUniquePtr<T>` with an object constructed with `memnew()` inside.
|
||||
template <typename T>
|
||||
GodotObjectUniquePtr<T> gd_make_unique() {
|
||||
return GodotObjectUniquePtr<T>(memnew(T));
|
||||
}
|
||||
|
||||
} // namespace zylann
|
||||
|
||||
#endif // ZN_GODOT_MEMORY_H
|
|
@ -0,0 +1,138 @@
|
|||
#include "mesh.h"
|
||||
#include <scene/resources/mesh.h>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "funcs.h"
|
||||
|
||||
namespace zylann {
|
||||
|
||||
bool is_surface_triangulated(Array surface) {
|
||||
PackedVector3Array positions = surface[Mesh::ARRAY_VERTEX];
|
||||
PackedInt32Array indices = surface[Mesh::ARRAY_INDEX];
|
||||
return positions.size() >= 3 && indices.size() >= 3;
|
||||
}
|
||||
|
||||
bool is_mesh_empty(Span<const Array> surfaces) {
|
||||
if (surfaces.size() == 0) {
|
||||
return true;
|
||||
}
|
||||
for (unsigned int i = 0; i < surfaces.size(); ++i) {
|
||||
Array surface = surfaces[i];
|
||||
if (is_surface_triangulated(surface)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_mesh_empty(const Mesh &mesh) {
|
||||
if (mesh.get_surface_count() == 0) {
|
||||
return true;
|
||||
}
|
||||
if (mesh.surface_get_array_len(0) == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Array generate_debug_seams_wireframe_surface(const Mesh &src_mesh, int surface_index) {
|
||||
if (src_mesh.surface_get_primitive_type(surface_index) != Mesh::PRIMITIVE_TRIANGLES) {
|
||||
return Array();
|
||||
}
|
||||
Array src_surface = src_mesh.surface_get_arrays(surface_index);
|
||||
if (src_surface.is_empty()) {
|
||||
return Array();
|
||||
}
|
||||
PackedVector3Array src_positions = src_surface[Mesh::ARRAY_VERTEX];
|
||||
PackedVector3Array src_normals = src_surface[Mesh::ARRAY_NORMAL];
|
||||
PackedInt32Array src_indices = src_surface[Mesh::ARRAY_INDEX];
|
||||
if (src_indices.size() < 3) {
|
||||
return Array();
|
||||
}
|
||||
struct Dupe {
|
||||
int dst_index = 0;
|
||||
int count = 0;
|
||||
};
|
||||
|
||||
// Using a map so we can have a comparator with floating error
|
||||
std::map<Vector3, Dupe> vertex_to_dupe;
|
||||
std::unordered_map<int, int> src_index_to_dst_index;
|
||||
std::vector<Vector3> dst_positions;
|
||||
{
|
||||
//const Vector3 *src_positions_read = src_positions.ptr();
|
||||
//const Vector3 *src_normals_read = src_normals.ptr();
|
||||
for (int i = 0; i < src_positions.size(); ++i) {
|
||||
const Vector3 pos = src_positions[i];
|
||||
auto dupe_it = vertex_to_dupe.find(pos);
|
||||
if (dupe_it == vertex_to_dupe.end()) {
|
||||
vertex_to_dupe.insert({ pos, Dupe() });
|
||||
} else {
|
||||
Dupe &dupe = dupe_it->second;
|
||||
if (dupe.count == 0) {
|
||||
dupe.dst_index = dst_positions.size();
|
||||
dst_positions.push_back(pos + src_normals[i] * 0.05);
|
||||
}
|
||||
++dupe.count;
|
||||
src_index_to_dst_index.insert({ i, dupe.dst_index });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> dst_indices;
|
||||
{
|
||||
//PoolIntArray::Read r = src_indices.read();
|
||||
for (int i = 0; i < src_indices.size(); i += 3) {
|
||||
const int vi0 = src_indices[i];
|
||||
const int vi1 = src_indices[i + 1];
|
||||
const int vi2 = src_indices[i + 2];
|
||||
|
||||
auto v0_it = src_index_to_dst_index.find(vi0);
|
||||
auto v1_it = src_index_to_dst_index.find(vi1);
|
||||
auto v2_it = src_index_to_dst_index.find(vi2);
|
||||
|
||||
if (v0_it != src_index_to_dst_index.end() && v1_it != src_index_to_dst_index.end()) {
|
||||
dst_indices.push_back(v0_it->second);
|
||||
dst_indices.push_back(v1_it->second);
|
||||
}
|
||||
if (v1_it != src_index_to_dst_index.end() && v2_it != src_index_to_dst_index.end()) {
|
||||
dst_indices.push_back(v1_it->second);
|
||||
dst_indices.push_back(v2_it->second);
|
||||
}
|
||||
if (v2_it != src_index_to_dst_index.end() && v0_it != src_index_to_dst_index.end()) {
|
||||
dst_indices.push_back(v2_it->second);
|
||||
dst_indices.push_back(v0_it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dst_indices.size() == 0) {
|
||||
return Array();
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(dst_indices.size() % 2 != 0, Array());
|
||||
ERR_FAIL_COND_V(dst_positions.size() < 2, Array());
|
||||
|
||||
PackedVector3Array dst_positions_pv;
|
||||
PackedInt32Array dst_indices_pv;
|
||||
raw_copy_to(dst_positions_pv, dst_positions);
|
||||
raw_copy_to(dst_indices_pv, dst_indices);
|
||||
Array dst_surface;
|
||||
dst_surface.resize(Mesh::ARRAY_MAX);
|
||||
dst_surface[Mesh::ARRAY_VERTEX] = dst_positions_pv;
|
||||
dst_surface[Mesh::ARRAY_INDEX] = dst_indices_pv;
|
||||
return dst_surface;
|
||||
|
||||
// Ref<ArrayMesh> wire_mesh;
|
||||
// wire_mesh.instance();
|
||||
// wire_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, dst_surface);
|
||||
|
||||
// Ref<SpatialMaterial> line_material;
|
||||
// line_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
|
||||
// line_material->set_albedo(Color(1.0, 0.0, 1.0));
|
||||
// wire_mesh->surface_set_material(0, line_material);
|
||||
|
||||
// return wire_mesh;
|
||||
}
|
||||
|
||||
} // namespace zylann
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef ZN_GODOT_MESH_H
|
||||
#define ZN_GODOT_MESH_H
|
||||
|
||||
#include "../span.h"
|
||||
#include <core/variant/array.h>
|
||||
|
||||
class Mesh;
|
||||
|
||||
namespace zylann {
|
||||
|
||||
bool is_surface_triangulated(Array surface);
|
||||
bool is_mesh_empty(const Mesh &mesh);
|
||||
bool is_mesh_empty(Span<const Array> surfaces);
|
||||
|
||||
// Generates a wireframe-mesh that highlights edges of a triangle-mesh where vertices are not shared
|
||||
Array generate_debug_seams_wireframe_surface(const Mesh &src_mesh, int surface_index);
|
||||
|
||||
} // namespace zylann
|
||||
|
||||
#endif // ZN_GODOT_MESH_H
|
|
@ -0,0 +1,14 @@
|
|||
#include "multimesh.h"
|
||||
#include <scene/resources/multimesh.h>
|
||||
|
||||
namespace zylann {
|
||||
|
||||
int get_visible_instance_count(const MultiMesh &mm) {
|
||||
int visible_count = mm.get_visible_instance_count();
|
||||
if (visible_count == -1) {
|
||||
visible_count = mm.get_instance_count();
|
||||
}
|
||||
return visible_count;
|
||||
}
|
||||
|
||||
} // namespace zylann
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef ZN_GODOT_MULTIMESH_H
|
||||
#define ZN_GODOT_MULTIMESH_H
|
||||
|
||||
class MultiMesh;
|
||||
|
||||
namespace zylann {
|
||||
|
||||
// This API can be confusing so I made a wrapper
|
||||
int get_visible_instance_count(const MultiMesh &mm);
|
||||
|
||||
} // namespace zylann
|
||||
|
||||
#endif // ZN_GODOT_MULTIMESH_H
|
|
@ -0,0 +1,28 @@
|
|||
#include "node.h"
|
||||
#include <scene/main/node.h>
|
||||
|
||||
namespace zylann {
|
||||
|
||||
template <typename F>
|
||||
void for_each_node_depth_first(Node *parent, F f) {
|
||||
ERR_FAIL_COND(parent == nullptr);
|
||||
f(parent);
|
||||
for (int i = 0; i < parent->get_child_count(); ++i) {
|
||||
for_each_node_depth_first(parent->get_child(i), f);
|
||||
}
|
||||
}
|
||||
|
||||
void set_nodes_owner(Node *root, Node *owner) {
|
||||
for_each_node_depth_first(root, [owner](Node *node) { //
|
||||
node->set_owner(owner);
|
||||
});
|
||||
}
|
||||
|
||||
void set_nodes_owner_except_root(Node *root, Node *owner) {
|
||||
ERR_FAIL_COND(root == nullptr);
|
||||
for (int i = 0; i < root->get_child_count(); ++i) {
|
||||
set_nodes_owner(root->get_child(i), owner);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace zylann
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef ZN_GODOT_NODE_H
|
||||
#define ZN_GODOT_NODE_H
|
||||
|
||||
class Node;
|
||||
|
||||
namespace zylann {
|
||||
|
||||
void set_nodes_owner(Node *root, Node *owner);
|
||||
void set_nodes_owner_except_root(Node *root, Node *owner);
|
||||
|
||||
} // namespace zylann
|
||||
|
||||
#endif // ZN_GODOT_NODE_H
|
|
@ -0,0 +1,39 @@
|
|||
#include "object.h"
|
||||
#include "../profiling.h"
|
||||
|
||||
namespace zylann {
|
||||
|
||||
uint64_t get_deep_hash(const Object &obj, uint32_t property_usage, uint64_t hash) {
|
||||
ZN_PROFILE_SCOPE();
|
||||
|
||||
hash = hash_djb2_one_64(obj.get_class_name().hash(), hash);
|
||||
|
||||
List<PropertyInfo> properties;
|
||||
obj.get_property_list(&properties, false);
|
||||
|
||||
// I'd like to use ConstIterator since I only read that list but that isn't possible :shrug:
|
||||
for (List<PropertyInfo>::Iterator it = properties.begin(); it != properties.end(); ++it) {
|
||||
const PropertyInfo property = *it;
|
||||
|
||||
if ((property.usage & property_usage) != 0) {
|
||||
const Variant value = obj.get(property.name);
|
||||
uint64_t value_hash = 0;
|
||||
|
||||
if (value.get_type() == Variant::OBJECT) {
|
||||
const Object *obj_value = value.operator Object *();
|
||||
if (obj_value != nullptr) {
|
||||
value_hash = get_deep_hash(*obj_value, property_usage, hash);
|
||||
}
|
||||
|
||||
} else {
|
||||
value_hash = value.hash();
|
||||
}
|
||||
|
||||
hash = hash_djb2_one_64(value_hash, hash);
|
||||
}
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
} //namespace zylann
|
|
@ -0,0 +1,22 @@
|
|||
#ifndef ZN_GODOT_OBJECT_H
|
||||
#define ZN_GODOT_OBJECT_H
|
||||
|
||||
#include <core/object/object.h>
|
||||
|
||||
namespace zylann {
|
||||
|
||||
// Turns out these functions are only used in editor for now.
|
||||
// They are generic, but I have to wrap them, otherwise GCC throws warnings-as-errors for them being unused.
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
||||
// Gets a hash of a given object from its properties. If properties are objects too, they are recursively parsed.
|
||||
// Note that restricting to editable properties is important to avoid costly properties with objects such as textures or
|
||||
// meshes.
|
||||
uint64_t get_deep_hash(
|
||||
const Object &obj, uint32_t property_usage = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR, uint64_t hash = 0);
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace zylann
|
||||
|
||||
#endif // ZN_GODOT_OBJECT_H
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef ZN_GODOT_REF_COUNTED_H
|
||||
#define ZN_GODOT_REF_COUNTED_H
|
||||
|
||||
#include <core/object/ref_counted.h>
|
||||
|
||||
namespace zylann {
|
||||
|
||||
// `(ref1 = ref2).is_valid()` does not work because Ref<T> does not implement an `operator=` returning the value.
|
||||
// So instead we can write it as `try_get_as(ref2, ref1)`
|
||||
template <typename From_T, typename To_T>
|
||||
inline bool try_get_as(const Ref<From_T> &from, Ref<To_T> &to) {
|
||||
to = from;
|
||||
return to.is_valid();
|
||||
}
|
||||
|
||||
// To allow using Ref<T> as key in Godot's HashMap
|
||||
template <typename T>
|
||||
struct RefHasher {
|
||||
static _FORCE_INLINE_ uint32_t hash(const Ref<T> &v) {
|
||||
return uint32_t(uint64_t(v.ptr())) * (0x9e3779b1L);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace zylann
|
||||
|
||||
namespace std {
|
||||
|
||||
// For Ref<T> keys in std::unordered_map, hashed by pointer, not by content
|
||||
template <typename T>
|
||||
struct hash<Ref<T>> {
|
||||
inline size_t operator()(const Ref<T> &v) const {
|
||||
return std::hash<const T *>{}(v.ptr());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // ZN_GODOT_REF_COUNTED_H
|
|
@ -0,0 +1,31 @@
|
|||
#include "string.h"
|
||||
#include <sstream>
|
||||
|
||||
namespace zylann {
|
||||
|
||||
PackedStringArray to_godot(const std::vector<std::string_view> &svv) {
|
||||
PackedStringArray psa;
|
||||
psa.resize(svv.size());
|
||||
for (unsigned int i = 0; i < svv.size(); ++i) {
|
||||
psa.write[i] = to_godot(svv[i]);
|
||||
}
|
||||
return psa;
|
||||
}
|
||||
|
||||
PackedStringArray to_godot(const std::vector<std::string> &sv) {
|
||||
PackedStringArray psa;
|
||||
psa.resize(sv.size());
|
||||
for (unsigned int i = 0; i < sv.size(); ++i) {
|
||||
psa.write[i] = to_godot(sv[i]);
|
||||
}
|
||||
return psa;
|
||||
}
|
||||
|
||||
} // namespace zylann
|
||||
|
||||
std::stringstream &operator<<(std::stringstream &ss, GodotStringWrapper s) {
|
||||
const CharString cs = s.s.utf8();
|
||||
// String has non-explicit constructors from various types making this ambiguous
|
||||
ss.std::stringstream::operator<<(cs.get_data());
|
||||
return ss;
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
#ifndef ZN_GODOT_STRING_H
|
||||
#define ZN_GODOT_STRING_H
|
||||
|
||||
#include <core/string/ustring.h>
|
||||
#include <iosfwd>
|
||||
#include <string_view>
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include <core/variant/variant.h>
|
||||
#include <vector>
|
||||
#endif
|
||||
|
||||
namespace zylann {
|
||||
|
||||
inline String to_godot(const std::string_view sv) {
|
||||
return String::utf8(sv.data(), sv.size());
|
||||
}
|
||||
|
||||
// Turns out these functions are only used in editor for now.
|
||||
// They are generic, but I have to wrap them, otherwise GCC throws warnings-as-errors for them being unused.
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
||||
PackedStringArray to_godot(const std::vector<std::string_view> &svv);
|
||||
PackedStringArray to_godot(const std::vector<std::string> &sv);
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace zylann
|
||||
|
||||
// Needed for `zylann::format()`.
|
||||
// I gave up trying to nicely convert Godot's String here... it has non-explicit `const char*` constructor, that makes
|
||||
// other overloads ambiguous...
|
||||
//std::stringstream &operator<<(std::stringstream &ss, const String &s);
|
||||
struct GodotStringWrapper {
|
||||
GodotStringWrapper(const String &p_s) : s(p_s) {}
|
||||
const String &s;
|
||||
};
|
||||
std::stringstream &operator<<(std::stringstream &ss, GodotStringWrapper s);
|
||||
|
||||
namespace std {
|
||||
|
||||
// For String keys in std::unordered_map
|
||||
template <>
|
||||
struct hash<String> {
|
||||
inline size_t operator()(const String &v) const {
|
||||
return v.hash();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // ZN_GODOT_STRING_H
|
|
@ -26,6 +26,10 @@ inline Vector3 to_vec3(const Vector3i v) {
|
|||
|
||||
// Godot => ZN
|
||||
|
||||
inline Vector2f to_vec2f(Vector2 v) {
|
||||
return Vector2f(v.x, v.y);
|
||||
}
|
||||
|
||||
inline Vector3f to_vec3f(Vector3i v) {
|
||||
return Vector3f(v.x, v.y, v.z);
|
||||
}
|
||||
|
@ -40,6 +44,10 @@ inline Vector3i16 to_vec3i16(const Vector3i v) {
|
|||
|
||||
// ZN => Godot
|
||||
|
||||
inline Vector2f to_vec2f(Vector2i v) {
|
||||
return Vector2f(v.x, v.y);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline Vector3i to_vec3i(Vector3T<T> v) {
|
||||
return Vector3i(v.x, v.y, v.z);
|
||||
|
|
Loading…
Reference in New Issue