Skip to content

Commit

Permalink
Optimize collision shapes, doubles build speed
Browse files Browse the repository at this point in the history
  • Loading branch information
Zylann committed Aug 25, 2019
1 parent 392dea3 commit d4ed637
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 6 deletions.
39 changes: 37 additions & 2 deletions terrain/voxel_block.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,40 @@
#include "voxel_block.h"
#include "../util/zprofiling.h"
#include <scene/3d/spatial.h>
#include <scene/resources/concave_polygon_shape.h>

// Faster version of Mesh::create_trimesh_shape()
// See https://github.com/Zylann/godot_voxel/issues/54
//
static Ref<ConcavePolygonShape> create_concave_polygon_shape(Array surface_arrays) {

PoolVector<Vector3> positions = surface_arrays[Mesh::ARRAY_VERTEX];
PoolVector<int> indices = surface_arrays[Mesh::ARRAY_INDEX];

ERR_FAIL_COND_V(positions.size() < 3, Ref<ConcavePolygonShape>());
ERR_FAIL_COND_V(indices.size() < 3, Ref<ConcavePolygonShape>());
ERR_FAIL_COND_V(indices.size() % 3 != 0, Ref<ConcavePolygonShape>());

int face_points_count = indices.size();
int face_count = face_points_count / 3;

PoolVector<Vector3> face_points;
face_points.resize(face_points_count);

{
PoolVector<Vector3>::Write w = face_points.write();
PoolVector<int>::Read index_r = indices.read();
PoolVector<Vector3>::Read position_r = positions.read();

for (int i = 0; i < face_points_count; ++i) {
w[i] = position_r[index_r[i]];
}
}

Ref<ConcavePolygonShape> shape = memnew(ConcavePolygonShape);
shape->set_faces(face_points);
return shape;
}

// Helper
VoxelBlock *VoxelBlock::create(Vector3i bpos, Ref<VoxelBuffer> buffer, unsigned int size, unsigned int p_lod_index) {
Expand All @@ -22,7 +57,7 @@ VoxelBlock::VoxelBlock() {
VoxelBlock::~VoxelBlock() {
}

void VoxelBlock::set_mesh(Ref<Mesh> mesh, Spatial *node, bool generate_collision, bool debug_collision) {
void VoxelBlock::set_mesh(Ref<Mesh> 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,
// which is killing performance when LOD is used (i.e many meshes are in pool but hidden)
Expand All @@ -47,7 +82,7 @@ void VoxelBlock::set_mesh(Ref<Mesh> mesh, Spatial *node, bool generate_collision
// TODO The day VoxelTerrain becomes a Spatial, this transform will need to be updatable separately

if (generate_collision) {
Ref<Shape> shape = mesh->create_trimesh_shape();
Ref<Shape> shape = create_concave_polygon_shape(surface_arrays);

if (!_static_body.is_valid()) {
_static_body.create();
Expand Down
2 changes: 1 addition & 1 deletion terrain/voxel_block.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class VoxelBlock {

~VoxelBlock();

void set_mesh(Ref<Mesh> mesh, Spatial *node, bool generate_collision, bool debug_collision);
void set_mesh(Ref<Mesh> mesh, Spatial *node, bool generate_collision, Array surface_arrays, bool debug_collision);
bool has_mesh() const;

void set_mesh_state(MeshState ms);
Expand Down
13 changes: 12 additions & 1 deletion terrain/voxel_lod_terrain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,9 @@ void VoxelLodTerrain::_process() {
Ref<ArrayMesh> mesh;
mesh.instance();

// TODO Allow multiple collision surfaces
Array collidable_surface;

unsigned int surface_index = 0;
const VoxelMeshUpdater::OutputBlockData &data = ob.data;
for (int i = 0; i < data.smooth_surfaces.surfaces.size(); ++i) {
Expand All @@ -895,6 +898,14 @@ void VoxelLodTerrain::_process() {
}

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);
mesh->surface_set_material(surface_index, _material);
// No multi-material supported yet
Expand All @@ -910,7 +921,7 @@ void VoxelLodTerrain::_process() {
has_collision = ob.lod < _collision_lod_count;
}

block->set_mesh(mesh, this, has_collision, get_tree()->is_debugging_collisions_hint());
block->set_mesh(mesh, this, has_collision, collidable_surface, get_tree()->is_debugging_collisions_hint());
block->mark_been_meshed();
}

Expand Down
19 changes: 17 additions & 2 deletions terrain/voxel_terrain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,7 @@ void VoxelTerrain::_process() {
CRASH_COND(block->get_mesh_state() != VoxelBlock::MESH_UPDATE_NOT_SENT);

// The block contains empty voxels
block->set_mesh(Ref<Mesh>(), this, _generate_collisions, get_tree()->is_debugging_collisions_hint());
block->set_mesh(Ref<Mesh>(), this, _generate_collisions, Array(), get_tree()->is_debugging_collisions_hint());
block->set_mesh_state(VoxelBlock::MESH_UP_TO_DATE);

// Optional, but I guess it might spare some memory
Expand Down Expand Up @@ -1010,6 +1010,9 @@ void VoxelTerrain::_process() {
Ref<ArrayMesh> mesh;
mesh.instance();

// TODO Allow multiple collision surfaces
Array collidable_surface;

int surface_index = 0;
const VoxelMeshUpdater::OutputBlockData &data = ob.data;
for (int i = 0; i < data.blocky_surfaces.surfaces.size(); ++i) {
Expand All @@ -1020,6 +1023,14 @@ void VoxelTerrain::_process() {
}

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.blocky_surfaces.primitive_type, surface);
mesh->surface_set_material(surface_index, _materials[i]);
++surface_index;
Expand All @@ -1033,6 +1044,10 @@ void VoxelTerrain::_process() {
}

CRASH_COND(surface.size() != Mesh::ARRAY_MAX);
if (!is_surface_triangulated(surface)) {
continue;
}

mesh->add_surface_from_arrays(data.smooth_surfaces.primitive_type, surface);
mesh->surface_set_material(surface_index, _materials[i]);
++surface_index;
Expand All @@ -1042,7 +1057,7 @@ void VoxelTerrain::_process() {
mesh = Ref<Mesh>();
}

block->set_mesh(mesh, this, _generate_collisions, get_tree()->is_debugging_collisions_hint());
block->set_mesh(mesh, this, _generate_collisions, collidable_surface, get_tree()->is_debugging_collisions_hint());
}

shift_up(_blocks_pending_main_thread_update, queue_index);
Expand Down
6 changes: 6 additions & 0 deletions util/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ inline float max(const float &a, const float &b) {
return a > b ? a : b;
}

inline bool is_surface_triangulated(Array surface) {
PoolVector3Array positions = surface[Mesh::ARRAY_VERTEX];
PoolIntArray indices = surface[Mesh::ARRAY_INDEX];
return positions.size() >= 3 && indices.size() >= 3;
}

inline bool is_mesh_empty(Ref<Mesh> mesh_ref) {
if (mesh_ref.is_null())
return true;
Expand Down

0 comments on commit d4ed637

Please sign in to comment.