Allow to specify which surfaces have collision
parent
fef56aad36
commit
fa027cbe4e
|
@ -45,7 +45,8 @@ Godot 4 is required from this version.
|
|||
|
||||
- Blocky voxels
|
||||
- `VoxelMesherBlocky`: materials are now unlimited and specified in each model, either as overrides or directly from mesh (You still need to consider draw calls when using many materials)
|
||||
- `VoxelMesherBlocky`: each model can have up to 2 materials
|
||||
- `VoxelMesherBlocky`: each model can have up to 2 materials (aka surfaces)
|
||||
- `VoxelMesherBlocky`: mesh collisions: added support for specifying which surfaces have collision
|
||||
|
||||
- Fixes
|
||||
- `VoxelBuffer`: frequently creating buffers with always different sizes no longer wastes memory
|
||||
|
|
|
@ -30,7 +30,7 @@ Ref<Mesh> build_mesh(const VoxelBufferInternal &voxels, VoxelMesher &mesher,
|
|||
std::vector<unsigned int> &surface_index_to_material, Ref<Image> &out_atlas, float p_scale, Vector3 p_offset) {
|
||||
//
|
||||
VoxelMesher::Output output;
|
||||
VoxelMesher::Input input = { voxels, 0 };
|
||||
VoxelMesher::Input input = { voxels, nullptr, nullptr, Vector3i(), 0, false };
|
||||
mesher.build(output, input);
|
||||
|
||||
if (output.surfaces.size() == 0) {
|
||||
|
|
|
@ -51,6 +51,11 @@ bool VoxelBlockyModel::_set(const StringName &p_name, const Variant &p_value) {
|
|||
const int index = name.substr(ZN_ARRAY_LENGTH("material_override_")).to_int();
|
||||
set_material_override(index, p_value);
|
||||
return true;
|
||||
|
||||
} else if (name.begins_with("collision_enabled_")) {
|
||||
const int index = name.substr(ZN_ARRAY_LENGTH("collision_enabled_")).to_int();
|
||||
set_mesh_collision_enabled(index, p_value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -72,6 +77,11 @@ bool VoxelBlockyModel::_get(const StringName &p_name, Variant &r_ret) const {
|
|||
const int index = name.substr(ZN_ARRAY_LENGTH("material_override_")).to_int();
|
||||
r_ret = get_material_override(index);
|
||||
return true;
|
||||
|
||||
} else if (name.begins_with("collision_enabled_")) {
|
||||
const int index = name.substr(ZN_ARRAY_LENGTH("collision_enabled_")).to_int();
|
||||
r_ret = is_mesh_collision_enabled(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -103,13 +113,13 @@ void VoxelBlockyModel::_get_property_list(List<PropertyInfo> *p_list) const {
|
|||
PROPERTY_HINT_RESOURCE_TYPE, Material::get_class_static()));
|
||||
}
|
||||
|
||||
// p_list->push_back(
|
||||
// PropertyInfo(Variant::NIL, "Surface collision", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
|
||||
p_list->push_back(PropertyInfo(
|
||||
Variant::NIL, "Mesh collision", PROPERTY_HINT_NONE, "collision_enabled_", PROPERTY_USAGE_GROUP));
|
||||
|
||||
// for (unsigned int i = 0; i < _surface_count; ++i) {
|
||||
// p_list->push_back(PropertyInfo(Variant::OBJECT, String("collision_enabled_{0}").format(varray(i)),
|
||||
// PROPERTY_HINT_RESOURCE_TYPE, Material::get_class_static()));
|
||||
// }
|
||||
for (unsigned int i = 0; i < _surface_count; ++i) {
|
||||
p_list->push_back(PropertyInfo(Variant::OBJECT, String("collision_enabled_{0}").format(varray(i)),
|
||||
PROPERTY_HINT_RESOURCE_TYPE, Material::get_class_static()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,15 +139,33 @@ void VoxelBlockyModel::set_color(Color color) {
|
|||
}
|
||||
|
||||
void VoxelBlockyModel::set_material_override(int index, Ref<Material> material) {
|
||||
ERR_FAIL_INDEX(index, _surface_count);
|
||||
// TODO Can't check for `_surface_count` instead, because there is no guarantee about the order in which Godot will
|
||||
// set properties when loading the resource. The mesh could be set later, so we can't know the number of surfaces.
|
||||
ERR_FAIL_INDEX(index, int(_surface_params.size()));
|
||||
_surface_params[index].material_override = material;
|
||||
}
|
||||
|
||||
Ref<Material> VoxelBlockyModel::get_material_override(int index) const {
|
||||
ERR_FAIL_INDEX_V(index, _surface_count, Ref<Material>());
|
||||
// TODO Can't check for `_surface_count` instead, because there is no guarantee about the order in which Godot will
|
||||
// set properties when loading the resource. The mesh could be set later, so we can't know the number of surfaces.
|
||||
ERR_FAIL_INDEX_V(index, int(_surface_params.size()), Ref<Material>());
|
||||
return _surface_params[index].material_override;
|
||||
}
|
||||
|
||||
void VoxelBlockyModel::set_mesh_collision_enabled(int surface_index, bool enabled) {
|
||||
// TODO Can't check for `_surface_count` instead, because there is no guarantee about the order in which Godot will
|
||||
// set properties when loading the resource. The mesh could be set later, so we can't know the number of surfaces.
|
||||
ERR_FAIL_INDEX(surface_index, int(_surface_params.size()));
|
||||
_surface_params[surface_index].collision_enabled = enabled;
|
||||
}
|
||||
|
||||
bool VoxelBlockyModel::is_mesh_collision_enabled(int surface_index) const {
|
||||
// TODO Can't check for `_surface_count` instead, because there is no guarantee about the order in which Godot will
|
||||
// set properties when loading the resource. The mesh could be set later, so we can't know the number of surfaces.
|
||||
ERR_FAIL_INDEX_V(surface_index, int(_surface_params.size()), false);
|
||||
return _surface_params[surface_index].collision_enabled;
|
||||
}
|
||||
|
||||
void VoxelBlockyModel::set_transparent(bool t) {
|
||||
if (t) {
|
||||
if (_transparency_index == 0) {
|
||||
|
@ -616,6 +644,11 @@ void VoxelBlockyModel::_b_set_collision_aabbs(Array array) {
|
|||
}
|
||||
}
|
||||
|
||||
// void ortho_simplify(Span<const Vector3f> vertices, Span<const int> indices, std::vector<int> &output) {
|
||||
// TODO Optimization: implement mesh simplification based on axis-aligned triangles.
|
||||
// It could be very effective on mesh collisions with the blocky mesher.
|
||||
// }
|
||||
|
||||
void VoxelBlockyModel::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_voxel_name", "name"), &VoxelBlockyModel::set_voxel_name);
|
||||
ClassDB::bind_method(D_METHOD("get_voxel_name"), &VoxelBlockyModel::get_voxel_name);
|
||||
|
@ -646,6 +679,10 @@ void VoxelBlockyModel::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_custom_mesh", "type"), &VoxelBlockyModel::set_custom_mesh);
|
||||
ClassDB::bind_method(D_METHOD("get_custom_mesh"), &VoxelBlockyModel::get_custom_mesh);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_mesh_collision_enabled", "surface_index", "enabled"),
|
||||
&VoxelBlockyModel::set_mesh_collision_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_mesh_collision_enabled"), &VoxelBlockyModel::is_mesh_collision_enabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_collision_aabbs", "aabbs"), &VoxelBlockyModel::_b_set_collision_aabbs);
|
||||
ClassDB::bind_method(D_METHOD("get_collision_aabbs"), &VoxelBlockyModel::_b_get_collision_aabbs);
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ public:
|
|||
FixedArray<std::vector<float>, Cube::SIDE_COUNT> side_tangents;
|
||||
|
||||
int material_id = -1;
|
||||
//bool collision_enabled = true;
|
||||
bool collision_enabled = true;
|
||||
|
||||
void clear() {
|
||||
positions.clear();
|
||||
|
@ -127,6 +127,9 @@ public:
|
|||
void set_material_override(int index, Ref<Material> material);
|
||||
Ref<Material> get_material_override(int index) const;
|
||||
|
||||
void set_mesh_collision_enabled(int surface_index, bool enabled);
|
||||
bool is_mesh_collision_enabled(int surface_index) const;
|
||||
|
||||
// TODO Might become obsoleted by transparency index
|
||||
void set_transparent(bool t = true);
|
||||
_FORCE_INLINE_ bool is_transparent() const {
|
||||
|
@ -227,7 +230,7 @@ private:
|
|||
// If assigned, these materials override those present on the mesh itself.
|
||||
Ref<Material> material_override;
|
||||
// If true and classic mesh physics are enabled, the surface will be present in the collider.
|
||||
//bool collision_enabled = true;
|
||||
bool collision_enabled = true;
|
||||
};
|
||||
|
||||
FixedArray<SurfaceParams, BakedData::Model::MAX_SURFACES> _surface_params;
|
||||
|
|
|
@ -49,8 +49,13 @@ static thread_local std::vector<int> tls_index_offsets;
|
|||
|
||||
template <typename Type_T>
|
||||
void generate_blocky_mesh(std::vector<VoxelMesherBlocky::Arrays> &out_arrays_per_material,
|
||||
const Span<Type_T> type_buffer, const Vector3i block_size, const VoxelBlockyLibrary::BakedData &library,
|
||||
bool bake_occlusion, float baked_occlusion_darkness) {
|
||||
VoxelMesher::Output::CollisionSurface *collision_surface, const Span<Type_T> type_buffer,
|
||||
const Vector3i block_size, const VoxelBlockyLibrary::BakedData &library, bool bake_occlusion,
|
||||
float baked_occlusion_darkness) {
|
||||
// TODO Optimization: not sure if this mandates a template function. There is so much more happening in this
|
||||
// function other than reading voxels, although reading is on the hottest path. It needs to be profiled. If
|
||||
// changing makes no difference, we could use a function pointer or switch inside instead to reduce executable size.
|
||||
|
||||
ERR_FAIL_COND(block_size.x < static_cast<int>(2 * VoxelMesherBlocky::PADDING) ||
|
||||
block_size.y < static_cast<int>(2 * VoxelMesherBlocky::PADDING) ||
|
||||
block_size.z < static_cast<int>(2 * VoxelMesherBlocky::PADDING));
|
||||
|
@ -69,6 +74,8 @@ void generate_blocky_mesh(std::vector<VoxelMesherBlocky::Arrays> &out_arrays_per
|
|||
index_offsets.clear();
|
||||
index_offsets.resize(out_arrays_per_material.size(), 0);
|
||||
|
||||
int collision_surface_index_offset = 0;
|
||||
|
||||
FixedArray<int, Cube::SIDE_COUNT> side_neighbor_lut;
|
||||
side_neighbor_lut[Cube::SIDE_LEFT] = row_size;
|
||||
side_neighbor_lut[Cube::SIDE_RIGHT] = -row_size;
|
||||
|
@ -142,7 +149,7 @@ void generate_blocky_mesh(std::vector<VoxelMesherBlocky::Arrays> &out_arrays_per
|
|||
const VoxelBlockyModel::BakedData::Model &model = voxel.model;
|
||||
|
||||
// Hybrid approach: extract cube faces and decimate those that aren't visible,
|
||||
// and still allow voxels to have geometry that is not a cube
|
||||
// and still allow voxels to have geometry that is not a cube.
|
||||
|
||||
// Sides
|
||||
for (unsigned int side = 0; side < Cube::SIDE_COUNT; ++side) {
|
||||
|
@ -297,6 +304,31 @@ void generate_blocky_mesh(std::vector<VoxelMesherBlocky::Arrays> &out_arrays_per
|
|||
}
|
||||
}
|
||||
|
||||
if (collision_surface != nullptr && surface.collision_enabled) {
|
||||
std::vector<Vector3f> &dst_positions = collision_surface->positions;
|
||||
std::vector<int> &dst_indices = collision_surface->indices;
|
||||
|
||||
{
|
||||
const unsigned int append_index = dst_positions.size();
|
||||
dst_positions.resize(dst_positions.size() + vertex_count);
|
||||
Vector3f *w = dst_positions.data() + append_index;
|
||||
for (unsigned int i = 0; i < vertex_count; ++i) {
|
||||
w[i] = side_positions[i] + pos;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int i = dst_indices.size();
|
||||
dst_indices.resize(dst_indices.size() + index_count);
|
||||
int *w = dst_indices.data();
|
||||
for (unsigned int j = 0; j < index_count; ++j) {
|
||||
w[i++] = collision_surface_index_offset + side_indices[j];
|
||||
}
|
||||
}
|
||||
|
||||
collision_surface_index_offset += vertex_count;
|
||||
}
|
||||
|
||||
index_offset += vertex_count;
|
||||
}
|
||||
}
|
||||
|
@ -346,6 +378,20 @@ void generate_blocky_mesh(std::vector<VoxelMesherBlocky::Arrays> &out_arrays_per
|
|||
arrays.indices.push_back(index_offset + indices[i]);
|
||||
}
|
||||
|
||||
if (collision_surface != nullptr && surface.collision_enabled) {
|
||||
std::vector<Vector3f> &dst_positions = collision_surface->positions;
|
||||
std::vector<int> &dst_indices = collision_surface->indices;
|
||||
|
||||
for (unsigned int i = 0; i < vertex_count; ++i) {
|
||||
dst_positions.push_back(positions[i] + pos);
|
||||
}
|
||||
for (unsigned int i = 0; i < index_count; ++i) {
|
||||
dst_indices.push_back(collision_surface_index_offset + indices[i]);
|
||||
}
|
||||
|
||||
collision_surface_index_offset += vertex_count;
|
||||
}
|
||||
|
||||
index_offset += vertex_count;
|
||||
}
|
||||
}
|
||||
|
@ -472,6 +518,11 @@ void VoxelMesherBlocky::build(VoxelMesher::Output &output, const VoxelMesher::In
|
|||
const Vector3i block_size = voxels.get_size();
|
||||
const VoxelBufferInternal::Depth channel_depth = voxels.get_channel_depth(channel);
|
||||
|
||||
VoxelMesher::Output::CollisionSurface *collision_surface = nullptr;
|
||||
if (input.collision_hint) {
|
||||
collision_surface = &output.collision_surface;
|
||||
}
|
||||
|
||||
unsigned int material_count = 0;
|
||||
{
|
||||
// We can only access baked data. Only this data is made for multithreaded access.
|
||||
|
@ -486,13 +537,14 @@ void VoxelMesherBlocky::build(VoxelMesher::Output &output, const VoxelMesher::In
|
|||
|
||||
switch (channel_depth) {
|
||||
case VoxelBufferInternal::DEPTH_8_BIT:
|
||||
generate_blocky_mesh(arrays_per_material, raw_channel, block_size, library_baked_data,
|
||||
params.bake_occlusion, baked_occlusion_darkness);
|
||||
generate_blocky_mesh(arrays_per_material, collision_surface, raw_channel, block_size,
|
||||
library_baked_data, params.bake_occlusion, baked_occlusion_darkness);
|
||||
break;
|
||||
|
||||
case VoxelBufferInternal::DEPTH_16_BIT:
|
||||
generate_blocky_mesh(arrays_per_material, raw_channel.reinterpret_cast_to<uint16_t>(), block_size,
|
||||
library_baked_data, params.bake_occlusion, baked_occlusion_darkness);
|
||||
generate_blocky_mesh(arrays_per_material, collision_surface,
|
||||
raw_channel.reinterpret_cast_to<uint16_t>(), block_size, library_baked_data,
|
||||
params.bake_occlusion, baked_occlusion_darkness);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -31,7 +31,11 @@ public:
|
|||
Vector3i origin_in_voxels;
|
||||
// LOD index. 0 means highest detail. 1 means half detail etc.
|
||||
// Not initialized because it confused GCC.
|
||||
int lod; // = 0;
|
||||
uint8_t lod; // = 0;
|
||||
// If true, collision information is required.
|
||||
// Sometimes it doesn't change anything as the rendering mesh can be used as collider,
|
||||
// but in other setups it can be different and will be returned in `collision_surface`.
|
||||
bool collision_hint = false;
|
||||
};
|
||||
|
||||
struct Output {
|
||||
|
@ -42,7 +46,14 @@ public:
|
|||
std::vector<Surface> surfaces;
|
||||
FixedArray<std::vector<Surface>, Cube::SIDE_COUNT> transition_surfaces;
|
||||
Mesh::PrimitiveType primitive_type = Mesh::PRIMITIVE_TRIANGLES;
|
||||
unsigned int mesh_flags = 0;
|
||||
uint32_t mesh_flags = 0;
|
||||
|
||||
struct CollisionSurface {
|
||||
std::vector<Vector3f> positions;
|
||||
std::vector<int> indices;
|
||||
};
|
||||
CollisionSurface collision_surface;
|
||||
|
||||
Ref<Image> atlas_image;
|
||||
};
|
||||
|
||||
|
|
|
@ -176,7 +176,8 @@ void MeshBlockTask::run(zylann::ThreadedTaskContext ctx) {
|
|||
|
||||
const Vector3i origin_in_voxels = position * (int(data_block_size) << lod);
|
||||
|
||||
const VoxelMesher::Input input = { voxels, meshing_dependency->generator.ptr(), data.get(), origin_in_voxels, lod };
|
||||
const VoxelMesher::Input input = { voxels, meshing_dependency->generator.ptr(), data.get(), origin_in_voxels, lod,
|
||||
collision_hint };
|
||||
mesher->build(_surfaces_output, input);
|
||||
|
||||
_has_run = true;
|
||||
|
|
|
@ -31,6 +31,7 @@ public:
|
|||
uint8_t lod;
|
||||
uint8_t blocks_count;
|
||||
uint8_t data_block_size;
|
||||
bool collision_hint;
|
||||
PriorityDependency priority_dependency;
|
||||
std::shared_ptr<MeshingDependency> meshing_dependency;
|
||||
std::shared_ptr<VoxelDataLodMap> data;
|
||||
|
|
|
@ -276,6 +276,7 @@ void VoxelServer::request_block_mesh(uint32_t volume_id, const BlockMeshInput &i
|
|||
task->blocks_count = input.data_blocks_count;
|
||||
task->position = input.render_block_position;
|
||||
task->lod = input.lod;
|
||||
task->collision_hint = input.collision_hint;
|
||||
task->meshing_dependency = volume.meshing_dependency;
|
||||
task->data_block_size = volume.data_block_size;
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ public:
|
|||
unsigned int data_blocks_count = 0;
|
||||
Vector3i render_block_position;
|
||||
uint8_t lod = 0;
|
||||
bool collision_hint = false;
|
||||
};
|
||||
|
||||
struct VolumeCallbacks {
|
||||
|
|
|
@ -1378,6 +1378,7 @@ void VoxelTerrain::process_meshing() {
|
|||
VoxelServer::BlockMeshInput mesh_request;
|
||||
mesh_request.render_block_position = mesh_block_pos;
|
||||
mesh_request.lod = 0;
|
||||
mesh_request.collision_hint = _generate_collisions;
|
||||
//mesh_request.data_blocks_count = data_box.size.volume();
|
||||
|
||||
// This iteration order is specifically chosen to match VoxelServer and threaded access
|
||||
|
@ -1439,7 +1440,9 @@ void VoxelTerrain::apply_mesh_update(const VoxelServer::BlockMeshOutput &ob) {
|
|||
|
||||
Ref<ArrayMesh> mesh;
|
||||
|
||||
std::vector<Array> collidable_surfaces;
|
||||
const bool gen_collisions = _generate_collisions && block->collision_viewers.get() > 0;
|
||||
const bool use_render_mesh_as_collider = gen_collisions && ob.surfaces.collision_surface.positions.size() == 0;
|
||||
std::vector<Array> render_surfaces;
|
||||
|
||||
int gd_surface_index = 0;
|
||||
for (unsigned int surface_index = 0; surface_index < ob.surfaces.surfaces.size(); ++surface_index) {
|
||||
|
@ -1454,7 +1457,9 @@ void VoxelTerrain::apply_mesh_update(const VoxelServer::BlockMeshOutput &ob) {
|
|||
continue;
|
||||
}
|
||||
|
||||
collidable_surfaces.push_back(arrays);
|
||||
if (use_render_mesh_as_collider) {
|
||||
render_surfaces.push_back(arrays);
|
||||
}
|
||||
|
||||
if (mesh.is_null()) {
|
||||
mesh.instantiate();
|
||||
|
@ -1470,7 +1475,7 @@ void VoxelTerrain::apply_mesh_update(const VoxelServer::BlockMeshOutput &ob) {
|
|||
|
||||
if (mesh.is_valid() && is_mesh_empty(**mesh)) {
|
||||
mesh = Ref<Mesh>();
|
||||
collidable_surfaces.clear();
|
||||
render_surfaces.clear();
|
||||
}
|
||||
|
||||
if (_instancer != nullptr) {
|
||||
|
@ -1486,18 +1491,28 @@ void VoxelTerrain::apply_mesh_update(const VoxelServer::BlockMeshOutput &ob) {
|
|||
}
|
||||
}
|
||||
|
||||
const bool gen_collisions = _generate_collisions && block->collision_viewers.get() > 0;
|
||||
|
||||
block->set_mesh(mesh, DirectMeshInstance::GIMode(get_gi_mode()));
|
||||
|
||||
if (_material_override.is_valid()) {
|
||||
block->set_material_override(_material_override);
|
||||
}
|
||||
|
||||
if (gen_collisions) {
|
||||
block->set_collision_mesh(to_span_const(collidable_surfaces), get_tree()->is_debugging_collisions_hint(), this,
|
||||
_collision_margin);
|
||||
const bool debug_collisions = get_tree()->is_debugging_collisions_hint();
|
||||
const VoxelMesher::Output::CollisionSurface &collision_surface = ob.surfaces.collision_surface;
|
||||
|
||||
if (use_render_mesh_as_collider) {
|
||||
block->set_collision_mesh(to_span(render_surfaces), debug_collisions, this, _collision_margin);
|
||||
|
||||
} else {
|
||||
block->set_collision_mesh(to_span(collision_surface.positions), to_span(collision_surface.indices),
|
||||
debug_collisions, this, _collision_margin);
|
||||
}
|
||||
|
||||
block->set_collision_layer(_collision_layer);
|
||||
block->set_collision_mask(_collision_mask);
|
||||
}
|
||||
|
||||
block->set_visible(true);
|
||||
block->set_parent_visible(is_visible());
|
||||
block->set_parent_transform(get_global_transform());
|
||||
|
|
|
@ -134,11 +134,24 @@ void VoxelMeshBlock::set_collision_mesh(
|
|||
drop_collision();
|
||||
return;
|
||||
}
|
||||
Ref<Shape3D> shape = create_concave_polygon_shape(surface_arrays);
|
||||
set_collision_shape(shape, debug_collision, node, margin);
|
||||
}
|
||||
|
||||
void VoxelMeshBlock::set_collision_mesh(
|
||||
Span<const Vector3f> positions, Span<const int> indices, bool debug_collision, Node3D *node, float margin) {
|
||||
if (positions.size() == 0) {
|
||||
drop_collision();
|
||||
return;
|
||||
}
|
||||
Ref<Shape3D> shape = create_concave_polygon_shape(positions, indices);
|
||||
set_collision_shape(shape, debug_collision, node, margin);
|
||||
}
|
||||
|
||||
void VoxelMeshBlock::set_collision_shape(Ref<Shape3D> shape, bool debug_collision, Node3D *node, float margin) {
|
||||
ERR_FAIL_COND(node == nullptr);
|
||||
ERR_FAIL_COND_MSG(node->get_world_3d() != _world, "Physics body and attached node must be from the same world");
|
||||
|
||||
Ref<Shape3D> shape = create_concave_polygon_shape(surface_arrays);
|
||||
if (shape.is_null()) {
|
||||
drop_collision();
|
||||
return;
|
||||
|
|
|
@ -47,6 +47,9 @@ public:
|
|||
// Collisions
|
||||
|
||||
void set_collision_mesh(Span<const Array> surface_arrays, bool debug_collision, Node3D *node, float margin);
|
||||
void set_collision_mesh(
|
||||
Span<const Vector3f> positions, Span<const int> indices, bool debug_collision, Node3D *node, float margin);
|
||||
void set_collision_shape(Ref<Shape3D> shape, bool debug_collision, Node3D *node, float margin);
|
||||
void set_collision_layer(int layer);
|
||||
void set_collision_mask(int mask);
|
||||
void set_collision_margin(float margin);
|
||||
|
|
|
@ -2063,7 +2063,7 @@ void test_voxel_mesher_cubes() {
|
|||
mesher.instantiate();
|
||||
mesher->set_color_mode(VoxelMesherCubes::COLOR_RAW);
|
||||
|
||||
VoxelMesher::Input input{ vb, nullptr, nullptr, Vector3i(), 0 };
|
||||
VoxelMesher::Input input{ vb, nullptr, nullptr, Vector3i(), 0, false };
|
||||
VoxelMesher::Output output;
|
||||
mesher->build(output, input);
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "funcs.h"
|
||||
#include "../math/conv.h"
|
||||
#include "../profiling.h"
|
||||
|
||||
#include <core/config/engine.h>
|
||||
|
@ -55,7 +56,7 @@ Ref<ConcavePolygonShape3D> create_concave_polygon_shape(Span<const Array> surfac
|
|||
return Ref<ConcavePolygonShape3D>();
|
||||
}
|
||||
|
||||
//copy the points into it
|
||||
// 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];
|
||||
|
@ -101,6 +102,39 @@ Ref<ConcavePolygonShape3D> create_concave_polygon_shape(Span<const Array> surfac
|
|||
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;
|
||||
}
|
||||
|
||||
int get_visible_instance_count(const MultiMesh &mm) {
|
||||
int visible_count = mm.get_visible_instance_count();
|
||||
if (visible_count == -1) {
|
||||
|
|
|
@ -22,6 +22,7 @@ bool is_surface_triangulated(Array surface);
|
|||
bool is_mesh_empty(const Mesh &mesh);
|
||||
|
||||
Ref<ConcavePolygonShape3D> create_concave_polygon_shape(Span<const Array> surfaces);
|
||||
Ref<ConcavePolygonShape3D> create_concave_polygon_shape(Span<const Vector3f> positions, Span<const int> indices);
|
||||
|
||||
// This API can be confusing so I made a wrapper
|
||||
int get_visible_instance_count(const MultiMesh &mm);
|
||||
|
|
Loading…
Reference in New Issue