Fix transition meshes not being scaled with LOD, provide LOD in mesher API

This commit is contained in:
Marc Gilleron 2020-01-04 23:24:33 +00:00
parent 716b820599
commit 8056f7e679
14 changed files with 90 additions and 59 deletions

View File

@ -56,7 +56,7 @@ void VoxelMesherBlocky::set_occlusion_enabled(bool enable) {
_bake_occlusion = enable;
}
void VoxelMesherBlocky::build(VoxelMesher::Output &output, const VoxelBuffer &buffer) {
void VoxelMesherBlocky::build(VoxelMesher::Output &output, const VoxelMesher::Input &input) {
//uint64_t time_before = OS::get_singleton()->get_ticks_usec();
const int channel = VoxelBuffer::CHANNEL_TYPE;
@ -86,9 +86,16 @@ void VoxelMesherBlocky::build(VoxelMesher::Output &output, const VoxelBuffer &bu
// - Slower
// => Could be implemented in a separate class?
const VoxelBuffer &voxels = input.voxels;
#ifdef TOOLS_ENABLED
if (input.lod != 0) {
WARN_PRINT("VoxelMesherBlocky received lod != 0, it is not supported");
}
#endif
// Data must be padded, hence the off-by-one
Vector3i min = Vector3i(get_minimum_padding());
Vector3i max = buffer.get_size() - Vector3i(get_maximum_padding());
Vector3i max = voxels.get_size() - Vector3i(get_maximum_padding());
int index_offsets[MAX_MATERIALS] = { 0 };
@ -99,7 +106,7 @@ void VoxelMesherBlocky::build(VoxelMesher::Output &output, const VoxelBuffer &bu
// That means we can use raw pointers to voxel data inside instead of using the higher-level getters,
// and then save a lot of time.
uint8_t *type_buffer = buffer.get_channel_raw(channel);
uint8_t *type_buffer = voxels.get_channel_raw(channel);
/* _
// | \
// /\ \\
@ -122,8 +129,8 @@ void VoxelMesherBlocky::build(VoxelMesher::Output &output, const VoxelBuffer &bu
// 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 = buffer.get_size().y;
int deck_size = buffer.get_size().x * row_size;
int row_size = voxels.get_size().y;
int deck_size = voxels.get_size().x * row_size;
int side_neighbor_lut[Cube::SIDE_COUNT];
side_neighbor_lut[Cube::SIDE_LEFT] = row_size;

View File

@ -27,7 +27,7 @@ public:
void set_occlusion_enabled(bool enable);
bool get_occlusion_enabled() const { return _bake_occlusion; }
void build(VoxelMesher::Output &output, const VoxelBuffer &voxels) override;
void build(VoxelMesher::Output &output, const VoxelMesher::Input &input) override;
VoxelMesher *clone() override;

View File

@ -49,6 +49,12 @@ Array MeshBuilder::commit(bool wireframe) {
return surface;
}
void MeshBuilder::scale(float scale) {
for (auto it = _positions.begin(); it != _positions.end(); ++it) {
*it *= scale;
}
}
void MeshBuilder::clear() {
_positions.clear();
_normals.clear();

View File

@ -37,6 +37,7 @@ public:
_indices.push_back(i);
}
void scale(float scale);
Array commit(bool wireframe);
void clear();

View File

@ -288,7 +288,15 @@ void foreach_node(OctreeNode *root, Action_T &a, int depth = 0) {
}
}
Array generate_debug_octree_mesh(OctreeNode *root) {
inline void scale_positions(PoolVector3Array &positions, float scale) {
PoolVector3Array::Write w = positions.write();
const uint32_t size = positions.size();
for (unsigned int i = 0; i < size; ++i) {
w[i] *= scale;
}
}
Array generate_debug_octree_mesh(OctreeNode *root, int scale) {
struct GetMaxDepth {
int max_depth;
@ -344,6 +352,10 @@ Array generate_debug_octree_mesh(OctreeNode *root) {
return Array();
}
if (scale != 1) {
scale_positions(arrays.positions, scale);
}
Array surface;
surface.resize(Mesh::ARRAY_MAX);
surface[Mesh::ARRAY_VERTEX] = arrays.positions;
@ -353,7 +365,7 @@ Array generate_debug_octree_mesh(OctreeNode *root) {
return surface;
}
Array generate_debug_dual_grid_mesh(const DualGrid &grid) {
Array generate_debug_dual_grid_mesh(const DualGrid &grid, int scale) {
PoolVector3Array positions;
PoolIntArray indices;
@ -380,6 +392,10 @@ Array generate_debug_dual_grid_mesh(const DualGrid &grid) {
return Array();
}
if (scale != 1) {
scale_positions(positions, scale);
}
Array surface;
surface.resize(Mesh::ARRAY_MAX);
surface[Mesh::ARRAY_VERTEX] = positions;
@ -1464,7 +1480,7 @@ VoxelMesherDMC::SeamMode VoxelMesherDMC::get_seam_mode() const {
return _seam_mode;
}
void VoxelMesherDMC::build(VoxelMesher::Output &output, const VoxelBuffer &voxels) {
void VoxelMesherDMC::build(VoxelMesher::Output &output, const VoxelMesher::Input &input) {
// Requirements:
// - Voxel data must be padded
@ -1472,6 +1488,8 @@ void VoxelMesherDMC::build(VoxelMesher::Output &output, const VoxelBuffer &voxel
_stats = {};
const VoxelBuffer &voxels = input.voxels;
if (voxels.is_uniform(VoxelBuffer::CHANNEL_SDF)) {
// That won't produce any polygon
return;
@ -1537,7 +1555,7 @@ void VoxelMesherDMC::build(VoxelMesher::Output &output, const VoxelBuffer &voxel
if (root != nullptr) {
if (_mesh_mode == MESH_DEBUG_OCTREE) {
surface = dmc::generate_debug_octree_mesh(root);
surface = dmc::generate_debug_octree_mesh(root, 1 << input.lod);
} else {
@ -1550,7 +1568,7 @@ void VoxelMesherDMC::build(VoxelMesher::Output &output, const VoxelBuffer &voxel
_stats.dualgrid_derivation_time = OS::get_singleton()->get_ticks_usec() - time_before;
if (_mesh_mode == MESH_DEBUG_DUAL_GRID) {
surface = dmc::generate_debug_dual_grid_mesh(_dual_grid);
surface = dmc::generate_debug_dual_grid_mesh(_dual_grid, 1 << input.lod);
} else {
@ -1576,6 +1594,9 @@ void VoxelMesherDMC::build(VoxelMesher::Output &output, const VoxelBuffer &voxel
if (surface.empty()) {
time_before = OS::get_singleton()->get_ticks_usec();
if (input.lod > 0) {
_mesh_builder.scale(1 << input.lod);
}
surface = _mesh_builder.commit(_mesh_mode == MESH_WIREFRAME);
_stats.commit_time = OS::get_singleton()->get_ticks_usec() - time_before;
}

View File

@ -101,7 +101,7 @@ public:
void set_seam_mode(SeamMode mode);
SeamMode get_seam_mode() const;
void build(VoxelMesher::Output &output, const VoxelBuffer &voxels) override;
void build(VoxelMesher::Output &output, const VoxelMesher::Input &input) override;
Dictionary get_statistics() const;

View File

@ -73,7 +73,7 @@ VoxelMesherMC::VoxelMesherMC() {
set_padding(MIN_PADDING, MAX_PADDING);
}
void VoxelMesherMC::build(VoxelMesher::Output &output, const VoxelBuffer &voxels) {
void VoxelMesherMC::build(VoxelMesher::Output &output, const VoxelMesher::Input &input) {
int channel = VoxelBuffer::CHANNEL_SDF;
@ -85,6 +85,7 @@ void VoxelMesherMC::build(VoxelMesher::Output &output, const VoxelBuffer &voxels
_output_normals.clear();
_output_indices.clear();
const VoxelBuffer &voxels = input.voxels;
build_internal(voxels, channel);
// OS::get_singleton()->print("vertices: %i, normals: %i, indices: %i\n",
// m_output_vertices.size(),

View File

@ -3,6 +3,7 @@
#include "../voxel_mesher.h"
// TODO Remove it.
// Simple marching cubes.
// Implementation is simplified from old Transvoxel code.
class VoxelMesherMC : public VoxelMesher {
@ -19,7 +20,7 @@ public:
VoxelMesherMC();
void build(VoxelMesher::Output &output, const VoxelBuffer &voxels) override;
void build(VoxelMesher::Output &output, const VoxelMesher::Input &input) override;
void set_seam_mode(SeamMode mode);
SeamMode get_seam_mode() const;

View File

@ -155,7 +155,20 @@ void VoxelMesherTransvoxel::fill_surface_arrays(Array &arrays) {
arrays[Mesh::ARRAY_INDEX] = indices;
}
void VoxelMesherTransvoxel::build(VoxelMesher::Output &output, const VoxelBuffer &voxels) {
void VoxelMesherTransvoxel::scale_output(float factor) {
for (auto it = _output_vertices.begin(); it != _output_vertices.end(); ++it) {
*it *= factor;
}
for (auto it = _output_extra.begin(); it != _output_extra.end(); ++it) {
Color &c = *it;
c.r *= factor;
c.g *= factor;
c.b *= factor;
}
}
void VoxelMesherTransvoxel::build(VoxelMesher::Output &output, const VoxelMesher::Input &input) {
int channel = VoxelBuffer::CHANNEL_SDF;
@ -165,6 +178,7 @@ void VoxelMesherTransvoxel::build(VoxelMesher::Output &output, const VoxelBuffer
// Once capacity is big enough, no more memory should be allocated
clear_output();
const VoxelBuffer &voxels = input.voxels;
build_internal(voxels, channel);
if (_output_vertices.size() == 0) {
@ -173,6 +187,10 @@ void VoxelMesherTransvoxel::build(VoxelMesher::Output &output, const VoxelBuffer
}
Array regular_arrays;
// TODO Bake LOD into the algorithm. It wasn't so far because it wasn't in the API
if (input.lod > 0) {
scale_output(1 << input.lod);
}
fill_surface_arrays(regular_arrays);
output.surfaces.push_back(regular_arrays);
@ -187,6 +205,10 @@ void VoxelMesherTransvoxel::build(VoxelMesher::Output &output, const VoxelBuffer
}
Array transition_arrays;
scale_output(lod_scale);
if (input.lod > 0) {
scale_output(1 << input.lod);
}
fill_surface_arrays(transition_arrays);
output.transition_surfaces[dir].push_back(transition_arrays);
}

View File

@ -15,7 +15,7 @@ public:
VoxelMesherTransvoxel();
void build(VoxelMesher::Output &output, const VoxelBuffer &voxels) override;
void build(VoxelMesher::Output &output, const VoxelMesher::Input &input) override;
VoxelMesher *clone() override;
@ -46,6 +46,7 @@ private:
int emit_vertex(Vector3 primary, Vector3 normal, uint16_t border_mask, Vector3 secondary);
void clear_output();
void fill_surface_arrays(Array &arrays);
void scale_output(float factor);
private:
FixedArray<std::vector<ReuseCell>, 2> _cache;

View File

@ -5,7 +5,8 @@ Ref<Mesh> VoxelMesher::build_mesh(Ref<VoxelBuffer> voxels) {
ERR_FAIL_COND_V(voxels.is_null(), Ref<ArrayMesh>());
Output output;
build(output, **voxels);
Input input = { **voxels, 0 };
build(output, input);
if (output.surfaces.empty()) {
return Ref<ArrayMesh>();
@ -21,7 +22,7 @@ Ref<Mesh> VoxelMesher::build_mesh(Ref<VoxelBuffer> voxels) {
return mesh;
}
void VoxelMesher::build(Output &output, const VoxelBuffer &voxels) {
void VoxelMesher::build(Output &output, const Input &input) {
ERR_PRINT("Not implemented");
}

View File

@ -9,6 +9,11 @@
class VoxelMesher : public Reference {
GDCLASS(VoxelMesher, Reference)
public:
struct Input {
const VoxelBuffer &voxels;
int lod = 0;
};
struct Output {
// Each surface correspond to a different material
Vector<Array> surfaces;
@ -17,7 +22,7 @@ public:
unsigned int compression_flags = Mesh::ARRAY_COMPRESS_DEFAULT;
};
virtual void build(Output &output, const VoxelBuffer &voxels);
virtual void build(Output &output, const Input &voxels);
// Get how many neighbor voxels need to be accessed around the meshed area.
// If this is not respected, the mesher might produce seams at the edges, or an error

View File

@ -263,6 +263,7 @@ void VoxelBlock::set_transition_mask(uint8_t m) {
tm |= bits[Cube::SIDE_NEGATIVE_Z] << 4;
tm |= bits[Cube::SIDE_POSITIVE_Z] << 5;
// TODO Use a StringName, VoxelStringNames
_shader_material->set_shader_param("u_transition_mask", tm);
}
for (int dir = 0; dir < Cube::SIDE_COUNT; ++dir) {

View File

@ -4,34 +4,6 @@
#include "voxel_lod_terrain.h"
#include <core/os/os.h>
static void scale_mesh_data(Vector<Array> &surfaces, float factor) {
for (int i = 0; i < surfaces.size(); ++i) {
Array &surface = surfaces.write[i]; // There is COW here too but should not happen, hopefully
if (surface.empty()) {
continue;
}
PoolVector3Array positions = surface[Mesh::ARRAY_VERTEX]; // Array of Variants here, implicit cast going on
// Now dear COW, let's make sure there is only ONE ref to that PoolVector,
// so you won't trash performance with pointless allocations
surface[Mesh::ARRAY_VERTEX] = Variant();
{
PoolVector3Array::Write w = positions.write();
int len = positions.size();
for (int j = 0; j < len; ++j) {
w[j] = w[j] * factor;
}
}
// Thank you
surface[Mesh::ARRAY_VERTEX] = positions;
}
}
VoxelMeshUpdater::VoxelMeshUpdater(unsigned int thread_count, MeshingParams params) {
print_line("Constructing VoxelMeshUpdater");
@ -103,21 +75,13 @@ void VoxelMeshUpdater::process_blocks_thread_func(
CRASH_COND(block.voxels.is_null());
VoxelMesher::Input input = { **block.voxels, ib.lod };
if (blocky_mesher.is_valid()) {
blocky_mesher->build(output.blocky_surfaces, **block.voxels);
blocky_mesher->build(output.blocky_surfaces, input);
}
if (smooth_mesher.is_valid()) {
smooth_mesher->build(output.smooth_surfaces, **block.voxels);
}
if (ib.lod > 0) {
// TODO Make this optional if the mesher can factor in the upscale already
float factor = 1 << ib.lod;
scale_mesh_data(output.blocky_surfaces.surfaces, factor);
scale_mesh_data(output.smooth_surfaces.surfaces, factor);
for (int i = 0; i < output.smooth_surfaces.transition_surfaces.size(); ++i) {
scale_mesh_data(output.smooth_surfaces.transition_surfaces[i], factor);
}
smooth_mesher->build(output.smooth_surfaces, input);
}
}
}