mirror of
https://github.com/Relintai/godot_voxel.git
synced 2024-11-19 02:47:18 +01:00
Added VoxelMap and VoxelTerrain + various fixes
This commit is contained in:
parent
e990286b54
commit
5662dfde7b
@ -2,6 +2,8 @@
|
||||
#include "voxel_buffer.h"
|
||||
#include "voxel_mesher.h"
|
||||
#include "voxel_library.h"
|
||||
#include "voxel_map.h"
|
||||
#include "voxel_terrain.h"
|
||||
|
||||
void register_voxel_types() {
|
||||
|
||||
@ -9,6 +11,8 @@ void register_voxel_types() {
|
||||
ObjectTypeDB::register_type<VoxelBuffer>();
|
||||
ObjectTypeDB::register_type<VoxelMesher>();
|
||||
ObjectTypeDB::register_type<VoxelLibrary>();
|
||||
ObjectTypeDB::register_type<VoxelMap>();
|
||||
ObjectTypeDB::register_type<VoxelTerrain>();
|
||||
|
||||
}
|
||||
|
||||
|
35
vector3i.h
35
vector3i.h
@ -28,10 +28,22 @@ struct Vector3i {
|
||||
z = Math::floor(f.z);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Vector3 to_vec3() {
|
||||
_FORCE_INLINE_ Vector3 to_vec3() const {
|
||||
return Vector3(x, y, z);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ int volume() const {
|
||||
return x*y*z;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ int length_sq() const {
|
||||
return x*x + y*y + z*z;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ float length() const {
|
||||
return Math::sqrt(length_sq());
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Vector3i & operator=(const Vector3i & other) {
|
||||
x = other.x;
|
||||
y = other.y;
|
||||
@ -60,9 +72,9 @@ struct Vector3i {
|
||||
if (y < min.y) y = min.y;
|
||||
if (z < min.z) z = min.z;
|
||||
|
||||
if (x >= max.x) x = max.x;
|
||||
if (y >= max.y) y = max.y;
|
||||
if (z >= max.z) z = max.z;
|
||||
if (x >= max.x) x = max.x - 1;
|
||||
if (y >= max.y) y = max.y - 1;
|
||||
if (z >= max.z) z = max.z - 1;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool is_contained_in(const Vector3i & min, const Vector3i & max) {
|
||||
@ -78,6 +90,21 @@ struct Vector3i {
|
||||
);
|
||||
}
|
||||
|
||||
static void sort_min_max(Vector3i & a, Vector3i & b) {
|
||||
sort_min_max(a.x, b.x);
|
||||
sort_min_max(a.y, b.y);
|
||||
sort_min_max(a.z, b.z);
|
||||
}
|
||||
|
||||
private:
|
||||
static _FORCE_INLINE_ void sort_min_max(int & a, int & b) {
|
||||
if (a > b) {
|
||||
int temp = a;
|
||||
a = b;
|
||||
b = a;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ Vector3i operator+(const Vector3i a, const Vector3i & b) {
|
||||
|
@ -93,20 +93,24 @@ void VoxelBuffer::fill(int defval, unsigned int channel_index) {
|
||||
void VoxelBuffer::fill_area(int defval, Vector3i min, Vector3i max, unsigned int channel_index) {
|
||||
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
|
||||
|
||||
Vector3i::sort_min_max(min, max);
|
||||
|
||||
min.clamp_to(Vector3i(0, 0, 0), _size);
|
||||
max.clamp_to(Vector3i(0, 0, 0), _size);
|
||||
max.clamp_to(Vector3i(0, 0, 0), _size + Vector3i(1,1,1));
|
||||
Vector3i area_size = max - min;
|
||||
|
||||
Channel & channel = _channels[channel_index];
|
||||
if (channel.data == NULL && channel.defval == defval)
|
||||
return;
|
||||
else
|
||||
create_channel(channel_index, _size);
|
||||
if (channel.data == NULL) {
|
||||
if (channel.defval == defval)
|
||||
return;
|
||||
else
|
||||
create_channel(channel_index, _size);
|
||||
}
|
||||
|
||||
Vector3i pos;
|
||||
for (pos.z = min.z; pos.z < max.z; ++pos.z) {
|
||||
for (pos.x = min.x; pos.x < max.x; ++pos.x) {
|
||||
unsigned int dst_ri = row_index(pos.x, pos.y, pos.z);
|
||||
unsigned int dst_ri = index(pos.x, pos.y, pos.z);
|
||||
memset(&channel.data[dst_ri], defval, area_size.y * sizeof(uint8_t));
|
||||
}
|
||||
}
|
||||
@ -164,12 +168,14 @@ void VoxelBuffer::copy_from(const VoxelBuffer & other, Vector3i src_min, Vector3
|
||||
Channel & channel = _channels[channel_index];
|
||||
const Channel & other_channel = other._channels[channel_index];
|
||||
|
||||
Vector3i::sort_min_max(src_min, src_max);
|
||||
|
||||
src_min.clamp_to(Vector3i(0, 0, 0), other._size);
|
||||
src_max.clamp_to(Vector3i(0, 0, 0), other._size);
|
||||
src_max.clamp_to(Vector3i(0, 0, 0), other._size + Vector3i(1,1,1));
|
||||
|
||||
dst_min.clamp_to(Vector3i(0, 0, 0), _size);
|
||||
Vector3i area_size = src_max - src_min;
|
||||
Vector3i dst_max = src_min + area_size;
|
||||
//Vector3i dst_max = dst_min + area_size;
|
||||
|
||||
if (area_size == _size) {
|
||||
copy_from(other, channel_index);
|
||||
@ -184,8 +190,8 @@ void VoxelBuffer::copy_from(const VoxelBuffer & other, Vector3i src_min, Vector3
|
||||
for (pos.z = 0; pos.z < area_size.z; ++pos.z) {
|
||||
for (pos.x = 0; pos.x < area_size.x; ++pos.x) {
|
||||
// Row direction is Y
|
||||
unsigned int src_ri = other.row_index(pos.x + src_min.x, pos.y + src_min.y, pos.z + src_min.z);
|
||||
unsigned int dst_ri = row_index(pos.x + dst_min.x, pos.y + dst_min.y, pos.z + dst_min.z);
|
||||
unsigned int src_ri = other.index(pos.x + src_min.x, pos.y + src_min.y, pos.z + src_min.z);
|
||||
unsigned int dst_ri = index(pos.x + dst_min.x, pos.y + dst_min.y, pos.z + dst_min.z);
|
||||
memcpy(&channel.data[dst_ri], &other_channel.data[src_ri], area_size.y * sizeof(uint8_t));
|
||||
}
|
||||
}
|
||||
@ -198,7 +204,7 @@ void VoxelBuffer::copy_from(const VoxelBuffer & other, Vector3i src_min, Vector3
|
||||
Vector3i pos;
|
||||
for (pos.z = 0; pos.z < area_size.z; ++pos.z) {
|
||||
for (pos.x = 0; pos.x < area_size.x; ++pos.x) {
|
||||
unsigned int dst_ri = row_index(pos.x + dst_min.x, pos.y + dst_min.y, pos.z + dst_min.z);
|
||||
unsigned int dst_ri = index(pos.x + dst_min.x, pos.y + dst_min.y, pos.z + dst_min.z);
|
||||
memset(&channel.data[dst_ri], other_channel.defval, area_size.y * sizeof(uint8_t));
|
||||
}
|
||||
}
|
||||
|
236
voxel_map.cpp
Normal file
236
voxel_map.cpp
Normal file
@ -0,0 +1,236 @@
|
||||
#include "voxel_map.h"
|
||||
|
||||
VoxelMap::VoxelMap() : _last_accessed_block(NULL) {
|
||||
for (unsigned int i = 0; i < VoxelBuffer::MAX_CHANNELS; ++i) {
|
||||
_default_voxel[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
VoxelMap::~VoxelMap() {
|
||||
|
||||
}
|
||||
|
||||
int VoxelMap::get_voxel(Vector3i pos, unsigned int c) {
|
||||
Vector3i bpos = voxel_to_block(pos);
|
||||
VoxelBlock * block = get_block(bpos);
|
||||
if (block == NULL) {
|
||||
return _default_voxel[c];
|
||||
}
|
||||
return block->voxels->get_voxel(pos - block_to_voxel(bpos), c);
|
||||
}
|
||||
|
||||
MeshInstance * VoxelBlock::get_mesh_instance(const Node & root) {
|
||||
if (mesh_instance_path.is_empty())
|
||||
return NULL;
|
||||
Node * n = root.get_node(mesh_instance_path);
|
||||
if (n == NULL)
|
||||
return NULL;
|
||||
return n->cast_to<MeshInstance>();
|
||||
}
|
||||
|
||||
VoxelBlock::~VoxelBlock() {
|
||||
|
||||
}
|
||||
|
||||
// Helper
|
||||
VoxelBlock * VoxelBlock::create(VoxelMap & map, Vector3i bpos, VoxelBuffer * buffer) {
|
||||
VoxelBlock * block = memnew(VoxelBlock);
|
||||
block->pos = bpos;
|
||||
if (buffer) {
|
||||
const int bs = VoxelBlock::SIZE;
|
||||
ERR_FAIL_COND_V(buffer->get_size() != Vector3i(bs, bs, bs), NULL);
|
||||
}
|
||||
else {
|
||||
buffer = memnew(VoxelBuffer);
|
||||
}
|
||||
ERR_FAIL_COND_V(buffer == NULL, NULL);
|
||||
block->voxels = Ref<VoxelBuffer>(buffer);
|
||||
//block->map = ↦
|
||||
return block;
|
||||
}
|
||||
|
||||
void VoxelMap::set_voxel(int value, Vector3i pos, unsigned int c) {
|
||||
Vector3i bpos = voxel_to_block(pos);
|
||||
VoxelBlock * block = get_block(pos);
|
||||
if (block == NULL) {
|
||||
set_block(bpos, VoxelBlock::create(*this, bpos));
|
||||
}
|
||||
block->voxels->set_voxel(value, pos - block_to_voxel(bpos), c);
|
||||
}
|
||||
|
||||
void VoxelMap::set_default_voxel(int value, unsigned int channel) {
|
||||
ERR_FAIL_INDEX(channel, VoxelBuffer::MAX_CHANNELS);
|
||||
_default_voxel[channel] = value;
|
||||
}
|
||||
|
||||
int VoxelMap::get_default_voxel(unsigned int channel) {
|
||||
ERR_FAIL_INDEX_V(channel, VoxelBuffer::MAX_CHANNELS, 0);
|
||||
return _default_voxel[channel];
|
||||
}
|
||||
|
||||
VoxelBlock * VoxelMap::get_block(Vector3i bpos) {
|
||||
if (_last_accessed_block && _last_accessed_block->pos == bpos) {
|
||||
return _last_accessed_block;
|
||||
}
|
||||
Ref<VoxelBlock> * p = _blocks.getptr(bpos);
|
||||
if (p) {
|
||||
_last_accessed_block = p->ptr();
|
||||
return _last_accessed_block;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void VoxelMap::set_block(Vector3i bpos, VoxelBlock * block) {
|
||||
if (_last_accessed_block == NULL || _last_accessed_block->pos == bpos) {
|
||||
_last_accessed_block = block;
|
||||
}
|
||||
_blocks.set(bpos, block);
|
||||
}
|
||||
|
||||
void VoxelMap::set_block_buffer(Vector3i bpos, Ref<VoxelBuffer> buffer) {
|
||||
ERR_FAIL_COND(buffer.is_null());
|
||||
VoxelBlock * block = get_block(bpos);
|
||||
if (block == NULL) {
|
||||
block = VoxelBlock::create(*this, bpos, *buffer);
|
||||
set_block(bpos, block);
|
||||
}
|
||||
else {
|
||||
block->voxels = buffer;
|
||||
}
|
||||
}
|
||||
|
||||
bool VoxelMap::has_block(Vector3i pos) const {
|
||||
return /*(_last_accessed_block != NULL && _last_accessed_block->pos == pos) ||*/ _blocks.has(pos);
|
||||
}
|
||||
|
||||
Vector3i g_moore_neighboring_3d[26] = {
|
||||
Vector3i(-1,-1,-1),
|
||||
Vector3i(0,-1,-1),
|
||||
Vector3i(1,-1,-1),
|
||||
Vector3i(-1,-1,0),
|
||||
Vector3i(0,-1,0),
|
||||
Vector3i(1,-1,0),
|
||||
Vector3i(-1,-1,1),
|
||||
Vector3i(0,-1,1),
|
||||
Vector3i(1,-1,1),
|
||||
|
||||
Vector3i(-1,0,-1),
|
||||
Vector3i(0,0,-1),
|
||||
Vector3i(1,0,-1),
|
||||
Vector3i(-1,0,0),
|
||||
//Vector3i(0,0,0),
|
||||
Vector3i(1,0,0),
|
||||
Vector3i(-1,0,1),
|
||||
Vector3i(0,0,1),
|
||||
Vector3i(1,0,1),
|
||||
|
||||
Vector3i(-1,1,-1),
|
||||
Vector3i(0,1,-1),
|
||||
Vector3i(1,1,-1),
|
||||
Vector3i(-1,1,0),
|
||||
Vector3i(0,1,0),
|
||||
Vector3i(1,1,0),
|
||||
Vector3i(-1,1,1),
|
||||
Vector3i(0,1,1),
|
||||
Vector3i(1,1,1),
|
||||
};
|
||||
|
||||
bool VoxelMap::is_block_surrounded(Vector3i pos) const {
|
||||
for (unsigned int i = 0; i < 26; ++i) {
|
||||
Vector3i bpos = pos + g_moore_neighboring_3d[i];
|
||||
if (!has_block(bpos)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void VoxelMap::get_buffer_copy(Vector3i min_pos, VoxelBuffer & dst_buffer, unsigned int channel) {
|
||||
ERR_FAIL_INDEX(channel, VoxelBuffer::MAX_CHANNELS);
|
||||
|
||||
Vector3i max_pos = min_pos + dst_buffer.get_size();
|
||||
|
||||
Vector3i min_block_pos = voxel_to_block(min_pos);
|
||||
Vector3i max_block_pos = voxel_to_block(max_pos - Vector3i(1,1,1)) + Vector3i(1,1,1);
|
||||
ERR_FAIL_COND((max_block_pos - min_block_pos) != Vector3(3, 3, 3));
|
||||
|
||||
Vector3i bpos;
|
||||
for (bpos.z = min_block_pos.z; bpos.z < max_block_pos.z; ++bpos.z) {
|
||||
for (bpos.x = min_block_pos.x; bpos.x < max_block_pos.x; ++bpos.x) {
|
||||
for (bpos.y = min_block_pos.y; bpos.y < max_block_pos.y; ++bpos.y) {
|
||||
|
||||
VoxelBlock * block = get_block(bpos);
|
||||
if (block) {
|
||||
|
||||
VoxelBuffer & src_buffer = **block->voxels;
|
||||
Vector3i offset = block_to_voxel(bpos);
|
||||
// Note: copy_from takes care of clamping the area if it's on an edge
|
||||
dst_buffer.copy_from(src_buffer, min_pos - offset, max_pos - offset, offset - min_pos, channel);
|
||||
}
|
||||
else {
|
||||
Vector3i offset = block_to_voxel(bpos);
|
||||
dst_buffer.fill_area(
|
||||
_default_voxel[channel],
|
||||
offset - min_pos,
|
||||
offset - min_pos + Vector3i(VoxelBlock::SIZE,VoxelBlock::SIZE, VoxelBlock::SIZE)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelMap::remove_blocks_not_in_area(Vector3i min, Vector3i max) {
|
||||
|
||||
Vector3i::sort_min_max(min, max);
|
||||
|
||||
Vector<Vector3i> to_remove;
|
||||
Vector3i * key = NULL;
|
||||
|
||||
while (_blocks.next(key)) {
|
||||
|
||||
Ref<VoxelBlock> & block_ref = _blocks.get(*key);
|
||||
ERR_FAIL_COND(block_ref.is_null()); // Should never trigger
|
||||
VoxelBlock & block = **block_ref;
|
||||
|
||||
if (!block.pos.is_contained_in(min, max)) {
|
||||
|
||||
//if (_observer)
|
||||
// _observer->block_removed(block);
|
||||
|
||||
to_remove.push_back(*key);
|
||||
|
||||
if (&block == _last_accessed_block)
|
||||
_last_accessed_block = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < to_remove.size(); ++i) {
|
||||
_blocks.erase(to_remove[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelMap::_bind_methods() {
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("get_voxel", "x", "y", "z", "c"), &VoxelMap::_get_voxel_binding, DEFVAL(0));
|
||||
ObjectTypeDB::bind_method(_MD("set_voxel", "value", "x", "y", "z", "c"), &VoxelMap::_set_voxel_binding, DEFVAL(0));
|
||||
ObjectTypeDB::bind_method(_MD("get_default_voxel", "channel"), &VoxelMap::get_default_voxel, DEFVAL(0));
|
||||
ObjectTypeDB::bind_method(_MD("set_default_voxel", "value", "channel"), &VoxelMap::set_default_voxel, DEFVAL(0));
|
||||
ObjectTypeDB::bind_method(_MD("has_block", "x", "y", "z"), &VoxelMap::_has_block_binding);
|
||||
ObjectTypeDB::bind_method(_MD("get_buffer_copy", "min_pos", "out_buffer:VoxelBuffer", "channel"), &VoxelMap::_get_buffer_copy_binding, DEFVAL(0));
|
||||
ObjectTypeDB::bind_method(_MD("set_block_buffer", "block_pos", "buffer:VoxelBuffer"), &VoxelMap::_set_block_buffer_binding);
|
||||
ObjectTypeDB::bind_method(_MD("voxel_to_block", "voxel_pos"), &VoxelMap::_voxel_to_block_binding);
|
||||
ObjectTypeDB::bind_method(_MD("block_to_voxel", "block_pos"), &VoxelMap::_block_to_voxel_binding);
|
||||
ObjectTypeDB::bind_method(_MD("get_block_size"), &VoxelMap::get_block_size);
|
||||
|
||||
//ADD_PROPERTY(PropertyInfo(Variant::INT, "iterations"), _SCS("set_iterations"), _SCS("get_iterations"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
void VoxelMap::_get_buffer_copy_binding(Vector3 pos, Ref<VoxelBuffer> dst_buffer_ref, unsigned int channel) {
|
||||
ERR_FAIL_COND(dst_buffer_ref.is_null());
|
||||
get_buffer_copy(Vector3i(pos), **dst_buffer_ref, channel);
|
||||
}
|
||||
|
155
voxel_map.h
Normal file
155
voxel_map.h
Normal file
@ -0,0 +1,155 @@
|
||||
#ifndef VOXEL_MAP_H
|
||||
#define VOXEL_MAP_H
|
||||
|
||||
#include <scene/main/node.h>
|
||||
#include <core/hash_map.h>
|
||||
#include <scene/3d/mesh_instance.h>
|
||||
#include "voxel_buffer.h"
|
||||
|
||||
class VoxelMap;
|
||||
|
||||
// Fixed-size voxel container used in VoxelMap. Used internally.
|
||||
class VoxelBlock : public Reference {
|
||||
OBJ_TYPE(VoxelBlock, Reference)
|
||||
public:
|
||||
static const int SIZE_POW2 = 4; // 3=>8, 4=>16, 5=>32...
|
||||
static const int SIZE = 1 << SIZE_POW2;
|
||||
|
||||
Ref<VoxelBuffer> voxels; // SIZE*SIZE*SIZE voxels
|
||||
Vector3i pos;
|
||||
NodePath mesh_instance_path;
|
||||
//VoxelMap * map;
|
||||
|
||||
MeshInstance * get_mesh_instance(const Node & root);
|
||||
|
||||
static VoxelBlock * create(VoxelMap & map, Vector3i bpos, VoxelBuffer * buffer = 0);
|
||||
|
||||
~VoxelBlock();
|
||||
|
||||
private:
|
||||
VoxelBlock() : Reference(), voxels(NULL) {}
|
||||
|
||||
};
|
||||
|
||||
//class IVoxelMapObserver {
|
||||
//public:
|
||||
// virtual void block_removed(VoxelBlock & block) = 0;
|
||||
//};
|
||||
|
||||
// Infinite voxel storage by means of octants like Gridmap
|
||||
class VoxelMap : public Reference {
|
||||
OBJ_TYPE(VoxelMap, Reference)
|
||||
|
||||
// Voxel values that will be returned if access is out of map bounds
|
||||
uint8_t _default_voxel[VoxelBuffer::MAX_CHANNELS];
|
||||
|
||||
// Blocks stored with a spatial hash in all 3D directions
|
||||
HashMap<Vector3i, Ref<VoxelBlock>, Vector3iHasher> _blocks;
|
||||
|
||||
// Voxel access will most frequently be in contiguous areas, so the same blocks are accessed.
|
||||
// To prevent too much hashing, this reference is checked before.
|
||||
VoxelBlock * _last_accessed_block;
|
||||
|
||||
//IVoxelMapObserver * _observer;
|
||||
|
||||
public:
|
||||
VoxelMap();
|
||||
~VoxelMap();
|
||||
|
||||
int get_voxel(Vector3i pos, unsigned int c = 0);
|
||||
void set_voxel(int value, Vector3i pos, unsigned int c = 0);
|
||||
|
||||
void set_default_voxel(int value, unsigned int channel=0);
|
||||
int get_default_voxel(unsigned int channel=0);
|
||||
|
||||
// Converts voxel coodinates into block coordinates
|
||||
static _FORCE_INLINE_ Vector3i voxel_to_block(Vector3i pos) {
|
||||
return Vector3i(
|
||||
//pos.x < 0 ? (pos.x + 1) / VoxelBlock::SIZE - 1 : pos.x / VoxelBlock::SIZE,
|
||||
//pos.y < 0 ? (pos.y + 1) / VoxelBlock::SIZE - 1 : pos.y / VoxelBlock::SIZE,
|
||||
//pos.z < 0 ? (pos.z + 1) / VoxelBlock::SIZE - 1 : pos.z / VoxelBlock::SIZE
|
||||
pos.x >> VoxelBlock::SIZE_POW2,
|
||||
pos.y >> VoxelBlock::SIZE_POW2,
|
||||
pos.z >> VoxelBlock::SIZE_POW2
|
||||
);
|
||||
}
|
||||
|
||||
// Converts block coodinates into voxel coordinates
|
||||
static _FORCE_INLINE_ Vector3i block_to_voxel(Vector3i bpos) {
|
||||
return bpos * VoxelBlock::SIZE;
|
||||
}
|
||||
|
||||
// Gets a copy of all voxels in the area starting at min_pos having the same size as dst_buffer.
|
||||
void get_buffer_copy(Vector3i min_pos, VoxelBuffer & dst_buffer, unsigned int channel = 0);
|
||||
|
||||
// Moves the given buffer into a block of the map. The buffer is referenced, no copy is made.
|
||||
void set_block_buffer(Vector3i bpos, Ref<VoxelBuffer> buffer);
|
||||
|
||||
void remove_blocks_not_in_area(Vector3i min, Vector3i max);
|
||||
|
||||
_FORCE_INLINE_ Ref<VoxelBlock> get_block_ref(Vector3i bpos) { return get_block(bpos); }
|
||||
|
||||
bool has_block(Vector3i pos) const;
|
||||
bool is_block_surrounded(Vector3i pos) const;
|
||||
|
||||
//void set_observer(IVoxelMapObserver * observer) { _observer = observer; }
|
||||
|
||||
private:
|
||||
VoxelBlock * get_block(Vector3i bpos);
|
||||
void set_block(Vector3i bpos, VoxelBlock * block);
|
||||
|
||||
_FORCE_INLINE_ int get_block_size() const { return VoxelBlock::SIZE; }
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
_FORCE_INLINE_ int _get_voxel_binding(int x, int y, int z, unsigned int c = 0) { return get_voxel(Vector3i(x, y, z), c); }
|
||||
_FORCE_INLINE_ void _set_voxel_binding(int value, int x, int y, int z, unsigned int c = 0) { set_voxel(value, Vector3i(x, y, z), c); }
|
||||
_FORCE_INLINE_ bool _has_block_binding(int x, int y, int z) { return has_block(Vector3i(x, y, z)); }
|
||||
_FORCE_INLINE_ Vector3 _voxel_to_block_binding(Vector3 pos) const { return voxel_to_block(Vector3i(pos)).to_vec3(); }
|
||||
_FORCE_INLINE_ Vector3 _block_to_voxel_binding(Vector3 pos) const { return block_to_voxel(Vector3i(pos)).to_vec3(); }
|
||||
bool _is_block_surrounded(Vector3 pos) const { return is_block_surrounded(Vector3i(pos)); }
|
||||
void _get_buffer_copy_binding(Vector3 pos, Ref<VoxelBuffer> dst_buffer_ref, unsigned int channel = 0);
|
||||
void _set_block_buffer_binding(Vector3 bpos, Ref<VoxelBuffer> buffer) { set_block_buffer(Vector3i(bpos), buffer); }
|
||||
|
||||
};
|
||||
|
||||
//class VoxelSector {
|
||||
//public:
|
||||
// static const int SIZE = 16;
|
||||
//
|
||||
//private:
|
||||
// Ref<VoxelBlock> blocks[SIZE * SIZE * SIZE];
|
||||
//};
|
||||
|
||||
//template <unsigned int P>
|
||||
//struct VoxelTree {
|
||||
//
|
||||
// const int SIZE = 1 << P;
|
||||
// const int VOLUME = P*P*P;
|
||||
//
|
||||
// uint8_t get_voxel(int x, int y, int z) {
|
||||
// unsigned int i = index(x / SIZE, y / SIZE, z / SIZE);
|
||||
// ERR_FAIL_COND_V(i >= VOLUME, 0);
|
||||
// if (subtrees) {
|
||||
// VoxelTree<N> * subtree = subtrees[i];
|
||||
// if (subtree) {
|
||||
// return subtree->get_voxel(x, y, z);
|
||||
// }
|
||||
// }
|
||||
// else if (blocks[i].is_valid()) {
|
||||
// return blocks[i]->voxels.get_voxel(x, y, z);
|
||||
// }
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// unsigned int index(int x, int y, int z) {
|
||||
// return (pos.z * SIZE + pos.x) * SIZE + pos.y;
|
||||
// }
|
||||
//
|
||||
// Ref<VoxelBlock> * blocks;
|
||||
// VoxelTree<N> * subtrees;
|
||||
// int level;
|
||||
//};
|
||||
|
||||
#endif // VOXEL_MAP_H
|
||||
|
@ -165,11 +165,14 @@ inline bool is_transparent(const VoxelLibrary & lib, int voxel_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Ref<Mesh> VoxelMesher::build(Ref<VoxelBuffer> buffer_ref) {
|
||||
Ref<Mesh> VoxelMesher::build_ref(Ref<VoxelBuffer> buffer_ref) {
|
||||
ERR_FAIL_COND_V(buffer_ref.is_null(), Ref<Mesh>());
|
||||
return build(**buffer_ref);
|
||||
}
|
||||
|
||||
Ref<Mesh> VoxelMesher::build(const VoxelBuffer & buffer) {
|
||||
ERR_FAIL_COND_V(_library.is_null(), Ref<Mesh>());
|
||||
|
||||
const VoxelBuffer & buffer = **buffer_ref;
|
||||
const VoxelLibrary & library = **_library;
|
||||
|
||||
for (unsigned int i = 0; i < MAX_MATERIALS; ++i) {
|
||||
@ -335,7 +338,6 @@ void VoxelMesher::_bind_methods() {
|
||||
ObjectTypeDB::bind_method(_MD("set_library", "voxel_library"), &VoxelMesher::set_library);
|
||||
ObjectTypeDB::bind_method(_MD("set_occlusion_enabled", "enable"), &VoxelMesher::set_occlusion_enabled);
|
||||
ObjectTypeDB::bind_method(_MD("set_occlusion_darkness", "value"), &VoxelMesher::set_occlusion_darkness);
|
||||
ObjectTypeDB::bind_method(_MD("build", "voxel_buffer"), &VoxelMesher::build);
|
||||
ObjectTypeDB::bind_method(_MD("build", "voxel_buffer"), &VoxelMesher::build_ref);
|
||||
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,8 @@ public:
|
||||
|
||||
void set_occlusion_enabled(bool enable);
|
||||
|
||||
Ref<Mesh> build(Ref<VoxelBuffer> buffer_ref);
|
||||
Ref<Mesh> build(const VoxelBuffer & buffer_ref);
|
||||
Ref<Mesh> build_ref(Ref<VoxelBuffer> buffer_ref);
|
||||
|
||||
protected:
|
||||
|
||||
|
197
voxel_terrain.cpp
Normal file
197
voxel_terrain.cpp
Normal file
@ -0,0 +1,197 @@
|
||||
#include "voxel_terrain.h"
|
||||
#include <scene/3d/mesh_instance.h>
|
||||
#include <os/os.h>
|
||||
|
||||
VoxelTerrain::VoxelTerrain(): Node(), _min_y(-4), _max_y(4) {
|
||||
|
||||
_map = Ref<VoxelMap>(memnew(VoxelMap));
|
||||
_mesher = Ref<VoxelMesher>(memnew(VoxelMesher));
|
||||
}
|
||||
|
||||
struct BlockUpdateComparator0 {
|
||||
inline bool operator()(const Vector3i & a, const Vector3i & b) const {
|
||||
return a.length_sq() > b.length_sq();
|
||||
}
|
||||
};
|
||||
|
||||
void VoxelTerrain::force_load_blocks(Vector3i center, Vector3i extents) {
|
||||
//Vector3i min = center - extents;
|
||||
//Vector3i max = center + extents + Vector3i(1,1,1);
|
||||
//Vector3i size = max - min;
|
||||
|
||||
_block_update_queue.clear();
|
||||
|
||||
Vector3i pos;
|
||||
for (pos.z = -extents.z; pos.z <= extents.z; ++pos.z) {
|
||||
for (pos.x = -extents.x; pos.x <= extents.x; ++pos.x) {
|
||||
for (pos.y = -extents.y; pos.y <= extents.y; ++pos.y) {
|
||||
_block_update_queue.push_back(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_block_update_queue.sort_custom<BlockUpdateComparator0>();
|
||||
|
||||
}
|
||||
|
||||
int VoxelTerrain::get_block_update_count() {
|
||||
return _block_update_queue.size();
|
||||
}
|
||||
|
||||
void VoxelTerrain::_notification(int p_what) {
|
||||
|
||||
switch (p_what) {
|
||||
|
||||
case NOTIFICATION_ENTER_TREE:
|
||||
set_process(true);
|
||||
break;
|
||||
|
||||
case NOTIFICATION_PROCESS:
|
||||
_process();
|
||||
break;
|
||||
|
||||
case NOTIFICATION_EXIT_TREE:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelTerrain::_process() {
|
||||
update_blocks();
|
||||
}
|
||||
|
||||
void VoxelTerrain::update_blocks() {
|
||||
OS & os = *OS::get_singleton();
|
||||
|
||||
uint32_t time_before = os.get_ticks_msec();
|
||||
uint32_t max_time = 1000 / 60;
|
||||
|
||||
while (!_block_update_queue.empty() && (os.get_ticks_msec() - time_before) < max_time) {
|
||||
|
||||
// TODO Move this to a thread
|
||||
// TODO Have VoxelTerrainGenerator in C++
|
||||
// TODO Keep track of MeshInstances!
|
||||
|
||||
// Get request
|
||||
Vector3i block_pos = _block_update_queue[_block_update_queue.size() - 1];
|
||||
|
||||
if (!_map->has_block(block_pos)) {
|
||||
|
||||
// Get script
|
||||
ScriptInstance * script = get_script_instance();
|
||||
if (script == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create buffer
|
||||
Ref<VoxelBuffer> buffer_ref = Ref<VoxelBuffer>(memnew(VoxelBuffer));
|
||||
const Vector3i block_size(VoxelBlock::SIZE, VoxelBlock::SIZE, VoxelBlock::SIZE);
|
||||
buffer_ref->create(block_size.x, block_size.y, block_size.y);
|
||||
|
||||
// Call script to generate buffer
|
||||
Variant arg1 = buffer_ref;
|
||||
Variant arg2 = block_pos.to_vec3();
|
||||
const Variant * args[2] = { &arg1, &arg2 };
|
||||
Variant::CallError err; // wut
|
||||
script->call_multilevel("_generate_block", args, 2);
|
||||
|
||||
// Check script return
|
||||
ERR_FAIL_COND(buffer_ref->get_size() != block_size);
|
||||
|
||||
// Store buffer
|
||||
_map->set_block_buffer(block_pos, buffer_ref);
|
||||
|
||||
// Update meshes
|
||||
Vector3i ndir;
|
||||
for (ndir.z = -1; ndir.z < 2; ++ndir.z) {
|
||||
for (ndir.x = -1; ndir.x < 2; ++ndir.x) {
|
||||
for (ndir.y = -1; ndir.y < 2; ++ndir.y) {
|
||||
Vector3i npos = block_pos + ndir;
|
||||
if (_map->is_block_surrounded(npos)) {
|
||||
update_block_mesh(npos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//update_block_mesh(block_pos);
|
||||
|
||||
}
|
||||
|
||||
// Pop request
|
||||
_block_update_queue.resize(_block_update_queue.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelTerrain::update_block_mesh(Vector3i block_pos) {
|
||||
Ref<VoxelBlock> block_ref = _map->get_block_ref(block_pos);
|
||||
if (block_ref.is_null()) {
|
||||
return;
|
||||
}
|
||||
if (block_ref->voxels->is_uniform(0) && block_ref->voxels->get_voxel(0, 0, 0, 0) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create buffer padded with neighbor voxels
|
||||
VoxelBuffer nbuffer;
|
||||
nbuffer.create(VoxelBlock::SIZE + 2, VoxelBlock::SIZE + 2, VoxelBlock::SIZE + 2);
|
||||
_map->get_buffer_copy(VoxelMap::block_to_voxel(block_pos) - Vector3i(1, 1, 1), nbuffer);
|
||||
|
||||
// TEST
|
||||
//if (block_pos == Vector3i(0, 0, 0)) {
|
||||
// printf(">>>\n");
|
||||
// String os;
|
||||
// for (unsigned int y = 0; y < nbuffer.get_size().y; ++y) {
|
||||
// for (unsigned int z = 0; z < nbuffer.get_size().z; ++z) {
|
||||
// for (unsigned int x = 0; x < nbuffer.get_size().x; ++x) {
|
||||
// if (nbuffer.get_voxel(x, y, z) == 0)
|
||||
// os += '-';
|
||||
// else
|
||||
// os += 'O';
|
||||
// }
|
||||
// os += '\n';
|
||||
// }
|
||||
// os += '\n';
|
||||
// }
|
||||
// wprintf(os.c_str());
|
||||
//}
|
||||
|
||||
// Build mesh (that part is the most CPU-intensive)
|
||||
Ref<Mesh> mesh = _mesher->build(nbuffer);
|
||||
|
||||
MeshInstance * mesh_instance = block_ref->get_mesh_instance(*this);
|
||||
if (mesh_instance == NULL) {
|
||||
// Create and spawn mesh
|
||||
mesh_instance = memnew(MeshInstance);
|
||||
mesh_instance->set_mesh(mesh);
|
||||
mesh_instance->set_translation(VoxelMap::block_to_voxel(block_pos).to_vec3());
|
||||
add_child(mesh_instance);
|
||||
block_ref->mesh_instance_path = mesh_instance->get_path();
|
||||
}
|
||||
else {
|
||||
// Update mesh
|
||||
mesh_instance->set_mesh(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
//void VoxelTerrain::block_removed(VoxelBlock & block) {
|
||||
// MeshInstance * mesh_instance = block.get_mesh_instance(*this);
|
||||
// if (mesh_instance) {
|
||||
// mesh_instance->queue_delete();
|
||||
// }
|
||||
//}
|
||||
|
||||
void VoxelTerrain::_bind_methods() {
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("get_block_update_count"), &VoxelTerrain::get_block_update_count);
|
||||
ObjectTypeDB::bind_method(_MD("get_mesher:VoxelMesher"), &VoxelTerrain::get_mesher);
|
||||
|
||||
// TODO Make those two static in VoxelMap?
|
||||
ObjectTypeDB::bind_method(_MD("voxel_to_block", "voxel_pos"), &VoxelTerrain::_voxel_to_block_binding);
|
||||
ObjectTypeDB::bind_method(_MD("block_to_voxel", "block_pos"), &VoxelTerrain::_block_to_voxel_binding);
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("force_load_blocks", "center", "extents"), &VoxelTerrain::_force_load_blocks_binding);
|
||||
|
||||
}
|
||||
|
52
voxel_terrain.h
Normal file
52
voxel_terrain.h
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef VOXEL_TERRAIN_H
|
||||
#define VOXEL_TERRAIN_H
|
||||
|
||||
#include <scene/main/node.h>
|
||||
#include "voxel_map.h"
|
||||
#include "voxel_mesher.h"
|
||||
|
||||
// Infinite static terrain made of voxels.
|
||||
// It is loaded around VoxelTerrainStreamers.
|
||||
class VoxelTerrain : public Node /*, public IVoxelMapObserver*/ {
|
||||
OBJ_TYPE(VoxelTerrain, Node)
|
||||
|
||||
// Parameters
|
||||
int _min_y; // In blocks, not voxels
|
||||
int _max_y;
|
||||
|
||||
// Voxel storage
|
||||
Ref<VoxelMap> _map;
|
||||
|
||||
Vector<Vector3i> _block_update_queue;
|
||||
Ref<VoxelMesher> _mesher;
|
||||
|
||||
public:
|
||||
VoxelTerrain();
|
||||
|
||||
void force_load_blocks(Vector3i center, Vector3i extents);
|
||||
|
||||
int get_block_update_count();
|
||||
|
||||
Ref<VoxelMesher> get_mesher() { return _mesher; }
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
void _process();
|
||||
|
||||
void update_blocks();
|
||||
void update_block_mesh(Vector3i block_pos);
|
||||
|
||||
// Observer events
|
||||
//void block_removed(VoxelBlock & block);
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
// Convenience
|
||||
Vector3 _voxel_to_block_binding(Vector3 pos) { return Vector3i(VoxelMap::voxel_to_block(pos)).to_vec3(); }
|
||||
Vector3 _block_to_voxel_binding(Vector3 pos) { return Vector3i(VoxelMap::block_to_voxel(pos)).to_vec3(); }
|
||||
void _force_load_blocks_binding(Vector3 center, Vector3 extents) { force_load_blocks(center, extents); }
|
||||
|
||||
};
|
||||
|
||||
#endif // VOXEL_TERRAIN_H
|
||||
|
Loading…
Reference in New Issue
Block a user