Split Godot utilities in their own files

master
Marc Gilleron 2022-08-20 18:25:23 +01:00
parent c191a1b3ad
commit f8883966db
37 changed files with 656 additions and 511 deletions

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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 {

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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>

View File

@ -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 {

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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 {

View File

@ -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)

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

36
util/godot/memory.h Normal file
View File

@ -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

138
util/godot/mesh.cpp Normal file
View File

@ -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

20
util/godot/mesh.h Normal file
View File

@ -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

14
util/godot/multimesh.cpp Normal file
View File

@ -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

13
util/godot/multimesh.h Normal file
View File

@ -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

28
util/godot/node.cpp Normal file
View File

@ -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

13
util/godot/node.h Normal file
View File

@ -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

39
util/godot/object.cpp Normal file
View File

@ -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

22
util/godot/object.h Normal file
View File

@ -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

38
util/godot/ref_counted.h Normal file
View File

@ -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

31
util/godot/string.cpp Normal file
View File

@ -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;
}

52
util/godot/string.h Normal file
View File

@ -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

View File

@ -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);