mirror of
https://github.com/Relintai/godot_voxel.git
synced 2025-01-09 04:59:40 +01:00
Expose things to the editor, first iteration, might be buggy
This commit is contained in:
parent
51596fef95
commit
df13e025ca
@ -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
204
voxel.cpp
@ -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
45
voxel.h
@ -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
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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?
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user