mirror of
https://github.com/Relintai/godot_voxel.git
synced 2024-11-11 20:35:08 +01:00
Add base class to all meshers, gather common things in it
This commit is contained in:
parent
12a97cca5a
commit
faefde721a
1
SCsub
1
SCsub
@ -4,6 +4,7 @@ env.add_source_files(env.modules_sources, "*.cpp")
|
|||||||
env.add_source_files(env.modules_sources, "meshers/blocky/*.cpp")
|
env.add_source_files(env.modules_sources, "meshers/blocky/*.cpp")
|
||||||
env.add_source_files(env.modules_sources, "meshers/transvoxel/*.cpp")
|
env.add_source_files(env.modules_sources, "meshers/transvoxel/*.cpp")
|
||||||
env.add_source_files(env.modules_sources, "meshers/dmc/*.cpp")
|
env.add_source_files(env.modules_sources, "meshers/dmc/*.cpp")
|
||||||
|
env.add_source_files(env.modules_sources, "meshers/*.cpp")
|
||||||
env.add_source_files(env.modules_sources, "providers/*.cpp")
|
env.add_source_files(env.modules_sources, "providers/*.cpp")
|
||||||
env.add_source_files(env.modules_sources, "util/*.cpp")
|
env.add_source_files(env.modules_sources, "util/*.cpp")
|
||||||
env.add_source_files(env.modules_sources, "terrain/*.cpp")
|
env.add_source_files(env.modules_sources, "terrain/*.cpp")
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
#include "../../voxel_library.h"
|
#include "../../voxel_library.h"
|
||||||
#include <core/os/os.h>
|
#include <core/os/os.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void raw_copy_to(PoolVector<T> &to, const Vector<T> &from) {
|
void raw_copy_to(PoolVector<T> &to, const Vector<T> &from) {
|
||||||
to.resize(from.size());
|
to.resize(from.size());
|
||||||
@ -11,26 +13,6 @@ void raw_copy_to(PoolVector<T> &to, const Vector<T> &from) {
|
|||||||
memcpy(w.ptr(), from.ptr(), from.size() * sizeof(T));
|
memcpy(w.ptr(), from.ptr(), from.size() * sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
VoxelMesherBlocky::VoxelMesherBlocky() :
|
|
||||||
_baked_occlusion_darkness(0.8),
|
|
||||||
_bake_occlusion(true) {}
|
|
||||||
|
|
||||||
void VoxelMesherBlocky::set_library(Ref<VoxelLibrary> library) {
|
|
||||||
_library = library;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VoxelMesherBlocky::set_occlusion_darkness(float darkness) {
|
|
||||||
_baked_occlusion_darkness = darkness;
|
|
||||||
if (_baked_occlusion_darkness < 0.0)
|
|
||||||
_baked_occlusion_darkness = 0.0;
|
|
||||||
else if (_baked_occlusion_darkness >= 1.0)
|
|
||||||
_baked_occlusion_darkness = 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VoxelMesherBlocky::set_occlusion_enabled(bool enable) {
|
|
||||||
_bake_occlusion = enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Color Color_greyscale(float c) {
|
inline Color Color_greyscale(float c) {
|
||||||
return Color(c, c, c);
|
return Color(c, c, c);
|
||||||
}
|
}
|
||||||
@ -51,36 +33,35 @@ inline bool is_transparent(const VoxelLibrary &lib, int voxel_id) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<ArrayMesh> VoxelMesherBlocky::build_mesh(Ref<VoxelBuffer> buffer_ref, unsigned int channel, Array materials, Ref<ArrayMesh> mesh) {
|
} // namespace
|
||||||
ERR_FAIL_COND_V(buffer_ref.is_null(), Ref<ArrayMesh>());
|
|
||||||
|
|
||||||
VoxelBuffer &buffer = **buffer_ref;
|
VoxelMesherBlocky::VoxelMesherBlocky() :
|
||||||
Array surfaces = build(buffer, channel, MINIMUM_PADDING);
|
_baked_occlusion_darkness(0.8),
|
||||||
|
_bake_occlusion(true) {}
|
||||||
|
|
||||||
if (mesh.is_null())
|
void VoxelMesherBlocky::set_library(Ref<VoxelLibrary> library) {
|
||||||
mesh.instance();
|
_library = library;
|
||||||
|
|
||||||
int surface = mesh->get_surface_count();
|
|
||||||
for (int i = 0; i < surfaces.size(); ++i) {
|
|
||||||
|
|
||||||
Array arrays = surfaces[i];
|
|
||||||
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays);
|
|
||||||
|
|
||||||
Ref<Material> material = materials[i];
|
|
||||||
if (material.is_valid()) {
|
|
||||||
mesh->surface_set_material(surface, material);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return mesh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Array VoxelMesherBlocky::build(const VoxelBuffer &buffer, unsigned int channel, int padding) {
|
void VoxelMesherBlocky::set_occlusion_darkness(float darkness) {
|
||||||
|
_baked_occlusion_darkness = darkness;
|
||||||
|
if (_baked_occlusion_darkness < 0.0)
|
||||||
|
_baked_occlusion_darkness = 0.0;
|
||||||
|
else if (_baked_occlusion_darkness >= 1.0)
|
||||||
|
_baked_occlusion_darkness = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelMesherBlocky::set_occlusion_enabled(bool enable) {
|
||||||
|
_bake_occlusion = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelMesherBlocky::build(VoxelMesher::Output &output, const VoxelBuffer &buffer, int padding) {
|
||||||
//uint64_t time_before = OS::get_singleton()->get_ticks_usec();
|
//uint64_t time_before = OS::get_singleton()->get_ticks_usec();
|
||||||
|
|
||||||
ERR_FAIL_COND_V(_library.is_null(), Array());
|
ERR_FAIL_COND(_library.is_null());
|
||||||
ERR_FAIL_COND_V(channel >= VoxelBuffer::MAX_CHANNELS, Array());
|
ERR_FAIL_COND(padding < MINIMUM_PADDING);
|
||||||
ERR_FAIL_COND_V(padding < MINIMUM_PADDING, Array());
|
|
||||||
|
const int channel = VoxelBuffer::CHANNEL_TYPE;
|
||||||
|
|
||||||
const VoxelLibrary &library = **_library;
|
const VoxelLibrary &library = **_library;
|
||||||
|
|
||||||
@ -118,7 +99,7 @@ Array VoxelMesherBlocky::build(const VoxelBuffer &buffer, unsigned int channel,
|
|||||||
// That means we can use raw pointers to voxel data inside instead of using the higher-level getters,
|
// 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.
|
// and then save a lot of time.
|
||||||
|
|
||||||
uint8_t *type_buffer = buffer.get_channel_raw(Voxel::CHANNEL_TYPE);
|
uint8_t *type_buffer = buffer.get_channel_raw(channel);
|
||||||
// _
|
// _
|
||||||
// | \
|
// | \
|
||||||
// /\ \\
|
// /\ \\
|
||||||
@ -132,7 +113,7 @@ Array VoxelMesherBlocky::build(const VoxelBuffer &buffer, unsigned int channel,
|
|||||||
// No data to read, the channel is probably uniform
|
// No data to read, the channel is probably uniform
|
||||||
// TODO This is an invalid behavior IF sending a full block of uniformly opaque cubes,
|
// TODO This is an invalid behavior IF sending a full block of uniformly opaque cubes,
|
||||||
// however not likely for terrains because with neighbor padding, such a case means no face would be generated anyways
|
// however not likely for terrains because with neighbor padding, such a case means no face would be generated anyways
|
||||||
return Array();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//CRASH_COND(memarr_len(type_buffer) != buffer.get_volume() * sizeof(uint8_t));
|
//CRASH_COND(memarr_len(type_buffer) != buffer.get_volume() * sizeof(uint8_t));
|
||||||
@ -367,8 +348,6 @@ Array VoxelMesherBlocky::build(const VoxelBuffer &buffer, unsigned int channel,
|
|||||||
// print_line(String("Made mesh v: ") + String::num(_arrays[0].positions.size())
|
// print_line(String("Made mesh v: ") + String::num(_arrays[0].positions.size())
|
||||||
// + String(", i: ") + String::num(_arrays[0].indices.size()));
|
// + String(", i: ") + String::num(_arrays[0].indices.size()));
|
||||||
|
|
||||||
Array surfaces;
|
|
||||||
|
|
||||||
// TODO We could return a single byte array and use Mesh::add_surface down the line?
|
// TODO We could return a single byte array and use Mesh::add_surface down the line?
|
||||||
|
|
||||||
for (int i = 0; i < MAX_MATERIALS; ++i) {
|
for (int i = 0; i < MAX_MATERIALS; ++i) {
|
||||||
@ -407,15 +386,19 @@ Array VoxelMesherBlocky::build(const VoxelBuffer &buffer, unsigned int channel,
|
|||||||
mesh_arrays[Mesh::ARRAY_INDEX] = indices;
|
mesh_arrays[Mesh::ARRAY_INDEX] = indices;
|
||||||
}
|
}
|
||||||
|
|
||||||
surfaces.append(mesh_arrays);
|
output.surfaces.push_back(mesh_arrays);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output.primitive_type = Mesh::PRIMITIVE_TRIANGLES;
|
||||||
|
|
||||||
//uint64_t time_commit = OS::get_singleton()->get_ticks_usec() - time_before;
|
//uint64_t time_commit = OS::get_singleton()->get_ticks_usec() - time_before;
|
||||||
|
|
||||||
//print_line(String("P: {0}, M: {1}, C: {2}").format(varray(time_prep, time_meshing, time_commit)));
|
//print_line(String("P: {0}, M: {1}, C: {2}").format(varray(time_prep, time_meshing, time_commit)));
|
||||||
|
}
|
||||||
|
|
||||||
return surfaces;
|
int VoxelMesherBlocky::get_minimum_padding() const {
|
||||||
|
return MINIMUM_PADDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoxelMesherBlocky::_bind_methods() {
|
void VoxelMesherBlocky::_bind_methods() {
|
||||||
@ -429,8 +412,6 @@ void VoxelMesherBlocky::_bind_methods() {
|
|||||||
ClassDB::bind_method(D_METHOD("set_occlusion_darkness", "value"), &VoxelMesherBlocky::set_occlusion_darkness);
|
ClassDB::bind_method(D_METHOD("set_occlusion_darkness", "value"), &VoxelMesherBlocky::set_occlusion_darkness);
|
||||||
ClassDB::bind_method(D_METHOD("get_occlusion_darkness"), &VoxelMesherBlocky::get_occlusion_darkness);
|
ClassDB::bind_method(D_METHOD("get_occlusion_darkness"), &VoxelMesherBlocky::get_occlusion_darkness);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("build_mesh", "voxel_buffer", "channel", "materials", "existing_mesh"), &VoxelMesherBlocky::build_mesh);
|
|
||||||
|
|
||||||
#ifdef VOXEL_PROFILING
|
#ifdef VOXEL_PROFILING
|
||||||
ClassDB::bind_method(D_METHOD("get_profiling_info"), &VoxelMesherBlocky::get_profiling_info);
|
ClassDB::bind_method(D_METHOD("get_profiling_info"), &VoxelMesherBlocky::get_profiling_info);
|
||||||
#endif
|
#endif
|
||||||
|
@ -3,14 +3,13 @@
|
|||||||
|
|
||||||
#include "../../util/zprofiling.h"
|
#include "../../util/zprofiling.h"
|
||||||
#include "../../voxel.h"
|
#include "../../voxel.h"
|
||||||
#include "../../voxel_buffer.h"
|
|
||||||
#include "../../voxel_library.h"
|
#include "../../voxel_library.h"
|
||||||
|
#include "../voxel_mesher.h"
|
||||||
#include <core/reference.h>
|
#include <core/reference.h>
|
||||||
#include <scene/resources/mesh.h>
|
#include <scene/resources/mesh.h>
|
||||||
|
|
||||||
// TODO Should be renamed VoxelMesherBlocky or something like that
|
class VoxelMesherBlocky : public VoxelMesher {
|
||||||
class VoxelMesherBlocky : public Reference {
|
GDCLASS(VoxelMesherBlocky, VoxelMesher)
|
||||||
GDCLASS(VoxelMesherBlocky, Reference)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const unsigned int MAX_MATERIALS = 8; // Arbitrary. Tweak if needed.
|
static const unsigned int MAX_MATERIALS = 8; // Arbitrary. Tweak if needed.
|
||||||
@ -27,13 +26,14 @@ public:
|
|||||||
void set_occlusion_enabled(bool enable);
|
void set_occlusion_enabled(bool enable);
|
||||||
bool get_occlusion_enabled() const { return _bake_occlusion; }
|
bool get_occlusion_enabled() const { return _bake_occlusion; }
|
||||||
|
|
||||||
Array build(const VoxelBuffer &buffer_ref, unsigned int channel, int padding);
|
void build(VoxelMesher::Output &output, const VoxelBuffer &voxels, int padding) override;
|
||||||
Ref<ArrayMesh> build_mesh(Ref<VoxelBuffer> buffer_ref, unsigned int channel, Array materials, Ref<ArrayMesh> mesh = Ref<ArrayMesh>());
|
int get_minimum_padding() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// TODO Replace those with std::vector, it's faster
|
||||||
struct Arrays {
|
struct Arrays {
|
||||||
Vector<Vector3> positions;
|
Vector<Vector3> positions;
|
||||||
Vector<Vector3> normals;
|
Vector<Vector3> normals;
|
||||||
|
@ -1317,7 +1317,7 @@ float VoxelMesherDMC::get_geometric_error() const {
|
|||||||
return _geometric_error;
|
return _geometric_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
Array VoxelMesherDMC::build(const VoxelBuffer &voxels) {
|
void VoxelMesherDMC::build(VoxelMesher::Output &output, const VoxelBuffer &voxels, int padding) {
|
||||||
|
|
||||||
// Requirements:
|
// Requirements:
|
||||||
// - Voxel data must be padded
|
// - Voxel data must be padded
|
||||||
@ -1328,17 +1328,18 @@ Array VoxelMesherDMC::build(const VoxelBuffer &voxels) {
|
|||||||
|
|
||||||
if (voxels.is_uniform(VoxelBuffer::CHANNEL_ISOLEVEL)) {
|
if (voxels.is_uniform(VoxelBuffer::CHANNEL_ISOLEVEL)) {
|
||||||
// That won't produce any polygon
|
// That won't produce any polygon
|
||||||
return Array();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int padding = 2;
|
ERR_FAIL_COND(padding < MINIMUM_PADDING);
|
||||||
|
|
||||||
const Vector3i buffer_size = voxels.get_size();
|
const Vector3i buffer_size = voxels.get_size();
|
||||||
// Taking previous power of two because the algorithm uses an integer cubic octree, and data should be padded
|
// Taking previous power of two because the algorithm uses an integer cubic octree, and data should be padded
|
||||||
int chunk_size = previous_power_of_2(MIN(MIN(buffer_size.x, buffer_size.y), buffer_size.z));
|
int chunk_size = previous_power_of_2(MIN(MIN(buffer_size.x, buffer_size.y), buffer_size.z));
|
||||||
|
|
||||||
ERR_FAIL_COND_V(voxels.get_size().x < chunk_size + padding * 2, Array());
|
ERR_FAIL_COND(voxels.get_size().x < chunk_size + padding * 2);
|
||||||
ERR_FAIL_COND_V(voxels.get_size().y < chunk_size + padding * 2, Array());
|
ERR_FAIL_COND(voxels.get_size().y < chunk_size + padding * 2);
|
||||||
ERR_FAIL_COND_V(voxels.get_size().z < chunk_size + padding * 2, Array());
|
ERR_FAIL_COND(voxels.get_size().z < chunk_size + padding * 2);
|
||||||
|
|
||||||
// Construct an intermediate to handle padding transparently
|
// Construct an intermediate to handle padding transparently
|
||||||
dmc::VoxelAccess voxels_access(voxels, Vector3i(padding));
|
dmc::VoxelAccess voxels_access(voxels, Vector3i(padding));
|
||||||
@ -1427,33 +1428,17 @@ Array VoxelMesherDMC::build(const VoxelBuffer &voxels) {
|
|||||||
// TODO Marching squares skirts
|
// TODO Marching squares skirts
|
||||||
|
|
||||||
// surfaces[material][array_type], for now single material
|
// surfaces[material][array_type], for now single material
|
||||||
Array surfaces;
|
output.surfaces.push_back(surface);
|
||||||
surfaces.append(surface);
|
|
||||||
return surfaces;
|
if (_mesh_mode == MESH_NORMAL) {
|
||||||
|
output.primitive_type = Mesh::PRIMITIVE_TRIANGLES;
|
||||||
|
} else {
|
||||||
|
output.primitive_type = Mesh::PRIMITIVE_LINES;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<ArrayMesh> VoxelMesherDMC::build_mesh(Ref<VoxelBuffer> voxels) {
|
int VoxelMesherDMC::get_minimum_padding() const {
|
||||||
|
return MINIMUM_PADDING;
|
||||||
ERR_FAIL_COND_V(voxels.is_null(), Ref<ArrayMesh>());
|
|
||||||
|
|
||||||
Array surfaces = build(**voxels);
|
|
||||||
|
|
||||||
if (surfaces.empty()) {
|
|
||||||
return Ref<ArrayMesh>();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<ArrayMesh> mesh;
|
|
||||||
mesh.instance();
|
|
||||||
|
|
||||||
for (int i = 0; i < surfaces.size(); ++i) {
|
|
||||||
if (_mesh_mode == MESH_NORMAL) {
|
|
||||||
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, surfaces[i]);
|
|
||||||
} else {
|
|
||||||
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, surfaces[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return mesh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Dictionary VoxelMesherDMC::get_stats() const {
|
Dictionary VoxelMesherDMC::get_stats() const {
|
||||||
@ -1476,7 +1461,6 @@ void VoxelMesherDMC::_bind_methods() {
|
|||||||
ClassDB::bind_method(D_METHOD("set_geometric_error", "error"), &VoxelMesherDMC::set_geometric_error);
|
ClassDB::bind_method(D_METHOD("set_geometric_error", "error"), &VoxelMesherDMC::set_geometric_error);
|
||||||
ClassDB::bind_method(D_METHOD("get_geometric_error"), &VoxelMesherDMC::get_geometric_error);
|
ClassDB::bind_method(D_METHOD("get_geometric_error"), &VoxelMesherDMC::get_geometric_error);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("build_mesh", "voxel_buffer"), &VoxelMesherDMC::build_mesh);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_stats"), &VoxelMesherDMC::get_stats);
|
ClassDB::bind_method(D_METHOD("get_stats"), &VoxelMesherDMC::get_stats);
|
||||||
|
|
||||||
BIND_ENUM_CONSTANT(MESH_NORMAL);
|
BIND_ENUM_CONSTANT(MESH_NORMAL);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef VOXEL_MESHER_DMC_H
|
#ifndef VOXEL_MESHER_DMC_H
|
||||||
#define VOXEL_MESHER_DMC_H
|
#define VOXEL_MESHER_DMC_H
|
||||||
|
|
||||||
#include "../../voxel_buffer.h"
|
#include "../voxel_mesher.h"
|
||||||
#include "hermite_value.h"
|
#include "hermite_value.h"
|
||||||
#include "mesh_builder.h"
|
#include "mesh_builder.h"
|
||||||
#include "object_pool.h"
|
#include "object_pool.h"
|
||||||
@ -64,9 +64,11 @@ struct DualGrid {
|
|||||||
|
|
||||||
} // namespace dmc
|
} // namespace dmc
|
||||||
|
|
||||||
class VoxelMesherDMC : public Reference {
|
class VoxelMesherDMC : public VoxelMesher {
|
||||||
GDCLASS(VoxelMesherDMC, Reference)
|
GDCLASS(VoxelMesherDMC, VoxelMesher)
|
||||||
public:
|
public:
|
||||||
|
static const int MINIMUM_PADDING = 2;
|
||||||
|
|
||||||
enum MeshMode {
|
enum MeshMode {
|
||||||
MESH_NORMAL,
|
MESH_NORMAL,
|
||||||
MESH_WIREFRAME,
|
MESH_WIREFRAME,
|
||||||
@ -91,8 +93,8 @@ public:
|
|||||||
void set_geometric_error(real_t geometric_error);
|
void set_geometric_error(real_t geometric_error);
|
||||||
float get_geometric_error() const;
|
float get_geometric_error() const;
|
||||||
|
|
||||||
Array build(const VoxelBuffer &voxels);
|
void build(VoxelMesher::Output &output, const VoxelBuffer &voxels, int padding) override;
|
||||||
Ref<ArrayMesh> build_mesh(Ref<VoxelBuffer> voxels);
|
int get_minimum_padding() const override;
|
||||||
|
|
||||||
Dictionary get_stats() const;
|
Dictionary get_stats() const;
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
#include "transvoxel_tables.cpp"
|
#include "transvoxel_tables.cpp"
|
||||||
#include <core/os/os.h>
|
#include <core/os/os.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
inline float tof(int8_t v) {
|
inline float tof(int8_t v) {
|
||||||
return static_cast<float>(v) / 256.f;
|
return static_cast<float>(v) / 256.f;
|
||||||
}
|
}
|
||||||
@ -58,6 +60,8 @@ void copy_to(PoolVector<T> &to, Vector<T> &from) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
VoxelMesherTransvoxel::ReuseCell::ReuseCell() {
|
VoxelMesherTransvoxel::ReuseCell::ReuseCell() {
|
||||||
case_index = 0;
|
case_index = 0;
|
||||||
for (unsigned int i = 0; i < 4; ++i) {
|
for (unsigned int i = 0; i < 4; ++i) {
|
||||||
@ -65,32 +69,15 @@ VoxelMesherTransvoxel::ReuseCell::ReuseCell() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VoxelMesherTransvoxel::VoxelMesherTransvoxel() {
|
int VoxelMesherTransvoxel::get_minimum_padding() const {
|
||||||
|
return MINIMUM_PADDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<ArrayMesh> VoxelMesherTransvoxel::build_mesh(Ref<VoxelBuffer> voxels_ref, unsigned int channel, Ref<ArrayMesh> mesh) {
|
void VoxelMesherTransvoxel::build(VoxelMesher::Output &output, const VoxelBuffer &voxels, int padding) {
|
||||||
|
|
||||||
ERR_FAIL_COND_V(voxels_ref.is_null(), Ref<ArrayMesh>());
|
ERR_FAIL_COND(padding < MINIMUM_PADDING);
|
||||||
|
|
||||||
VoxelBuffer &buffer = **voxels_ref;
|
int channel = VoxelBuffer::CHANNEL_ISOLEVEL;
|
||||||
Array surfaces = build(buffer, channel);
|
|
||||||
|
|
||||||
if (mesh.is_null())
|
|
||||||
mesh.instance();
|
|
||||||
|
|
||||||
//int surface = mesh->get_surface_count();
|
|
||||||
for (int i = 0; i < surfaces.size(); ++i) {
|
|
||||||
Array arrays = surfaces[i];
|
|
||||||
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays);
|
|
||||||
//mesh->surface_set_material(surface, _materials[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return mesh;
|
|
||||||
}
|
|
||||||
|
|
||||||
Array VoxelMesherTransvoxel::build(const VoxelBuffer &voxels, unsigned int channel) {
|
|
||||||
|
|
||||||
ERR_FAIL_COND_V(channel >= VoxelBuffer::MAX_CHANNELS, Array());
|
|
||||||
|
|
||||||
// Initialize dynamic memory:
|
// Initialize dynamic memory:
|
||||||
// These vectors are re-used.
|
// These vectors are re-used.
|
||||||
@ -109,7 +96,7 @@ Array VoxelMesherTransvoxel::build(const VoxelBuffer &voxels, unsigned int chann
|
|||||||
|
|
||||||
if (m_output_vertices.size() == 0) {
|
if (m_output_vertices.size() == 0) {
|
||||||
// The mesh can be empty
|
// The mesh can be empty
|
||||||
return Array();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PoolVector<Vector3> vertices;
|
PoolVector<Vector3> vertices;
|
||||||
@ -128,10 +115,8 @@ Array VoxelMesherTransvoxel::build(const VoxelBuffer &voxels, unsigned int chann
|
|||||||
}
|
}
|
||||||
arrays[Mesh::ARRAY_INDEX] = indices;
|
arrays[Mesh::ARRAY_INDEX] = indices;
|
||||||
|
|
||||||
Array surfaces;
|
output.surfaces.push_back(arrays);
|
||||||
surfaces.append(arrays);
|
output.primitive_type = Mesh::PRIMITIVE_TRIANGLES;
|
||||||
|
|
||||||
return surfaces;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoxelMesherTransvoxel::build_internal(const VoxelBuffer &voxels, unsigned int channel) {
|
void VoxelMesherTransvoxel::build_internal(const VoxelBuffer &voxels, unsigned int channel) {
|
||||||
@ -393,6 +378,4 @@ void VoxelMesherTransvoxel::emit_vertex(Vector3 primary, Vector3 normal) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VoxelMesherTransvoxel::_bind_methods() {
|
void VoxelMesherTransvoxel::_bind_methods() {
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("build", "voxels", "channel", "existing_mesh"), &VoxelMesherTransvoxel::build_mesh, DEFVAL(Variant()));
|
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
#ifndef VOXEL_MESHER_SMOOTH_H
|
#ifndef VOXEL_MESHER_SMOOTH_H
|
||||||
#define VOXEL_MESHER_SMOOTH_H
|
#define VOXEL_MESHER_SMOOTH_H
|
||||||
|
|
||||||
#include "../../voxel_buffer.h"
|
#include "../voxel_mesher.h"
|
||||||
#include <scene/resources/mesh.h>
|
#include <scene/resources/mesh.h>
|
||||||
|
|
||||||
class VoxelMesherTransvoxel : public Reference {
|
class VoxelMesherTransvoxel : public VoxelMesher {
|
||||||
GDCLASS(VoxelMesherTransvoxel, Reference)
|
GDCLASS(VoxelMesherTransvoxel, VoxelMesher)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VoxelMesherTransvoxel();
|
static const int MINIMUM_PADDING = 2;
|
||||||
|
|
||||||
Ref<ArrayMesh> build_mesh(Ref<VoxelBuffer> voxels_ref, unsigned int channel, Ref<ArrayMesh> mesh = Ref<ArrayMesh>());
|
void build(VoxelMesher::Output &output, const VoxelBuffer &voxels, int padding) override;
|
||||||
Array build(const VoxelBuffer &voxels, unsigned int channel);
|
int get_minimum_padding() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
36
meshers/voxel_mesher.cpp
Normal file
36
meshers/voxel_mesher.cpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#include "voxel_mesher.h"
|
||||||
|
|
||||||
|
Ref<Mesh> VoxelMesher::build_mesh(Ref<VoxelBuffer> voxels) {
|
||||||
|
|
||||||
|
ERR_FAIL_COND_V(voxels.is_null(), Ref<ArrayMesh>());
|
||||||
|
|
||||||
|
Output output;
|
||||||
|
build(output, **voxels, get_minimum_padding());
|
||||||
|
|
||||||
|
if (output.surfaces.empty()) {
|
||||||
|
return Ref<ArrayMesh>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<ArrayMesh> mesh;
|
||||||
|
mesh.instance();
|
||||||
|
|
||||||
|
for (int i = 0; i < output.surfaces.size(); ++i) {
|
||||||
|
mesh->add_surface_from_arrays(output.primitive_type, output.surfaces[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelMesher::build(Output &output, const VoxelBuffer &voxels, int padding) {
|
||||||
|
}
|
||||||
|
|
||||||
|
int VoxelMesher::get_minimum_padding() const {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelMesher::_bind_methods() {
|
||||||
|
|
||||||
|
// Shortcut if you want to generate a mesh directly from a fixed grid of voxels.
|
||||||
|
// Useful for testing the different meshers.
|
||||||
|
ClassDB::bind_method(D_METHOD("build_mesh", "voxel_buffer"), &VoxelMesher::build_mesh);
|
||||||
|
}
|
24
meshers/voxel_mesher.h
Normal file
24
meshers/voxel_mesher.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef VOXEL_MESHER_H
|
||||||
|
#define VOXEL_MESHER_H
|
||||||
|
|
||||||
|
#include "../voxel_buffer.h"
|
||||||
|
#include <scene/resources/mesh.h>
|
||||||
|
|
||||||
|
class VoxelMesher : public Reference {
|
||||||
|
GDCLASS(VoxelMesher, Reference)
|
||||||
|
public:
|
||||||
|
struct Output {
|
||||||
|
Vector<Array> surfaces;
|
||||||
|
Mesh::PrimitiveType primitive_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual void build(Output &output, const VoxelBuffer &voxels, int padding);
|
||||||
|
virtual int get_minimum_padding() const;
|
||||||
|
|
||||||
|
Ref<Mesh> build_mesh(Ref<VoxelBuffer> voxels);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void _bind_methods();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VOXEL_MESHER_H
|
@ -34,6 +34,7 @@ void register_voxel_types() {
|
|||||||
ClassDB::register_class<VoxelIsoSurfaceTool>();
|
ClassDB::register_class<VoxelIsoSurfaceTool>();
|
||||||
|
|
||||||
// Meshers
|
// Meshers
|
||||||
|
ClassDB::register_class<VoxelMesher>();
|
||||||
ClassDB::register_class<VoxelMesherBlocky>();
|
ClassDB::register_class<VoxelMesherBlocky>();
|
||||||
ClassDB::register_class<VoxelMesherTransvoxel>();
|
ClassDB::register_class<VoxelMesherTransvoxel>();
|
||||||
ClassDB::register_class<VoxelMesherDMC>();
|
ClassDB::register_class<VoxelMesherDMC>();
|
||||||
|
@ -107,6 +107,17 @@ void VoxelMeshUpdater::pop(Output &output) {
|
|||||||
_shared_output.blocks.clear();
|
_shared_output.blocks.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int VoxelMeshUpdater::get_required_padding() const {
|
||||||
|
|
||||||
|
int padding = _blocky_mesher->get_minimum_padding();
|
||||||
|
|
||||||
|
if (_dmc_mesher.is_valid()) {
|
||||||
|
padding = max(padding, _dmc_mesher->get_minimum_padding());
|
||||||
|
}
|
||||||
|
|
||||||
|
return padding;
|
||||||
|
}
|
||||||
|
|
||||||
void VoxelMeshUpdater::_thread_func(void *p_self) {
|
void VoxelMeshUpdater::_thread_func(void *p_self) {
|
||||||
VoxelMeshUpdater *self = reinterpret_cast<VoxelMeshUpdater *>(p_self);
|
VoxelMeshUpdater *self = reinterpret_cast<VoxelMeshUpdater *>(p_self);
|
||||||
self->thread_func();
|
self->thread_func();
|
||||||
@ -181,17 +192,12 @@ void VoxelMeshUpdater::process_block(const InputBlock &block, OutputBlock &outpu
|
|||||||
|
|
||||||
CRASH_COND(block.voxels.is_null());
|
CRASH_COND(block.voxels.is_null());
|
||||||
|
|
||||||
int padding = 1;
|
int padding = get_required_padding();
|
||||||
if (_dmc_mesher.is_valid()) {
|
|
||||||
padding = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build cubic parts of the mesh
|
_blocky_mesher->build(output.blocky_surfaces, **block.voxels, padding);
|
||||||
output.model_surfaces = _blocky_mesher->build(**block.voxels, Voxel::CHANNEL_TYPE, padding);
|
|
||||||
|
|
||||||
if (_dmc_mesher.is_valid()) {
|
if (_dmc_mesher.is_valid()) {
|
||||||
// Build smooth parts of the mesh
|
_dmc_mesher->build(output.smooth_surfaces, **block.voxels, padding);
|
||||||
output.smooth_surfaces = _dmc_mesher->build(**block.voxels);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
output.position = block.position;
|
output.position = block.position;
|
||||||
|
@ -26,8 +26,8 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct OutputBlock {
|
struct OutputBlock {
|
||||||
Array model_surfaces;
|
VoxelMesher::Output blocky_surfaces;
|
||||||
Array smooth_surfaces;
|
VoxelMesher::Output smooth_surfaces;
|
||||||
Vector3i position;
|
Vector3i position;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -66,6 +66,8 @@ public:
|
|||||||
void push(const Input &input);
|
void push(const Input &input);
|
||||||
void pop(Output &output);
|
void pop(Output &output);
|
||||||
|
|
||||||
|
int get_required_padding() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void _thread_func(void *p_self);
|
static void _thread_func(void *p_self);
|
||||||
void thread_func();
|
void thread_func();
|
||||||
|
@ -286,15 +286,6 @@ void VoxelTerrain::reset_updater() {
|
|||||||
_block_updater = memnew(VoxelMeshUpdater(_library, params));
|
_block_updater = memnew(VoxelMeshUpdater(_library, params));
|
||||||
}
|
}
|
||||||
|
|
||||||
int VoxelTerrain::get_block_padding() const {
|
|
||||||
// How many neighbor voxels we should pad for mesh updates to be seamless
|
|
||||||
// TODO Generalize padding retrieval, or split terrain systems because blocky and smooth are two different beasts
|
|
||||||
// - Blocky needs padding of 1
|
|
||||||
// - Transvoxel needs padding of 2
|
|
||||||
// - DMC needs padding of 2
|
|
||||||
return _smooth_meshing_enabled ? 2 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int get_border_index(int x, int max) {
|
inline int get_border_index(int x, int max) {
|
||||||
return x == 0 ? 0 : x != max ? 1 : 2;
|
return x == 0 ? 0 : x != max ? 1 : 2;
|
||||||
}
|
}
|
||||||
@ -757,7 +748,7 @@ void VoxelTerrain::_process() {
|
|||||||
|
|
||||||
// TODO Make the buffer re-usable
|
// TODO Make the buffer re-usable
|
||||||
unsigned int block_size = _map->get_block_size();
|
unsigned int block_size = _map->get_block_size();
|
||||||
unsigned int padding = get_block_padding();
|
unsigned int padding = _block_updater->get_required_padding();
|
||||||
nbuffer->create(block_size + 2 * padding, block_size + 2 * padding, block_size + 2 * padding);
|
nbuffer->create(block_size + 2 * padding, block_size + 2 * padding, block_size + 2 * padding);
|
||||||
|
|
||||||
unsigned int channels_mask = (1 << VoxelBuffer::CHANNEL_TYPE) | (1 << VoxelBuffer::CHANNEL_ISOLEVEL);
|
unsigned int channels_mask = (1 << VoxelBuffer::CHANNEL_TYPE) | (1 << VoxelBuffer::CHANNEL_ISOLEVEL);
|
||||||
@ -820,30 +811,30 @@ void VoxelTerrain::_process() {
|
|||||||
mesh.instance();
|
mesh.instance();
|
||||||
|
|
||||||
int surface_index = 0;
|
int surface_index = 0;
|
||||||
for (int i = 0; i < ob.model_surfaces.size(); ++i) {
|
for (int i = 0; i < ob.blocky_surfaces.surfaces.size(); ++i) {
|
||||||
|
|
||||||
Array surface = ob.model_surfaces[i];
|
Array surface = ob.blocky_surfaces.surfaces[i];
|
||||||
if (surface.empty()) {
|
if (surface.empty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
CRASH_COND(surface.size() != Mesh::ARRAY_MAX);
|
CRASH_COND(surface.size() != Mesh::ARRAY_MAX);
|
||||||
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, surface);
|
mesh->add_surface_from_arrays(ob.blocky_surfaces.primitive_type, surface);
|
||||||
mesh->surface_set_material(surface_index, _materials[i]);
|
mesh->surface_set_material(surface_index, _materials[i]);
|
||||||
|
|
||||||
++surface_index;
|
++surface_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < ob.smooth_surfaces.size(); ++i) {
|
for (int i = 0; i < ob.smooth_surfaces.surfaces.size(); ++i) {
|
||||||
|
|
||||||
Array surface = ob.smooth_surfaces[i];
|
Array surface = ob.smooth_surfaces.surfaces[i];
|
||||||
if (surface.empty()) {
|
if (surface.empty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
CRASH_COND(surface.size() != Mesh::ARRAY_MAX);
|
CRASH_COND(surface.size() != Mesh::ARRAY_MAX);
|
||||||
// TODO Problem here, the mesher could be configured to output wireframe! Need to output some MeshData struct instead
|
// TODO Problem here, the mesher could be configured to output wireframe! Need to output some MeshData struct instead
|
||||||
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, surface);
|
mesh->add_surface_from_arrays(ob.smooth_surfaces.primitive_type, surface);
|
||||||
mesh->surface_set_material(surface_index, _materials[i]);
|
mesh->surface_set_material(surface_index, _materials[i]);
|
||||||
// No material supported yet
|
// No material supported yet
|
||||||
++surface_index;
|
++surface_index;
|
||||||
|
@ -98,7 +98,6 @@ private:
|
|||||||
|
|
||||||
void make_all_view_dirty_deferred();
|
void make_all_view_dirty_deferred();
|
||||||
void reset_updater();
|
void reset_updater();
|
||||||
int get_block_padding() const;
|
|
||||||
|
|
||||||
Spatial *get_viewer(NodePath path) const;
|
Spatial *get_viewer(NodePath path) const;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user