Merge branch 'master' into voxel_gen_graph
This commit is contained in:
commit
33d38e61b1
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@
|
||||
*.o
|
||||
*.autosave
|
||||
*.bc
|
||||
*.d
|
||||
|
4
SCsub
4
SCsub
@ -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'])
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user