From e045c0f35091b8738885ab5b02dd5bb4a694d1b3 Mon Sep 17 00:00:00 2001 From: Nathan Date: Tue, 28 Apr 2020 21:50:37 -0700 Subject: [PATCH 1/8] Generate a collision mesh for each surface --- terrain/voxel_block.cpp | 69 +++++++++++++++++++++++++++++++++++++ terrain/voxel_block.h | 1 + terrain/voxel_terrain.cpp | 12 +++---- util/direct_static_body.cpp | 19 ++++++---- util/direct_static_body.h | 2 +- 5 files changed, 87 insertions(+), 16 deletions(-) diff --git a/terrain/voxel_block.cpp b/terrain/voxel_block.cpp index fdeb4db7..f6c6fb8c 100644 --- a/terrain/voxel_block.cpp +++ b/terrain/voxel_block.cpp @@ -36,6 +36,17 @@ static Ref create_concave_polygon_shape(Array surface_array return shape; } +static Vector> create_concave_polygon_shapes(Vector surface_arrays) { + + Vector> shapes = Vector>(); + + for(int i = 0; i < surface_arrays.size(); i++) { + shapes.push_back(create_concave_polygon_shape(surface_arrays[i])); + } + + return shapes; +} + // Helper VoxelBlock *VoxelBlock::create(Vector3i bpos, Ref buffer, unsigned int size, unsigned int p_lod_index) { const int bs = size; @@ -85,6 +96,64 @@ void VoxelBlock::set_world(Ref p_world) { } } +void VoxelBlock::set_mesh(Ref mesh, Spatial *node, bool generate_collision, Vector surface_arrays, bool debug_collision) { + if (mesh.is_valid()) { + + ERR_FAIL_COND(node == nullptr); + ERR_FAIL_COND(node->get_world() != _world); + + Transform transform(Basis(), _position_in_voxels.to_vec3()); + + if (!_mesh_instance.is_valid()) { + // Create instance if it doesn't exist + _mesh_instance.create(); + set_mesh_instance_visible(_mesh_instance, _visible && _parent_visible); + } + + _mesh_instance.set_mesh(mesh); + _mesh_instance.set_transform(transform); + // TODO The day VoxelTerrain becomes a Spatial, this transform will need to be updatable separately + + if (_shader_material.is_valid()) { + _mesh_instance.set_material_override(_shader_material); + } +#ifdef VOXEL_DEBUG_LOD_MATERIALS + _mesh_instance.set_material_override(_debug_material); +#endif + + if (generate_collision) { + Vector> shapes = create_concave_polygon_shapes(surface_arrays); + + if (!_static_body.is_valid()) { + _static_body.create(); + _static_body.set_world(*_world); + _static_body.set_attached_object(node); + _static_body.set_transform(transform); + } else { + _static_body.remove_shape(0); + } + for(int i = 0; i < shapes.size(); i++){ + _static_body.add_shape(shapes[i]); + } + _static_body.set_debug(debug_collision, *_world); + _static_body.set_shape_enabled(0, _visible); + } + + } else { + + if (_mesh_instance.is_valid()) { + // Delete instance if it exists + _mesh_instance.destroy(); + } + + if (_static_body.is_valid()) { + _static_body.destroy(); + } + } + + ++_mesh_update_count; +} + void VoxelBlock::set_mesh(Ref mesh, Spatial *node, bool generate_collision, Array surface_arrays, bool debug_collision) { // TODO Don't add mesh instance to the world if it's not visible. // I suspect Godot is trying to include invisible mesh instances into the culling process, diff --git a/terrain/voxel_block.h b/terrain/voxel_block.h index 2e653a0b..d68a985a 100644 --- a/terrain/voxel_block.h +++ b/terrain/voxel_block.h @@ -35,6 +35,7 @@ public: void set_world(Ref p_world); void set_mesh(Ref mesh, Spatial *node, bool generate_collision, Array surface_arrays, bool debug_collision); + void set_mesh(Ref mesh, Spatial *node, bool generate_collision, Vector surface_arrays, bool debug_collision); void set_transition_mesh(Ref mesh, int side); bool has_mesh() const; diff --git a/terrain/voxel_terrain.cpp b/terrain/voxel_terrain.cpp index a1101d35..3c9c95bc 100644 --- a/terrain/voxel_terrain.cpp +++ b/terrain/voxel_terrain.cpp @@ -1007,7 +1007,7 @@ void VoxelTerrain::_process() { mesh.instance(); // TODO Allow multiple collision surfaces - Array collidable_surface; + Vector collidable_surfaces; int surface_index = 0; const VoxelMeshUpdater::OutputBlockData &data = ob.data; @@ -1023,9 +1023,7 @@ void VoxelTerrain::_process() { continue; } - if (collidable_surface.empty()) { - collidable_surface = surface; - } + collidable_surfaces.push_back(surface); mesh->add_surface_from_arrays(data.blocky_surfaces.primitive_type, surface, Array(), data.blocky_surfaces.compression_flags); mesh->surface_set_material(surface_index, _materials[i]); @@ -1044,9 +1042,7 @@ void VoxelTerrain::_process() { continue; } - if (collidable_surface.empty()) { - collidable_surface = surface; - } + collidable_surfaces.push_back(surface); mesh->add_surface_from_arrays(data.smooth_surfaces.primitive_type, surface, Array(), data.smooth_surfaces.compression_flags); mesh->surface_set_material(surface_index, _materials[i]); @@ -1057,7 +1053,7 @@ void VoxelTerrain::_process() { mesh = Ref(); } - block->set_mesh(mesh, this, _generate_collisions, collidable_surface, get_tree()->is_debugging_collisions_hint()); + block->set_mesh(mesh, this, _generate_collisions, collidable_surfaces, get_tree()->is_debugging_collisions_hint()); block->set_parent_visible(is_visible()); } diff --git a/util/direct_static_body.cpp b/util/direct_static_body.cpp index c3891008..ca0fe20f 100644 --- a/util/direct_static_body.cpp +++ b/util/direct_static_body.cpp @@ -23,7 +23,10 @@ void DirectStaticBody::destroy() { ps.free(_body); _body = RID(); // The shape need to be destroyed after the body - _shape.unref(); + for(int i = 0; i < _shape.size(); i++){ + Ref shape = _shape[i]; + shape.unref(); + } } if (_debug_mesh_instance.is_valid()) { _debug_mesh_instance.destroy(); @@ -46,11 +49,11 @@ void DirectStaticBody::set_transform(Transform transform) { void DirectStaticBody::add_shape(Ref shape) { ERR_FAIL_COND(!_body.is_valid()); PhysicsServer::get_singleton()->body_add_shape(_body, shape->get_rid(), Transform(), false); - // No use case for multishape yet - _shape = shape; + _shape.push_back(shape); if (_debug_mesh_instance.is_valid()) { - Ref mesh = _shape->get_debug_mesh(); + Ref shape = _shape[0]; + Ref mesh = shape->get_debug_mesh(); _debug_mesh_instance.set_mesh(mesh); } } @@ -58,7 +61,8 @@ void DirectStaticBody::add_shape(Ref shape) { void DirectStaticBody::remove_shape(int shape_index) { ERR_FAIL_COND(!_body.is_valid()); PhysicsServer::get_singleton()->body_remove_shape(_body, shape_index); - _shape.unref(); + Ref shape = _shape[shape_index]; + shape.unref(); if (_debug_mesh_instance.is_valid()) { _debug_mesh_instance.set_mesh(Ref()); @@ -103,8 +107,9 @@ void DirectStaticBody::set_debug(bool enabled, World *world) { Transform transform = PhysicsServer::get_singleton()->body_get_state(_body, PhysicsServer::BODY_STATE_TRANSFORM); _debug_mesh_instance.set_transform(transform); - if (_shape.is_valid()) { - Ref mesh = _shape->get_debug_mesh(); + if (_shape[0].is_valid()) { + Ref shape = _shape[0]; + Ref mesh = shape->get_debug_mesh(); _debug_mesh_instance.set_mesh(mesh); } diff --git a/util/direct_static_body.h b/util/direct_static_body.h index 562de4a3..77e88998 100644 --- a/util/direct_static_body.h +++ b/util/direct_static_body.h @@ -28,7 +28,7 @@ public: private: RID _body; - Ref _shape; + Vector> _shape; DirectMeshInstance _debug_mesh_instance; }; From 89691f6ece7bd3a1a856317ba677dcac9bc283d6 Mon Sep 17 00:00:00 2001 From: Nathan Date: Wed, 6 May 2020 14:42:46 -0700 Subject: [PATCH 2/8] Added function in mesher to create collision mesh --- meshers/blocky/voxel_mesher_blocky.cpp | 318 +++++++++++++++++++++++++ meshers/voxel_mesher.h | 1 + terrain/voxel_terrain.cpp | 14 +- 3 files changed, 327 insertions(+), 6 deletions(-) diff --git a/meshers/blocky/voxel_mesher_blocky.cpp b/meshers/blocky/voxel_mesher_blocky.cpp index 05ef0ffa..55e0f11e 100644 --- a/meshers/blocky/voxel_mesher_blocky.cpp +++ b/meshers/blocky/voxel_mesher_blocky.cpp @@ -47,6 +47,285 @@ inline bool contributes_to_ao(const VoxelLibrary &lib, int voxel_id) { } // namespace +template +static void generate_collision_surface( + VoxelMesherBlocky::Arrays &arrays, + const ArraySlice type_buffer, + const Vector3i block_size, + const VoxelLibrary &library, + bool bake_occlusion, float baked_occlusion_darkness) { + + // Build lookup tables so to speed up voxel access. + // These are values to add to an address in order to get given neighbor. + + int row_size = block_size.y; + int deck_size = block_size.x * row_size; + + // Data must be padded, hence the off-by-one + Vector3i min = Vector3i(VoxelMesherBlocky::PADDING); + Vector3i max = block_size - Vector3i(VoxelMesherBlocky::PADDING); + + int index_offsets[VoxelMesherBlocky::MAX_MATERIALS] = { 0 }; + + FixedArray side_neighbor_lut; + side_neighbor_lut[Cube::SIDE_LEFT] = row_size; + side_neighbor_lut[Cube::SIDE_RIGHT] = -row_size; + side_neighbor_lut[Cube::SIDE_BACK] = -deck_size; + side_neighbor_lut[Cube::SIDE_FRONT] = deck_size; + side_neighbor_lut[Cube::SIDE_BOTTOM] = -1; + side_neighbor_lut[Cube::SIDE_TOP] = 1; + + FixedArray edge_neighbor_lut; + edge_neighbor_lut[Cube::EDGE_BOTTOM_BACK] = side_neighbor_lut[Cube::SIDE_BOTTOM] + side_neighbor_lut[Cube::SIDE_BACK]; + edge_neighbor_lut[Cube::EDGE_BOTTOM_FRONT] = side_neighbor_lut[Cube::SIDE_BOTTOM] + side_neighbor_lut[Cube::SIDE_FRONT]; + edge_neighbor_lut[Cube::EDGE_BOTTOM_LEFT] = side_neighbor_lut[Cube::SIDE_BOTTOM] + side_neighbor_lut[Cube::SIDE_LEFT]; + edge_neighbor_lut[Cube::EDGE_BOTTOM_RIGHT] = side_neighbor_lut[Cube::SIDE_BOTTOM] + side_neighbor_lut[Cube::SIDE_RIGHT]; + edge_neighbor_lut[Cube::EDGE_BACK_LEFT] = side_neighbor_lut[Cube::SIDE_BACK] + side_neighbor_lut[Cube::SIDE_LEFT]; + edge_neighbor_lut[Cube::EDGE_BACK_RIGHT] = side_neighbor_lut[Cube::SIDE_BACK] + side_neighbor_lut[Cube::SIDE_RIGHT]; + edge_neighbor_lut[Cube::EDGE_FRONT_LEFT] = side_neighbor_lut[Cube::SIDE_FRONT] + side_neighbor_lut[Cube::SIDE_LEFT]; + edge_neighbor_lut[Cube::EDGE_FRONT_RIGHT] = side_neighbor_lut[Cube::SIDE_FRONT] + side_neighbor_lut[Cube::SIDE_RIGHT]; + edge_neighbor_lut[Cube::EDGE_TOP_BACK] = side_neighbor_lut[Cube::SIDE_TOP] + side_neighbor_lut[Cube::SIDE_BACK]; + edge_neighbor_lut[Cube::EDGE_TOP_FRONT] = side_neighbor_lut[Cube::SIDE_TOP] + side_neighbor_lut[Cube::SIDE_FRONT]; + edge_neighbor_lut[Cube::EDGE_TOP_LEFT] = side_neighbor_lut[Cube::SIDE_TOP] + side_neighbor_lut[Cube::SIDE_LEFT]; + edge_neighbor_lut[Cube::EDGE_TOP_RIGHT] = side_neighbor_lut[Cube::SIDE_TOP] + side_neighbor_lut[Cube::SIDE_RIGHT]; + + FixedArray corner_neighbor_lut; + + corner_neighbor_lut[Cube::CORNER_BOTTOM_BACK_LEFT] = + side_neighbor_lut[Cube::SIDE_BOTTOM] + + side_neighbor_lut[Cube::SIDE_BACK] + + side_neighbor_lut[Cube::SIDE_LEFT]; + + corner_neighbor_lut[Cube::CORNER_BOTTOM_BACK_RIGHT] = + side_neighbor_lut[Cube::SIDE_BOTTOM] + + side_neighbor_lut[Cube::SIDE_BACK] + + side_neighbor_lut[Cube::SIDE_RIGHT]; + + corner_neighbor_lut[Cube::CORNER_BOTTOM_FRONT_RIGHT] = + side_neighbor_lut[Cube::SIDE_BOTTOM] + + side_neighbor_lut[Cube::SIDE_FRONT] + + side_neighbor_lut[Cube::SIDE_RIGHT]; + + corner_neighbor_lut[Cube::CORNER_BOTTOM_FRONT_LEFT] = + side_neighbor_lut[Cube::SIDE_BOTTOM] + + side_neighbor_lut[Cube::SIDE_FRONT] + + side_neighbor_lut[Cube::SIDE_LEFT]; + + corner_neighbor_lut[Cube::CORNER_TOP_BACK_LEFT] = + side_neighbor_lut[Cube::SIDE_TOP] + + side_neighbor_lut[Cube::SIDE_BACK] + + side_neighbor_lut[Cube::SIDE_LEFT]; + + corner_neighbor_lut[Cube::CORNER_TOP_BACK_RIGHT] = + side_neighbor_lut[Cube::SIDE_TOP] + + side_neighbor_lut[Cube::SIDE_BACK] + + side_neighbor_lut[Cube::SIDE_RIGHT]; + + corner_neighbor_lut[Cube::CORNER_TOP_FRONT_RIGHT] = + side_neighbor_lut[Cube::SIDE_TOP] + + side_neighbor_lut[Cube::SIDE_FRONT] + + side_neighbor_lut[Cube::SIDE_RIGHT]; + + corner_neighbor_lut[Cube::CORNER_TOP_FRONT_LEFT] = + side_neighbor_lut[Cube::SIDE_TOP] + + side_neighbor_lut[Cube::SIDE_FRONT] + + side_neighbor_lut[Cube::SIDE_LEFT]; + + //uint64_t time_prep = OS::get_singleton()->get_ticks_usec() - time_before; + //time_before = OS::get_singleton()->get_ticks_usec(); + + for (unsigned int z = min.z; z < (unsigned int)max.z; ++z) { + for (unsigned int x = min.x; x < (unsigned int)max.x; ++x) { + for (unsigned int y = min.y; y < (unsigned int)max.y; ++y) { + // min and max are chosen such that you can visit 1 neighbor away from the current voxel without size check + + const int voxel_index = y + x * row_size + z * deck_size; + const int voxel_id = type_buffer[voxel_index]; + + if (voxel_id != 0 && library.has_voxel(voxel_id)) { + + const Voxel &voxel = library.get_voxel_const(voxel_id); + + int &index_offset = index_offsets[voxel.get_material_id()]; + + // Hybrid approach: extract cube faces and decimate those that aren't visible, + // and still allow voxels to have geometry that is not a cube + + // Sides + for (unsigned int side = 0; side < Cube::SIDE_COUNT; ++side) { + + const std::vector &side_positions = voxel.get_model_side_positions(side); + const unsigned int vertex_count = side_positions.size(); + + if (vertex_count == 0) { + continue; + } + + const int neighbor_voxel_id = type_buffer[voxel_index + side_neighbor_lut[side]]; + + if (!is_face_visible(library, voxel, neighbor_voxel_id, side)) { + continue; + } + + // The face is visible + + int shaded_corner[8] = { 0 }; + + if (bake_occlusion) { + + // Combinatory solution for https://0fps.net/2013/07/03/ambient-occlusion-for-minecraft-like-worlds/ + // (inverted) + // function vertexAO(side1, side2, corner) { + // if(side1 && side2) { + // return 0 + // } + // return 3 - (side1 + side2 + corner) + // } + + for (unsigned int j = 0; j < 4; ++j) { + const unsigned int edge = Cube::g_side_edges[side][j]; + const int edge_neighbor_id = type_buffer[voxel_index + edge_neighbor_lut[edge]]; + if (contributes_to_ao(library, edge_neighbor_id)) { + ++shaded_corner[Cube::g_edge_corners[edge][0]]; + ++shaded_corner[Cube::g_edge_corners[edge][1]]; + } + } + for (unsigned int j = 0; j < 4; ++j) { + const unsigned int corner = Cube::g_side_corners[side][j]; + if (shaded_corner[corner] == 2) { + shaded_corner[corner] = 3; + } else { + const int corner_neigbor_id = type_buffer[voxel_index + corner_neighbor_lut[corner]]; + if (contributes_to_ao(library, corner_neigbor_id)) { + ++shaded_corner[corner]; + } + } + } + } + + const std::vector &side_uvs = voxel.get_model_side_uv(side); + + // Subtracting 1 because the data is padded + Vector3 pos(x - 1, y - 1, z - 1); + + // Append vertices of the faces in one go, don't use push_back + + { + const int append_index = arrays.positions.size(); + arrays.positions.resize(arrays.positions.size() + vertex_count); + Vector3 *w = arrays.positions.data() + append_index; + for (unsigned int i = 0; i < vertex_count; ++i) { + w[i] = side_positions[i] + pos; + } + } + + { + const int append_index = arrays.uvs.size(); + arrays.uvs.resize(arrays.uvs.size() + vertex_count); + memcpy(arrays.uvs.data() + append_index, side_uvs.data(), vertex_count * sizeof(Vector2)); + } + + { + const int append_index = arrays.normals.size(); + arrays.normals.resize(arrays.normals.size() + vertex_count); + Vector3 *w = arrays.normals.data() + append_index; + for (unsigned int i = 0; i < vertex_count; ++i) { + w[i] = Cube::g_side_normals[side].to_vec3(); + } + } + + { + const int append_index = arrays.colors.size(); + arrays.colors.resize(arrays.colors.size() + vertex_count); + Color *w = arrays.colors.data() + append_index; + const Color modulate_color = voxel.get_color(); + + if (bake_occlusion) { + + for (unsigned int i = 0; i < vertex_count; ++i) { + Vector3 v = side_positions[i]; + + // General purpose occlusion colouring. + // TODO Optimize for cubes + // TODO Fix occlusion inconsistency caused by triangles orientation? Not sure if worth it + float shade = 0; + for (unsigned int j = 0; j < 4; ++j) { + unsigned int corner = Cube::g_side_corners[side][j]; + if (shaded_corner[corner]) { + float s = baked_occlusion_darkness * static_cast(shaded_corner[corner]); + //float k = 1.f - Cube::g_corner_position[corner].distance_to(v); + float k = 1.f - Cube::g_corner_position[corner].distance_squared_to(v); + if (k < 0.0) { + k = 0.0; + } + s *= k; + if (s > shade) { + shade = s; + } + } + } + const float gs = 1.0 - shade; + w[i] = Color(gs, gs, gs) * modulate_color; + } + + } else { + for (unsigned int i = 0; i < vertex_count; ++i) { + w[i] = modulate_color; + } + } + } + + const std::vector &side_indices = voxel.get_model_side_indices(side); + const unsigned int index_count = side_indices.size(); + + { + int i = arrays.indices.size(); + arrays.indices.resize(arrays.indices.size() + index_count); + int *w = arrays.indices.data(); + for (unsigned int j = 0; j < index_count; ++j) { + w[i++] = index_offset + side_indices[j]; + } + } + + index_offset += vertex_count; + } + + // Inside + if (voxel.get_model_positions().size() != 0) { + // TODO Get rid of push_backs + + const std::vector &positions = voxel.get_model_positions(); + unsigned int vertex_count = positions.size(); + const Color modulate_color = voxel.get_color(); + + const std::vector &normals = voxel.get_model_normals(); + const std::vector &uvs = voxel.get_model_uv(); + + Vector3 pos(x - 1, y - 1, z - 1); + + for (unsigned int i = 0; i < vertex_count; ++i) { + arrays.normals.push_back(normals[i]); + arrays.uvs.push_back(uvs[i]); + arrays.positions.push_back(positions[i] + pos); + // TODO handle ambient occlusion on inner parts + arrays.colors.push_back(modulate_color); + } + + const std::vector &indices = voxel.get_model_indices(); + unsigned int index_count = indices.size(); + + for (unsigned int i = 0; i < index_count; ++i) { + arrays.indices.push_back(index_offset + indices[i]); + } + + index_offset += vertex_count; + } + } + } + } + } +} + template static void generate_blocky_mesh( FixedArray &out_arrays_per_material, @@ -427,16 +706,22 @@ void VoxelMesherBlocky::build(VoxelMesher::Output &output, const VoxelMesher::In const Vector3i block_size = voxels.get_size(); const VoxelBuffer::Depth channel_depth = voxels.get_channel_depth(channel); + Arrays collision_arrays; + switch (channel_depth) { case VoxelBuffer::DEPTH_8_BIT: generate_blocky_mesh(_arrays_per_material, raw_channel, block_size, library, _bake_occlusion, baked_occlusion_darkness); + generate_collision_surface(collision_arrays, raw_channel, + block_size, library, _bake_occlusion, baked_occlusion_darkness); break; case VoxelBuffer::DEPTH_16_BIT: generate_blocky_mesh(_arrays_per_material, raw_channel.reinterpret_cast_to(), block_size, library, _bake_occlusion, baked_occlusion_darkness); + generate_collision_surface(collision_arrays, raw_channel.reinterpret_cast_to(), + block_size, library, _bake_occlusion, baked_occlusion_darkness); break; default: @@ -482,6 +767,39 @@ void VoxelMesherBlocky::build(VoxelMesher::Output &output, const VoxelMesher::In } } + //create the collision surface + if (collision_arrays.positions.size() != 0) { + + Array collision_surface; + collision_surface.resize(Mesh::ARRAY_MAX); + + { + PoolVector positions; + PoolVector uvs; + PoolVector normals; + PoolVector colors; + PoolVector indices; + + raw_copy_to(positions, collision_arrays.positions); + raw_copy_to(uvs, collision_arrays.uvs); + raw_copy_to(normals, collision_arrays.normals); + raw_copy_to(colors, collision_arrays.colors); + raw_copy_to(indices, collision_arrays.indices); + + collision_surface[Mesh::ARRAY_VERTEX] = positions; + collision_surface[Mesh::ARRAY_TEX_UV] = uvs; + collision_surface[Mesh::ARRAY_NORMAL] = normals; + collision_surface[Mesh::ARRAY_COLOR] = colors; + collision_surface[Mesh::ARRAY_INDEX] = indices; + } + + output.collision_surface = collision_surface; + + } else { + // Empty + output.collision_surface = Array(); + } + output.primitive_type = Mesh::PRIMITIVE_TRIANGLES; } diff --git a/meshers/voxel_mesher.h b/meshers/voxel_mesher.h index fbc4a022..7d20dbe4 100644 --- a/meshers/voxel_mesher.h +++ b/meshers/voxel_mesher.h @@ -17,6 +17,7 @@ public: struct Output { // Each surface correspond to a different material Vector surfaces; + Array collision_surface; FixedArray, Cube::SIDE_COUNT> transition_surfaces; Mesh::PrimitiveType primitive_type = Mesh::PRIMITIVE_TRIANGLES; unsigned int compression_flags = Mesh::ARRAY_COMPRESS_DEFAULT; diff --git a/terrain/voxel_terrain.cpp b/terrain/voxel_terrain.cpp index 3c9c95bc..eff638d4 100644 --- a/terrain/voxel_terrain.cpp +++ b/terrain/voxel_terrain.cpp @@ -1007,10 +1007,13 @@ void VoxelTerrain::_process() { mesh.instance(); // TODO Allow multiple collision surfaces - Vector collidable_surfaces; + Array collidable_surface = Array(); int surface_index = 0; const VoxelMeshUpdater::OutputBlockData &data = ob.data; + if (!data.blocky_surfaces.collision_surface.empty()) { + collidable_surface = data.blocky_surfaces.collision_surface; + } for (int i = 0; i < data.blocky_surfaces.surfaces.size(); ++i) { Array surface = data.blocky_surfaces.surfaces[i]; @@ -1023,13 +1026,14 @@ void VoxelTerrain::_process() { continue; } - collidable_surfaces.push_back(surface); - mesh->add_surface_from_arrays(data.blocky_surfaces.primitive_type, surface, Array(), data.blocky_surfaces.compression_flags); mesh->surface_set_material(surface_index, _materials[i]); ++surface_index; } + /*if (!data.smooth_surfaces.collision_surface.empty()) { + collidable_surface = data.smooth_surfaces.collision_surface; + }*/ for (int i = 0; i < data.smooth_surfaces.surfaces.size(); ++i) { Array surface = data.smooth_surfaces.surfaces[i]; @@ -1042,8 +1046,6 @@ void VoxelTerrain::_process() { continue; } - collidable_surfaces.push_back(surface); - mesh->add_surface_from_arrays(data.smooth_surfaces.primitive_type, surface, Array(), data.smooth_surfaces.compression_flags); mesh->surface_set_material(surface_index, _materials[i]); ++surface_index; @@ -1053,7 +1055,7 @@ void VoxelTerrain::_process() { mesh = Ref(); } - block->set_mesh(mesh, this, _generate_collisions, collidable_surfaces, get_tree()->is_debugging_collisions_hint()); + block->set_mesh(mesh, this, _generate_collisions, collidable_surface, get_tree()->is_debugging_collisions_hint()); block->set_parent_visible(is_visible()); } From 704f491678bdc1555292805606e3e45aedc43d24 Mon Sep 17 00:00:00 2001 From: Nathan Date: Wed, 6 May 2020 15:48:31 -0700 Subject: [PATCH 3/8] Instead of regenerating the mesh, simply join all the meshes together --- meshers/blocky/voxel_mesher_blocky.cpp | 295 ++----------------------- 1 file changed, 16 insertions(+), 279 deletions(-) diff --git a/meshers/blocky/voxel_mesher_blocky.cpp b/meshers/blocky/voxel_mesher_blocky.cpp index 55e0f11e..1c8f56ac 100644 --- a/meshers/blocky/voxel_mesher_blocky.cpp +++ b/meshers/blocky/voxel_mesher_blocky.cpp @@ -47,282 +47,23 @@ inline bool contributes_to_ao(const VoxelLibrary &lib, int voxel_id) { } // namespace -template static void generate_collision_surface( - VoxelMesherBlocky::Arrays &arrays, - const ArraySlice type_buffer, - const Vector3i block_size, - const VoxelLibrary &library, - bool bake_occlusion, float baked_occlusion_darkness) { + VoxelMesherBlocky::Arrays &out_arrays, + FixedArray &arrays_per_material) { - // Build lookup tables so to speed up voxel access. - // These are values to add to an address in order to get given neighbor. - - int row_size = block_size.y; - int deck_size = block_size.x * row_size; - - // Data must be padded, hence the off-by-one - Vector3i min = Vector3i(VoxelMesherBlocky::PADDING); - Vector3i max = block_size - Vector3i(VoxelMesherBlocky::PADDING); - - int index_offsets[VoxelMesherBlocky::MAX_MATERIALS] = { 0 }; - - FixedArray side_neighbor_lut; - side_neighbor_lut[Cube::SIDE_LEFT] = row_size; - side_neighbor_lut[Cube::SIDE_RIGHT] = -row_size; - side_neighbor_lut[Cube::SIDE_BACK] = -deck_size; - side_neighbor_lut[Cube::SIDE_FRONT] = deck_size; - side_neighbor_lut[Cube::SIDE_BOTTOM] = -1; - side_neighbor_lut[Cube::SIDE_TOP] = 1; - - FixedArray edge_neighbor_lut; - edge_neighbor_lut[Cube::EDGE_BOTTOM_BACK] = side_neighbor_lut[Cube::SIDE_BOTTOM] + side_neighbor_lut[Cube::SIDE_BACK]; - edge_neighbor_lut[Cube::EDGE_BOTTOM_FRONT] = side_neighbor_lut[Cube::SIDE_BOTTOM] + side_neighbor_lut[Cube::SIDE_FRONT]; - edge_neighbor_lut[Cube::EDGE_BOTTOM_LEFT] = side_neighbor_lut[Cube::SIDE_BOTTOM] + side_neighbor_lut[Cube::SIDE_LEFT]; - edge_neighbor_lut[Cube::EDGE_BOTTOM_RIGHT] = side_neighbor_lut[Cube::SIDE_BOTTOM] + side_neighbor_lut[Cube::SIDE_RIGHT]; - edge_neighbor_lut[Cube::EDGE_BACK_LEFT] = side_neighbor_lut[Cube::SIDE_BACK] + side_neighbor_lut[Cube::SIDE_LEFT]; - edge_neighbor_lut[Cube::EDGE_BACK_RIGHT] = side_neighbor_lut[Cube::SIDE_BACK] + side_neighbor_lut[Cube::SIDE_RIGHT]; - edge_neighbor_lut[Cube::EDGE_FRONT_LEFT] = side_neighbor_lut[Cube::SIDE_FRONT] + side_neighbor_lut[Cube::SIDE_LEFT]; - edge_neighbor_lut[Cube::EDGE_FRONT_RIGHT] = side_neighbor_lut[Cube::SIDE_FRONT] + side_neighbor_lut[Cube::SIDE_RIGHT]; - edge_neighbor_lut[Cube::EDGE_TOP_BACK] = side_neighbor_lut[Cube::SIDE_TOP] + side_neighbor_lut[Cube::SIDE_BACK]; - edge_neighbor_lut[Cube::EDGE_TOP_FRONT] = side_neighbor_lut[Cube::SIDE_TOP] + side_neighbor_lut[Cube::SIDE_FRONT]; - edge_neighbor_lut[Cube::EDGE_TOP_LEFT] = side_neighbor_lut[Cube::SIDE_TOP] + side_neighbor_lut[Cube::SIDE_LEFT]; - edge_neighbor_lut[Cube::EDGE_TOP_RIGHT] = side_neighbor_lut[Cube::SIDE_TOP] + side_neighbor_lut[Cube::SIDE_RIGHT]; - - FixedArray corner_neighbor_lut; - - corner_neighbor_lut[Cube::CORNER_BOTTOM_BACK_LEFT] = - side_neighbor_lut[Cube::SIDE_BOTTOM] + - side_neighbor_lut[Cube::SIDE_BACK] + - side_neighbor_lut[Cube::SIDE_LEFT]; - - corner_neighbor_lut[Cube::CORNER_BOTTOM_BACK_RIGHT] = - side_neighbor_lut[Cube::SIDE_BOTTOM] + - side_neighbor_lut[Cube::SIDE_BACK] + - side_neighbor_lut[Cube::SIDE_RIGHT]; - - corner_neighbor_lut[Cube::CORNER_BOTTOM_FRONT_RIGHT] = - side_neighbor_lut[Cube::SIDE_BOTTOM] + - side_neighbor_lut[Cube::SIDE_FRONT] + - side_neighbor_lut[Cube::SIDE_RIGHT]; - - corner_neighbor_lut[Cube::CORNER_BOTTOM_FRONT_LEFT] = - side_neighbor_lut[Cube::SIDE_BOTTOM] + - side_neighbor_lut[Cube::SIDE_FRONT] + - side_neighbor_lut[Cube::SIDE_LEFT]; - - corner_neighbor_lut[Cube::CORNER_TOP_BACK_LEFT] = - side_neighbor_lut[Cube::SIDE_TOP] + - side_neighbor_lut[Cube::SIDE_BACK] + - side_neighbor_lut[Cube::SIDE_LEFT]; - - corner_neighbor_lut[Cube::CORNER_TOP_BACK_RIGHT] = - side_neighbor_lut[Cube::SIDE_TOP] + - side_neighbor_lut[Cube::SIDE_BACK] + - side_neighbor_lut[Cube::SIDE_RIGHT]; - - corner_neighbor_lut[Cube::CORNER_TOP_FRONT_RIGHT] = - side_neighbor_lut[Cube::SIDE_TOP] + - side_neighbor_lut[Cube::SIDE_FRONT] + - side_neighbor_lut[Cube::SIDE_RIGHT]; - - corner_neighbor_lut[Cube::CORNER_TOP_FRONT_LEFT] = - side_neighbor_lut[Cube::SIDE_TOP] + - side_neighbor_lut[Cube::SIDE_FRONT] + - side_neighbor_lut[Cube::SIDE_LEFT]; - - //uint64_t time_prep = OS::get_singleton()->get_ticks_usec() - time_before; - //time_before = OS::get_singleton()->get_ticks_usec(); - - for (unsigned int z = min.z; z < (unsigned int)max.z; ++z) { - for (unsigned int x = min.x; x < (unsigned int)max.x; ++x) { - for (unsigned int y = min.y; y < (unsigned int)max.y; ++y) { - // min and max are chosen such that you can visit 1 neighbor away from the current voxel without size check - - const int voxel_index = y + x * row_size + z * deck_size; - const int voxel_id = type_buffer[voxel_index]; - - if (voxel_id != 0 && library.has_voxel(voxel_id)) { - - const Voxel &voxel = library.get_voxel_const(voxel_id); - - int &index_offset = index_offsets[voxel.get_material_id()]; - - // Hybrid approach: extract cube faces and decimate those that aren't visible, - // and still allow voxels to have geometry that is not a cube - - // Sides - for (unsigned int side = 0; side < Cube::SIDE_COUNT; ++side) { - - const std::vector &side_positions = voxel.get_model_side_positions(side); - const unsigned int vertex_count = side_positions.size(); - - if (vertex_count == 0) { - continue; - } - - const int neighbor_voxel_id = type_buffer[voxel_index + side_neighbor_lut[side]]; - - if (!is_face_visible(library, voxel, neighbor_voxel_id, side)) { - continue; - } - - // The face is visible - - int shaded_corner[8] = { 0 }; - - if (bake_occlusion) { - - // Combinatory solution for https://0fps.net/2013/07/03/ambient-occlusion-for-minecraft-like-worlds/ - // (inverted) - // function vertexAO(side1, side2, corner) { - // if(side1 && side2) { - // return 0 - // } - // return 3 - (side1 + side2 + corner) - // } - - for (unsigned int j = 0; j < 4; ++j) { - const unsigned int edge = Cube::g_side_edges[side][j]; - const int edge_neighbor_id = type_buffer[voxel_index + edge_neighbor_lut[edge]]; - if (contributes_to_ao(library, edge_neighbor_id)) { - ++shaded_corner[Cube::g_edge_corners[edge][0]]; - ++shaded_corner[Cube::g_edge_corners[edge][1]]; - } - } - for (unsigned int j = 0; j < 4; ++j) { - const unsigned int corner = Cube::g_side_corners[side][j]; - if (shaded_corner[corner] == 2) { - shaded_corner[corner] = 3; - } else { - const int corner_neigbor_id = type_buffer[voxel_index + corner_neighbor_lut[corner]]; - if (contributes_to_ao(library, corner_neigbor_id)) { - ++shaded_corner[corner]; - } - } - } - } - - const std::vector &side_uvs = voxel.get_model_side_uv(side); - - // Subtracting 1 because the data is padded - Vector3 pos(x - 1, y - 1, z - 1); - - // Append vertices of the faces in one go, don't use push_back - - { - const int append_index = arrays.positions.size(); - arrays.positions.resize(arrays.positions.size() + vertex_count); - Vector3 *w = arrays.positions.data() + append_index; - for (unsigned int i = 0; i < vertex_count; ++i) { - w[i] = side_positions[i] + pos; - } - } - - { - const int append_index = arrays.uvs.size(); - arrays.uvs.resize(arrays.uvs.size() + vertex_count); - memcpy(arrays.uvs.data() + append_index, side_uvs.data(), vertex_count * sizeof(Vector2)); - } - - { - const int append_index = arrays.normals.size(); - arrays.normals.resize(arrays.normals.size() + vertex_count); - Vector3 *w = arrays.normals.data() + append_index; - for (unsigned int i = 0; i < vertex_count; ++i) { - w[i] = Cube::g_side_normals[side].to_vec3(); - } - } - - { - const int append_index = arrays.colors.size(); - arrays.colors.resize(arrays.colors.size() + vertex_count); - Color *w = arrays.colors.data() + append_index; - const Color modulate_color = voxel.get_color(); - - if (bake_occlusion) { - - for (unsigned int i = 0; i < vertex_count; ++i) { - Vector3 v = side_positions[i]; - - // General purpose occlusion colouring. - // TODO Optimize for cubes - // TODO Fix occlusion inconsistency caused by triangles orientation? Not sure if worth it - float shade = 0; - for (unsigned int j = 0; j < 4; ++j) { - unsigned int corner = Cube::g_side_corners[side][j]; - if (shaded_corner[corner]) { - float s = baked_occlusion_darkness * static_cast(shaded_corner[corner]); - //float k = 1.f - Cube::g_corner_position[corner].distance_to(v); - float k = 1.f - Cube::g_corner_position[corner].distance_squared_to(v); - if (k < 0.0) { - k = 0.0; - } - s *= k; - if (s > shade) { - shade = s; - } - } - } - const float gs = 1.0 - shade; - w[i] = Color(gs, gs, gs) * modulate_color; - } - - } else { - for (unsigned int i = 0; i < vertex_count; ++i) { - w[i] = modulate_color; - } - } - } - - const std::vector &side_indices = voxel.get_model_side_indices(side); - const unsigned int index_count = side_indices.size(); - - { - int i = arrays.indices.size(); - arrays.indices.resize(arrays.indices.size() + index_count); - int *w = arrays.indices.data(); - for (unsigned int j = 0; j < index_count; ++j) { - w[i++] = index_offset + side_indices[j]; - } - } - - index_offset += vertex_count; - } - - // Inside - if (voxel.get_model_positions().size() != 0) { - // TODO Get rid of push_backs - - const std::vector &positions = voxel.get_model_positions(); - unsigned int vertex_count = positions.size(); - const Color modulate_color = voxel.get_color(); - - const std::vector &normals = voxel.get_model_normals(); - const std::vector &uvs = voxel.get_model_uv(); - - Vector3 pos(x - 1, y - 1, z - 1); - - for (unsigned int i = 0; i < vertex_count; ++i) { - arrays.normals.push_back(normals[i]); - arrays.uvs.push_back(uvs[i]); - arrays.positions.push_back(positions[i] + pos); - // TODO handle ambient occlusion on inner parts - arrays.colors.push_back(modulate_color); - } - - const std::vector &indices = voxel.get_model_indices(); - unsigned int index_count = indices.size(); - - for (unsigned int i = 0; i < index_count; ++i) { - arrays.indices.push_back(index_offset + indices[i]); - } - - index_offset += vertex_count; - } - } - } + for (int i = 0; i < arrays_per_material.size(); i++) { + if (arrays_per_material[i].positions.empty()) { + continue; } + + const VoxelMesherBlocky::Arrays &arrays = arrays_per_material[i]; + + out_arrays.positions.insert(out_arrays.positions.end(), arrays.positions.begin(), arrays.positions.end()); + out_arrays.normals.insert(out_arrays.normals.end(), arrays.normals.begin(), arrays.normals.end()); + out_arrays.uvs.insert(out_arrays.uvs.end(), arrays.uvs.begin(), arrays.uvs.end()); + out_arrays.colors.insert(out_arrays.colors.end(), arrays.colors.begin(), arrays.colors.end()); + out_arrays.indices.insert(out_arrays.indices.end(), arrays.indices.begin(), arrays.indices.end()); + } } @@ -706,22 +447,16 @@ void VoxelMesherBlocky::build(VoxelMesher::Output &output, const VoxelMesher::In const Vector3i block_size = voxels.get_size(); const VoxelBuffer::Depth channel_depth = voxels.get_channel_depth(channel); - Arrays collision_arrays; - switch (channel_depth) { case VoxelBuffer::DEPTH_8_BIT: generate_blocky_mesh(_arrays_per_material, raw_channel, block_size, library, _bake_occlusion, baked_occlusion_darkness); - generate_collision_surface(collision_arrays, raw_channel, - block_size, library, _bake_occlusion, baked_occlusion_darkness); break; case VoxelBuffer::DEPTH_16_BIT: generate_blocky_mesh(_arrays_per_material, raw_channel.reinterpret_cast_to(), block_size, library, _bake_occlusion, baked_occlusion_darkness); - generate_collision_surface(collision_arrays, raw_channel.reinterpret_cast_to(), - block_size, library, _bake_occlusion, baked_occlusion_darkness); break; default: @@ -768,6 +503,8 @@ void VoxelMesherBlocky::build(VoxelMesher::Output &output, const VoxelMesher::In } //create the collision surface + Arrays collision_arrays; + generate_collision_surface(collision_arrays, _arrays_per_material); if (collision_arrays.positions.size() != 0) { Array collision_surface; From c69afee67fe7493cf92f1035737fb6b6dda8a036 Mon Sep 17 00:00:00 2001 From: Nathan Date: Wed, 6 May 2020 16:14:11 -0700 Subject: [PATCH 4/8] Clean up some stuff that should not have happened --- terrain/voxel_block.cpp | 58 ------------------------------------- terrain/voxel_terrain.cpp | 7 +++-- util/direct_static_body.cpp | 19 +++++------- util/direct_static_body.h | 2 +- 4 files changed, 12 insertions(+), 74 deletions(-) diff --git a/terrain/voxel_block.cpp b/terrain/voxel_block.cpp index f6c6fb8c..fc88a244 100644 --- a/terrain/voxel_block.cpp +++ b/terrain/voxel_block.cpp @@ -96,64 +96,6 @@ void VoxelBlock::set_world(Ref p_world) { } } -void VoxelBlock::set_mesh(Ref mesh, Spatial *node, bool generate_collision, Vector surface_arrays, bool debug_collision) { - if (mesh.is_valid()) { - - ERR_FAIL_COND(node == nullptr); - ERR_FAIL_COND(node->get_world() != _world); - - Transform transform(Basis(), _position_in_voxels.to_vec3()); - - if (!_mesh_instance.is_valid()) { - // Create instance if it doesn't exist - _mesh_instance.create(); - set_mesh_instance_visible(_mesh_instance, _visible && _parent_visible); - } - - _mesh_instance.set_mesh(mesh); - _mesh_instance.set_transform(transform); - // TODO The day VoxelTerrain becomes a Spatial, this transform will need to be updatable separately - - if (_shader_material.is_valid()) { - _mesh_instance.set_material_override(_shader_material); - } -#ifdef VOXEL_DEBUG_LOD_MATERIALS - _mesh_instance.set_material_override(_debug_material); -#endif - - if (generate_collision) { - Vector> shapes = create_concave_polygon_shapes(surface_arrays); - - if (!_static_body.is_valid()) { - _static_body.create(); - _static_body.set_world(*_world); - _static_body.set_attached_object(node); - _static_body.set_transform(transform); - } else { - _static_body.remove_shape(0); - } - for(int i = 0; i < shapes.size(); i++){ - _static_body.add_shape(shapes[i]); - } - _static_body.set_debug(debug_collision, *_world); - _static_body.set_shape_enabled(0, _visible); - } - - } else { - - if (_mesh_instance.is_valid()) { - // Delete instance if it exists - _mesh_instance.destroy(); - } - - if (_static_body.is_valid()) { - _static_body.destroy(); - } - } - - ++_mesh_update_count; -} - void VoxelBlock::set_mesh(Ref mesh, Spatial *node, bool generate_collision, Array surface_arrays, bool debug_collision) { // TODO Don't add mesh instance to the world if it's not visible. // I suspect Godot is trying to include invisible mesh instances into the culling process, diff --git a/terrain/voxel_terrain.cpp b/terrain/voxel_terrain.cpp index eff638d4..3574ca59 100644 --- a/terrain/voxel_terrain.cpp +++ b/terrain/voxel_terrain.cpp @@ -1031,9 +1031,6 @@ void VoxelTerrain::_process() { ++surface_index; } - /*if (!data.smooth_surfaces.collision_surface.empty()) { - collidable_surface = data.smooth_surfaces.collision_surface; - }*/ for (int i = 0; i < data.smooth_surfaces.surfaces.size(); ++i) { Array surface = data.smooth_surfaces.surfaces[i]; @@ -1046,6 +1043,10 @@ void VoxelTerrain::_process() { continue; } + if (collidable_surface.empty()) { + collidable_surface = surface; + } + mesh->add_surface_from_arrays(data.smooth_surfaces.primitive_type, surface, Array(), data.smooth_surfaces.compression_flags); mesh->surface_set_material(surface_index, _materials[i]); ++surface_index; diff --git a/util/direct_static_body.cpp b/util/direct_static_body.cpp index ca0fe20f..c3891008 100644 --- a/util/direct_static_body.cpp +++ b/util/direct_static_body.cpp @@ -23,10 +23,7 @@ void DirectStaticBody::destroy() { ps.free(_body); _body = RID(); // The shape need to be destroyed after the body - for(int i = 0; i < _shape.size(); i++){ - Ref shape = _shape[i]; - shape.unref(); - } + _shape.unref(); } if (_debug_mesh_instance.is_valid()) { _debug_mesh_instance.destroy(); @@ -49,11 +46,11 @@ void DirectStaticBody::set_transform(Transform transform) { void DirectStaticBody::add_shape(Ref shape) { ERR_FAIL_COND(!_body.is_valid()); PhysicsServer::get_singleton()->body_add_shape(_body, shape->get_rid(), Transform(), false); - _shape.push_back(shape); + // No use case for multishape yet + _shape = shape; if (_debug_mesh_instance.is_valid()) { - Ref shape = _shape[0]; - Ref mesh = shape->get_debug_mesh(); + Ref mesh = _shape->get_debug_mesh(); _debug_mesh_instance.set_mesh(mesh); } } @@ -61,8 +58,7 @@ void DirectStaticBody::add_shape(Ref shape) { void DirectStaticBody::remove_shape(int shape_index) { ERR_FAIL_COND(!_body.is_valid()); PhysicsServer::get_singleton()->body_remove_shape(_body, shape_index); - Ref shape = _shape[shape_index]; - shape.unref(); + _shape.unref(); if (_debug_mesh_instance.is_valid()) { _debug_mesh_instance.set_mesh(Ref()); @@ -107,9 +103,8 @@ void DirectStaticBody::set_debug(bool enabled, World *world) { Transform transform = PhysicsServer::get_singleton()->body_get_state(_body, PhysicsServer::BODY_STATE_TRANSFORM); _debug_mesh_instance.set_transform(transform); - if (_shape[0].is_valid()) { - Ref shape = _shape[0]; - Ref mesh = shape->get_debug_mesh(); + if (_shape.is_valid()) { + Ref mesh = _shape->get_debug_mesh(); _debug_mesh_instance.set_mesh(mesh); } diff --git a/util/direct_static_body.h b/util/direct_static_body.h index 77e88998..562de4a3 100644 --- a/util/direct_static_body.h +++ b/util/direct_static_body.h @@ -28,7 +28,7 @@ public: private: RID _body; - Vector> _shape; + Ref _shape; DirectMeshInstance _debug_mesh_instance; }; From 6db4246e9aac56815228e8a09de09b9959c0b33b Mon Sep 17 00:00:00 2001 From: Nathan Date: Wed, 6 May 2020 16:20:24 -0700 Subject: [PATCH 5/8] Missed something to clean up --- terrain/voxel_block.h | 1 - 1 file changed, 1 deletion(-) diff --git a/terrain/voxel_block.h b/terrain/voxel_block.h index d68a985a..2e653a0b 100644 --- a/terrain/voxel_block.h +++ b/terrain/voxel_block.h @@ -35,7 +35,6 @@ public: void set_world(Ref p_world); void set_mesh(Ref mesh, Spatial *node, bool generate_collision, Array surface_arrays, bool debug_collision); - void set_mesh(Ref mesh, Spatial *node, bool generate_collision, Vector surface_arrays, bool debug_collision); void set_transition_mesh(Ref mesh, int side); bool has_mesh() const; From 713c57388abd1da1a9e0ebd41abfeec21ea721ff Mon Sep 17 00:00:00 2001 From: Nathan Date: Wed, 6 May 2020 19:47:00 -0700 Subject: [PATCH 6/8] Changing the approch once again due to misunderstanding Zylann --- meshers/blocky/voxel_mesher_blocky.cpp | 55 -------------------------- meshers/voxel_mesher.h | 1 - terrain/voxel_block.cpp | 52 +++++++++++------------- terrain/voxel_block.h | 2 +- terrain/voxel_lod_terrain.cpp | 12 +++--- terrain/voxel_terrain.cpp | 17 ++++---- 6 files changed, 37 insertions(+), 102 deletions(-) diff --git a/meshers/blocky/voxel_mesher_blocky.cpp b/meshers/blocky/voxel_mesher_blocky.cpp index 1c8f56ac..05ef0ffa 100644 --- a/meshers/blocky/voxel_mesher_blocky.cpp +++ b/meshers/blocky/voxel_mesher_blocky.cpp @@ -47,26 +47,6 @@ inline bool contributes_to_ao(const VoxelLibrary &lib, int voxel_id) { } // namespace -static void generate_collision_surface( - VoxelMesherBlocky::Arrays &out_arrays, - FixedArray &arrays_per_material) { - - for (int i = 0; i < arrays_per_material.size(); i++) { - if (arrays_per_material[i].positions.empty()) { - continue; - } - - const VoxelMesherBlocky::Arrays &arrays = arrays_per_material[i]; - - out_arrays.positions.insert(out_arrays.positions.end(), arrays.positions.begin(), arrays.positions.end()); - out_arrays.normals.insert(out_arrays.normals.end(), arrays.normals.begin(), arrays.normals.end()); - out_arrays.uvs.insert(out_arrays.uvs.end(), arrays.uvs.begin(), arrays.uvs.end()); - out_arrays.colors.insert(out_arrays.colors.end(), arrays.colors.begin(), arrays.colors.end()); - out_arrays.indices.insert(out_arrays.indices.end(), arrays.indices.begin(), arrays.indices.end()); - - } -} - template static void generate_blocky_mesh( FixedArray &out_arrays_per_material, @@ -502,41 +482,6 @@ void VoxelMesherBlocky::build(VoxelMesher::Output &output, const VoxelMesher::In } } - //create the collision surface - Arrays collision_arrays; - generate_collision_surface(collision_arrays, _arrays_per_material); - if (collision_arrays.positions.size() != 0) { - - Array collision_surface; - collision_surface.resize(Mesh::ARRAY_MAX); - - { - PoolVector positions; - PoolVector uvs; - PoolVector normals; - PoolVector colors; - PoolVector indices; - - raw_copy_to(positions, collision_arrays.positions); - raw_copy_to(uvs, collision_arrays.uvs); - raw_copy_to(normals, collision_arrays.normals); - raw_copy_to(colors, collision_arrays.colors); - raw_copy_to(indices, collision_arrays.indices); - - collision_surface[Mesh::ARRAY_VERTEX] = positions; - collision_surface[Mesh::ARRAY_TEX_UV] = uvs; - collision_surface[Mesh::ARRAY_NORMAL] = normals; - collision_surface[Mesh::ARRAY_COLOR] = colors; - collision_surface[Mesh::ARRAY_INDEX] = indices; - } - - output.collision_surface = collision_surface; - - } else { - // Empty - output.collision_surface = Array(); - } - output.primitive_type = Mesh::PRIMITIVE_TRIANGLES; } diff --git a/meshers/voxel_mesher.h b/meshers/voxel_mesher.h index 7d20dbe4..fbc4a022 100644 --- a/meshers/voxel_mesher.h +++ b/meshers/voxel_mesher.h @@ -17,7 +17,6 @@ public: struct Output { // Each surface correspond to a different material Vector surfaces; - Array collision_surface; FixedArray, Cube::SIDE_COUNT> transition_surfaces; Mesh::PrimitiveType primitive_type = Mesh::PRIMITIVE_TRIANGLES; unsigned int compression_flags = Mesh::ARRAY_COMPRESS_DEFAULT; diff --git a/terrain/voxel_block.cpp b/terrain/voxel_block.cpp index fc88a244..de66c054 100644 --- a/terrain/voxel_block.cpp +++ b/terrain/voxel_block.cpp @@ -7,27 +7,32 @@ // Faster version of Mesh::create_trimesh_shape() // See https://github.com/Zylann/godot_voxel/issues/54 // -static Ref create_concave_polygon_shape(Array surface_arrays) { - - PoolVector positions = surface_arrays[Mesh::ARRAY_VERTEX]; - PoolVector indices = surface_arrays[Mesh::ARRAY_INDEX]; - - ERR_FAIL_COND_V(positions.size() < 3, Ref()); - ERR_FAIL_COND_V(indices.size() < 3, Ref()); - ERR_FAIL_COND_V(indices.size() % 3 != 0, Ref()); - - int face_points_count = indices.size(); +static Ref create_concave_polygon_shape(Vector surfaces) { PoolVector face_points; - face_points.resize(face_points_count); - { - PoolVector::Write w = face_points.write(); - PoolVector::Read index_r = indices.read(); - PoolVector::Read position_r = positions.read(); + for(int i = 0; i < surfaces.size(); i++) { + const Array &surface_arrays = *surfaces[i]; - for (int i = 0; i < face_points_count; ++i) { - w[i] = position_r[index_r[i]]; + PoolVector positions = surface_arrays[Mesh::ARRAY_VERTEX]; + PoolVector indices = surface_arrays[Mesh::ARRAY_INDEX]; + + ERR_FAIL_COND_V(positions.size() < 3, Ref()); + ERR_FAIL_COND_V(indices.size() < 3, Ref()); + ERR_FAIL_COND_V(indices.size() % 3 != 0, Ref()); + + int face_points_count = indices.size(); + int face_points_start = face_points.size(); + face_points.resize(face_points_start + face_points_count); + + { + PoolVector::Write w = face_points.write(); + PoolVector::Read index_r = indices.read(); + PoolVector::Read position_r = positions.read(); + + for (int i = face_points_start; i < face_points_count; ++i) { + w[i] = position_r[index_r[i]]; + } } } @@ -36,17 +41,6 @@ static Ref create_concave_polygon_shape(Array surface_array return shape; } -static Vector> create_concave_polygon_shapes(Vector surface_arrays) { - - Vector> shapes = Vector>(); - - for(int i = 0; i < surface_arrays.size(); i++) { - shapes.push_back(create_concave_polygon_shape(surface_arrays[i])); - } - - return shapes; -} - // Helper VoxelBlock *VoxelBlock::create(Vector3i bpos, Ref buffer, unsigned int size, unsigned int p_lod_index) { const int bs = size; @@ -96,7 +90,7 @@ void VoxelBlock::set_world(Ref p_world) { } } -void VoxelBlock::set_mesh(Ref mesh, Spatial *node, bool generate_collision, Array surface_arrays, bool debug_collision) { +void VoxelBlock::set_mesh(Ref mesh, Spatial *node, bool generate_collision, Vector surface_arrays, bool debug_collision) { // TODO Don't add mesh instance to the world if it's not visible. // I suspect Godot is trying to include invisible mesh instances into the culling process, // which is killing performance when LOD is used (i.e many meshes are in pool but hidden) diff --git a/terrain/voxel_block.h b/terrain/voxel_block.h index 2e653a0b..4877b0bb 100644 --- a/terrain/voxel_block.h +++ b/terrain/voxel_block.h @@ -34,7 +34,7 @@ public: // Visuals and physics void set_world(Ref p_world); - void set_mesh(Ref mesh, Spatial *node, bool generate_collision, Array surface_arrays, bool debug_collision); + void set_mesh(Ref mesh, Spatial *node, bool generate_collision, Vector surface_arrays, bool debug_collision); void set_transition_mesh(Ref mesh, int side); bool has_mesh() const; diff --git a/terrain/voxel_lod_terrain.cpp b/terrain/voxel_lod_terrain.cpp index e85e7cdb..7d3f4180 100644 --- a/terrain/voxel_lod_terrain.cpp +++ b/terrain/voxel_lod_terrain.cpp @@ -14,7 +14,7 @@ const uint32_t MAIN_THREAD_MESHING_BUDGET_MS = 8; namespace { Ref build_mesh(const Vector surfaces, Mesh::PrimitiveType primitive, int compression_flags, - Ref material, Array *collidable_surface) { + Ref material, Vector &collidable_surface) { Ref mesh; mesh.instance(); @@ -32,8 +32,8 @@ Ref build_mesh(const Vector surfaces, Mesh::PrimitiveType prim continue; } - if (collidable_surface != nullptr && collidable_surface->empty()) { - *collidable_surface = surface; + if (collidable_surface[0]->empty()) { + *collidable_surface[0] = surface; } mesh->add_surface_from_arrays(primitive, surface, Array(), compression_flags); @@ -1198,12 +1198,12 @@ void VoxelLodTerrain::_process() { const VoxelMesher::Output mesh_data = ob.data.smooth_surfaces; // TODO Allow multiple collision surfaces - Array collidable_surface; + Vector collidable_surface; Ref mesh = build_mesh( mesh_data.surfaces, mesh_data.primitive_type, mesh_data.compression_flags, - _material, &collidable_surface); + _material, collidable_surface); bool has_collision = _generate_collisions; if (has_collision && _collision_lod_count != -1) { @@ -1220,7 +1220,7 @@ void VoxelLodTerrain::_process() { mesh_data.transition_surfaces[dir], mesh_data.primitive_type, mesh_data.compression_flags, - _material, nullptr); + _material, Vector()); block->set_transition_mesh(transition_mesh, dir); } diff --git a/terrain/voxel_terrain.cpp b/terrain/voxel_terrain.cpp index 3574ca59..0651277f 100644 --- a/terrain/voxel_terrain.cpp +++ b/terrain/voxel_terrain.cpp @@ -921,7 +921,7 @@ void VoxelTerrain::_process() { CRASH_COND(block->get_mesh_state() != VoxelBlock::MESH_UPDATE_NOT_SENT); // The block contains empty voxels - block->set_mesh(Ref(), this, _generate_collisions, Array(), get_tree()->is_debugging_collisions_hint()); + block->set_mesh(Ref(), this, _generate_collisions, Vector(), get_tree()->is_debugging_collisions_hint()); block->set_mesh_state(VoxelBlock::MESH_UP_TO_DATE); // Optional, but I guess it might spare some memory @@ -1007,13 +1007,10 @@ void VoxelTerrain::_process() { mesh.instance(); // TODO Allow multiple collision surfaces - Array collidable_surface = Array(); + Vector collidable_surfaces; //need to put both blocky and smooth surfaces into one list int surface_index = 0; const VoxelMeshUpdater::OutputBlockData &data = ob.data; - if (!data.blocky_surfaces.collision_surface.empty()) { - collidable_surface = data.blocky_surfaces.collision_surface; - } for (int i = 0; i < data.blocky_surfaces.surfaces.size(); ++i) { Array surface = data.blocky_surfaces.surfaces[i]; @@ -1021,6 +1018,8 @@ void VoxelTerrain::_process() { continue; } + collidable_surfaces.push_back((Array*)&data.blocky_surfaces.surfaces[i]); + CRASH_COND(surface.size() != Mesh::ARRAY_MAX); if (!is_surface_triangulated(surface)) { continue; @@ -1038,15 +1037,13 @@ void VoxelTerrain::_process() { continue; } + collidable_surfaces.push_back((Array*)&data.smooth_surfaces.surfaces[i]); + CRASH_COND(surface.size() != Mesh::ARRAY_MAX); if (!is_surface_triangulated(surface)) { continue; } - if (collidable_surface.empty()) { - collidable_surface = surface; - } - mesh->add_surface_from_arrays(data.smooth_surfaces.primitive_type, surface, Array(), data.smooth_surfaces.compression_flags); mesh->surface_set_material(surface_index, _materials[i]); ++surface_index; @@ -1056,7 +1053,7 @@ void VoxelTerrain::_process() { mesh = Ref(); } - block->set_mesh(mesh, this, _generate_collisions, collidable_surface, get_tree()->is_debugging_collisions_hint()); + block->set_mesh(mesh, this, _generate_collisions, collidable_surfaces, get_tree()->is_debugging_collisions_hint()); block->set_parent_visible(is_visible()); } From 6b43c9a187df2cd0428e877836f865660b609a0b Mon Sep 17 00:00:00 2001 From: Nathan Date: Thu, 7 May 2020 13:55:16 -0700 Subject: [PATCH 7/8] General improvements --- terrain/voxel_block.cpp | 28 ++++++++++++++++++++-------- terrain/voxel_block.h | 2 +- terrain/voxel_lod_terrain.cpp | 14 ++++---------- terrain/voxel_terrain.cpp | 8 ++++---- 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/terrain/voxel_block.cpp b/terrain/voxel_block.cpp index de66c054..a639a117 100644 --- a/terrain/voxel_block.cpp +++ b/terrain/voxel_block.cpp @@ -7,12 +7,24 @@ // Faster version of Mesh::create_trimesh_shape() // See https://github.com/Zylann/godot_voxel/issues/54 // -static Ref create_concave_polygon_shape(Vector surfaces) { +static Ref create_concave_polygon_shape(Vector surfaces) { PoolVector face_points; + int face_points_size = 0; + //find the correct size for face_points for(int i = 0; i < surfaces.size(); i++) { - const Array &surface_arrays = *surfaces[i]; + const Array &surface_arrays = surfaces[i]; + PoolVector indices = surface_arrays[Mesh::ARRAY_INDEX]; + + face_points_size += indices.size(); + } + face_points.resize(face_points_size); + + //copy the points into it + int face_points_offset = 0; + for(int i = 0; i < surfaces.size(); i++) { + const Array &surface_arrays = surfaces[i]; PoolVector positions = surface_arrays[Mesh::ARRAY_VERTEX]; PoolVector indices = surface_arrays[Mesh::ARRAY_INDEX]; @@ -21,19 +33,19 @@ static Ref create_concave_polygon_shape(Vector surf ERR_FAIL_COND_V(indices.size() < 3, Ref()); ERR_FAIL_COND_V(indices.size() % 3 != 0, Ref()); - int face_points_count = indices.size(); - int face_points_start = face_points.size(); - face_points.resize(face_points_start + face_points_count); + int face_points_count = face_points_offset + indices.size(); { PoolVector::Write w = face_points.write(); PoolVector::Read index_r = indices.read(); PoolVector::Read position_r = positions.read(); - for (int i = face_points_start; i < face_points_count; ++i) { - w[i] = position_r[index_r[i]]; + for (int p = face_points_offset; p < face_points_count; ++p) { + w[p] = position_r[index_r[p - face_points_offset]]; } } + + face_points_offset += indices.size(); } Ref shape = memnew(ConcavePolygonShape); @@ -90,7 +102,7 @@ void VoxelBlock::set_world(Ref p_world) { } } -void VoxelBlock::set_mesh(Ref mesh, Spatial *node, bool generate_collision, Vector surface_arrays, bool debug_collision) { +void VoxelBlock::set_mesh(Ref mesh, Spatial *node, bool generate_collision, Vector surface_arrays, bool debug_collision) { // TODO Don't add mesh instance to the world if it's not visible. // I suspect Godot is trying to include invisible mesh instances into the culling process, // which is killing performance when LOD is used (i.e many meshes are in pool but hidden) diff --git a/terrain/voxel_block.h b/terrain/voxel_block.h index 4877b0bb..8b804605 100644 --- a/terrain/voxel_block.h +++ b/terrain/voxel_block.h @@ -34,7 +34,7 @@ public: // Visuals and physics void set_world(Ref p_world); - void set_mesh(Ref mesh, Spatial *node, bool generate_collision, Vector surface_arrays, bool debug_collision); + void set_mesh(Ref mesh, Spatial *node, bool generate_collision, Vector surface_arrays, bool debug_collision); void set_transition_mesh(Ref mesh, int side); bool has_mesh() const; diff --git a/terrain/voxel_lod_terrain.cpp b/terrain/voxel_lod_terrain.cpp index 7d3f4180..d6a81452 100644 --- a/terrain/voxel_lod_terrain.cpp +++ b/terrain/voxel_lod_terrain.cpp @@ -14,7 +14,7 @@ const uint32_t MAIN_THREAD_MESHING_BUDGET_MS = 8; namespace { Ref build_mesh(const Vector surfaces, Mesh::PrimitiveType primitive, int compression_flags, - Ref material, Vector &collidable_surface) { + Ref material) { Ref mesh; mesh.instance(); @@ -32,10 +32,6 @@ Ref build_mesh(const Vector surfaces, Mesh::PrimitiveType prim continue; } - if (collidable_surface[0]->empty()) { - *collidable_surface[0] = surface; - } - mesh->add_surface_from_arrays(primitive, surface, Array(), compression_flags); mesh->surface_set_material(surface_index, material); // No multi-material supported yet @@ -1197,20 +1193,18 @@ void VoxelLodTerrain::_process() { const VoxelMesher::Output mesh_data = ob.data.smooth_surfaces; - // TODO Allow multiple collision surfaces - Vector collidable_surface; Ref mesh = build_mesh( mesh_data.surfaces, mesh_data.primitive_type, mesh_data.compression_flags, - _material, collidable_surface); + _material); bool has_collision = _generate_collisions; if (has_collision && _collision_lod_count != -1) { has_collision = ob.lod < _collision_lod_count; } - block->set_mesh(mesh, this, has_collision, collidable_surface, get_tree()->is_debugging_collisions_hint()); + block->set_mesh(mesh, this, has_collision, mesh_data.surfaces, get_tree()->is_debugging_collisions_hint()); { VOXEL_PROFILE_SCOPE(profile_process_receive_mesh_updates_block_update_transitions); @@ -1220,7 +1214,7 @@ void VoxelLodTerrain::_process() { mesh_data.transition_surfaces[dir], mesh_data.primitive_type, mesh_data.compression_flags, - _material, Vector()); + _material); block->set_transition_mesh(transition_mesh, dir); } diff --git a/terrain/voxel_terrain.cpp b/terrain/voxel_terrain.cpp index 0651277f..f73292d8 100644 --- a/terrain/voxel_terrain.cpp +++ b/terrain/voxel_terrain.cpp @@ -921,7 +921,7 @@ void VoxelTerrain::_process() { CRASH_COND(block->get_mesh_state() != VoxelBlock::MESH_UPDATE_NOT_SENT); // The block contains empty voxels - block->set_mesh(Ref(), this, _generate_collisions, Vector(), get_tree()->is_debugging_collisions_hint()); + block->set_mesh(Ref(), this, _generate_collisions, Vector(), get_tree()->is_debugging_collisions_hint()); block->set_mesh_state(VoxelBlock::MESH_UP_TO_DATE); // Optional, but I guess it might spare some memory @@ -1007,7 +1007,7 @@ void VoxelTerrain::_process() { mesh.instance(); // TODO Allow multiple collision surfaces - Vector collidable_surfaces; //need to put both blocky and smooth surfaces into one list + Vector collidable_surfaces; //need to put both blocky and smooth surfaces into one list int surface_index = 0; const VoxelMeshUpdater::OutputBlockData &data = ob.data; @@ -1018,7 +1018,7 @@ void VoxelTerrain::_process() { continue; } - collidable_surfaces.push_back((Array*)&data.blocky_surfaces.surfaces[i]); + collidable_surfaces.push_back(data.blocky_surfaces.surfaces[i]); CRASH_COND(surface.size() != Mesh::ARRAY_MAX); if (!is_surface_triangulated(surface)) { @@ -1037,7 +1037,7 @@ void VoxelTerrain::_process() { continue; } - collidable_surfaces.push_back((Array*)&data.smooth_surfaces.surfaces[i]); + collidable_surfaces.push_back(data.smooth_surfaces.surfaces[i]); CRASH_COND(surface.size() != Mesh::ARRAY_MAX); if (!is_surface_triangulated(surface)) { From a17ab8bbe7c8c315e44e4badfd507501906086a5 Mon Sep 17 00:00:00 2001 From: Nathan Date: Thu, 7 May 2020 17:33:16 -0700 Subject: [PATCH 8/8] clean up for Zylann --- terrain/voxel_terrain.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/terrain/voxel_terrain.cpp b/terrain/voxel_terrain.cpp index f73292d8..2873727f 100644 --- a/terrain/voxel_terrain.cpp +++ b/terrain/voxel_terrain.cpp @@ -1006,7 +1006,6 @@ void VoxelTerrain::_process() { Ref mesh; mesh.instance(); - // TODO Allow multiple collision surfaces Vector collidable_surfaces; //need to put both blocky and smooth surfaces into one list int surface_index = 0; @@ -1018,13 +1017,13 @@ void VoxelTerrain::_process() { continue; } - collidable_surfaces.push_back(data.blocky_surfaces.surfaces[i]); - CRASH_COND(surface.size() != Mesh::ARRAY_MAX); if (!is_surface_triangulated(surface)) { continue; } + collidable_surfaces.push_back(surface); + mesh->add_surface_from_arrays(data.blocky_surfaces.primitive_type, surface, Array(), data.blocky_surfaces.compression_flags); mesh->surface_set_material(surface_index, _materials[i]); ++surface_index; @@ -1037,13 +1036,13 @@ void VoxelTerrain::_process() { continue; } - collidable_surfaces.push_back(data.smooth_surfaces.surfaces[i]); - CRASH_COND(surface.size() != Mesh::ARRAY_MAX); if (!is_surface_triangulated(surface)) { continue; } + collidable_surfaces.push_back(surface); + mesh->add_surface_from_arrays(data.smooth_surfaces.primitive_type, surface, Array(), data.smooth_surfaces.compression_flags); mesh->surface_set_material(surface_index, _materials[i]); ++surface_index;