mirror of
https://github.com/Relintai/godot_voxel.git
synced 2024-11-11 20:35:08 +01:00
Added project: first working prototype, but lots of TODOs
This commit is contained in:
commit
1acabf1307
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*.obj
|
||||||
|
*.pyc
|
5
SCsub
Normal file
5
SCsub
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Import('env')
|
||||||
|
|
||||||
|
env.add_source_files(env.modules_sources,"*.cpp")
|
||||||
|
env.add_source_files(env.modules_sources,"lib/*.c")
|
||||||
|
|
11
config.py
Normal file
11
config.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
|
||||||
|
def can_build(platform):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def configure(env):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
16
register_types.cpp
Normal file
16
register_types.cpp
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include "register_types.h"
|
||||||
|
#include "voxel_buffer.h"
|
||||||
|
#include "voxel_mesh_builder.h"
|
||||||
|
|
||||||
|
void register_voxel_types() {
|
||||||
|
|
||||||
|
ObjectTypeDB::register_type<Voxel>();
|
||||||
|
ObjectTypeDB::register_type<VoxelBuffer>();
|
||||||
|
ObjectTypeDB::register_type<VoxelMeshBuilder>();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void unregister_voxel_types() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
2
register_types.h
Normal file
2
register_types.h
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
void register_voxel_types();
|
||||||
|
void unregister_voxel_types();
|
36
vector3i.h
Normal file
36
vector3i.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#ifndef VOXEL_VECTOR3I_H
|
||||||
|
#define VOXEL_VECTOR3I_H
|
||||||
|
|
||||||
|
struct Vector3i {
|
||||||
|
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int z;
|
||||||
|
|
||||||
|
Vector3i() : x(0), y(0), z(0) {}
|
||||||
|
|
||||||
|
Vector3i(int px, int py, int pz) : x(px), y(py), z(pz) {}
|
||||||
|
|
||||||
|
Vector3i(const Vector3i & other) {
|
||||||
|
*this = other;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3i & operator=(const Vector3i & other) {
|
||||||
|
x = other.x;
|
||||||
|
y = other.y;
|
||||||
|
z = other.z;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Vector3i & other) {
|
||||||
|
return x == other.x && y == other.y && z == other.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const Vector3i & other) {
|
||||||
|
return x != other.x && y != other.y && z != other.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VOXEL_VECTOR3I_H
|
||||||
|
|
102
voxel.cpp
Normal file
102
voxel.cpp
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#include "voxel.h"
|
||||||
|
|
||||||
|
Voxel::Voxel() : Reference(), _id(0), _material_id(0), _is_transparent(false), _color(1.f, 1.f, 1.f) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voxel::set_id(int id) {
|
||||||
|
ERR_FAIL_COND(id < 0 || id >= 256);
|
||||||
|
_id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voxel::set_cube_geometry(float 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) {
|
||||||
|
_model_side_vertices[side].resize(6);
|
||||||
|
DVector<Vector3>::Write w = _model_side_vertices[side].write();
|
||||||
|
for (unsigned int i = 0; i < 6; ++i) {
|
||||||
|
w[i] = vertices[side][i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voxel::set_cube_uv_all_sides(Vector3 atlas_pos) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voxel::set_cube_uv_tbs_sides(Vector3 top_atlas_pos, Vector3 side_atlas_pos, Vector3 bottom_atlas_pos) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voxel::_bind_methods() {
|
||||||
|
|
||||||
|
ObjectTypeDB::bind_method(_MD("set_name", "name"), &Voxel::set_name);
|
||||||
|
ObjectTypeDB::bind_method(_MD("get_name"), &Voxel::get_name);
|
||||||
|
|
||||||
|
ObjectTypeDB::bind_method(_MD("set_id", "id"), &Voxel::set_id);
|
||||||
|
ObjectTypeDB::bind_method(_MD("get_id"), &Voxel::get_id);
|
||||||
|
|
||||||
|
ObjectTypeDB::bind_method(_MD("set_color", "color"), &Voxel::set_color);
|
||||||
|
ObjectTypeDB::bind_method(_MD("get_color"), &Voxel::get_color);
|
||||||
|
|
||||||
|
ObjectTypeDB::bind_method(_MD("set_cube_geometry", "height"), &Voxel::set_cube_geometry, DEFVAL(1.f));
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
}
|
||||||
|
|
70
voxel.h
Normal file
70
voxel.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#ifndef VOXEL_TYPE_H
|
||||||
|
#define VOXEL_TYPE_H
|
||||||
|
|
||||||
|
#include <reference.h>
|
||||||
|
|
||||||
|
// Definition of one type of voxel.
|
||||||
|
// A voxel can be a simple coloured cube, or a more complex model.
|
||||||
|
class Voxel : public Reference {
|
||||||
|
OBJ_TYPE(Voxel, Reference)
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum Side {
|
||||||
|
SIDE_LEFT = 0,
|
||||||
|
SIDE_RIGHT,
|
||||||
|
SIDE_BOTTOM,
|
||||||
|
SIDE_TOP,
|
||||||
|
SIDE_BACK,
|
||||||
|
SIDE_FRONT,
|
||||||
|
|
||||||
|
SIDE_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
int _id;
|
||||||
|
String _name;
|
||||||
|
int _material_id;
|
||||||
|
bool _is_transparent;
|
||||||
|
|
||||||
|
Color _color;
|
||||||
|
DVector<Vector3> _model_vertices;
|
||||||
|
DVector<Vector3> _model_normals;
|
||||||
|
DVector<Vector2> _model_uv;
|
||||||
|
DVector<Vector3> _model_side_vertices[SIDE_COUNT];
|
||||||
|
DVector<Vector2> _model_side_uv[SIDE_COUNT];
|
||||||
|
|
||||||
|
// TODO Child voxel types
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Voxel();
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void set_name(String name) { _name = name; }
|
||||||
|
_FORCE_INLINE_ String get_name() const { return _name; }
|
||||||
|
|
||||||
|
void set_id(int id);
|
||||||
|
_FORCE_INLINE_ int get_id() const { return _id; }
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void set_color(Color color) { _color = color; }
|
||||||
|
_FORCE_INLINE_ Color get_color() const { return _color; }
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void set_material_id(unsigned int id) { _material_id = id; }
|
||||||
|
_FORCE_INLINE_ unsigned int get_material_id() const { return _material_id; }
|
||||||
|
|
||||||
|
void set_cube_geometry(float sy = 1);
|
||||||
|
void set_cube_uv_all_sides(Vector3 atlas_pos);
|
||||||
|
void set_cube_uv_tbs_sides(Vector3 top_atlas_pos, Vector3 side_atlas_pos, Vector3 bottom_atlas_pos);
|
||||||
|
|
||||||
|
const DVector<Vector3> & get_model_vertices() const { return _model_vertices; }
|
||||||
|
const DVector<Vector3> & get_model_normals() const { return _model_normals; }
|
||||||
|
const DVector<Vector2> & get_model_uv() const { return _model_uv; }
|
||||||
|
const DVector<Vector3> & get_model_side_vertices(unsigned int side) const { return _model_side_vertices[side]; }
|
||||||
|
const DVector<Vector2> & get_model_side_uv(unsigned int side) const { return _model_side_uv[side]; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void _bind_methods();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VOXEL_TYPE_H
|
||||||
|
|
202
voxel_buffer.cpp
Normal file
202
voxel_buffer.cpp
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
#include "voxel_buffer.h"
|
||||||
|
|
||||||
|
|
||||||
|
VoxelBuffer::VoxelBuffer() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
VoxelBuffer::~VoxelBuffer() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelBuffer::create(int sx, int sy, int sz) {
|
||||||
|
if (sx <= 0 || sy <= 0 || sz <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Vector3i new_size(sx, sy, sz);
|
||||||
|
if (new_size != _size) {
|
||||||
|
for (unsigned int i = 0; i < MAX_CHANNELS; ++i) {
|
||||||
|
Channel & channel = _channels[i];
|
||||||
|
if (channel.data) {
|
||||||
|
// TODO Optimize with realloc
|
||||||
|
delete_channel(i, _size);
|
||||||
|
create_channel(i, new_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_size = new_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelBuffer::clear() {
|
||||||
|
for (unsigned int i = 0; i < MAX_CHANNELS; ++i) {
|
||||||
|
Channel & channel = _channels[i];
|
||||||
|
if (channel.data) {
|
||||||
|
delete_channel(i, _size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelBuffer::clear_channel(unsigned int channel_index, int clear_value) {
|
||||||
|
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
|
||||||
|
delete_channel(channel_index, _size);
|
||||||
|
_channels[channel_index].defval = clear_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int VoxelBuffer::get_voxel(int x, int y, int z, unsigned int channel_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(channel_index, MAX_CHANNELS, 0);
|
||||||
|
|
||||||
|
x -= _offset.x;
|
||||||
|
y -= _offset.y;
|
||||||
|
z -= _offset.z;
|
||||||
|
|
||||||
|
const Channel & channel = _channels[channel_index];
|
||||||
|
|
||||||
|
if (validate_local_pos(x, y, z) && channel.data) {
|
||||||
|
return channel.data[z][x][y];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return channel.defval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int VoxelBuffer::get_voxel_local(int x, int y, int z, unsigned int channel_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(channel_index, MAX_CHANNELS, 0);
|
||||||
|
|
||||||
|
const Channel & channel = _channels[channel_index];
|
||||||
|
|
||||||
|
if (validate_local_pos(x, y, z) && channel.data) {
|
||||||
|
return channel.data[z][x][y];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return channel.defval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelBuffer::set_voxel(int value, int x, int y, int z, unsigned int channel_index) {
|
||||||
|
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
|
||||||
|
|
||||||
|
x -= _offset.x;
|
||||||
|
y -= _offset.y;
|
||||||
|
z -= _offset.z;
|
||||||
|
|
||||||
|
ERR_FAIL_COND(!validate_local_pos(x, y, z));
|
||||||
|
|
||||||
|
Channel & channel = _channels[channel_index];
|
||||||
|
|
||||||
|
if (channel.defval != value) {
|
||||||
|
if (channel.data == NULL) {
|
||||||
|
create_channel(channel_index, _size);
|
||||||
|
}
|
||||||
|
channel.data[z][x][y] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelBuffer::set_voxel_v(int value, Vector3 pos, unsigned int channel_index) {
|
||||||
|
set_voxel(value, pos.x, pos.y, pos.z, channel_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelBuffer::fill(int defval, unsigned int channel_index) {
|
||||||
|
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
|
||||||
|
|
||||||
|
Channel & channel = _channels[channel_index];
|
||||||
|
|
||||||
|
for (unsigned int z = 0; z < _size.z; ++z) {
|
||||||
|
for (unsigned int x = 0; x < _size.x; ++x) {
|
||||||
|
uint8_t * column = channel.data[z][x];
|
||||||
|
for (unsigned int y = 0; y < _size.y; ++y) {
|
||||||
|
column[y] = defval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VoxelBuffer::is_uniform(unsigned int channel_index) {
|
||||||
|
ERR_FAIL_INDEX_V(channel_index, MAX_CHANNELS, true);
|
||||||
|
|
||||||
|
Channel & channel = _channels[channel_index];
|
||||||
|
if (channel.data == NULL)
|
||||||
|
return true;
|
||||||
|
uint8_t voxel = channel.data[0][0][0];
|
||||||
|
for (unsigned int z = 0; z < _size.z; ++z) {
|
||||||
|
for (unsigned int x = 0; x < _size.x; ++x) {
|
||||||
|
uint8_t * column = channel.data[z][x];
|
||||||
|
for (unsigned int y = 0; y < _size.y; ++y) {
|
||||||
|
if (column[y] != voxel) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelBuffer::optimize() {
|
||||||
|
for (unsigned int i = 0; i < MAX_CHANNELS; ++i) {
|
||||||
|
if (_channels[i].data && is_uniform(i)) {
|
||||||
|
clear_channel(i, _channels[i].data[0][0][0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelBuffer::create_channel(int i, Vector3i size, uint8_t defval) {
|
||||||
|
|
||||||
|
Channel & channel = _channels[i];
|
||||||
|
channel.data = (uint8_t***)memalloc(size.z * sizeof(uint8_t**));
|
||||||
|
|
||||||
|
for (unsigned int z = 0; z < size.z; ++z) {
|
||||||
|
|
||||||
|
uint8_t ** plane = (uint8_t**)memalloc(size.x * sizeof(uint8_t*));
|
||||||
|
channel.data[z] = plane;
|
||||||
|
|
||||||
|
for (unsigned int x = 0; x < size.x; ++x) {
|
||||||
|
|
||||||
|
uint8_t * column = (uint8_t*)memalloc(size.y * sizeof(uint8_t));
|
||||||
|
plane[x] = column;
|
||||||
|
|
||||||
|
for (unsigned int y = 0; y < size.y; ++y) {
|
||||||
|
column[y] = defval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelBuffer::delete_channel(int i, Vector3i size) {
|
||||||
|
|
||||||
|
Channel & channel = _channels[i];
|
||||||
|
|
||||||
|
for (unsigned int z = 0; z < size.z; ++z) {
|
||||||
|
for (unsigned int x = 0; x < size.x; ++x) {
|
||||||
|
memfree(channel.data[z][x]);
|
||||||
|
}
|
||||||
|
memfree(channel.data[z]);
|
||||||
|
}
|
||||||
|
memfree(channel.data);
|
||||||
|
channel.data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelBuffer::_bind_methods() {
|
||||||
|
|
||||||
|
ObjectTypeDB::bind_method(_MD("create", "sx", "sy", "sz"), &VoxelBuffer::create);
|
||||||
|
ObjectTypeDB::bind_method(_MD("clear"), &VoxelBuffer::clear);
|
||||||
|
|
||||||
|
ObjectTypeDB::bind_method(_MD("get_size_x"), &VoxelBuffer::get_size_x);
|
||||||
|
ObjectTypeDB::bind_method(_MD("get_size_y"), &VoxelBuffer::get_size_y);
|
||||||
|
ObjectTypeDB::bind_method(_MD("get_size_z"), &VoxelBuffer::get_size_z);
|
||||||
|
|
||||||
|
ObjectTypeDB::bind_method(_MD("get_offset_x"), &VoxelBuffer::get_offset_x);
|
||||||
|
ObjectTypeDB::bind_method(_MD("get_offset_y"), &VoxelBuffer::get_offset_y);
|
||||||
|
ObjectTypeDB::bind_method(_MD("get_offset_z"), &VoxelBuffer::get_offset_z);
|
||||||
|
|
||||||
|
ObjectTypeDB::bind_method(_MD("set_offset", "x", "y", "z"), &VoxelBuffer::set_offset);
|
||||||
|
|
||||||
|
ObjectTypeDB::bind_method(_MD("set_voxel", "value", "x", "y", "z", "channel"), &VoxelBuffer::set_voxel, DEFVAL(0));
|
||||||
|
ObjectTypeDB::bind_method(_MD("set_voxel_v", "value", "pos", "channel"), &VoxelBuffer::set_voxel, DEFVAL(0));
|
||||||
|
ObjectTypeDB::bind_method(_MD("get_voxel", "x", "y", "z", "channel"), &VoxelBuffer::set_voxel, DEFVAL(0));
|
||||||
|
|
||||||
|
ObjectTypeDB::bind_method(_MD("fill", "value", "channel"), &VoxelBuffer::fill, DEFVAL(0));
|
||||||
|
|
||||||
|
ObjectTypeDB::bind_method(_MD("is_uniform", "channel"), &VoxelBuffer::is_uniform, DEFVAL(0));
|
||||||
|
ObjectTypeDB::bind_method(_MD("optimize"), &VoxelBuffer::optimize);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
86
voxel_buffer.h
Normal file
86
voxel_buffer.h
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#ifndef VOXEL_BUFFER_H
|
||||||
|
#define VOXEL_BUFFER_H
|
||||||
|
|
||||||
|
#include <reference.h>
|
||||||
|
#include <vector.h>
|
||||||
|
#include "vector3i.h"
|
||||||
|
|
||||||
|
// Dense voxels data storage.
|
||||||
|
// Organized in 8-bit channels like images, all optional.
|
||||||
|
// Note: for float storage (marching cubes for example), you can map [0..256] to [0..1] and save 3 bytes per cell
|
||||||
|
|
||||||
|
class VoxelBuffer : public Reference {
|
||||||
|
OBJ_TYPE(VoxelBuffer, Reference)
|
||||||
|
|
||||||
|
// Arbitrary value, 8 should be enough. Tweak for your needs.
|
||||||
|
static const int MAX_CHANNELS = 8;
|
||||||
|
|
||||||
|
struct Channel {
|
||||||
|
// Allocated when the channel is populated.
|
||||||
|
// Array of array of arrays, in order [z][x][y] because it makes vertical-wise access faster (the engine is Y-up).
|
||||||
|
uint8_t *** data;
|
||||||
|
|
||||||
|
uint8_t defval; // Default value when data is null
|
||||||
|
|
||||||
|
Channel() : data(NULL), defval(0) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Each channel can store arbitary data.
|
||||||
|
// For example, you can decide to store colors (R, G, B, A), gameplay types (type, state, light) or both.
|
||||||
|
Channel _channels[MAX_CHANNELS];
|
||||||
|
|
||||||
|
// How many voxels are there in the three directions. All populated channels have the same size.
|
||||||
|
Vector3i _size;
|
||||||
|
|
||||||
|
// Offset applied to coordinates when accessing voxels.
|
||||||
|
// Use _local versions to bypass this.
|
||||||
|
Vector3i _offset;
|
||||||
|
|
||||||
|
public:
|
||||||
|
VoxelBuffer();
|
||||||
|
~VoxelBuffer();
|
||||||
|
|
||||||
|
void create(int sx, int sy, int sz);
|
||||||
|
void clear();
|
||||||
|
void clear_channel(unsigned int channel_index, int clear_value=0);
|
||||||
|
|
||||||
|
_FORCE_INLINE_ int get_size_x() const { return _size.x; }
|
||||||
|
_FORCE_INLINE_ int get_size_y() const { return _size.y; }
|
||||||
|
_FORCE_INLINE_ int get_size_z() const { return _size.z; }
|
||||||
|
|
||||||
|
_FORCE_INLINE_ int get_offset_x() const { return _offset.x; }
|
||||||
|
_FORCE_INLINE_ int get_offset_y() const { return _offset.y; }
|
||||||
|
_FORCE_INLINE_ int get_offset_z() const { return _offset.z; }
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void set_offset(int x, int y, int z) { _offset = Vector3i(x,y,z); }
|
||||||
|
|
||||||
|
int get_voxel(int x, int y, int z, unsigned int channel_index=0) const;
|
||||||
|
int get_voxel_local(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_v(int value, Vector3 pos, unsigned int channel_index = 0);
|
||||||
|
|
||||||
|
void fill(int defval, unsigned int channel_index = 0);
|
||||||
|
|
||||||
|
bool is_uniform(unsigned int channel_index = 0);
|
||||||
|
|
||||||
|
void optimize();
|
||||||
|
|
||||||
|
//void copy_from(Ref<VoxelBuffer> other);
|
||||||
|
|
||||||
|
_FORCE_INLINE_ bool validate_local_pos(unsigned int x, unsigned int y, unsigned int z) const {
|
||||||
|
return x < _size.x
|
||||||
|
&& y < _size.y
|
||||||
|
&& z < _size.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void create_channel(int i, Vector3i size, uint8_t defval=0);
|
||||||
|
void delete_channel(int i, Vector3i size);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void _bind_methods();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VOXEL_BUFFER_H
|
||||||
|
|
123
voxel_mesh_builder.cpp
Normal file
123
voxel_mesh_builder.cpp
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
#include "voxel_mesh_builder.h"
|
||||||
|
|
||||||
|
static 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),
|
||||||
|
};
|
||||||
|
|
||||||
|
VoxelMeshBuilder::VoxelMeshBuilder() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelMeshBuilder::add_voxel_type(Ref<Voxel> voxel) {
|
||||||
|
ERR_FAIL_COND(voxel.is_null());
|
||||||
|
ERR_FAIL_COND(voxel->get_id() >= MAX_VOXEL_TYPES);
|
||||||
|
ERR_FAIL_COND(voxel->get_material_id() >= MAX_MATERIALS);
|
||||||
|
unsigned int id = voxel->get_id();
|
||||||
|
_voxel_types[id] = voxel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelMeshBuilder::set_material(Ref<Material> material, unsigned int id) {
|
||||||
|
ERR_FAIL_COND(id >= MAX_MATERIALS);
|
||||||
|
_materials[id] = material;
|
||||||
|
_surface_tool[id].set_material(material);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<Mesh> VoxelMeshBuilder::build(Ref<VoxelBuffer> buffer_ref) {
|
||||||
|
ERR_FAIL_COND_V(buffer_ref.is_null(), Ref<Mesh>());
|
||||||
|
|
||||||
|
const VoxelBuffer & buffer = **buffer_ref;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < MAX_MATERIALS; ++i) {
|
||||||
|
_surface_tool[i].begin(Mesh::PRIMITIVE_TRIANGLES);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
for (unsigned int z = 1; z < buffer.get_size_z()-1; ++z) {
|
||||||
|
for (unsigned int x = 1; x < buffer.get_size_x()-1; ++x) {
|
||||||
|
for (unsigned int y = 1; y < buffer.get_size_y()-1; ++y) {
|
||||||
|
|
||||||
|
int voxel_id = buffer.get_voxel_local(x, y, z, 0);
|
||||||
|
|
||||||
|
if (voxel_id != 0 && !_voxel_types[voxel_id].is_null()) {
|
||||||
|
|
||||||
|
const Voxel & voxel = **_voxel_types[voxel_id];
|
||||||
|
|
||||||
|
SurfaceTool & st = _surface_tool[voxel.get_material_id()];
|
||||||
|
|
||||||
|
// Hybrid approach: extract cube faces and decimate those that aren't visible,
|
||||||
|
// and still allow voxels to have geometry that is not a cube
|
||||||
|
|
||||||
|
// Sides
|
||||||
|
for (unsigned int side = 0; side < Voxel::SIDE_COUNT; ++side) {
|
||||||
|
|
||||||
|
const DVector<Vector3> & vertices = voxel.get_model_side_vertices(side);
|
||||||
|
if (vertices.size() != 0) {
|
||||||
|
|
||||||
|
Vector3i normal = g_side_normals[side];
|
||||||
|
unsigned nx = x + normal.x;
|
||||||
|
unsigned ny = y + normal.y;
|
||||||
|
unsigned nz = z + normal.z;
|
||||||
|
|
||||||
|
int neighbor_voxel_id = buffer.get_voxel_local(nx, ny, nz, 0);
|
||||||
|
// TODO Better face visibility test
|
||||||
|
if (neighbor_voxel_id == 0) {
|
||||||
|
|
||||||
|
DVector<Vector3>::Read r = vertices.read();
|
||||||
|
Vector3 pos(x - 1, y - 1, z - 1);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < vertices.size(); ++i) {
|
||||||
|
st.add_normal(Vector3(normal.x, normal.y, normal.z));
|
||||||
|
st.add_vertex(r[i] + pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inside
|
||||||
|
if (voxel.get_model_vertices().size() != 0) {
|
||||||
|
|
||||||
|
const DVector<Vector3> & vertices = voxel.get_model_vertices();
|
||||||
|
DVector<Vector3>::Read rv = voxel.get_model_vertices().read();
|
||||||
|
DVector<Vector3>::Read rn = voxel.get_model_normals().read();
|
||||||
|
Vector3 pos(x - 1, y - 1, z - 1);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < vertices.size(); ++i) {
|
||||||
|
st.add_normal(rn[i]);
|
||||||
|
st.add_vertex(rv[i] + pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit mesh
|
||||||
|
Ref<Mesh> mesh_ref = _surface_tool[0].commit();
|
||||||
|
_surface_tool[0].clear();
|
||||||
|
for (unsigned int i = 1; i < MAX_MATERIALS; ++i) {
|
||||||
|
if (_materials[i].is_valid()) {
|
||||||
|
SurfaceTool & st = _surface_tool[i];
|
||||||
|
st.commit(mesh_ref);
|
||||||
|
st.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mesh_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelMeshBuilder::_bind_methods() {
|
||||||
|
|
||||||
|
ObjectTypeDB::bind_method(_MD("add_voxel_type", "voxel"), &VoxelMeshBuilder::add_voxel_type);
|
||||||
|
ObjectTypeDB::bind_method(_MD("set_material", "material", "id"), &VoxelMeshBuilder::set_material);
|
||||||
|
ObjectTypeDB::bind_method(_MD("build", "voxel_buffer"), &VoxelMeshBuilder::build);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
35
voxel_mesh_builder.h
Normal file
35
voxel_mesh_builder.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#ifndef VOXEL_MESH_BUILDER
|
||||||
|
#define VOXEL_MESH_BUILDER
|
||||||
|
|
||||||
|
#include <reference.h>
|
||||||
|
#include <scene/resources/mesh.h>
|
||||||
|
#include <scene/resources/surface_tool.h>
|
||||||
|
#include "voxel.h"
|
||||||
|
#include "voxel_buffer.h"
|
||||||
|
|
||||||
|
class VoxelMeshBuilder : public Reference {
|
||||||
|
OBJ_TYPE(VoxelMeshBuilder, Reference);
|
||||||
|
|
||||||
|
static const unsigned int MAX_VOXEL_TYPES = 256; // Required limit because voxel types are stored in 8 bits
|
||||||
|
static const unsigned int MAX_MATERIALS = 8; // Arbitrary. Tweak if needed.
|
||||||
|
|
||||||
|
Ref<Voxel> _voxel_types[MAX_VOXEL_TYPES];
|
||||||
|
Ref<Material> _materials[MAX_MATERIALS];
|
||||||
|
SurfaceTool _surface_tool[MAX_MATERIALS];
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
VoxelMeshBuilder();
|
||||||
|
|
||||||
|
void add_voxel_type(Ref<Voxel> voxel);
|
||||||
|
void set_material(Ref<Material> material, unsigned int id);
|
||||||
|
|
||||||
|
Ref<Mesh> build(Ref<VoxelBuffer> buffer_ref);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void _bind_methods();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // VOXEL_MESH_BUILDER
|
Loading…
Reference in New Issue
Block a user