2017-08-20 06:48:55 -07:00
|
|
|
#include "voxel_block.h"
|
2019-08-25 14:59:55 -07:00
|
|
|
#include "../util/zprofiling.h"
|
2019-08-25 05:04:49 -07:00
|
|
|
#include <scene/3d/spatial.h>
|
2019-08-25 14:59:55 -07:00
|
|
|
#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;
|
|
|
|
}
|
2017-08-20 06:48:55 -07:00
|
|
|
|
|
|
|
// Helper
|
2019-05-03 16:02:10 -07:00
|
|
|
VoxelBlock *VoxelBlock::create(Vector3i bpos, Ref<VoxelBuffer> buffer, unsigned int size, unsigned int p_lod_index) {
|
2017-08-20 06:48:55 -07:00
|
|
|
const int bs = size;
|
|
|
|
ERR_FAIL_COND_V(buffer.is_null(), NULL);
|
|
|
|
ERR_FAIL_COND_V(buffer->get_size() != Vector3i(bs, bs, bs), NULL);
|
|
|
|
|
|
|
|
VoxelBlock *block = memnew(VoxelBlock);
|
2019-05-25 07:50:01 -07:00
|
|
|
block->position = bpos;
|
2019-05-03 16:02:10 -07:00
|
|
|
block->lod_index = p_lod_index;
|
|
|
|
block->_position_in_voxels = bpos * (size << p_lod_index);
|
2017-08-20 06:48:55 -07:00
|
|
|
block->voxels = buffer;
|
2019-05-09 14:00:54 -07:00
|
|
|
|
2017-08-20 06:48:55 -07:00
|
|
|
return block;
|
|
|
|
}
|
|
|
|
|
2019-08-24 17:11:38 -07:00
|
|
|
VoxelBlock::VoxelBlock() {
|
2019-05-08 12:36:27 -07:00
|
|
|
}
|
2017-08-20 09:37:08 -07:00
|
|
|
|
2019-05-08 12:36:27 -07:00
|
|
|
VoxelBlock::~VoxelBlock() {
|
2017-08-20 06:48:55 -07:00
|
|
|
}
|
|
|
|
|
2019-08-25 14:59:55 -07:00
|
|
|
void VoxelBlock::set_mesh(Ref<Mesh> mesh, Spatial *node, bool generate_collision, Array surface_arrays, bool debug_collision) {
|
2019-05-25 06:34:09 -07:00
|
|
|
// 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)
|
|
|
|
// This needs investigation.
|
2017-08-20 09:37:08 -07:00
|
|
|
|
2019-04-23 17:29:47 -07:00
|
|
|
if (mesh.is_valid()) {
|
2017-08-20 09:37:08 -07:00
|
|
|
|
2019-08-25 05:04:49 -07:00
|
|
|
ERR_FAIL_COND(node == nullptr);
|
|
|
|
Ref<World> world = node->get_world();
|
|
|
|
ERR_FAIL_COND(world.is_null());
|
|
|
|
|
2019-08-24 17:11:38 -07:00
|
|
|
if (!_mesh_instance.is_valid()) {
|
2017-08-20 09:37:08 -07:00
|
|
|
// Create instance if it doesn't exist
|
2019-08-24 17:11:38 -07:00
|
|
|
_mesh_instance.create();
|
|
|
|
_mesh_instance.set_world(*world);
|
2019-08-29 16:13:28 -07:00
|
|
|
_mesh_instance.set_visible(_visible);
|
2017-08-20 09:37:08 -07:00
|
|
|
}
|
|
|
|
|
2019-08-25 05:04:49 -07:00
|
|
|
Transform transform(Basis(), _position_in_voxels.to_vec3());
|
|
|
|
|
2019-08-24 17:11:38 -07:00
|
|
|
_mesh_instance.set_mesh(mesh);
|
2019-08-25 05:04:49 -07:00
|
|
|
_mesh_instance.set_transform(transform);
|
2017-08-20 09:37:08 -07:00
|
|
|
// TODO The day VoxelTerrain becomes a Spatial, this transform will need to be updatable separately
|
|
|
|
|
2019-08-25 05:04:49 -07:00
|
|
|
if (generate_collision) {
|
2019-08-25 14:59:55 -07:00
|
|
|
Ref<Shape> shape = create_concave_polygon_shape(surface_arrays);
|
2019-08-25 05:04:49 -07:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
_static_body.add_shape(shape);
|
|
|
|
_static_body.set_debug(debug_collision, *world);
|
2019-08-29 16:13:28 -07:00
|
|
|
_static_body.set_shape_enabled(0, _visible);
|
2019-08-25 05:04:49 -07:00
|
|
|
}
|
|
|
|
|
2017-08-20 09:37:08 -07:00
|
|
|
} else {
|
|
|
|
|
2019-04-23 17:29:47 -07:00
|
|
|
if (_mesh_instance.is_valid()) {
|
2017-08-20 09:37:08 -07:00
|
|
|
// Delete instance if it exists
|
2019-08-24 17:11:38 -07:00
|
|
|
_mesh_instance.destroy();
|
2017-08-20 09:37:08 -07:00
|
|
|
}
|
2019-08-25 05:04:49 -07:00
|
|
|
|
|
|
|
if (_static_body.is_valid()) {
|
|
|
|
_static_body.destroy();
|
|
|
|
}
|
2017-08-20 09:37:08 -07:00
|
|
|
}
|
|
|
|
|
2018-09-29 09:08:05 -07:00
|
|
|
++_mesh_update_count;
|
|
|
|
|
2019-04-23 17:29:47 -07:00
|
|
|
// if(_mesh_update_count > 1) {
|
|
|
|
// print_line(String("Block {0} was updated {1} times").format(varray(pos.to_vec3(), _mesh_update_count)));
|
|
|
|
// }
|
2017-08-20 09:37:08 -07:00
|
|
|
}
|
2017-08-27 17:32:23 -07:00
|
|
|
|
2019-05-03 16:02:10 -07:00
|
|
|
bool VoxelBlock::has_mesh() const {
|
2019-08-24 17:11:38 -07:00
|
|
|
return _mesh_instance.get_mesh().is_valid();
|
2019-05-03 16:02:10 -07:00
|
|
|
}
|
|
|
|
|
2019-05-09 14:00:54 -07:00
|
|
|
void VoxelBlock::set_mesh_state(MeshState ms) {
|
|
|
|
_mesh_state = ms;
|
|
|
|
}
|
|
|
|
|
|
|
|
VoxelBlock::MeshState VoxelBlock::get_mesh_state() const {
|
|
|
|
return _mesh_state;
|
|
|
|
}
|
|
|
|
|
2019-08-24 17:11:38 -07:00
|
|
|
void VoxelBlock::set_world(World *world) {
|
2019-04-23 17:29:47 -07:00
|
|
|
if (_mesh_instance.is_valid()) {
|
2019-08-24 17:11:38 -07:00
|
|
|
_mesh_instance.set_world(world);
|
2017-08-27 17:32:23 -07:00
|
|
|
}
|
2019-08-25 05:04:49 -07:00
|
|
|
if (_static_body.is_valid()) {
|
|
|
|
_static_body.set_world(world);
|
|
|
|
}
|
2017-08-27 17:32:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelBlock::set_visible(bool visible) {
|
2019-08-31 13:46:19 -07:00
|
|
|
if (_visible && visible) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_visible = visible;
|
|
|
|
_set_visible(_visible && _parent_visible);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VoxelBlock::is_visible() const {
|
|
|
|
return _visible;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelBlock::_set_visible(bool visible) {
|
2019-04-23 17:29:47 -07:00
|
|
|
if (_mesh_instance.is_valid()) {
|
2019-08-24 17:11:38 -07:00
|
|
|
_mesh_instance.set_visible(visible);
|
2017-08-27 17:32:23 -07:00
|
|
|
}
|
2019-08-25 05:04:49 -07:00
|
|
|
if (_static_body.is_valid()) {
|
|
|
|
_static_body.set_shape_enabled(0, visible);
|
|
|
|
}
|
2019-05-03 16:02:10 -07:00
|
|
|
}
|
|
|
|
|
2019-08-31 13:46:19 -07:00
|
|
|
void VoxelBlock::set_parent_visible(bool parent_visible) {
|
|
|
|
if (_parent_visible && parent_visible) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_parent_visible = parent_visible;
|
|
|
|
_set_visible(_visible && _parent_visible);
|
2017-08-27 17:32:23 -07:00
|
|
|
}
|
2019-09-03 14:54:40 -07:00
|
|
|
|
|
|
|
void VoxelBlock::set_needs_lodding(bool need_lodding) {
|
|
|
|
_needs_lodding = need_lodding;
|
|
|
|
}
|
2019-09-06 15:24:56 -07:00
|
|
|
|
|
|
|
bool VoxelBlock::is_modified() const {
|
|
|
|
return _modified;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelBlock::set_modified(bool modified) {
|
2019-09-07 13:19:12 -07:00
|
|
|
// if (_modified != modified) {
|
|
|
|
// print_line(String("Marking block {0}[lod{1}] as modified").format(varray(bpos.to_vec3(), lod_index)));
|
|
|
|
// }
|
2019-09-06 15:24:56 -07:00
|
|
|
_modified = modified;
|
|
|
|
}
|