Got rid of SurfaceTool in the cubic mesher, moved cube tables in their own file, tweaked their winding

This commit is contained in:
Marc Gilleron 2017-08-19 21:10:31 +02:00
parent 239eb11451
commit cb6de49b96
7 changed files with 351 additions and 234 deletions

141
cube_tables.cpp Normal file
View File

@ -0,0 +1,141 @@
#include "cube_tables.h"
namespace CubeTables {
// The following tables respect the following conventions
//
// 7-------6
// /| /|
// / | / | Corners
// 4-------5 |
// | 3----|--2
// | / | / y z
// |/ |/ |/
// 0-------1 o--x
//
//
// o---10----o
// /| /|
// 11 7 9 6 Edges
// / | / |
// o----8----o |
// | o---2-|---o
// 4 / 5 /
// | 3 | 1
// |/ |/
// o----0----o
//
// Sides are ordered according to the Voxel::Side enum.
//
//static const unsigned int CORNER_COUNT = 8;
//static const unsigned int EDGE_COUNT = 12;
// Order doesn't technically matter here, they are for reading convenience.
// If you need a special ordering, combine index with other tables
const Vector3 g_corner_position[CORNER_COUNT] = {
Vector3(0, 0, 0),
Vector3(1, 0, 0),
Vector3(1, 0, 1),
Vector3(0, 0, 1),
Vector3(0, 1, 0),
Vector3(1, 1, 0),
Vector3(1, 1, 1),
Vector3(0, 1, 1)
};
const int g_side_quad_triangles[Voxel::SIDE_COUNT][6] = {
// LEFT
{ 0, 1, 2, 0, 2, 3 },
// RIGHT
{ 0, 1, 2, 0, 2, 3 },
// BOTTOM
{ 0, 1, 2, 0, 2, 3 },
// TOP
{ 0, 1, 2, 0, 2, 3 },
// BACK
{ 0, 1, 2, 0, 2, 3 },
// FRONT
{ 0, 1, 2, 0, 2, 3 },
};
const unsigned int g_side_coord[Voxel::SIDE_COUNT] = { 0, 0, 1, 1, 2, 2 };
const unsigned int g_side_sign[Voxel::SIDE_COUNT] = { 0, 1, 0, 1, 0, 1 };
const Vector3i g_side_normals[Voxel::SIDE_COUNT] = {
Vector3i(-1, 0, 0),
Vector3i(1, 0, 0),
Vector3i(0, -1, 0),
Vector3i(0, 1, 0),
Vector3i(0, 0, -1),
Vector3i(0, 0, 1),
};
// Corners have same winding, relative to the face's normal
const unsigned int g_side_corners[Voxel::SIDE_COUNT][4] = {
{ 3, 0, 4, 7 },
{ 1, 2, 6, 5 },
{ 1, 0, 3, 2 },
{ 4, 5, 6, 7 },
{ 0, 1, 5, 4 },
{ 2, 3, 7, 6 }
};
const unsigned int g_side_edges[Voxel::SIDE_COUNT][4] = {
{ 3, 7, 11, 4 },
{ 1, 6, 9, 5 },
{ 0, 1, 2, 3 },
{ 8, 9, 10, 11 },
{ 0, 5, 8, 4 },
{ 2, 6, 10, 7 }
};
// 3---2
// | / | {0,1,2,0,2,3}
// 0---1
//static const unsigned int g_vertex_to_corner[Voxel::SIDE_COUNT][6] = {
// { 0, 3, 7, 0, 7, 4 },
// { 2, 1, 5, 2, 5, 6 },
// { 0, 1, 2, 0, 2, 3 },
// { 7, 6, 5, 7, 5, 4 },
// { 1, 0, 4 ,1, 4, 5 },
// { 3, 2, 6, 3, 6, 7 }
//};
const Vector3i g_corner_inormals[CORNER_COUNT] = {
Vector3i(-1, -1, -1),
Vector3i(1, -1, -1),
Vector3i(1, -1, 1),
Vector3i(-1, -1, 1),
Vector3i(-1, 1, -1),
Vector3i(1, 1, -1),
Vector3i(1, 1, 1),
Vector3i(-1, 1, 1)
};
const Vector3i g_edge_inormals[EDGE_COUNT] = {
Vector3i(0, -1, -1),
Vector3i(1, -1, 0),
Vector3i(0, -1, 1),
Vector3i(-1, -1, 0),
Vector3i(-1, 0, -1),
Vector3i(1, 0, -1),
Vector3i(1, 0, 1),
Vector3i(-1, 0, 1),
Vector3i(0, 1, -1),
Vector3i(1, 1, 0),
Vector3i(0, 1, 1),
Vector3i(-1, 1, 0)
};
const unsigned int g_edge_corners[EDGE_COUNT][2] = {
{ 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 0 },
{ 0, 4 }, { 1, 5 }, { 2, 6 }, { 3, 7 },
{ 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 4 }
};
} // namespace CubeTables

34
cube_tables.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef CUBE_TABLES_H
#define CUBE_TABLES_H
#include <vector3.h>
#include "vector3i.h"
#include "voxel.h"
namespace CubeTables {
const unsigned int CORNER_COUNT = 8;
const unsigned int EDGE_COUNT = 12;
extern const Vector3 g_corner_position[CORNER_COUNT];
extern const int g_side_quad_triangles[Voxel::SIDE_COUNT][6];
extern const unsigned int g_side_coord[Voxel::SIDE_COUNT];
extern const unsigned int g_side_sign[Voxel::SIDE_COUNT];
extern const Vector3i g_side_normals[Voxel::SIDE_COUNT];
extern const unsigned int g_side_corners[Voxel::SIDE_COUNT][4];
extern const unsigned int g_side_edges[Voxel::SIDE_COUNT][4];
extern const Vector3i g_corner_inormals[CORNER_COUNT];
extern const Vector3i g_edge_inormals[EDGE_COUNT];
extern const unsigned int g_edge_corners[EDGE_COUNT][2];
} // namespace CubeTables
#endif // CUBE_TABLES_H

105
voxel.cpp
View File

@ -1,6 +1,7 @@
#include "voxel.h" #include "voxel.h"
#include "voxel_library.h" #include "voxel_library.h"
#include "voxel_mesher.h" #include "voxel_mesher.h"
#include "cube_tables.h"
#define STRLEN(x) (sizeof(x) / sizeof(x[0])) #define STRLEN(x) (sizeof(x) / sizeof(x[0]))
@ -128,13 +129,17 @@ void Voxel::set_geometry_type(GeometryType type) {
case GEOMETRY_NONE: { case GEOMETRY_NONE: {
// Clear all geometry // Clear all geometry
_model_vertices.resize(0); _model_positions.resize(0);
_model_normals.resize(0); _model_normals.resize(0);
_model_uv.resize(0); _model_uvs.resize(0);
_model_indices.resize(0);
for (int side = 0; side < SIDE_COUNT; ++side) { for (int side = 0; side < SIDE_COUNT; ++side) {
_model_side_vertices[side].resize(0); _model_side_positions[side].resize(0);
_model_side_uv[side].resize(0); _model_side_uvs[side].resize(0);
_model_side_indices[side].resize(0);
} }
} break; } break;
case GEOMETRY_CUBE: case GEOMETRY_CUBE:
@ -167,56 +172,27 @@ VoxelLibrary *Voxel::get_library() const {
Ref<Voxel> Voxel::set_cube_geometry(float sy) { Ref<Voxel> Voxel::set_cube_geometry(float sy) {
sy = 1.0 + sy; sy = 1.0 + sy;
const Vector3 vertices[SIDE_COUNT][6] = {
{ // LEFT
Vector3(0, 0, 0),
Vector3(0, sy, 0),
Vector3(0, sy, 1),
Vector3(0, 0, 0),
Vector3(0, sy, 1),
Vector3(0, 0, 1) },
{ // RIGHT
Vector3(1, 0, 0),
Vector3(1, sy, 1),
Vector3(1, sy, 0),
Vector3(1, 0, 0),
Vector3(1, 0, 1),
Vector3(1, sy, 1) },
{ // BOTTOM
Vector3(0, 0, 0),
Vector3(1, 0, 1),
Vector3(1, 0, 0),
Vector3(0, 0, 0),
Vector3(0, 0, 1),
Vector3(1, 0, 1) },
{ // TOP
Vector3(0, sy, 0),
Vector3(1, sy, 0),
Vector3(1, sy, 1),
Vector3(0, sy, 0),
Vector3(1, sy, 1),
Vector3(0, sy, 1) },
{ // BACK
Vector3(0, 0, 0),
Vector3(1, 0, 0),
Vector3(1, sy, 0),
Vector3(0, 0, 0),
Vector3(1, sy, 0),
Vector3(0, sy, 0) },
{ // FRONT
Vector3(1, 0, 1),
Vector3(0, 0, 1),
Vector3(1, sy, 1),
Vector3(0, 0, 1),
Vector3(0, sy, 1),
Vector3(1, sy, 1) }
};
for (unsigned int side = 0; side < SIDE_COUNT; ++side) { for (unsigned int side = 0; side < SIDE_COUNT; ++side) {
_model_side_vertices[side].resize(6);
PoolVector<Vector3>::Write w = _model_side_vertices[side].write(); {
_model_side_positions[side].resize(4);
PoolVector<Vector3>::Write w = _model_side_positions[side].write();
for (unsigned int i = 0; i < 4; ++i) {
int corner = CubeTables::g_side_corners[side][i];
Vector3 p = CubeTables::g_corner_position[corner];
if(p.y > 0.9)
p.y = sy;
w[i] = p;
}
}
{
_model_side_indices[side].resize(6);
PoolVector<int>::Write w = _model_side_indices[side].write();
for (unsigned int i = 0; i < 6; ++i) { for (unsigned int i = 0; i < 6; ++i) {
w[i] = vertices[side][i]; w[i] = CubeTables::g_side_quad_triangles[side][i];
}
} }
} }
@ -242,34 +218,17 @@ void Voxel::update_cube_uv_sides() {
const Vector2 uv[4] = { const Vector2 uv[4] = {
Vector2(e, e), Vector2(e, e),
Vector2(1.f - e, e), Vector2(1.f - e, e),
Vector2(e, 1.f - e),
Vector2(1.f - e, 1.f - e), Vector2(1.f - e, 1.f - e),
}; Vector2(e, 1.f - e),
// TODO The only reason why there are 6 entries per array is because SurfaceTool is used to access them.
// in the near future, there should be only 4, to account for one quad!
const int uv6[SIDE_COUNT][6] = {
// LEFT
{ 2, 0, 1, 2, 1, 3 },
// RIGHT
{ 2, 1, 0, 2, 3, 1 },
// BOTTOM
{ 0, 3, 1, 0, 2, 3 },
// TOP
{ 0, 1, 3, 0, 3, 2 },
// BACK
{ 2, 3, 1, 2, 1, 0 },
// FRONT
{ 3, 2, 1, 2, 0, 1 }
}; };
float s = 1.0 / (float)library->get_atlas_size(); float s = 1.0 / (float)library->get_atlas_size();
for (unsigned int side = 0; side < SIDE_COUNT; ++side) { for (unsigned int side = 0; side < SIDE_COUNT; ++side) {
_model_side_uv[side].resize(6); _model_side_uvs[side].resize(4);
PoolVector<Vector2>::Write w = _model_side_uv[side].write(); PoolVector<Vector2>::Write w = _model_side_uvs[side].write();
for (unsigned int i = 0; i < 6; ++i) { for (unsigned int i = 0; i < 4; ++i) {
w[i] = (_cube_tiles[side] + uv[uv6[side][i]]) * s; w[i] = (_cube_tiles[side] + uv[i]) * s;
} }
} }
} }

26
voxel.h
View File

@ -65,11 +65,14 @@ public:
// Getters for native usage only // Getters for native usage only
const PoolVector<Vector3> &get_model_vertices() const { return _model_vertices; } const PoolVector<Vector3> &get_model_positions() const { return _model_positions; }
const PoolVector<Vector3> &get_model_normals() const { return _model_normals; } const PoolVector<Vector3> &get_model_normals() const { return _model_normals; }
const PoolVector<Vector2> &get_model_uv() const { return _model_uv; } const PoolVector<Vector2> &get_model_uv() const { return _model_uvs; }
const PoolVector<Vector3> &get_model_side_vertices(unsigned int side) const { return _model_side_vertices[side]; } const PoolVector<int> &get_model_indices() const { return _model_indices; }
const PoolVector<Vector2> &get_model_side_uv(unsigned int side) const { return _model_side_uv[side]; }
const PoolVector<Vector3> &get_model_side_positions(unsigned int side) const { return _model_side_positions[side]; }
const PoolVector<Vector2> &get_model_side_uv(unsigned int side) const { return _model_side_uvs[side]; }
const PoolVector<int> &get_model_side_indices(unsigned int side) const { return _model_side_indices[side]; }
void set_library(Ref<VoxelLibrary> lib); void set_library(Ref<VoxelLibrary> lib);
@ -104,13 +107,18 @@ private:
Vector2 _cube_tiles[SIDE_COUNT]; Vector2 _cube_tiles[SIDE_COUNT];
// Model // Model
PoolVector<Vector3> _model_vertices; PoolVector<Vector3> _model_positions;
PoolVector<Vector3> _model_normals; PoolVector<Vector3> _model_normals;
PoolVector<Vector2> _model_uv; PoolVector<Vector2> _model_uvs;
PoolVector<Vector3> _model_side_vertices[SIDE_COUNT]; PoolVector<int> _model_indices;
PoolVector<Vector2> _model_side_uv[SIDE_COUNT]; // Model sides:
// They are separated because this way we can occlude them easily.
// Due to these defining cube side triangles, normals are known already.
PoolVector<Vector3> _model_side_positions[SIDE_COUNT];
PoolVector<Vector2> _model_side_uvs[SIDE_COUNT];
PoolVector<int> _model_side_indices[SIDE_COUNT];
// TODO Child voxel types // TODO Child voxel types?
}; };
VARIANT_ENUM_CAST(Voxel::ChannelMode) VARIANT_ENUM_CAST(Voxel::ChannelMode)

View File

@ -45,8 +45,10 @@ public:
int get_voxel(int x, int y, int z, unsigned int channel_index = 0) const; int get_voxel(int x, int y, int z, unsigned int channel_index = 0) const;
void set_voxel(int value, int x, int y, int z, unsigned int channel_index = 0); void set_voxel(int value, int x, int y, int z, unsigned int channel_index = 0);
void set_voxel_v(int value, Vector3 pos, unsigned int channel_index = 0); void set_voxel_v(int value, Vector3 pos, unsigned int channel_index = 0);
_FORCE_INLINE_ void set_voxel_iso(real_t value, int x, int y, int z, unsigned int channel_index = 0) { set_voxel(iso_to_byte(value), x, y, z, channel_index); } _FORCE_INLINE_ void set_voxel_iso(real_t value, int x, int y, int z, unsigned int channel_index = 0) { set_voxel(iso_to_byte(value), x, y, z, channel_index); }
_FORCE_INLINE_ real_t get_voxel_iso(int x, int y, int z, unsigned int channel_index = 0) const { return byte_to_iso(get_voxel(x, y, z, channel_index)); } _FORCE_INLINE_ real_t get_voxel_iso(int x, int y, int z, unsigned int channel_index = 0) const { return byte_to_iso(get_voxel(x, y, z, channel_index)); }
_FORCE_INLINE_ int get_voxel(const Vector3i pos, unsigned int channel_index = 0) const { return get_voxel(pos.x, pos.y, pos.z, channel_index); } _FORCE_INLINE_ int get_voxel(const Vector3i pos, unsigned int channel_index = 0) const { return get_voxel(pos.x, pos.y, pos.z, channel_index); }
_FORCE_INLINE_ void set_voxel(int value, const Vector3i pos, unsigned int channel_index = 0) { set_voxel(value, pos.x, pos.y, pos.z, channel_index); } _FORCE_INLINE_ void set_voxel(int value, const Vector3i pos, unsigned int channel_index = 0) { set_voxel(value, pos.x, pos.y, pos.z, channel_index); }

View File

@ -1,122 +1,20 @@
#include "voxel_mesher.h" #include "voxel_mesher.h"
#include "voxel_library.h" #include "voxel_library.h"
#include "cube_tables.h"
// The following tables respect the following conventions
//
// 7-------6
// /| /|
// / | / | Corners
// 4-------5 |
// | 3----|--2
// | / | / y z
// |/ |/ |/
// 0-------1 o--x
//
//
// o---10----o
// /| /|
// 11 7 9 6 Edges
// / | / |
// o----8----o |
// | o---2-|---o
// 4 / 5 /
// | 3 | 1
// |/ |/
// o----0----o
//
// Sides are ordered according to the Voxel::Side enum.
//
static const unsigned int CORNER_COUNT = 8; template <typename T>
static const unsigned int EDGE_COUNT = 12; void copy_to(PoolVector<T> &to, const Vector<T> &from) {
static const Vector3 g_corner_position[CORNER_COUNT] = { to.resize(from.size());
Vector3(0, 0, 0),
Vector3(1, 0, 0),
Vector3(1, 0, 1),
Vector3(0, 0, 1),
Vector3(0, 1, 0),
Vector3(1, 1, 0),
Vector3(1, 1, 1),
Vector3(0, 1, 1)
};
static const unsigned int g_side_coord[Voxel::SIDE_COUNT] = { 0, 0, 1, 1, 2, 2 }; typename PoolVector<T>::Write w = to.write();
static const unsigned int g_side_sign[Voxel::SIDE_COUNT] = { 0, 1, 0, 1, 0, 1 };
static const Vector3i g_side_normals[Voxel::SIDE_COUNT] = { for (unsigned int i = 0; i < from.size(); ++i) {
Vector3i(-1, 0, 0), w[i] = from[i];
Vector3i(1, 0, 0), }
Vector3i(0, -1, 0), }
Vector3i(0, 1, 0),
Vector3i(0, 0, -1),
Vector3i(0, 0, 1),
};
static const unsigned int g_side_corners[Voxel::SIDE_COUNT][4] = {
{ 0, 3, 7, 4 },
{ 1, 2, 6, 5 },
{ 0, 1, 2, 3 },
{ 4, 5, 6, 7 },
{ 0, 1, 5, 4 },
{ 3, 2, 6, 7 }
};
static const unsigned int g_side_edges[Voxel::SIDE_COUNT][4] = {
{ 3, 7, 11, 4 },
{ 1, 6, 9, 5 },
{ 0, 1, 2, 3 },
{ 8, 9, 10, 11 },
{ 0, 5, 8, 4 },
{ 2, 6, 10, 7 }
};
// 3---2
// | / | {0,1,2,0,2,3}
// 0---1
//static const unsigned int g_vertex_to_corner[Voxel::SIDE_COUNT][6] = {
// { 0, 3, 7, 0, 7, 4 },
// { 2, 1, 5, 2, 5, 6 },
// { 0, 1, 2, 0, 2, 3 },
// { 7, 6, 5, 7, 5, 4 },
// { 1, 0, 4 ,1, 4, 5 },
// { 3, 2, 6, 3, 6, 7 }
//};
static const Vector3i g_corner_inormals[CORNER_COUNT] = {
Vector3i(-1, -1, -1),
Vector3i(1, -1, -1),
Vector3i(1, -1, 1),
Vector3i(-1, -1, 1),
Vector3i(-1, 1, -1),
Vector3i(1, 1, -1),
Vector3i(1, 1, 1),
Vector3i(-1, 1, 1)
};
static const Vector3i g_edge_inormals[EDGE_COUNT] = {
Vector3i(0, -1, -1),
Vector3i(1, -1, 0),
Vector3i(0, -1, 1),
Vector3i(-1, -1, 0),
Vector3i(-1, 0, -1),
Vector3i(1, 0, -1),
Vector3i(1, 0, 1),
Vector3i(-1, 0, 1),
Vector3i(0, 1, -1),
Vector3i(1, 1, 0),
Vector3i(0, 1, 1),
Vector3i(-1, 1, 0)
};
static const unsigned int g_edge_corners[EDGE_COUNT][2] = {
{ 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 0 },
{ 0, 4 }, { 1, 5 }, { 2, 6 }, { 3, 7 },
{ 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 4 }
};
VoxelMesher::VoxelMesher() VoxelMesher::VoxelMesher()
: _baked_occlusion_darkness(0.75), : _baked_occlusion_darkness(0.75),
@ -182,8 +80,12 @@ Ref<ArrayMesh> VoxelMesher::build(const VoxelBuffer &buffer, unsigned int channe
const VoxelLibrary &library = **_library; const VoxelLibrary &library = **_library;
for (unsigned int i = 0; i < MAX_MATERIALS; ++i) { for (unsigned int i = 0; i < MAX_MATERIALS; ++i) {
_surface_tool[i].begin(Mesh::PRIMITIVE_TRIANGLES); Arrays &a = _arrays[i];
_surface_tool[i].set_material(_materials[i]); a.positions.clear();
a.normals.clear();
a.uvs.clear();
a.colors.clear();
a.indices.clear();
} }
float baked_occlusion_darkness; float baked_occlusion_darkness;
@ -206,19 +108,23 @@ Ref<ArrayMesh> VoxelMesher::build(const VoxelBuffer &buffer, unsigned int channe
min.clamp_to(pad, max); min.clamp_to(pad, max);
max.clamp_to(min, buffer.get_size() - pad); max.clamp_to(min, buffer.get_size() - pad);
int index_offset = 0;
// Iterate 3D padded data to extract voxel faces. // Iterate 3D padded data to extract voxel faces.
// This is the most intensive job in this class, so all required data should be as fit as possible. // This is the most intensive job in this class, so all required data should be as fit as possible.
for (unsigned int z = min.z; z < max.z; ++z) { for (unsigned int z = min.z; z < max.z; ++z) {
for (unsigned int x = min.x; x < max.x; ++x) { for (unsigned int x = min.x; x < max.x; ++x) {
for (unsigned int y = min.y; y < max.y; ++y) { for (unsigned int y = min.y; y < max.y; ++y) {
// TODO In this intensive routine, there is a way to make voxel access fastest by getting a pointer to the channel,
// and using offset lookup to get neighbors rather than going through get_voxel validations
int voxel_id = buffer.get_voxel(x, y, z, 0); int voxel_id = buffer.get_voxel(x, y, z, 0);
if (voxel_id != 0 && library.has_voxel(voxel_id)) { if (voxel_id != 0 && library.has_voxel(voxel_id)) {
const Voxel &voxel = library.get_voxel_const(voxel_id); const Voxel &voxel = library.get_voxel_const(voxel_id);
SurfaceTool &st = _surface_tool[voxel.get_material_id()]; Arrays &arrays = _arrays[voxel.get_material_id()];
// Hybrid approach: extract cube faces and decimate those that aren't visible, // Hybrid approach: extract cube faces and decimate those that aren't visible,
// and still allow voxels to have geometry that is not a cube // and still allow voxels to have geometry that is not a cube
@ -226,10 +132,12 @@ Ref<ArrayMesh> VoxelMesher::build(const VoxelBuffer &buffer, unsigned int channe
// Sides // Sides
for (unsigned int side = 0; side < Voxel::SIDE_COUNT; ++side) { for (unsigned int side = 0; side < Voxel::SIDE_COUNT; ++side) {
const PoolVector<Vector3> &vertices = voxel.get_model_side_vertices(side); const PoolVector<Vector3> &positions = voxel.get_model_side_positions(side);
if (vertices.size() != 0) { int vertex_count = positions.size();
Vector3i normal = g_side_normals[side]; if (vertex_count != 0) {
Vector3i normal = CubeTables::g_side_normals[side];
unsigned nx = x + normal.x; unsigned nx = x + normal.x;
unsigned ny = y + normal.y; unsigned ny = y + normal.y;
unsigned nz = z + normal.z; unsigned nz = z + normal.z;
@ -247,22 +155,22 @@ Ref<ArrayMesh> VoxelMesher::build(const VoxelBuffer &buffer, unsigned int channe
// Combinatory solution for https://0fps.net/2013/07/03/ambient-occlusion-for-minecraft-like-worlds/ // Combinatory solution for https://0fps.net/2013/07/03/ambient-occlusion-for-minecraft-like-worlds/
for (unsigned int j = 0; j < 4; ++j) { for (unsigned int j = 0; j < 4; ++j) {
unsigned int edge = g_side_edges[side][j]; unsigned int edge = CubeTables::g_side_edges[side][j];
Vector3i edge_normal = g_edge_inormals[edge]; Vector3i edge_normal = CubeTables::g_edge_inormals[edge];
unsigned ex = x + edge_normal.x; unsigned ex = x + edge_normal.x;
unsigned ey = y + edge_normal.y; unsigned ey = y + edge_normal.y;
unsigned ez = z + edge_normal.z; unsigned ez = z + edge_normal.z;
if (!is_transparent(library, buffer.get_voxel(ex, ey, ez))) { if (!is_transparent(library, buffer.get_voxel(ex, ey, ez))) {
shaded_corner[g_edge_corners[edge][0]] += 1; shaded_corner[CubeTables::g_edge_corners[edge][0]] += 1;
shaded_corner[g_edge_corners[edge][1]] += 1; shaded_corner[CubeTables::g_edge_corners[edge][1]] += 1;
} }
} }
for (unsigned int j = 0; j < 4; ++j) { for (unsigned int j = 0; j < 4; ++j) {
unsigned int corner = g_side_corners[side][j]; unsigned int corner = CubeTables::g_side_corners[side][j];
if (shaded_corner[corner] == 2) { if (shaded_corner[corner] == 2) {
shaded_corner[corner] = 3; shaded_corner[corner] = 3;
} else { } else {
Vector3i corner_normal = g_corner_inormals[corner]; Vector3i corner_normal = CubeTables::g_corner_inormals[corner];
unsigned int cx = x + corner_normal.x; unsigned int cx = x + corner_normal.x;
unsigned int cy = y + corner_normal.y; unsigned int cy = y + corner_normal.y;
unsigned int cz = z + corner_normal.z; unsigned int cz = z + corner_normal.z;
@ -273,11 +181,12 @@ Ref<ArrayMesh> VoxelMesher::build(const VoxelBuffer &buffer, unsigned int channe
} }
} }
PoolVector<Vector3>::Read rv = vertices.read(); PoolVector<Vector3>::Read rv = positions.read();
PoolVector<Vector2>::Read rt = voxel.get_model_side_uv(side).read(); PoolVector<Vector2>::Read rt = voxel.get_model_side_uv(side).read();
Vector3 pos(x - 1, y - 1, z - 1); Vector3 pos(x - 1, y - 1, z - 1);
for (unsigned int i = 0; i < vertices.size(); ++i) { for (unsigned int i = 0; i < vertex_count; ++i) {
Vector3 v = rv[i]; Vector3 v = rv[i];
if (_bake_occlusion) { if (_bake_occlusion) {
@ -286,10 +195,10 @@ Ref<ArrayMesh> VoxelMesher::build(const VoxelBuffer &buffer, unsigned int channe
// TODO Fix occlusion inconsistency caused by triangles orientation // TODO Fix occlusion inconsistency caused by triangles orientation
float shade = 0; float shade = 0;
for (unsigned int j = 0; j < 4; ++j) { for (unsigned int j = 0; j < 4; ++j) {
unsigned int corner = g_side_corners[side][j]; unsigned int corner = CubeTables::g_side_corners[side][j];
if (shaded_corner[corner]) { if (shaded_corner[corner]) {
float s = baked_occlusion_darkness * static_cast<float>(shaded_corner[corner]); float s = baked_occlusion_darkness * static_cast<float>(shaded_corner[corner]);
float k = 1.0 - g_corner_position[corner].distance_to(v); float k = 1.0 - CubeTables::g_corner_position[corner].distance_to(v);
if (k < 0.0) if (k < 0.0)
k = 0.0; k = 0.0;
s *= k; s *= k;
@ -298,31 +207,61 @@ Ref<ArrayMesh> VoxelMesher::build(const VoxelBuffer &buffer, unsigned int channe
} }
} }
float gs = 1.0 - shade; float gs = 1.0 - shade;
st.add_color(Color(gs, gs, gs)); arrays.colors.push_back(Color(gs, gs, gs));
} }
st.add_normal(Vector3(normal.x, normal.y, normal.z)); // TODO Investigate wether those vectors can be replaced by a simpler, faster one for PODs
st.add_uv(rt[i]); // TODO Resize beforehands rather than push_back (even if the vector is preallocated)
st.add_vertex(v + pos); arrays.normals.push_back(Vector3(normal.x, normal.y, normal.z));
arrays.uvs.push_back(rt[i]);
arrays.positions.push_back(v + pos);
} }
const PoolVector<int> &side_indices = voxel.get_model_side_indices(side);
PoolVector<int>::Read ri = side_indices.read();
unsigned int index_count = side_indices.size();
for(unsigned int i = 0; i < index_count; ++i) {
arrays.indices.push_back(index_offset + ri[i]);
}
index_offset += vertex_count;
} }
} }
} }
// Inside // Inside
if (voxel.get_model_vertices().size() != 0) { if (voxel.get_model_positions().size() != 0) {
const PoolVector<Vector3> &vertices = voxel.get_model_vertices(); const PoolVector<Vector3> &vertices = voxel.get_model_positions();
PoolVector<Vector3>::Read rv = voxel.get_model_vertices().read(); int vertex_count = vertices.size();
PoolVector<Vector3>::Read rv = vertices.read();
PoolVector<Vector3>::Read rn = voxel.get_model_normals().read(); PoolVector<Vector3>::Read rn = voxel.get_model_normals().read();
PoolVector<Vector2>::Read rt = voxel.get_model_uv().read(); PoolVector<Vector2>::Read rt = voxel.get_model_uv().read();
Vector3 pos(x - 1, y - 1, z - 1); Vector3 pos(x - 1, y - 1, z - 1);
for (unsigned int i = 0; i < vertices.size(); ++i) { for (unsigned int i = 0; i < vertex_count; ++i) {
st.add_normal(rn[i]); arrays.normals.push_back(rn[i]);
st.add_uv(rt[i]); arrays.uvs.push_back(rt[i]);
st.add_vertex(rv[i] + pos); arrays.positions.push_back(rv[i] + pos);
} }
if(_bake_occlusion) {
// TODO handle ambient occlusion on inner parts
arrays.colors.push_back(Color(1,1,1));
}
const PoolVector<int> &indices = voxel.get_model_indices();
PoolVector<int>::Read ri = indices.read();
unsigned int index_count = indices.size();
for(unsigned int i = 0; i < index_count; ++i) {
arrays.indices.push_back(index_offset + ri[i]);
}
index_offset += vertex_count;
} }
} }
} }
@ -337,22 +276,49 @@ Ref<ArrayMesh> VoxelMesher::build(const VoxelBuffer &buffer, unsigned int channe
if (mesh.is_null()) if (mesh.is_null())
mesh_ref = Ref<ArrayMesh>(memnew(ArrayMesh)); mesh_ref = Ref<ArrayMesh>(memnew(ArrayMesh));
for (unsigned int i = 0; i < MAX_MATERIALS; ++i) { VOXEL_PROFILE_BEGIN("mesher_add_surfaces")
SurfaceTool &st = _surface_tool[i];
// Index mesh to reduce memory usage and make upload to VRAM faster // print_line(String("Made mesh v: ") + String::num(_arrays[0].positions.size())
// TODO actually, we could make it indexed from the ground up without using SurfaceTool, so we also save time! // + String(", i: ") + String::num(_arrays[0].indices.size()));
// VOXEL_PROFILE_BEGIN("mesher_surfacetool_index")
// st.index();
// VOXEL_PROFILE_END("mesher_surfacetool_index")
VOXEL_PROFILE_BEGIN("mesher_surfacetool_commit") int surface = 0;
mesh_ref = st.commit(mesh_ref); for(int i = 0; i < MAX_MATERIALS; ++i) {
VOXEL_PROFILE_END("mesher_surfacetool_commit")
st.clear(); const Arrays &arrays = _arrays[i];
if(arrays.positions.size() != 0) {
Array mesh_arrays;
mesh_arrays.resize(Mesh::ARRAY_MAX);
{
PoolVector<Vector3> positions;
PoolVector<Vector2> uvs;
PoolVector<Vector3> normals;
PoolVector<Color> colors;
PoolVector<int> indices;
copy_to(positions, arrays.positions);
copy_to(uvs, arrays.uvs);
copy_to(normals, arrays.normals);
copy_to(colors, arrays.colors);
copy_to(indices, arrays.indices);
mesh_arrays[Mesh::ARRAY_VERTEX] = positions;
mesh_arrays[Mesh::ARRAY_TEX_UV] = uvs;
mesh_arrays[Mesh::ARRAY_NORMAL] = normals;
mesh_arrays[Mesh::ARRAY_COLOR] = colors;
mesh_arrays[Mesh::ARRAY_INDEX] = indices;
} }
mesh_ref->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, mesh_arrays);
mesh_ref->surface_set_material(surface, _materials[i]);
++surface;
}
}
VOXEL_PROFILE_END("mesher_add_surfaces")
return mesh_ref; return mesh_ref;
} }

View File

@ -7,7 +7,6 @@
#include "zprofiling.h" #include "zprofiling.h"
#include <reference.h> #include <reference.h>
#include <scene/resources/mesh.h> #include <scene/resources/mesh.h>
#include <scene/resources/surface_tool.h>
// TODO Should be renamed VoxelMesherCubic or something like that // TODO Should be renamed VoxelMesherCubic or something like that
class VoxelMesher : public Reference { class VoxelMesher : public Reference {
@ -37,9 +36,17 @@ protected:
static void _bind_methods(); static void _bind_methods();
private: private:
struct Arrays {
Vector<Vector3> positions;
Vector<Vector3> normals;
Vector<Vector2> uvs;
Vector<Color> colors;
Vector<int> indices;
};
Ref<VoxelLibrary> _library; Ref<VoxelLibrary> _library;
Ref<Material> _materials[MAX_MATERIALS]; Ref<Material> _materials[MAX_MATERIALS];
SurfaceTool _surface_tool[MAX_MATERIALS]; Arrays _arrays[MAX_MATERIALS];
float _baked_occlusion_darkness; float _baked_occlusion_darkness;
bool _bake_occlusion; bool _bake_occlusion;