Expose things to the editor, first iteration, might be buggy

master
Marc Gilleron 2017-08-15 02:24:52 +02:00
parent 51596fef95
commit df13e025ca
10 changed files with 485 additions and 110 deletions

View File

@ -68,6 +68,10 @@ struct Vector3i {
z -= other.z;
}
_FORCE_INLINE_ Vector3i operator-() const {
return Vector3i(-x, -y, -z);
}
_FORCE_INLINE_ int &operator[](unsigned int i) {
return coords[i];
}

204
voxel.cpp
View File

@ -2,15 +2,96 @@
#include "voxel_library.h"
#include "voxel_mesher.h"
#define STRLEN(x) (sizeof(x) / sizeof(x[0]))
Voxel::Voxel()
: Reference(),
: Resource(),
_id(-1),
_material_id(0),
_is_transparent(false),
_library(NULL),
_color(1.f, 1.f, 1.f) {}
_color(1.f, 1.f, 1.f),
_geometry_type(GEOMETRY_NONE),
_cube_geometry_padding_y(0) {}
Ref<Voxel> Voxel::set_name(String name) {
static Voxel::Side name_to_side(const String &s) {
if (s == "left")
return Voxel::SIDE_LEFT;
if (s == "right")
return Voxel::SIDE_RIGHT;
if (s == "top")
return Voxel::SIDE_TOP;
if (s == "bottom")
return Voxel::SIDE_BOTTOM;
if (s == "front")
return Voxel::SIDE_FRONT;
if (s == "back")
return Voxel::SIDE_BACK;
return Voxel::SIDE_COUNT; // Invalid
}
bool Voxel::_set(const StringName &p_name, const Variant &p_value) {
String name = p_name;
// TODO Eventualy these could be Rect2 for maximum flexibility?
if (name.begins_with("cube_tiles/")) {
String s = name.substr(STRLEN("cube_tiles/"), name.length());
Voxel::Side side = name_to_side(s);
if (side != Voxel::SIDE_COUNT) {
Vector2 v = p_value;
set_cube_uv_side(side, v);
return true;
}
} else if (name == "cube_geometry/padding_y") {
_cube_geometry_padding_y = p_value;
set_cube_geometry(_cube_geometry_padding_y);
return true;
}
return false;
}
bool Voxel::_get(const StringName &p_name, Variant &r_ret) const {
String name = p_name;
if (name.begins_with("cube_tiles/")) {
String s = name.substr(STRLEN("cube_tiles/"), name.length());
Voxel::Side side = name_to_side(s);
if (side != Voxel::SIDE_COUNT) {
r_ret = _cube_tiles[side];
return true;
}
} else if (name == "cube_geometry/padding_y") {
r_ret = _cube_geometry_padding_y;
return true;
}
return false;
}
void Voxel::_get_property_list(List<PropertyInfo> *p_list) const {
if (_geometry_type == GEOMETRY_CUBE) {
p_list->push_back(PropertyInfo(Variant::REAL, "cube_geometry/padding_y"));
p_list->push_back(PropertyInfo(Variant::VECTOR2, "cube_tiles/left"));
p_list->push_back(PropertyInfo(Variant::VECTOR2, "cube_tiles/right"));
p_list->push_back(PropertyInfo(Variant::VECTOR2, "cube_tiles/bottom"));
p_list->push_back(PropertyInfo(Variant::VECTOR2, "cube_tiles/top"));
p_list->push_back(PropertyInfo(Variant::VECTOR2, "cube_tiles/back"));
p_list->push_back(PropertyInfo(Variant::VECTOR2, "cube_tiles/front"));
}
}
Ref<Voxel> Voxel::set_voxel_name(String name) {
_name = name;
return Ref<Voxel>(this);
}
@ -39,7 +120,53 @@ Ref<Voxel> Voxel::set_transparent(bool t) {
return Ref<Voxel>(this);
}
void Voxel::set_geometry_type(GeometryType type) {
_geometry_type = type;
switch (_geometry_type) {
case GEOMETRY_NONE: {
// Clear all geometry
_model_vertices.resize(0);
_model_normals.resize(0);
_model_uv.resize(0);
for (int side = 0; side < SIDE_COUNT; ++side) {
_model_side_vertices[side].resize(0);
_model_side_uv[side].resize(0);
}
} break;
case GEOMETRY_CUBE:
set_cube_geometry(_cube_geometry_padding_y);
update_cube_uv_sides();
break;
default:
print_line("Wtf? Unknown geometry type");
break;
}
}
Voxel::GeometryType Voxel::get_geometry_type() const {
return _geometry_type;
}
void Voxel::set_library(Ref<VoxelLibrary> lib) {
_library.set_ref(lib);
// Update model UVs because atlas size is defined by the library
update_cube_uv_sides();
}
VoxelLibrary *Voxel::get_library() const {
Object *v = _library.get_ref();
if (v)
return v->cast_to<VoxelLibrary>();
return NULL;
}
Ref<Voxel> Voxel::set_cube_geometry(float sy) {
sy = 1.0 + sy;
const Vector3 vertices[SIDE_COUNT][6] = {
{ // LEFT
Vector3(0, 0, 0),
@ -96,8 +223,20 @@ Ref<Voxel> Voxel::set_cube_geometry(float sy) {
return Ref<Voxel>(this);
}
Ref<Voxel> Voxel::_set_cube_uv_sides(const Vector2 atlas_pos[6]) {
ERR_FAIL_COND_V(_library == NULL, Ref<Voxel>());
void Voxel::set_cube_uv_side(int side, Vector2 tile_pos) {
_cube_tiles[side] = tile_pos;
// TODO Better have a dirty flag, otherwise UVs will be needlessly updated at least 6 times everytime a Voxel resource is loaded!
update_cube_uv_sides();
}
void Voxel::update_cube_uv_sides() {
VoxelLibrary *library = get_library();
//ERR_FAIL_COND(library == NULL);
if(library == NULL) {
// Not an error, the Voxel might have been created before the library, and can't be used without anyways
print_line("VoxelLibrary not set yet");
return;
}
float e = 0.001;
const Vector2 uv[4] = {
@ -107,6 +246,8 @@ Ref<Voxel> Voxel::_set_cube_uv_sides(const Vector2 atlas_pos[6]) {
Vector2(1.f - 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 },
@ -122,41 +263,15 @@ Ref<Voxel> Voxel::_set_cube_uv_sides(const Vector2 atlas_pos[6]) {
{ 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) {
_model_side_uv[side].resize(6);
PoolVector<Vector2>::Write w = _model_side_uv[side].write();
for (unsigned int i = 0; i < 6; ++i) {
w[i] = (atlas_pos[side] + uv[uv6[side][i]]) * s;
w[i] = (_cube_tiles[side] + uv[uv6[side][i]]) * s;
}
}
return Ref<Voxel>(this);
}
Ref<Voxel> Voxel::set_cube_uv_all_sides(Vector2 atlas_pos) {
const Vector2 positions[6] = {
atlas_pos,
atlas_pos,
atlas_pos,
atlas_pos,
atlas_pos,
atlas_pos
};
return _set_cube_uv_sides(positions);
}
Ref<Voxel> Voxel::set_cube_uv_tbs_sides(Vector2 top_atlas_pos, Vector2 side_atlas_pos, Vector2 bottom_atlas_pos) {
const Vector2 positions[6] = {
side_atlas_pos,
side_atlas_pos,
bottom_atlas_pos,
top_atlas_pos,
side_atlas_pos,
side_atlas_pos,
};
return _set_cube_uv_sides(positions);
}
//Ref<Voxel> Voxel::set_xquad_geometry(Vector2 atlas_pos) {
@ -166,8 +281,8 @@ Ref<Voxel> Voxel::set_cube_uv_tbs_sides(Vector2 top_atlas_pos, Vector2 side_atla
void Voxel::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_name", "name"), &Voxel::set_name);
ClassDB::bind_method(D_METHOD("get_name"), &Voxel::get_name);
ClassDB::bind_method(D_METHOD("set_voxel_name", "name"), &Voxel::set_voxel_name);
ClassDB::bind_method(D_METHOD("get_voxel_name"), &Voxel::get_voxel_name);
ClassDB::bind_method(D_METHOD("set_id", "id"), &Voxel::set_id);
ClassDB::bind_method(D_METHOD("get_id"), &Voxel::get_id);
@ -175,15 +290,24 @@ void Voxel::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_color", "color"), &Voxel::set_color);
ClassDB::bind_method(D_METHOD("get_color"), &Voxel::get_color);
ClassDB::bind_method(D_METHOD("set_transparent", "color"), &Voxel::set_transparent, DEFVAL(true));
ClassDB::bind_method(D_METHOD("set_transparent", "transparent"), &Voxel::set_transparent, DEFVAL(true));
ClassDB::bind_method(D_METHOD("is_transparent"), &Voxel::is_transparent);
ClassDB::bind_method(D_METHOD("set_material_id", "id"), &Voxel::set_material_id);
ClassDB::bind_method(D_METHOD("get_material_id"), &Voxel::get_material_id);
ClassDB::bind_method(D_METHOD("set_cube_geometry", "height"), &Voxel::set_cube_geometry, DEFVAL(1.f));
ClassDB::bind_method(D_METHOD("set_cube_uv_all_sides", "atlas_pos"), &Voxel::set_cube_uv_all_sides);
ClassDB::bind_method(D_METHOD("set_cube_uv_tbs_sides", "top_atlas_pos", "side_atlas_pos", "bottom_atlas_pos"), &Voxel::set_cube_uv_tbs_sides);
ClassDB::bind_method(D_METHOD("set_geometry_type", "type"), &Voxel::set_geometry_type);
ClassDB::bind_method(D_METHOD("get_geometry_type"), &Voxel::get_geometry_type);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "voxel_name"), "set_name", "get_name");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transparent"), "set_transparent", "is_transparent");
ADD_PROPERTY(PropertyInfo(Variant::INT, "material_id"), "set_material_id", "get_material_id");
ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry_type", PROPERTY_HINT_ENUM, "None,Cube"), "set_geometry_type", "get_geometry_type");
BIND_CONSTANT(GEOMETRY_NONE);
BIND_CONSTANT(GEOMETRY_CUBE);
BIND_CONSTANT(GEOMETRY_MAX);
BIND_CONSTANT(CHANNEL_TYPE)
BIND_CONSTANT(CHANNEL_ISOLEVEL)

45
voxel.h
View File

@ -1,15 +1,15 @@
#ifndef VOXEL_TYPE_H
#define VOXEL_TYPE_H
#include <reference.h>
#include <resource.h>
class VoxelLibrary;
// Definition of one type of voxel.
// A voxel can be a simple coloured cube, or a more complex model.
// Important: it is recommended that you create voxels from a library rather than using new().
class Voxel : public Reference {
GDCLASS(Voxel, Reference)
class Voxel : public Resource {
GDCLASS(Voxel, Resource)
public:
enum Side {
@ -36,8 +36,8 @@ public:
// Properties
Ref<Voxel> set_name(String name);
_FORCE_INLINE_ String get_name() const { return _name; }
Ref<Voxel> set_voxel_name(String name);
_FORCE_INLINE_ String get_voxel_name() const { return _name; }
Ref<Voxel> set_id(int id);
_FORCE_INLINE_ int get_id() const { return _id; }
@ -51,12 +51,17 @@ public:
Ref<Voxel> set_transparent(bool t = true);
_FORCE_INLINE_ bool is_transparent() const { return _is_transparent; }
//-------------------------------------------
// Built-in geometry generators
Ref<Voxel> set_cube_geometry(float sy = 1);
Ref<Voxel> set_cube_uv_all_sides(Vector2 atlas_pos);
Ref<Voxel> set_cube_uv_tbs_sides(Vector2 top_atlas_pos, Vector2 side_atlas_pos, Vector2 bottom_atlas_pos);
//Ref<Voxel> set_xquad_geometry(Vector2 atlas_pos);
enum GeometryType {
GEOMETRY_NONE = 0,
GEOMETRY_CUBE = 1,
GEOMETRY_MAX
};
void set_geometry_type(GeometryType type);
GeometryType get_geometry_type() const;
// Getters for native usage only
@ -66,15 +71,25 @@ public:
const PoolVector<Vector3> &get_model_side_vertices(unsigned int side) const { return _model_side_vertices[side]; }
const PoolVector<Vector2> &get_model_side_uv(unsigned int side) const { return _model_side_uv[side]; }
void set_library_ptr(VoxelLibrary *lib) { _library = lib; }
void set_library(Ref<VoxelLibrary> lib);
protected:
Ref<Voxel> _set_cube_uv_sides(const Vector2 atlas_pos[6]);
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
void set_cube_uv_side(int side, Vector2 tile_pos);
void update_cube_uv_sides();
VoxelLibrary *get_library() const;
static void _bind_methods();
Ref<Voxel> set_cube_geometry(float sy = 1);
//Ref<Voxel> set_xquad_geometry(Vector2 atlas_pos);
private:
VoxelLibrary *_library;
WeakRef _library;
// Identifiers
int _id;
@ -83,9 +98,12 @@ private:
// Properties
int _material_id;
bool _is_transparent;
Color _color;
GeometryType _geometry_type;
float _cube_geometry_padding_y;
Vector2 _cube_tiles[SIDE_COUNT];
// Model
Color _color;
PoolVector<Vector3> _model_vertices;
PoolVector<Vector3> _model_normals;
PoolVector<Vector2> _model_uv;
@ -96,5 +114,6 @@ private:
};
VARIANT_ENUM_CAST(Voxel::ChannelMode)
VARIANT_ENUM_CAST(Voxel::GeometryType)
#endif // VOXEL_TYPE_H

View File

@ -1,18 +1,96 @@
#include "voxel_library.h"
VoxelLibrary::VoxelLibrary()
: Reference(), _atlas_size(1) {
// Defaults
create_voxel(0, "air")->set_transparent(true);
create_voxel(1, "solid")->set_transparent(false)->set_cube_geometry();
VoxelLibrary::VoxelLibrary() :
Resource(), _atlas_size(1) {
}
VoxelLibrary::~VoxelLibrary() {
for (unsigned int i = 0; i < MAX_VOXEL_TYPES; ++i) {
if (_voxel_types[i].is_valid()) {
_voxel_types[i]->set_library_ptr(NULL);
// Handled with a WeakRef
// for (unsigned int i = 0; i < MAX_VOXEL_TYPES; ++i) {
// if (_voxel_types[i].is_valid()) {
// _voxel_types[i]->set_library(NULL);
// }
// }
}
int VoxelLibrary::get_voxel_count() const {
int count = 0;
for(int i = 0; i < MAX_VOXEL_TYPES; ++i) {
if(_voxel_types[i].is_valid())
++count;
}
return count;
}
void VoxelLibrary::load_default() {
create_voxel(0, "air")->set_transparent(true);
create_voxel(1, "solid")
->set_transparent(false)
->set_geometry_type(Voxel::GEOMETRY_CUBE);
_max_count = 2;
}
// TODO Add a way to add voxels
bool VoxelLibrary::_set(const StringName &p_name, const Variant &p_value) {
// if(p_name == "voxels/max") {
// int v = p_value;
// _max_count = CLAMP(v, 0, MAX_VOXEL_TYPES);
// for(int i = _max_count; i < MAX_VOXEL_TYPES; ++i) {
// _voxel_types[i] = Ref<Voxel>();
// return true;
// }
// } else
if (p_name.operator String().begins_with("voxels/")) {
int idx = p_name.operator String().get_slicec('/', 1).to_int();
if (idx >= 0 && idx < MAX_VOXEL_TYPES) {
Ref<Voxel> voxel = p_value;
_voxel_types[idx] = voxel;
if(voxel.is_valid()) {
voxel->set_library(Ref<VoxelLibrary>(this));
}
// Note: if the voxel is set to null, we could set the previous one's library reference to null.
// however it Voxels use a weak reference, so it's not really needed
return true;
}
}
return false;
}
bool VoxelLibrary::_get(const StringName &p_name, Variant &r_ret) const {
// if(p_name == "voxels/max") {
// r_ret = _max_count;
// return true;
// } else
if (p_name.operator String().begins_with("voxels/")) {
int idx = p_name.operator String().get_slicec('/', 1).to_int();
if (idx >= 0 && idx < MAX_VOXEL_TYPES) {
r_ret = _voxel_types[idx];
return true;
}
}
return false;
}
void VoxelLibrary::_get_property_list(List<PropertyInfo> *p_list) const {
//p_list->push_back(PropertyInfo(Variant::INT, "voxels/max"));
//for(int i = 0; i < _max_count; ++i) {
for(int i = 0; i < MAX_VOXEL_TYPES; ++i) {
p_list->push_back(PropertyInfo(Variant::OBJECT, "voxels/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "Voxel"));
}
}
void VoxelLibrary::set_atlas_size(int s) {
@ -23,10 +101,13 @@ void VoxelLibrary::set_atlas_size(int s) {
Ref<Voxel> VoxelLibrary::create_voxel(int id, String name) {
ERR_FAIL_COND_V(id < 0 || id >= MAX_VOXEL_TYPES, Ref<Voxel>());
Ref<Voxel> voxel(memnew(Voxel));
voxel->set_library_ptr(this);
voxel->set_library(Ref<VoxelLibrary>(this));
voxel->set_id(id);
voxel->set_name(name);
voxel->set_voxel_name(name);
_voxel_types[id] = voxel;
if(id >= _max_count) {
_max_count = id + 1;
}
return voxel;
}
@ -41,4 +122,9 @@ void VoxelLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_voxel", "id"), &VoxelLibrary::_get_voxel_bind);
ClassDB::bind_method(D_METHOD("set_atlas_size", "square_size"), &VoxelLibrary::set_atlas_size);
ClassDB::bind_method(D_METHOD("get_atlas_size"), &VoxelLibrary::get_atlas_size);
ADD_PROPERTY(PropertyInfo(Variant::INT, "atlas_size"), "set_atlas_size", "get_atlas_size");
}

View File

@ -2,10 +2,10 @@
#define VOXEL_LIBRARY_H
#include "voxel.h"
#include <reference.h>
#include <resource.h>
class VoxelLibrary : public Reference {
GDCLASS(VoxelLibrary, Reference)
class VoxelLibrary : public Resource {
GDCLASS(VoxelLibrary, Resource)
public:
static const unsigned int MAX_VOXEL_TYPES = 256; // Required limit because voxel types are stored in 8 bits
@ -19,6 +19,10 @@ public:
// Use this factory rather than creating voxels from scratch
Ref<Voxel> create_voxel(int id, String name);
int get_voxel_count() const;
void load_default();
// Internal getters
_FORCE_INLINE_ bool has_voxel(int id) const { return _voxel_types[id].is_valid(); }
@ -27,10 +31,15 @@ public:
protected:
static void _bind_methods();
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
Ref<Voxel> _get_voxel_bind(int id);
private:
Ref<Voxel> _voxel_types[MAX_VOXEL_TYPES];
int _max_count;
int _atlas_size;
};

View File

@ -123,7 +123,6 @@ VoxelMesher::VoxelMesher()
_bake_occlusion(true) {}
void VoxelMesher::set_library(Ref<VoxelLibrary> library) {
ERR_FAIL_COND(library.is_null());
_library = library;
}
@ -339,21 +338,19 @@ Ref<ArrayMesh> VoxelMesher::build(const VoxelBuffer &buffer, unsigned int channe
mesh_ref = Ref<ArrayMesh>(memnew(ArrayMesh));
for (unsigned int i = 0; i < MAX_MATERIALS; ++i) {
if (_materials[i].is_valid()) {
SurfaceTool &st = _surface_tool[i];
SurfaceTool &st = _surface_tool[i];
// Index mesh to reduce memory usage and make upload to VRAM faster
// TODO actually, we could make it indexed from the ground up without using SurfaceTool, so we also save time!
// VOXEL_PROFILE_BEGIN("mesher_surfacetool_index")
// st.index();
// VOXEL_PROFILE_END("mesher_surfacetool_index")
// Index mesh to reduce memory usage and make upload to VRAM faster
// TODO actually, we could make it indexed from the ground up without using SurfaceTool, so we also save time!
// VOXEL_PROFILE_BEGIN("mesher_surfacetool_index")
// st.index();
// VOXEL_PROFILE_END("mesher_surfacetool_index")
VOXEL_PROFILE_BEGIN("mesher_surfacetool_commit")
mesh_ref = st.commit(mesh_ref);
VOXEL_PROFILE_END("mesher_surfacetool_commit")
VOXEL_PROFILE_BEGIN("mesher_surfacetool_commit")
mesh_ref = st.commit(mesh_ref);
VOXEL_PROFILE_END("mesher_surfacetool_commit")
st.clear();
}
st.clear();
}
return mesh_ref;

View File

@ -1,11 +1,11 @@
#ifndef VOXEL_PROVIDER_H
#define VOXEL_PROVIDER_H
#include "reference.h"
#include <resource.h>
#include "voxel_buffer.h"
class VoxelProvider : public Reference {
GDCLASS(VoxelProvider, Reference)
class VoxelProvider : public Resource {
GDCLASS(VoxelProvider, Resource)
public:
virtual void emerge_block(Ref<VoxelBuffer> out_buffer, Vector3i block_pos);
virtual void immerge_block(Ref<VoxelBuffer> buffer, Vector3i block_pos);

View File

@ -4,7 +4,7 @@
VARIANT_ENUM_CAST(VoxelProviderTest::Mode)
VoxelProviderTest::VoxelProviderTest() {
_mode = MODE_FLAT;
_mode = MODE_WAVES;
_voxel_type = 1;
_pattern_size = Vector3i(10, 10, 10);
}
@ -72,19 +72,32 @@ void VoxelProviderTest::generate_block_waves(VoxelBuffer &out_buffer, Vector3i b
float period_x = 1.f / static_cast<float>(_pattern_size.x);
float period_z = 1.f / static_cast<float>(_pattern_size.z);
for (int rz = 0; rz < size.z; ++rz) {
for (int rx = 0; rx < size.x; ++rx) {
//out_buffer.fill(0, 1); // TRANSVOXEL TEST
float x = origin.x + rx;
float z = origin.z + rz;
if(origin.y + size.y < Math::floor(_pattern_offset.y - 1.5*amplitude)) {
// Everything is ground
out_buffer.fill(_voxel_type);
int h = _pattern_offset.y + amplitude * (Math::cos(x * period_x) + Math::sin(z * period_z));
int rh = h - origin.y;
if (rh > size.y)
rh = size.y;
} else if(origin.y > Math::ceil(_pattern_offset.y + 1.5*amplitude)) {
// Everything is air
return;
for (int ry = 0; ry < rh; ++ry) {
out_buffer.set_voxel(_voxel_type, rx, ry, rz, 0);
} else {
for (int rz = 0; rz < size.z; ++rz) {
for (int rx = 0; rx < size.x; ++rx) {
float x = origin.x + rx;
float z = origin.z + rz;
int h = _pattern_offset.y + amplitude * (Math::cos(x * period_x) + Math::sin(z * period_z));
int rh = h - origin.y;
if (rh > size.y)
rh = size.y;
for (int ry = 0; ry < rh; ++ry) {
out_buffer.set_voxel(_voxel_type, rx, ry, rz, 0);
//out_buffer.set_voxel(255, rx, ry, rz, 1); // TRANSVOXEL TEST
}
}
}
}
@ -104,6 +117,13 @@ void VoxelProviderTest::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pattern_offset", "offset"), &VoxelProviderTest::_set_pattern_offset);
ClassDB::bind_method(D_METHOD("get_pattern_offset"), &VoxelProviderTest::_get_pattern_offset);
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Flat,Waves"), "set_mode", "get_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "voxel_type", PROPERTY_HINT_RANGE, "0,255,1"), "set_voxel_type", "get_voxel_type");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "pattern_size"), "set_pattern_size", "get_pattern_size");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "pattern_offset"), "set_pattern_offset", "get_pattern_offset");
BIND_CONSTANT(MODE_FLAT);
BIND_CONSTANT(MODE_WAVES);
}

View File

@ -9,6 +9,8 @@ VoxelTerrain::VoxelTerrain()
_map = Ref<VoxelMap>(memnew(VoxelMap));
_mesher = Ref<VoxelMesher>(memnew(VoxelMesher));
_mesher_smooth = Ref<VoxelMesherSmooth>(memnew(VoxelMesherSmooth));
_view_distance_blocks = 8;
}
Vector3i g_viewer_block_pos; // TODO UGLY! Lambdas or pointers needed...
@ -20,33 +22,98 @@ struct BlockUpdateComparator {
}
};
void VoxelTerrain::set_provider(Ref<VoxelProvider> provider) {
_provider = provider;
// TODO See if there is a way to specify materials in voxels directly?
bool VoxelTerrain::_set(const StringName &p_name, const Variant &p_value) {
if (p_name.operator String().begins_with("material/")) {
int idx = p_name.operator String().get_slicec('/', 1).to_int();
if (idx >= VoxelMesher::MAX_MATERIALS || idx < 0)
return false;
set_material(idx, p_value);
return true;
}
return false;
}
Ref<VoxelProvider> VoxelTerrain::get_provider() {
bool VoxelTerrain::_get(const StringName &p_name, Variant &r_ret) const {
if (p_name.operator String().begins_with("material/")) {
int idx = p_name.operator String().get_slicec('/', 1).to_int();
if (idx >= VoxelMesher::MAX_MATERIALS || idx < 0)
return false;
r_ret = get_material(idx);
return true;
}
return false;
}
void VoxelTerrain::_get_property_list(List<PropertyInfo> *p_list) const {
for (int i = 0; i < VoxelMesher::MAX_MATERIALS; ++i) {
p_list->push_back(PropertyInfo(Variant::OBJECT, "material/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,SpatialMaterial"));
}
}
void VoxelTerrain::set_provider(Ref<VoxelProvider> provider) {
if(provider != _provider) {
_provider = provider;
make_all_view_dirty();
}
}
Ref<VoxelProvider> VoxelTerrain::get_provider() const {
return _provider;
}
Ref<VoxelLibrary> VoxelTerrain::get_voxel_library() {
Ref<VoxelLibrary> VoxelTerrain::get_voxel_library() const {
return _mesher->get_library();
}
void VoxelTerrain::set_voxel_library(Ref<VoxelLibrary> library) {
if(library != _mesher->get_library()) {
#ifdef TOOLS_ENABLED
if(library->get_voxel_count() == 0) {
library->load_default();
}
#endif
_mesher->set_library(library);
make_all_view_dirty();
}
}
void VoxelTerrain::set_generate_collisions(bool enabled) {
_generate_collisions = enabled;
}
int VoxelTerrain::get_view_distance() const {
return _view_distance_blocks * VoxelBlock::SIZE;
}
void VoxelTerrain::set_view_distance(int distance_in_voxels) {
ERR_FAIL_COND(distance_in_voxels < 0)
int d = distance_in_voxels / VoxelBlock::SIZE;
if(d != _view_distance_blocks) {
_view_distance_blocks = d;
make_all_view_dirty();
// TODO Immerge blocks too far away
}
}
void VoxelTerrain::set_viewer_path(NodePath path) {
if (!path.is_empty())
ERR_FAIL_COND(get_viewer(path) == NULL);
_viewer_path = path;
}
NodePath VoxelTerrain::get_viewer_path() {
NodePath VoxelTerrain::get_viewer_path() const {
return _viewer_path;
}
Spatial *VoxelTerrain::get_viewer(NodePath path) {
Spatial *VoxelTerrain::get_viewer(NodePath path) const {
if (path.is_empty())
return NULL;
Node *node = get_node(path);
@ -55,6 +122,15 @@ Spatial *VoxelTerrain::get_viewer(NodePath path) {
return node->cast_to<Spatial>();
}
void VoxelTerrain::set_material(int id, Ref<Material> material) {
// TODO Update existing block surfaces
_mesher->set_material(material, id);
}
Ref<Material> VoxelTerrain::get_material(int id) const {
return _mesher->get_material(id);
}
//void VoxelTerrain::clear_update_queue() {
// _block_update_queue.clear();
// _dirty_blocks.clear();
@ -85,6 +161,12 @@ void VoxelTerrain::make_blocks_dirty(Vector3i min, Vector3i size) {
}
}
void VoxelTerrain::make_all_view_dirty() {
Vector3i radius(_view_distance_blocks, _view_distance_blocks, _view_distance_blocks);
// TODO Take viewer and fixed range into account
make_blocks_dirty(-radius, 2*radius);
}
inline int get_border_index(int x, int max) {
return x == 0 ? 0 : x != max ? 1 : 2;
}
@ -217,6 +299,11 @@ void VoxelTerrain::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE:
break;
case NOTIFICATION_READY:
// TODO This should also react to viewer movement
make_all_view_dirty();
break;
default:
break;
}
@ -258,6 +345,7 @@ void VoxelTerrain::update_blocks() {
bool entire_block_changed = false;
if (!_map->has_block(block_pos)) {
// The block's data isn't loaded yet
// Create buffer
if (!_provider.is_null()) {
@ -274,6 +362,7 @@ void VoxelTerrain::update_blocks() {
_provider->emerge_block(buffer_ref, block_pos);
// Check script return
// TODO Shouldn't halt execution though, as it can bring the map in an invalid state!
ERR_FAIL_COND(buffer_ref->get_size() != block_size);
VOXEL_PROFILE_END("block_generation")
@ -316,11 +405,10 @@ void VoxelTerrain::update_blocks() {
static inline bool is_mesh_empty(Ref<Mesh> mesh_ref) {
if (mesh_ref.is_null())
return true;
Mesh &mesh = **mesh_ref;
const Mesh &mesh = **mesh_ref;
if (mesh.get_surface_count() == 0)
return true;
// TODO Shouldn't it have an index to the surface rather than just the type? Oo
if (mesh.surface_get_array_len(Mesh::ARRAY_VERTEX) == 0)
if (mesh.surface_get_array_len(0) == 0)
return true;
return false;
}
@ -450,14 +538,20 @@ void VoxelTerrain::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_provider", "provider"), &VoxelTerrain::set_provider);
ClassDB::bind_method(D_METHOD("get_provider"), &VoxelTerrain::get_provider);
ClassDB::bind_method(D_METHOD("set_voxel_library", "library"), &VoxelTerrain::set_voxel_library);
ClassDB::bind_method(D_METHOD("get_voxel_library"), &VoxelTerrain::get_voxel_library);
ClassDB::bind_method(D_METHOD("set_view_distance", "distance_in_voxels"), &VoxelTerrain::set_view_distance);
ClassDB::bind_method(D_METHOD("get_view_distance"), &VoxelTerrain::get_view_distance);
ClassDB::bind_method(D_METHOD("get_block_update_count"), &VoxelTerrain::get_block_update_count);
ClassDB::bind_method(D_METHOD("get_mesher"), &VoxelTerrain::get_mesher);
ClassDB::bind_method(D_METHOD("get_generate_collisions"), &VoxelTerrain::get_generate_collisions);
ClassDB::bind_method(D_METHOD("set_generate_collisions", "enabled"), &VoxelTerrain::set_generate_collisions);
ClassDB::bind_method(D_METHOD("get_viewer"), &VoxelTerrain::get_viewer_path);
ClassDB::bind_method(D_METHOD("set_viewer", "path"), &VoxelTerrain::set_viewer_path);
ClassDB::bind_method(D_METHOD("get_viewer_path"), &VoxelTerrain::get_viewer_path);
ClassDB::bind_method(D_METHOD("set_viewer_path", "path"), &VoxelTerrain::set_viewer_path);
ClassDB::bind_method(D_METHOD("get_storage"), &VoxelTerrain::get_map);
@ -474,4 +568,10 @@ void VoxelTerrain::_bind_methods() {
#ifdef VOXEL_PROFILING
ClassDB::bind_method(D_METHOD("get_profiling_info"), &VoxelTerrain::get_profiling_info);
#endif
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "provider", PROPERTY_HINT_RESOURCE_TYPE, "VoxelProvider"), "set_provider", "get_provider");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "voxel_library", PROPERTY_HINT_RESOURCE_TYPE, "VoxelLibrary"), "set_voxel_library", "get_voxel_library");
ADD_PROPERTY(PropertyInfo(Variant::INT, "view_distance"), "set_view_distance", "get_view_distance");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "viewer_path"), "set_viewer_path", "get_viewer_path");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_collisions"), "set_generate_collisions", "get_generate_collisions");
}

View File

@ -16,11 +16,13 @@ public:
VoxelTerrain();
void set_provider(Ref<VoxelProvider> provider);
Ref<VoxelProvider> get_provider();
Ref<VoxelProvider> get_provider() const;
void set_voxel_library(Ref<VoxelLibrary> library);
Ref<VoxelLibrary> get_voxel_library() const;
void force_load_blocks(Vector3i center, Vector3i extents);
int get_block_update_count();
//void clear_update_queue();
void make_block_dirty(Vector3i bpos);
void make_blocks_dirty(Vector3i min, Vector3i size);
@ -28,25 +30,36 @@ public:
bool is_block_dirty(Vector3i bpos);
void set_generate_collisions(bool enabled);
bool get_generate_collisions() { return _generate_collisions; }
bool get_generate_collisions() const { return _generate_collisions; }
int get_view_distance() const;
void set_view_distance(int distance_in_voxels);
void set_viewer_path(NodePath path);
NodePath get_viewer_path();
NodePath get_viewer_path() const;
void set_material(int id, Ref<Material> material);
Ref<Material> get_material(int id) const;
Ref<VoxelMesher> get_mesher() { return _mesher; }
Ref<VoxelMap> get_map() { return _map; }
Ref<VoxelLibrary> get_voxel_library();
protected:
void _notification(int p_what);
private:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
void _process();
void update_blocks();
void update_block_mesh(Vector3i block_pos);
Spatial *get_viewer(NodePath path);
void make_all_view_dirty();
Spatial *get_viewer(NodePath path) const;
// Observer events
//void block_removed(VoxelBlock & block);
@ -70,6 +83,9 @@ private:
// Voxel storage
Ref<VoxelMap> _map;
// How many blocks to load around the viewer
int _view_distance_blocks;
// TODO Terrains only need to handle the visible portion of voxels, which reduces the bounds blocks to handle.
// Therefore, could a simple grid be better to use than a hashmap?