Merge branch 'master' into voxel_gen_graph

This commit is contained in:
Marc Gilleron 2020-05-08 18:09:09 +01:00
commit 33d38e61b1
14 changed files with 135 additions and 55 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@
*.o
*.autosave
*.bc
*.d

4
SCsub
View File

@ -24,6 +24,10 @@ files = [
for f in files:
env_voxel.add_source_files(env.modules_sources, f)
# Ignored clang warnings because Godot's codebase is old and isn't using override yet
if env['platform'] == 'osx' or env['platform'] == 'android':
env_voxel.Append(CXXFLAGS=['-Wno-inconsistent-missing-override'])
# Doesn't work, because reasons
#if env.msvc:
# env_voxel.Append(CXXFLAGS=['/std:c++17'])

View File

@ -402,6 +402,43 @@ void Voxel::set_side_pattern_index(int side, uint32_t i) {
_side_pattern_index[side] = i;
}
Ref<Resource> Voxel::duplicate(bool p_subresources) const {
Ref<Voxel> d_ref;
d_ref.instance();
Voxel &d = **d_ref;
d._library = _library;
d._id = -1;
d._name = _name;
d._material_id = _material_id;
d._is_transparent = _is_transparent;
d._color = _color;
d._geometry_type = _geometry_type;
d._cube_geometry_padding_y = _cube_geometry_padding_y;
d._cube_tiles = _cube_tiles;
d._custom_mesh = _custom_mesh;
d._collision_aabbs = _collision_aabbs;
d._contributes_to_ao = _contributes_to_ao;
d._side_pattern_index = _side_pattern_index;
d._model_positions = _model_positions;
d._model_normals = _model_normals;
d._model_indices = _model_indices;
d._model_uvs = _model_uvs;
d._model_side_positions = _model_side_positions;
d._model_side_uvs = _model_side_uvs;
d._model_side_indices = _model_side_indices;
if (p_subresources) {
if (d._custom_mesh.is_valid()) {
d._custom_mesh = d._custom_mesh->duplicate(p_subresources);
}
}
return d_ref;
}
void Voxel::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_voxel_name", "name"), &Voxel::set_voxel_name);
@ -453,7 +490,7 @@ void Voxel::_bind_methods() {
Array Voxel::_b_get_collision_aabbs() const {
Array array;
array.resize(_collision_aabbs.size());
for (int i = 0; i < _collision_aabbs.size(); ++i) {
for (size_t i = 0; i < _collision_aabbs.size(); ++i) {
array[i] = _collision_aabbs[i];
}
return array;

View File

@ -7,7 +7,7 @@
class VoxelLibrary;
// TODO Rename VoxelType?
// TODO Rename VoxelBlockyType?
// Definition of one type of voxel.
// A voxel can be a simple coloured cube, or a more complex model.
// Important: it is recommended that you create voxels from a library rather than using new().
@ -60,6 +60,8 @@ public:
void set_geometry_type(GeometryType type);
GeometryType get_geometry_type() const;
Ref<Resource> duplicate(bool p_subresources) const override;
//------------------------------------------
// Properties for native usage only

View File

@ -181,7 +181,7 @@ void VoxelLibrary::generate_side_culling_matrix() {
};
std::vector<Pattern> patterns;
uint32_t full_side_pattern_index = -1;
uint32_t full_side_pattern_index = NULL_INDEX;
// Gather patterns
for (uint16_t type_id = 0; type_id < _voxel_types.size(); ++type_id) {
@ -248,7 +248,7 @@ void VoxelLibrary::generate_side_culling_matrix() {
}
// Find if the same pattern already exists
unsigned int pattern_index = -1;
uint32_t pattern_index = NULL_INDEX;
for (unsigned int i = 0; i < patterns.size(); ++i) {
if (patterns[i].bitmap == bitmap) {
pattern_index = i;
@ -258,7 +258,7 @@ void VoxelLibrary::generate_side_culling_matrix() {
// Get or create pattern
Pattern *pattern = nullptr;
if (pattern_index != -1) {
if (pattern_index != NULL_INDEX) {
pattern = &patterns[pattern_index];
} else {
pattern_index = patterns.size();
@ -269,7 +269,7 @@ void VoxelLibrary::generate_side_culling_matrix() {
CRASH_COND(pattern == nullptr);
if (full_side_pattern_index == -1 && bitmap.all()) {
if (full_side_pattern_index == NULL_INDEX && bitmap.all()) {
full_side_pattern_index = pattern_index;
}
if (pattern_index != full_side_pattern_index) {

View File

@ -11,6 +11,7 @@ class VoxelLibrary : public Resource {
public:
// Limit based on maximum supported by VoxelMesherBlocky
static const unsigned int MAX_VOXEL_TYPES = 65536;
static const uint32_t NULL_INDEX = 0xFFFFFFFF;
VoxelLibrary();
~VoxelLibrary();

View File

@ -299,7 +299,7 @@ inline void scale_positions(PoolVector3Array &positions, float scale) {
Array generate_debug_octree_mesh(OctreeNode *root, int scale) {
struct GetMaxDepth {
int max_depth;
int max_depth = 0;
void operator()(OctreeNode *_, int depth) {
if (depth > max_depth) {
max_depth = depth;

View File

@ -7,28 +7,45 @@
// 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();
static Ref<ConcavePolygonShape> create_concave_polygon_shape(Vector<Array> surfaces) {
PoolVector<Vector3> face_points;
face_points.resize(face_points_count);
int face_points_size = 0;
{
PoolVector<Vector3>::Write w = face_points.write();
PoolVector<int>::Read index_r = indices.read();
PoolVector<Vector3>::Read position_r = positions.read();
//find the correct size for face_points
for(int i = 0; i < surfaces.size(); i++) {
const Array &surface_arrays = surfaces[i];
PoolVector<int> indices = surface_arrays[Mesh::ARRAY_INDEX];
for (int i = 0; i < face_points_count; ++i) {
w[i] = position_r[index_r[i]];
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<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 = face_points_offset + indices.size();
{
PoolVector<Vector3>::Write w = face_points.write();
PoolVector<int>::Read index_r = indices.read();
PoolVector<Vector3>::Read position_r = positions.read();
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<ConcavePolygonShape> shape = memnew(ConcavePolygonShape);
@ -85,7 +102,7 @@ void VoxelBlock::set_world(Ref<World> p_world) {
}
}
void VoxelBlock::set_mesh(Ref<Mesh> mesh, Spatial *node, bool generate_collision, Array surface_arrays, bool debug_collision) {
void VoxelBlock::set_mesh(Ref<Mesh> mesh, Spatial *node, bool generate_collision, Vector<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)

View File

@ -34,7 +34,7 @@ public:
// Visuals and physics
void set_world(Ref<World> p_world);
void set_mesh(Ref<Mesh> mesh, Spatial *node, bool generate_collision, Array surface_arrays, bool debug_collision);
void set_mesh(Ref<Mesh> mesh, Spatial *node, bool generate_collision, Vector<Array> surface_arrays, bool debug_collision);
void set_transition_mesh(Ref<Mesh> mesh, int side);
bool has_mesh() const;

View File

@ -83,7 +83,7 @@ static Vector3 get_motion(AABB box, Vector3 motion, const std::vector<AABB> &env
AABB expanded_box = expand_with_vector(box, motion);
Vector<AABB> colliding_boxes;
for (int i = 0; i < environment_boxes.size(); ++i) {
for (size_t i = 0; i < environment_boxes.size(); ++i) {
const AABB &other = environment_boxes[i];
if (expanded_box.intersects(other)) {
colliding_boxes.push_back(other);

View File

@ -14,7 +14,7 @@ const uint32_t MAIN_THREAD_MESHING_BUDGET_MS = 8;
namespace {
Ref<ArrayMesh> build_mesh(const Vector<Array> surfaces, Mesh::PrimitiveType primitive, int compression_flags,
Ref<Material> material, Array *collidable_surface) {
Ref<Material> material) {
Ref<ArrayMesh> mesh;
mesh.instance();
@ -32,10 +32,6 @@ Ref<ArrayMesh> build_mesh(const Vector<Array> surfaces, Mesh::PrimitiveType prim
continue;
}
if (collidable_surface != nullptr && collidable_surface->empty()) {
*collidable_surface = 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
Array collidable_surface;
Ref<ArrayMesh> 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, nullptr);
_material);
block->set_transition_mesh(transition_mesh, dir);
}

View File

@ -36,7 +36,7 @@ VoxelTerrain::VoxelTerrain() {
VoxelTerrain::~VoxelTerrain() {
print_line("Destroying VoxelTerrain");
if (_stream_thread) {
if (_stream_thread != nullptr) {
// Schedule saving of all modified blocks,
// without copy because we are destroying the map anyways
save_all_modified_blocks(false);
@ -51,7 +51,7 @@ VoxelTerrain::~VoxelTerrain() {
String VoxelTerrain::get_configuration_warning() const {
if (_stream.is_valid()) {
if (! (_stream->get_used_channels_mask() & ((1<<VoxelBuffer::CHANNEL_TYPE) | (1<<VoxelBuffer::CHANNEL_SDF)))) {
if (!(_stream->get_used_channels_mask() & ((1 << VoxelBuffer::CHANNEL_TYPE) | (1 << VoxelBuffer::CHANNEL_SDF)))) {
return TTR("VoxelTerrain supports only stream channels \"Type\" or \"Sdf\".");
}
}
@ -710,6 +710,8 @@ void VoxelTerrain::get_viewer_pos_and_direction(Vector3 &out_pos, Vector3 &out_d
void VoxelTerrain::send_block_data_requests() {
ERR_FAIL_COND(_stream_thread == nullptr);
VoxelDataLoader::Input input;
Vector3 viewer_pos;
@ -736,7 +738,6 @@ void VoxelTerrain::send_block_data_requests() {
}
void VoxelTerrain::_process() {
// TODO Should be able to run without library, tho!
if (_library.is_null()) {
return;
@ -791,13 +792,17 @@ void VoxelTerrain::_process() {
_last_view_distance_blocks = _view_distance_blocks;
_last_viewer_block_pos = viewer_block_pos;
send_block_data_requests();
// It's possible the user didn't set a stream yet
if (_stream_thread != nullptr) {
send_block_data_requests();
}
_stats.time_request_blocks_to_load = profiling_clock.restart();
// Get block loading responses
// Note: if block loading is too fast, this can cause stutters. It should only happen on first load, though.
{
if (_stream_thread != nullptr) {
VoxelDataLoader::Output output;
_stream_thread->pop(output);
//print_line(String("Receiving {0} blocks").format(varray(output.emerged_blocks.size())));
@ -893,6 +898,8 @@ void VoxelTerrain::_process() {
// Send mesh updates
{
ERR_FAIL_COND(_block_updater == nullptr);
VoxelMeshUpdater::Input input;
input.priority_position = viewer_block_pos;
input.priority_direction = viewer_direction;
@ -904,7 +911,7 @@ void VoxelTerrain::_process() {
// Smooth meshing works on more neighbors, so checking a single block isn't enough to ignore it,
// but that will slow down meshing a lot.
// TODO This is one reason to separate terrain systems between blocky and smooth (other reason is LOD)
if (! (_stream->get_used_channels_mask() & (1<<VoxelBuffer::CHANNEL_SDF))) {
if (!(_stream->get_used_channels_mask() & (1 << VoxelBuffer::CHANNEL_SDF))) {
VoxelBlock *block = _map->get_block(block_pos);
if (block == nullptr) {
continue;
@ -921,7 +928,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, Array(), get_tree()->is_debugging_collisions_hint());
block->set_mesh(Ref<Mesh>(), this, _generate_collisions, Vector<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
@ -1006,8 +1013,7 @@ void VoxelTerrain::_process() {
Ref<ArrayMesh> mesh;
mesh.instance();
// TODO Allow multiple collision surfaces
Array collidable_surface;
Vector<Array> collidable_surfaces; //need to put both blocky and smooth surfaces into one list
int surface_index = 0;
const VoxelMeshUpdater::OutputBlockData &data = ob.data;
@ -1023,9 +1029,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 +1048,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 +1059,7 @@ void VoxelTerrain::_process() {
mesh = Ref<Mesh>();
}
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());
}
@ -1072,7 +1074,7 @@ void VoxelTerrain::_process() {
Ref<VoxelTool> VoxelTerrain::get_voxel_tool() {
Ref<VoxelTool> vt = memnew(VoxelToolTerrain(this, _map));
if (_stream.is_valid()) {
if(_stream->get_used_channels_mask() & (1<<VoxelBuffer::CHANNEL_SDF)) {
if (_stream->get_used_channels_mask() & (1 << VoxelBuffer::CHANNEL_SDF)) {
vt->set_channel(VoxelBuffer::CHANNEL_SDF);
} else {
vt->set_channel(VoxelBuffer::CHANNEL_TYPE);

View File

@ -75,6 +75,28 @@ bool voxel_raycast(
} else
tcross_z = g_infinite; // Will never cross on X
// Workaround for integer positions
// Adapted from https://github.com/bulletphysics/bullet3/blob/3dbe5426bf7387e532c17df9a1c5e5a4972c298a/src/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp#L418
if (tcross_x == 0.0) {
tcross_x += tdelta_x;
// If going backwards, we should ignore the position we would get by the above flooring,
// because the ray is not heading in that direction
if (xi_step == -1)
hit_pos.x -= 1;
}
if (tcross_y == 0.0) {
tcross_y += tdelta_y;
if (yi_step == -1)
hit_pos.y -= 1;
}
if (tcross_z == 0.0) {
tcross_z += tdelta_z;
if (zi_step == -1)
hit_pos.z -= 1;
}
/* Iteration */
do {

View File

@ -80,7 +80,7 @@ void VoxelMemoryPool::debug_print() {
int i = 0;
while ((key = _pools.next(key))) {
Pool *pool = _pools.get(*key);
print_line(String("Pool {0} for size {1}: {2} blocks").format(varray(i, *key, pool->blocks.size())));
print_line(String("Pool {0} for size {1}: {2} blocks").format(varray(i, *key, static_cast<int>(pool->blocks.size()))));
++i;
}
}