Renamed VoxelInstanceLibraryItem => VoxelInstanceLibraryMultiMeshItem

master
Marc Gilleron 2022-01-10 22:56:18 +00:00
parent 1fbe9c4731
commit 9454759b02
13 changed files with 563 additions and 560 deletions

View File

@ -1,5 +1,5 @@
#include "voxel_instance_library_editor_plugin.h"
#include "../../terrain/instancing/voxel_instance_library_item.h"
#include "../../terrain/instancing/voxel_instance_library_multimesh_item.h"
#include "../../terrain/instancing/voxel_instance_library_scene_item.h"
#include <scene/gui/dialogs.h>
@ -125,7 +125,7 @@ void VoxelInstanceLibraryEditorPlugin::_on_button_pressed(int id) {
case BUTTON_ADD_MULTIMESH_ITEM: {
ERR_FAIL_COND(_library.is_null());
Ref<VoxelInstanceLibraryItem> item;
Ref<VoxelInstanceLibraryMultiMeshItem> item;
item.instantiate();
// Setup some defaults
Ref<BoxMesh> mesh;
@ -201,7 +201,7 @@ void VoxelInstanceLibraryEditorPlugin::_on_remove_item_confirmed() {
ERR_FAIL_COND(_library.is_null());
ERR_FAIL_COND(_item_id_to_remove == -1);
Ref<VoxelInstanceLibraryItemBase> item = _library->get_item(_item_id_to_remove);
Ref<VoxelInstanceLibraryItem> item = _library->get_item(_item_id_to_remove);
UndoRedo &ur = get_undo_redo();
ur.create_action("Remove item");
@ -259,9 +259,9 @@ void VoxelInstanceLibraryEditorPlugin::update_multimesh_item_from_scene(String f
Ref<PackedScene> scene = ResourceLoader::load(fpath);
ERR_FAIL_COND(scene.is_null());
Ref<VoxelInstanceLibraryItemBase> item_base = _library->get_item(item_id);
Ref<VoxelInstanceLibraryItem> item_base = _library->get_item(item_id);
ERR_FAIL_COND_MSG(item_base.is_null(), "Item not found");
Ref<VoxelInstanceLibraryItem> item = item_base;
Ref<VoxelInstanceLibraryMultiMeshItem> item = item_base;
ERR_FAIL_COND_MSG(item.is_null(), "Item not using multimeshes");
Node *node = scene->instantiate();

View File

@ -82,8 +82,8 @@ void register_voxel_types() {
ClassDB::register_class<VoxelBlockyLibrary>();
ClassDB::register_class<VoxelColorPalette>();
ClassDB::register_class<VoxelInstanceLibrary>();
ClassDB::register_class<VoxelInstanceLibraryItemBase>();
ClassDB::register_class<VoxelInstanceLibraryItem>();
ClassDB::register_virtual_class<VoxelInstanceLibraryItem>();
ClassDB::register_class<VoxelInstanceLibraryMultiMeshItem>();
ClassDB::register_class<VoxelInstanceLibrarySceneItem>();
// Storage
@ -174,6 +174,10 @@ void register_voxel_types() {
// Compatibility with older version
ClassDB::add_compatibility_class("VoxelLibrary", "VoxelBlockyLibrary");
ClassDB::add_compatibility_class("Voxel", "VoxelBlockyModel");
ClassDB::add_compatibility_class("VoxelInstanceLibraryItem", "VoxelInstanceLibraryMultiMeshItem");
// Not possible to add a compat class for this one because the new name is indistinguishable from an old one.
// However this is an abstract class so it should not be found in resources hopefully
//ClassDB::add_compatibility_class("VoxelInstanceLibraryItemBase", "VoxelInstanceLibraryItem");
}
void unregister_voxel_types() {

View File

@ -8,7 +8,7 @@
namespace zylann::voxel {
VoxelInstanceLibrary::~VoxelInstanceLibrary() {
for_each_item([this](int id, VoxelInstanceLibraryItemBase &item) { item.remove_listener(this, id); });
for_each_item([this](int id, VoxelInstanceLibraryItem &item) { item.remove_listener(this, id); });
}
int VoxelInstanceLibrary::get_next_available_id() {
@ -19,39 +19,39 @@ int VoxelInstanceLibrary::get_next_available_id() {
}
}
void VoxelInstanceLibrary::add_item(int id, Ref<VoxelInstanceLibraryItemBase> item) {
void VoxelInstanceLibrary::add_item(int id, Ref<VoxelInstanceLibraryItem> item) {
ERR_FAIL_COND(_items.has(id));
ERR_FAIL_COND(id < 0 || id >= MAX_ID);
ERR_FAIL_COND(item.is_null());
_items.insert(id, item);
item->add_listener(this, id);
notify_listeners(id, VoxelInstanceLibraryItemBase::CHANGE_ADDED);
notify_listeners(id, VoxelInstanceLibraryItem::CHANGE_ADDED);
notify_property_list_changed();
}
void VoxelInstanceLibrary::remove_item(int id) {
Map<int, Ref<VoxelInstanceLibraryItemBase>>::Element *E = _items.find(id);
Map<int, Ref<VoxelInstanceLibraryItem>>::Element *E = _items.find(id);
ERR_FAIL_COND(E == nullptr);
Ref<VoxelInstanceLibraryItemBase> item = E->value();
Ref<VoxelInstanceLibraryItem> item = E->value();
if (item.is_valid()) {
item->remove_listener(this, id);
}
_items.erase(E);
notify_listeners(id, VoxelInstanceLibraryItemBase::CHANGE_REMOVED);
notify_listeners(id, VoxelInstanceLibraryItem::CHANGE_REMOVED);
notify_property_list_changed();
}
void VoxelInstanceLibrary::clear() {
for_each_item([this](int id, const VoxelInstanceLibraryItemBase &item) {
notify_listeners(id, VoxelInstanceLibraryItemBase::CHANGE_REMOVED);
for_each_item([this](int id, const VoxelInstanceLibraryItem &item) {
notify_listeners(id, VoxelInstanceLibraryItem::CHANGE_REMOVED);
});
_items.clear();
notify_property_list_changed();
}
int VoxelInstanceLibrary::find_item_by_name(String name) const {
for (Map<int, Ref<VoxelInstanceLibraryItemBase>>::Element *E = _items.front(); E != nullptr; E = E->next()) {
const Ref<VoxelInstanceLibraryItemBase> &item = E->value();
for (Map<int, Ref<VoxelInstanceLibraryItem>>::Element *E = _items.front(); E != nullptr; E = E->next()) {
const Ref<VoxelInstanceLibraryItem> &item = E->value();
ERR_FAIL_COND_V(item.is_null(), -1);
if (item->get_name() == name) {
return E->key();
@ -64,40 +64,40 @@ int VoxelInstanceLibrary::get_item_count() const {
return _items.size();
}
Ref<VoxelInstanceLibraryItemBase> VoxelInstanceLibrary::_b_get_item(int id) {
Map<int, Ref<VoxelInstanceLibraryItemBase>>::Element *E = _items.find(id);
Ref<VoxelInstanceLibraryItemBase> item;
Ref<VoxelInstanceLibraryItem> VoxelInstanceLibrary::_b_get_item(int id) {
Map<int, Ref<VoxelInstanceLibraryItem>>::Element *E = _items.find(id);
Ref<VoxelInstanceLibraryItem> item;
if (E != nullptr) {
item = E->value();
}
return item;
}
VoxelInstanceLibraryItemBase *VoxelInstanceLibrary::get_item(int id) {
Map<int, Ref<VoxelInstanceLibraryItemBase>>::Element *E = _items.find(id);
VoxelInstanceLibraryItem *VoxelInstanceLibrary::get_item(int id) {
Map<int, Ref<VoxelInstanceLibraryItem>>::Element *E = _items.find(id);
if (E != nullptr) {
Ref<VoxelInstanceLibraryItemBase> &item = E->value();
Ref<VoxelInstanceLibraryItem> &item = E->value();
ERR_FAIL_COND_V(item.is_null(), nullptr);
return *item;
}
return nullptr;
}
const VoxelInstanceLibraryItemBase *VoxelInstanceLibrary::get_item_const(int id) const {
const Map<int, Ref<VoxelInstanceLibraryItemBase>>::Element *E = _items.find(id);
const VoxelInstanceLibraryItem *VoxelInstanceLibrary::get_item_const(int id) const {
const Map<int, Ref<VoxelInstanceLibraryItem>>::Element *E = _items.find(id);
if (E != nullptr) {
const Ref<VoxelInstanceLibraryItemBase> &item = E->value();
const Ref<VoxelInstanceLibraryItem> &item = E->value();
ERR_FAIL_COND_V(item.is_null(), nullptr);
return *item;
}
return nullptr;
}
void VoxelInstanceLibrary::on_library_item_changed(int id, VoxelInstanceLibraryItemBase::ChangeType change) {
void VoxelInstanceLibrary::on_library_item_changed(int id, VoxelInstanceLibraryItem::ChangeType change) {
notify_listeners(id, change);
}
void VoxelInstanceLibrary::notify_listeners(int item_id, VoxelInstanceLibraryItemBase::ChangeType change) {
void VoxelInstanceLibrary::notify_listeners(int item_id, VoxelInstanceLibraryItem::ChangeType change) {
for (int i = 0; i < _listeners.size(); ++i) {
IListener *listener = _listeners[i];
listener->on_library_item_changed(item_id, change);
@ -121,10 +121,10 @@ bool VoxelInstanceLibrary::_set(const StringName &p_name, const Variant &p_value
if (name.begins_with("item_")) {
const int id = name.substr(5).to_int();
Ref<VoxelInstanceLibraryItemBase> item = p_value;
Ref<VoxelInstanceLibraryItem> item = p_value;
ERR_FAIL_COND_V_MSG(item.is_null(), false, "Setting a null item is not allowed");
Map<int, Ref<VoxelInstanceLibraryItemBase>>::Element *E = _items.find(id);
Map<int, Ref<VoxelInstanceLibraryItem>>::Element *E = _items.find(id);
if (E == nullptr) {
add_item(id, item);
@ -132,14 +132,14 @@ bool VoxelInstanceLibrary::_set(const StringName &p_name, const Variant &p_value
} else {
// Replace
if (E->value() != item) {
Ref<VoxelInstanceLibraryItemBase> old_item = E->value();
Ref<VoxelInstanceLibraryItem> old_item = E->value();
if (old_item.is_valid()) {
old_item->remove_listener(this, id);
notify_listeners(id, VoxelInstanceLibraryItemBase::CHANGE_REMOVED);
notify_listeners(id, VoxelInstanceLibraryItem::CHANGE_REMOVED);
}
E->value() = item;
item->add_listener(this, id);
notify_listeners(id, VoxelInstanceLibraryItemBase::CHANGE_ADDED);
notify_listeners(id, VoxelInstanceLibraryItem::CHANGE_ADDED);
}
}
@ -152,7 +152,7 @@ bool VoxelInstanceLibrary::_get(const StringName &p_name, Variant &r_ret) const
const String name = p_name;
if (name.begins_with("item_")) {
const int id = name.substr(5).to_int();
const Map<int, Ref<VoxelInstanceLibraryItemBase>>::Element *E = _items.find(id);
const Map<int, Ref<VoxelInstanceLibraryItem>>::Element *E = _items.find(id);
if (E != nullptr) {
r_ret = E->value();
return true;
@ -162,10 +162,9 @@ bool VoxelInstanceLibrary::_get(const StringName &p_name, Variant &r_ret) const
}
void VoxelInstanceLibrary::_get_property_list(List<PropertyInfo> *p_list) const {
for (Map<int, Ref<VoxelInstanceLibraryItemBase>>::Element *E = _items.front(); E != nullptr; E = E->next()) {
for (Map<int, Ref<VoxelInstanceLibraryItem>>::Element *E = _items.front(); E != nullptr; E = E->next()) {
const String name = "item_" + itos(E->key());
p_list->push_back(
PropertyInfo(Variant::OBJECT, name, PROPERTY_HINT_RESOURCE_TYPE, "VoxelInstanceLibraryItemBase"));
p_list->push_back(PropertyInfo(Variant::OBJECT, name, PROPERTY_HINT_RESOURCE_TYPE, "VoxelInstanceLibraryItem"));
}
}

View File

@ -1,25 +1,25 @@
#ifndef VOXEL_INSTANCE_MODEL_LIBRARY_H
#define VOXEL_INSTANCE_MODEL_LIBRARY_H
#include "voxel_instance_library_item_base.h"
#include "voxel_instance_library_item.h"
namespace zylann::voxel {
// Contains a list of items that can be used by VoxelInstancer, associated with a unique ID
class VoxelInstanceLibrary : public Resource, public VoxelInstanceLibraryItemBase::IListener {
class VoxelInstanceLibrary : public Resource, public VoxelInstanceLibraryItem::IListener {
GDCLASS(VoxelInstanceLibrary, Resource)
public:
static const int MAX_ID = 0xffff;
class IListener {
public:
virtual void on_library_item_changed(int id, VoxelInstanceLibraryItemBase::ChangeType change) = 0;
virtual void on_library_item_changed(int id, VoxelInstanceLibraryItem::ChangeType change) = 0;
};
~VoxelInstanceLibrary();
int get_next_available_id();
void add_item(int id, Ref<VoxelInstanceLibraryItemBase> item);
void add_item(int id, Ref<VoxelInstanceLibraryItem> item);
void remove_item(int id);
void clear();
int find_item_by_name(String name) const;
@ -27,12 +27,12 @@ public:
// Internal
const VoxelInstanceLibraryItemBase *get_item_const(int id) const;
VoxelInstanceLibraryItemBase *get_item(int id);
const VoxelInstanceLibraryItem *get_item_const(int id) const;
VoxelInstanceLibraryItem *get_item(int id);
template <typename F>
void for_each_item(F f) {
for (Map<int, Ref<VoxelInstanceLibraryItemBase>>::Element *E = _items.front(); E != nullptr; E = E->next()) {
for (Map<int, Ref<VoxelInstanceLibraryItem>>::Element *E = _items.front(); E != nullptr; E = E->next()) {
CRASH_COND(E->value().is_null());
f(E->key(), **E->value());
}
@ -42,21 +42,21 @@ public:
void remove_listener(IListener *listener);
protected:
Ref<VoxelInstanceLibraryItemBase> _b_get_item(int id);
Ref<VoxelInstanceLibraryItem> _b_get_item(int id);
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;
private:
void on_library_item_changed(int id, VoxelInstanceLibraryItemBase::ChangeType change) override;
void notify_listeners(int item_id, VoxelInstanceLibraryItemBase::ChangeType change);
void on_library_item_changed(int id, VoxelInstanceLibraryItem::ChangeType change) override;
void notify_listeners(int item_id, VoxelInstanceLibraryItem::ChangeType change);
static void _bind_methods();
// ID => Item
// Using a Map keeps items ordered, so the last item has highest ID
Map<int, Ref<VoxelInstanceLibraryItemBase>> _items;
Map<int, Ref<VoxelInstanceLibraryItem>> _items;
Vector<IListener *> _listeners;
};

View File

@ -2,267 +2,108 @@
#include "voxel_instancer.h"
#include <core/core_string_names.h>
#include <scene/3d/collision_shape_3d.h>
#include <scene/3d/mesh_instance_3d.h>
#include <scene/3d/physics_body_3d.h>
namespace zylann::voxel {
void VoxelInstanceLibraryItem::set_mesh(Ref<Mesh> mesh, int mesh_lod_index) {
ERR_FAIL_INDEX(mesh_lod_index, static_cast<int>(_mesh_lods.size()));
if (_mesh_lods[mesh_lod_index] == mesh) {
void VoxelInstanceLibraryItem::set_item_name(String name) {
_name = name;
}
String VoxelInstanceLibraryItem::get_item_name() const {
return _name;
}
void VoxelInstanceLibraryItem::set_lod_index(int lod) {
ERR_FAIL_COND(lod < 0 || lod >= VoxelInstancer::MAX_LOD);
if (_lod_index == lod) {
return;
}
_mesh_lods[mesh_lod_index] = mesh;
// Update count
unsigned int count = _mesh_lods.size();
for (unsigned int i = _mesh_lods.size() - 1; i > 0; --i) {
if (_mesh_lods[i].is_valid()) {
break;
}
--count;
}
_mesh_lod_count = count;
notify_listeners(CHANGE_VISUAL);
_lod_index = lod;
notify_listeners(CHANGE_LOD_INDEX);
}
int VoxelInstanceLibraryItem::get_mesh_lod_count() const {
return _mesh_lod_count;
int VoxelInstanceLibraryItem::get_lod_index() const {
return _lod_index;
}
Ref<Mesh> VoxelInstanceLibraryItem::get_mesh(int mesh_lod_index) const {
ERR_FAIL_INDEX_V(mesh_lod_index, static_cast<int>(_mesh_lods.size()), Ref<Mesh>());
return _mesh_lods[mesh_lod_index];
}
void VoxelInstanceLibraryItem::set_material_override(Ref<Material> material) {
if (material == _material_override) {
void VoxelInstanceLibraryItem::set_generator(Ref<VoxelInstanceGenerator> generator) {
if (_generator == generator) {
return;
}
_material_override = material;
notify_listeners(CHANGE_VISUAL);
}
Ref<Material> VoxelInstanceLibraryItem::get_material_override() const {
return _material_override;
}
void VoxelInstanceLibraryItem::set_cast_shadows_setting(RenderingServer::ShadowCastingSetting mode) {
if (mode == _shadow_casting_setting) {
return;
if (_generator.is_valid()) {
_generator->disconnect(CoreStringNames::get_singleton()->changed,
callable_mp(this, &VoxelInstanceLibraryItem::_on_generator_changed));
}
_shadow_casting_setting = mode;
notify_listeners(CHANGE_VISUAL);
_generator = generator;
if (_generator.is_valid()) {
_generator->connect(CoreStringNames::get_singleton()->changed,
callable_mp(this, &VoxelInstanceLibraryItem::_on_generator_changed));
}
notify_listeners(CHANGE_GENERATOR);
}
RenderingServer::ShadowCastingSetting VoxelInstanceLibraryItem::get_cast_shadows_setting() const {
return _shadow_casting_setting;
Ref<VoxelInstanceGenerator> VoxelInstanceLibraryItem::get_generator() const {
return _generator;
}
void VoxelInstanceLibraryItem::set_collision_layer(int collision_layer) {
_collision_layer = collision_layer;
void VoxelInstanceLibraryItem::set_persistent(bool persistent) {
_persistent = persistent;
}
int VoxelInstanceLibraryItem::get_collision_layer() const {
return _collision_layer;
bool VoxelInstanceLibraryItem::is_persistent() const {
return _persistent;
}
void VoxelInstanceLibraryItem::set_collision_mask(int collision_mask) {
_collision_mask = collision_mask;
void VoxelInstanceLibraryItem::add_listener(IListener *listener, int id) {
ListenerSlot slot;
slot.listener = listener;
slot.id = id;
ERR_FAIL_COND(_listeners.find(slot) != -1);
_listeners.push_back(slot);
}
int VoxelInstanceLibraryItem::get_collision_mask() const {
return _collision_mask;
void VoxelInstanceLibraryItem::remove_listener(IListener *listener, int id) {
ListenerSlot slot;
slot.listener = listener;
slot.id = id;
int i = _listeners.find(slot);
ERR_FAIL_COND(i == -1);
_listeners.remove_at(i);
}
static RenderingServer::ShadowCastingSetting node_to_visual_server_enum(GeometryInstance3D::ShadowCastingSetting v) {
switch (v) {
case GeometryInstance3D::SHADOW_CASTING_SETTING_OFF:
return RenderingServer::SHADOW_CASTING_SETTING_OFF;
case GeometryInstance3D::SHADOW_CASTING_SETTING_ON:
return RenderingServer::SHADOW_CASTING_SETTING_ON;
case GeometryInstance3D::SHADOW_CASTING_SETTING_DOUBLE_SIDED:
return RenderingServer::SHADOW_CASTING_SETTING_DOUBLE_SIDED;
case GeometryInstance3D::SHADOW_CASTING_SETTING_SHADOWS_ONLY:
return RenderingServer::SHADOW_CASTING_SETTING_SHADOWS_ONLY;
default:
ERR_PRINT("Unknown ShadowCastingSetting value");
return RenderingServer::SHADOW_CASTING_SETTING_OFF;
void VoxelInstanceLibraryItem::notify_listeners(ChangeType change) {
for (int i = 0; i < _listeners.size(); ++i) {
ListenerSlot &slot = _listeners.write[i];
slot.listener->on_library_item_changed(slot.id, change);
}
}
void VoxelInstanceLibraryItem::setup_from_template(Node *root) {
ERR_FAIL_COND(root == nullptr);
_collision_shapes.clear();
PhysicsBody3D *physics_body = Object::cast_to<PhysicsBody3D>(root);
if (physics_body != nullptr) {
_collision_layer = physics_body->get_collision_layer();
_collision_mask = physics_body->get_collision_mask();
}
for (int i = 0; i < root->get_child_count(); ++i) {
MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(root->get_child(i));
if (mi != nullptr) {
_mesh_lods[0] = mi->get_mesh();
_mesh_lod_count = 1;
_material_override = mi->get_material_override();
_shadow_casting_setting = node_to_visual_server_enum(mi->get_cast_shadows_setting());
}
if (physics_body != nullptr) {
CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(physics_body->get_child(i));
if (cs != nullptr) {
CollisionShapeInfo info;
info.shape = cs->get_shape();
info.transform = cs->get_transform();
_collision_shapes.push_back(info);
}
}
}
notify_listeners(CHANGE_VISUAL);
}
static Array serialize_collision_shape_infos(Vector<VoxelInstanceLibraryItem::CollisionShapeInfo> infos) {
Array a;
for (int i = 0; i < infos.size(); ++i) {
const VoxelInstanceLibraryItem::CollisionShapeInfo &info = infos[i];
ERR_FAIL_COND_V(info.shape.is_null(), Array());
// TODO Shape might or might not be shared, could have odd side-effects,
// but not sure how to properly fix these edge cases without convoluted code
a.push_back(info.shape);
a.push_back(info.transform);
}
return a;
}
static Vector<VoxelInstanceLibraryItem::CollisionShapeInfo> deserialize_collision_shape_infos(Array a) {
Vector<VoxelInstanceLibraryItem::CollisionShapeInfo> infos;
ERR_FAIL_COND_V(a.size() % 2 != 0, infos);
for (int i = 0; i < a.size(); i += 2) {
VoxelInstanceLibraryItem::CollisionShapeInfo info;
info.shape = a[i];
info.transform = a[i + 1];
ERR_FAIL_COND_V(info.shape.is_null(), Vector<VoxelInstanceLibraryItem::CollisionShapeInfo>());
infos.push_back(info);
}
return infos;
}
Array VoxelInstanceLibraryItem::serialize_multimesh_item_properties() const {
Array a;
for (unsigned int i = 0; i < _mesh_lods.size(); ++i) {
a.push_back(_mesh_lods[i]);
}
a.push_back(_mesh_lod_count);
a.push_back(_material_override);
a.push_back(_shadow_casting_setting);
a.push_back(_collision_layer);
a.push_back(_collision_mask);
a.push_back(serialize_collision_shape_infos(_collision_shapes));
return a;
}
void VoxelInstanceLibraryItem::deserialize_multimesh_item_properties(Array a) {
ERR_FAIL_COND(a.size() != int(_mesh_lods.size()) + 6);
int ai = 0;
for (unsigned int i = 0; i < _mesh_lods.size(); ++i) {
_mesh_lods[i] = a[ai++];
}
_mesh_lod_count = a[ai++];
_material_override = a[ai++];
_shadow_casting_setting = RenderingServer::ShadowCastingSetting(int(a[ai++])); // ugh...
_collision_layer = a[ai++];
_collision_mask = a[ai++];
_collision_shapes = deserialize_collision_shape_infos(a[ai++]);
notify_listeners(CHANGE_VISUAL);
}
void VoxelInstanceLibraryItem::_b_set_collision_shapes(Array shape_infos) {
_collision_shapes = deserialize_collision_shape_infos(shape_infos);
}
Array VoxelInstanceLibraryItem::_b_get_collision_shapes() const {
return serialize_collision_shape_infos(_collision_shapes);
void VoxelInstanceLibraryItem::_on_generator_changed() {
notify_listeners(CHANGE_GENERATOR);
}
void VoxelInstanceLibraryItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_mesh", "mesh", "mesh_lod_index"), &VoxelInstanceLibraryItem::set_mesh);
ClassDB::bind_method(D_METHOD("get_mesh", "mesh_lod_index"), &VoxelInstanceLibraryItem::get_mesh);
// Can't be just "set_name" because Resource already defines that, despite being for a `resource_name` property
ClassDB::bind_method(D_METHOD("set_item_name", "name"), &VoxelInstanceLibraryItem::set_item_name);
ClassDB::bind_method(D_METHOD("get_item_name"), &VoxelInstanceLibraryItem::get_item_name);
ClassDB::bind_method(D_METHOD("_set_mesh_lod0", "mesh"), &VoxelInstanceLibraryItem::_b_set_mesh_lod0);
ClassDB::bind_method(D_METHOD("_set_mesh_lod1", "mesh"), &VoxelInstanceLibraryItem::_b_set_mesh_lod1);
ClassDB::bind_method(D_METHOD("_set_mesh_lod2", "mesh"), &VoxelInstanceLibraryItem::_b_set_mesh_lod2);
ClassDB::bind_method(D_METHOD("_set_mesh_lod3", "mesh"), &VoxelInstanceLibraryItem::_b_set_mesh_lod3);
ClassDB::bind_method(D_METHOD("set_lod_index", "lod"), &VoxelInstanceLibraryItem::set_lod_index);
ClassDB::bind_method(D_METHOD("get_lod_index"), &VoxelInstanceLibraryItem::get_lod_index);
ClassDB::bind_method(D_METHOD("_get_mesh_lod0"), &VoxelInstanceLibraryItem::_b_get_mesh_lod0);
ClassDB::bind_method(D_METHOD("_get_mesh_lod1"), &VoxelInstanceLibraryItem::_b_get_mesh_lod1);
ClassDB::bind_method(D_METHOD("_get_mesh_lod2"), &VoxelInstanceLibraryItem::_b_get_mesh_lod2);
ClassDB::bind_method(D_METHOD("_get_mesh_lod3"), &VoxelInstanceLibraryItem::_b_get_mesh_lod3);
ClassDB::bind_method(D_METHOD("set_generator", "generator"), &VoxelInstanceLibraryItem::set_generator);
ClassDB::bind_method(D_METHOD("get_generator"), &VoxelInstanceLibraryItem::get_generator);
ClassDB::bind_method(
D_METHOD("set_material_override", "material"), &VoxelInstanceLibraryItem::set_material_override);
ClassDB::bind_method(D_METHOD("get_material_override"), &VoxelInstanceLibraryItem::get_material_override);
ClassDB::bind_method(D_METHOD("set_persistent", "persistent"), &VoxelInstanceLibraryItem::set_persistent);
ClassDB::bind_method(D_METHOD("is_persistent"), &VoxelInstanceLibraryItem::is_persistent);
ClassDB::bind_method(
D_METHOD("set_cast_shadows_setting", "mode"), &VoxelInstanceLibraryItem::set_cast_shadows_setting);
ClassDB::bind_method(D_METHOD("get_cast_shadows_setting"), &VoxelInstanceLibraryItem::get_cast_shadows_setting);
//ClassDB::bind_method(D_METHOD("_on_generator_changed"), &VoxelInstanceLibraryItem::_on_generator_changed);
ClassDB::bind_method(
D_METHOD("set_collision_layer", "collision_layer"), &VoxelInstanceLibraryItem::set_collision_layer);
ClassDB::bind_method(D_METHOD("get_collision_layer"), &VoxelInstanceLibraryItem::get_collision_layer);
ClassDB::bind_method(
D_METHOD("set_collision_mask", "collision_mask"), &VoxelInstanceLibraryItem::set_collision_mask);
ClassDB::bind_method(D_METHOD("get_collision_mask"), &VoxelInstanceLibraryItem::get_collision_mask);
ClassDB::bind_method(
D_METHOD("set_collision_shapes", "shape_infos"), &VoxelInstanceLibraryItem::_b_set_collision_shapes);
ClassDB::bind_method(D_METHOD("get_collision_shapes"), &VoxelInstanceLibraryItem::_b_get_collision_shapes);
ClassDB::bind_method(D_METHOD("setup_from_template", "node"), &VoxelInstanceLibraryItem::setup_from_template);
// Used in editor only
ClassDB::bind_method(D_METHOD("_deserialize_multimesh_item_properties", "props"),
&VoxelInstanceLibraryItem::deserialize_multimesh_item_properties);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "_set_mesh_lod0",
"_get_mesh_lod0");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh_lod1", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "_set_mesh_lod1",
"_get_mesh_lod1");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh_lod2", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "_set_mesh_lod2",
"_get_mesh_lod2");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh_lod3", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "_set_mesh_lod3",
"_get_mesh_lod3");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_override", PROPERTY_HINT_RESOURCE_TYPE, "Material"),
"set_material_override", "get_material_override");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cast_shadow", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"),
"set_cast_shadows_setting", "get_cast_shadows_setting");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer",
"get_collision_layer");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask",
"get_collision_mask");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "collision_shapes"), "set_collision_shapes", "get_collision_shapes");
BIND_CONSTANT(MAX_MESH_LODS);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "name"), "set_item_name", "get_item_name");
ADD_PROPERTY(
PropertyInfo(Variant::INT, "lod_index", PROPERTY_HINT_RANGE, "0,8,1"), "set_lod_index", "get_lod_index");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "generator", PROPERTY_HINT_RESOURCE_TYPE, "VoxelInstanceGenerator"),
"set_generator", "get_generator");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "persistent"), "set_persistent", "is_persistent");
}
} // namespace zylann::voxel

View File

@ -1,96 +1,78 @@
#ifndef VOXEL_INSTANCE_LIBRARY_ITEM_H
#define VOXEL_INSTANCE_LIBRARY_ITEM_H
#include "voxel_instance_library_item_base.h"
#include <scene/resources/mesh.h>
#include <scene/resources/shape_3d.h>
// TODO Rename VoxelInstanceLibraryMultimeshItem (did not do it for compatibility)
#include "../../util/fixed_array.h"
#include "voxel_instance_generator.h"
namespace zylann::voxel {
// Settings for a model that can be used by VoxelInstancer
class VoxelInstanceLibraryItem : public VoxelInstanceLibraryItemBase {
GDCLASS(VoxelInstanceLibraryItem, VoxelInstanceLibraryItemBase)
class VoxelInstanceLibraryItem : public Resource {
GDCLASS(VoxelInstanceLibraryItem, Resource)
public:
static const int MAX_MESH_LODS = 4;
struct CollisionShapeInfo {
Transform3D transform;
Ref<Shape3D> shape;
enum ChangeType { //
CHANGE_LOD_INDEX,
CHANGE_GENERATOR,
CHANGE_VISUAL,
CHANGE_ADDED,
CHANGE_REMOVED,
CHANGE_SCENE
};
void set_mesh(Ref<Mesh> mesh, int mesh_lod_index);
Ref<Mesh> get_mesh(int mesh_lod_index) const;
int get_mesh_lod_count() const;
class IListener {
public:
virtual void on_library_item_changed(int id, ChangeType change) = 0;
};
void set_material_override(Ref<Material> material);
Ref<Material> get_material_override() const;
void set_item_name(String name);
String get_item_name() const;
void set_cast_shadows_setting(RenderingServer::ShadowCastingSetting mode);
RenderingServer::ShadowCastingSetting get_cast_shadows_setting() const;
void set_lod_index(int lod);
int get_lod_index() const;
void set_collision_layer(int collision_layer);
int get_collision_layer() const;
void set_generator(Ref<VoxelInstanceGenerator> generator);
Ref<VoxelInstanceGenerator> get_generator() const;
void set_collision_mask(int collision_mask);
int get_collision_mask() const;
void setup_from_template(Node *root);
void set_persistent(bool persistent);
bool is_persistent() const;
// Internal
inline const Vector<CollisionShapeInfo> &get_collision_shapes() const {
return _collision_shapes;
}
void add_listener(IListener *listener, int id);
void remove_listener(IListener *listener, int id);
Array serialize_multimesh_item_properties() const;
void deserialize_multimesh_item_properties(Array a);
protected:
void notify_listeners(ChangeType change);
private:
void _on_generator_changed();
static void _bind_methods();
void _b_set_collision_shapes(Array shape_infos);
Array _b_get_collision_shapes() const;
// For the user, not used by the engine
String _name;
Ref<Mesh> _b_get_mesh_lod0() const {
return get_mesh(0);
}
Ref<Mesh> _b_get_mesh_lod1() const {
return get_mesh(1);
}
Ref<Mesh> _b_get_mesh_lod2() const {
return get_mesh(2);
}
Ref<Mesh> _b_get_mesh_lod3() const {
return get_mesh(3);
}
// If a layer is persistent, any change to its instances will be saved if the volume has a stream
// supporting instances. It will also not generate on top of modified surfaces.
// If a layer is not persistent, changes won't get saved, and it will keep generating on all compliant
// surfaces.
bool _persistent = false;
void _b_set_mesh_lod0(Ref<Mesh> mesh) {
set_mesh(mesh, 0);
}
void _b_set_mesh_lod1(Ref<Mesh> mesh) {
set_mesh(mesh, 1);
}
void _b_set_mesh_lod2(Ref<Mesh> mesh) {
set_mesh(mesh, 2);
}
void _b_set_mesh_lod3(Ref<Mesh> mesh) {
set_mesh(mesh, 3);
}
// Which LOD of the octree this model will spawn into.
// Higher means larger distances, but lower precision and density
int _lod_index = 0;
FixedArray<Ref<Mesh>, MAX_MESH_LODS> _mesh_lods;
unsigned int _mesh_lod_count = 1;
Ref<VoxelInstanceGenerator> _generator;
// It is preferred to have materials on the mesh already,
// but this is in case OBJ meshes are used, which often dont have a material of their own
Ref<Material> _material_override;
struct ListenerSlot {
IListener *listener;
int id;
RenderingServer::ShadowCastingSetting _shadow_casting_setting = RenderingServer::SHADOW_CASTING_SETTING_ON;
inline bool operator==(const ListenerSlot &other) const {
return listener == other.listener && id == other.id;
}
};
int _collision_mask = 1;
int _collision_layer = 1;
Vector<CollisionShapeInfo> _collision_shapes;
Vector<ListenerSlot> _listeners;
};
} // namespace zylann::voxel

View File

@ -1,109 +0,0 @@
#include "voxel_instance_library_item.h"
#include "voxel_instancer.h"
#include <core/core_string_names.h>
namespace zylann::voxel {
void VoxelInstanceLibraryItemBase::set_item_name(String name) {
_name = name;
}
String VoxelInstanceLibraryItemBase::get_item_name() const {
return _name;
}
void VoxelInstanceLibraryItemBase::set_lod_index(int lod) {
ERR_FAIL_COND(lod < 0 || lod >= VoxelInstancer::MAX_LOD);
if (_lod_index == lod) {
return;
}
_lod_index = lod;
notify_listeners(CHANGE_LOD_INDEX);
}
int VoxelInstanceLibraryItemBase::get_lod_index() const {
return _lod_index;
}
void VoxelInstanceLibraryItemBase::set_generator(Ref<VoxelInstanceGenerator> generator) {
if (_generator == generator) {
return;
}
if (_generator.is_valid()) {
_generator->disconnect(CoreStringNames::get_singleton()->changed,
callable_mp(this, &VoxelInstanceLibraryItemBase::_on_generator_changed));
}
_generator = generator;
if (_generator.is_valid()) {
_generator->connect(CoreStringNames::get_singleton()->changed,
callable_mp(this, &VoxelInstanceLibraryItemBase::_on_generator_changed));
}
notify_listeners(CHANGE_GENERATOR);
}
Ref<VoxelInstanceGenerator> VoxelInstanceLibraryItemBase::get_generator() const {
return _generator;
}
void VoxelInstanceLibraryItemBase::set_persistent(bool persistent) {
_persistent = persistent;
}
bool VoxelInstanceLibraryItemBase::is_persistent() const {
return _persistent;
}
void VoxelInstanceLibraryItemBase::add_listener(IListener *listener, int id) {
ListenerSlot slot;
slot.listener = listener;
slot.id = id;
ERR_FAIL_COND(_listeners.find(slot) != -1);
_listeners.push_back(slot);
}
void VoxelInstanceLibraryItemBase::remove_listener(IListener *listener, int id) {
ListenerSlot slot;
slot.listener = listener;
slot.id = id;
int i = _listeners.find(slot);
ERR_FAIL_COND(i == -1);
_listeners.remove_at(i);
}
void VoxelInstanceLibraryItemBase::notify_listeners(ChangeType change) {
for (int i = 0; i < _listeners.size(); ++i) {
ListenerSlot &slot = _listeners.write[i];
slot.listener->on_library_item_changed(slot.id, change);
}
}
void VoxelInstanceLibraryItemBase::_on_generator_changed() {
notify_listeners(CHANGE_GENERATOR);
}
void VoxelInstanceLibraryItemBase::_bind_methods() {
// Can't be just "set_name" because Resource already defines that, despite being for a `resource_name` property
ClassDB::bind_method(D_METHOD("set_item_name", "name"), &VoxelInstanceLibraryItemBase::set_item_name);
ClassDB::bind_method(D_METHOD("get_item_name"), &VoxelInstanceLibraryItemBase::get_item_name);
ClassDB::bind_method(D_METHOD("set_lod_index", "lod"), &VoxelInstanceLibraryItemBase::set_lod_index);
ClassDB::bind_method(D_METHOD("get_lod_index"), &VoxelInstanceLibraryItemBase::get_lod_index);
ClassDB::bind_method(D_METHOD("set_generator", "generator"), &VoxelInstanceLibraryItemBase::set_generator);
ClassDB::bind_method(D_METHOD("get_generator"), &VoxelInstanceLibraryItemBase::get_generator);
ClassDB::bind_method(D_METHOD("set_persistent", "persistent"), &VoxelInstanceLibraryItemBase::set_persistent);
ClassDB::bind_method(D_METHOD("is_persistent"), &VoxelInstanceLibraryItemBase::is_persistent);
//ClassDB::bind_method(D_METHOD("_on_generator_changed"), &VoxelInstanceLibraryItemBase::_on_generator_changed);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "name"), "set_item_name", "get_item_name");
ADD_PROPERTY(
PropertyInfo(Variant::INT, "lod_index", PROPERTY_HINT_RANGE, "0,8,1"), "set_lod_index", "get_lod_index");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "generator", PROPERTY_HINT_RESOURCE_TYPE, "VoxelInstanceGenerator"),
"set_generator", "get_generator");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "persistent"), "set_persistent", "is_persistent");
}
} // namespace zylann::voxel

View File

@ -1,82 +0,0 @@
#ifndef VOXEL_INSTANCE_LIBRARY_ITEM_BASE_H
#define VOXEL_INSTANCE_LIBRARY_ITEM_BASE_H
#include "../../util/fixed_array.h"
#include "voxel_instance_generator.h"
// TODO Rename VoxelInstanceLibraryItem (did not do it for compatibility)
namespace zylann::voxel {
class VoxelInstanceLibraryItemBase : public Resource {
GDCLASS(VoxelInstanceLibraryItemBase, Resource)
public:
enum ChangeType { //
CHANGE_LOD_INDEX,
CHANGE_GENERATOR,
CHANGE_VISUAL,
CHANGE_ADDED,
CHANGE_REMOVED,
CHANGE_SCENE
};
class IListener {
public:
virtual void on_library_item_changed(int id, ChangeType change) = 0;
};
void set_item_name(String name);
String get_item_name() const;
void set_lod_index(int lod);
int get_lod_index() const;
void set_generator(Ref<VoxelInstanceGenerator> generator);
Ref<VoxelInstanceGenerator> get_generator() const;
void set_persistent(bool persistent);
bool is_persistent() const;
// Internal
void add_listener(IListener *listener, int id);
void remove_listener(IListener *listener, int id);
protected:
void notify_listeners(ChangeType change);
private:
void _on_generator_changed();
static void _bind_methods();
// For the user, not used by the engine
String _name;
// If a layer is persistent, any change to its instances will be saved if the volume has a stream
// supporting instances. It will also not generate on top of modified surfaces.
// If a layer is not persistent, changes won't get saved, and it will keep generating on all compliant
// surfaces.
bool _persistent = false;
// Which LOD of the octree this model will spawn into.
// Higher means larger distances, but lower precision and density
int _lod_index = 0;
Ref<VoxelInstanceGenerator> _generator;
struct ListenerSlot {
IListener *listener;
int id;
inline bool operator==(const ListenerSlot &other) const {
return listener == other.listener && id == other.id;
}
};
Vector<ListenerSlot> _listeners;
};
} // namespace zylann::voxel
#endif // VOXEL_INSTANCE_LIBRARY_ITEM_BASE_H

View File

@ -0,0 +1,271 @@
#include "voxel_instance_library_multimesh_item.h"
#include "voxel_instancer.h"
#include <core/core_string_names.h>
#include <scene/3d/collision_shape_3d.h>
#include <scene/3d/mesh_instance_3d.h>
#include <scene/3d/physics_body_3d.h>
namespace zylann::voxel {
void VoxelInstanceLibraryMultiMeshItem::set_mesh(Ref<Mesh> mesh, int mesh_lod_index) {
ERR_FAIL_INDEX(mesh_lod_index, static_cast<int>(_mesh_lods.size()));
if (_mesh_lods[mesh_lod_index] == mesh) {
return;
}
_mesh_lods[mesh_lod_index] = mesh;
// Update count
unsigned int count = _mesh_lods.size();
for (unsigned int i = _mesh_lods.size() - 1; i > 0; --i) {
if (_mesh_lods[i].is_valid()) {
break;
}
--count;
}
_mesh_lod_count = count;
notify_listeners(CHANGE_VISUAL);
}
int VoxelInstanceLibraryMultiMeshItem::get_mesh_lod_count() const {
return _mesh_lod_count;
}
Ref<Mesh> VoxelInstanceLibraryMultiMeshItem::get_mesh(int mesh_lod_index) const {
ERR_FAIL_INDEX_V(mesh_lod_index, static_cast<int>(_mesh_lods.size()), Ref<Mesh>());
return _mesh_lods[mesh_lod_index];
}
void VoxelInstanceLibraryMultiMeshItem::set_material_override(Ref<Material> material) {
if (material == _material_override) {
return;
}
_material_override = material;
notify_listeners(CHANGE_VISUAL);
}
Ref<Material> VoxelInstanceLibraryMultiMeshItem::get_material_override() const {
return _material_override;
}
void VoxelInstanceLibraryMultiMeshItem::set_cast_shadows_setting(RenderingServer::ShadowCastingSetting mode) {
if (mode == _shadow_casting_setting) {
return;
}
_shadow_casting_setting = mode;
notify_listeners(CHANGE_VISUAL);
}
RenderingServer::ShadowCastingSetting VoxelInstanceLibraryMultiMeshItem::get_cast_shadows_setting() const {
return _shadow_casting_setting;
}
void VoxelInstanceLibraryMultiMeshItem::set_collision_layer(int collision_layer) {
_collision_layer = collision_layer;
}
int VoxelInstanceLibraryMultiMeshItem::get_collision_layer() const {
return _collision_layer;
}
void VoxelInstanceLibraryMultiMeshItem::set_collision_mask(int collision_mask) {
_collision_mask = collision_mask;
}
int VoxelInstanceLibraryMultiMeshItem::get_collision_mask() const {
return _collision_mask;
}
static RenderingServer::ShadowCastingSetting node_to_visual_server_enum(GeometryInstance3D::ShadowCastingSetting v) {
switch (v) {
case GeometryInstance3D::SHADOW_CASTING_SETTING_OFF:
return RenderingServer::SHADOW_CASTING_SETTING_OFF;
case GeometryInstance3D::SHADOW_CASTING_SETTING_ON:
return RenderingServer::SHADOW_CASTING_SETTING_ON;
case GeometryInstance3D::SHADOW_CASTING_SETTING_DOUBLE_SIDED:
return RenderingServer::SHADOW_CASTING_SETTING_DOUBLE_SIDED;
case GeometryInstance3D::SHADOW_CASTING_SETTING_SHADOWS_ONLY:
return RenderingServer::SHADOW_CASTING_SETTING_SHADOWS_ONLY;
default:
ERR_PRINT("Unknown ShadowCastingSetting value");
return RenderingServer::SHADOW_CASTING_SETTING_OFF;
}
}
void VoxelInstanceLibraryMultiMeshItem::setup_from_template(Node *root) {
ERR_FAIL_COND(root == nullptr);
_collision_shapes.clear();
PhysicsBody3D *physics_body = Object::cast_to<PhysicsBody3D>(root);
if (physics_body != nullptr) {
_collision_layer = physics_body->get_collision_layer();
_collision_mask = physics_body->get_collision_mask();
}
for (int i = 0; i < root->get_child_count(); ++i) {
MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(root->get_child(i));
if (mi != nullptr) {
// TODO Support LODs
_mesh_lods[0] = mi->get_mesh();
_mesh_lod_count = 1;
_material_override = mi->get_material_override();
_shadow_casting_setting = node_to_visual_server_enum(mi->get_cast_shadows_setting());
}
if (physics_body != nullptr) {
CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(physics_body->get_child(i));
if (cs != nullptr) {
CollisionShapeInfo info;
info.shape = cs->get_shape();
info.transform = cs->get_transform();
_collision_shapes.push_back(info);
}
}
}
notify_listeners(CHANGE_VISUAL);
}
static Array serialize_collision_shape_infos(Vector<VoxelInstanceLibraryMultiMeshItem::CollisionShapeInfo> infos) {
Array a;
for (int i = 0; i < infos.size(); ++i) {
const VoxelInstanceLibraryMultiMeshItem::CollisionShapeInfo &info = infos[i];
ERR_FAIL_COND_V(info.shape.is_null(), Array());
// TODO Shape might or might not be shared, could have odd side-effects,
// but not sure how to properly fix these edge cases without convoluted code
a.push_back(info.shape);
a.push_back(info.transform);
}
return a;
}
static Vector<VoxelInstanceLibraryMultiMeshItem::CollisionShapeInfo> deserialize_collision_shape_infos(Array a) {
Vector<VoxelInstanceLibraryMultiMeshItem::CollisionShapeInfo> infos;
ERR_FAIL_COND_V(a.size() % 2 != 0, infos);
for (int i = 0; i < a.size(); i += 2) {
VoxelInstanceLibraryMultiMeshItem::CollisionShapeInfo info;
info.shape = a[i];
info.transform = a[i + 1];
ERR_FAIL_COND_V(info.shape.is_null(), Vector<VoxelInstanceLibraryMultiMeshItem::CollisionShapeInfo>());
infos.push_back(info);
}
return infos;
}
Array VoxelInstanceLibraryMultiMeshItem::serialize_multimesh_item_properties() const {
Array a;
for (unsigned int i = 0; i < _mesh_lods.size(); ++i) {
a.push_back(_mesh_lods[i]);
}
a.push_back(_mesh_lod_count);
a.push_back(_material_override);
a.push_back(_shadow_casting_setting);
a.push_back(_collision_layer);
a.push_back(_collision_mask);
a.push_back(serialize_collision_shape_infos(_collision_shapes));
return a;
}
void VoxelInstanceLibraryMultiMeshItem::deserialize_multimesh_item_properties(Array a) {
ERR_FAIL_COND(a.size() != int(_mesh_lods.size()) + 6);
int ai = 0;
for (unsigned int i = 0; i < _mesh_lods.size(); ++i) {
_mesh_lods[i] = a[ai++];
}
_mesh_lod_count = a[ai++];
_material_override = a[ai++];
_shadow_casting_setting = RenderingServer::ShadowCastingSetting(int(a[ai++])); // ugh...
_collision_layer = a[ai++];
_collision_mask = a[ai++];
_collision_shapes = deserialize_collision_shape_infos(a[ai++]);
notify_listeners(CHANGE_VISUAL);
}
void VoxelInstanceLibraryMultiMeshItem::_b_set_collision_shapes(Array shape_infos) {
_collision_shapes = deserialize_collision_shape_infos(shape_infos);
}
Array VoxelInstanceLibraryMultiMeshItem::_b_get_collision_shapes() const {
return serialize_collision_shape_infos(_collision_shapes);
}
void VoxelInstanceLibraryMultiMeshItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_mesh", "mesh", "mesh_lod_index"), &VoxelInstanceLibraryMultiMeshItem::set_mesh);
ClassDB::bind_method(D_METHOD("get_mesh", "mesh_lod_index"), &VoxelInstanceLibraryMultiMeshItem::get_mesh);
ClassDB::bind_method(D_METHOD("_set_mesh_lod0", "mesh"), &VoxelInstanceLibraryMultiMeshItem::_b_set_mesh_lod0);
ClassDB::bind_method(D_METHOD("_set_mesh_lod1", "mesh"), &VoxelInstanceLibraryMultiMeshItem::_b_set_mesh_lod1);
ClassDB::bind_method(D_METHOD("_set_mesh_lod2", "mesh"), &VoxelInstanceLibraryMultiMeshItem::_b_set_mesh_lod2);
ClassDB::bind_method(D_METHOD("_set_mesh_lod3", "mesh"), &VoxelInstanceLibraryMultiMeshItem::_b_set_mesh_lod3);
ClassDB::bind_method(D_METHOD("_get_mesh_lod0"), &VoxelInstanceLibraryMultiMeshItem::_b_get_mesh_lod0);
ClassDB::bind_method(D_METHOD("_get_mesh_lod1"), &VoxelInstanceLibraryMultiMeshItem::_b_get_mesh_lod1);
ClassDB::bind_method(D_METHOD("_get_mesh_lod2"), &VoxelInstanceLibraryMultiMeshItem::_b_get_mesh_lod2);
ClassDB::bind_method(D_METHOD("_get_mesh_lod3"), &VoxelInstanceLibraryMultiMeshItem::_b_get_mesh_lod3);
ClassDB::bind_method(
D_METHOD("set_material_override", "material"), &VoxelInstanceLibraryMultiMeshItem::set_material_override);
ClassDB::bind_method(D_METHOD("get_material_override"), &VoxelInstanceLibraryMultiMeshItem::get_material_override);
ClassDB::bind_method(
D_METHOD("set_cast_shadows_setting", "mode"), &VoxelInstanceLibraryMultiMeshItem::set_cast_shadows_setting);
ClassDB::bind_method(
D_METHOD("get_cast_shadows_setting"), &VoxelInstanceLibraryMultiMeshItem::get_cast_shadows_setting);
ClassDB::bind_method(D_METHOD("set_collision_layer", "collision_layer"),
&VoxelInstanceLibraryMultiMeshItem::set_collision_layer);
ClassDB::bind_method(D_METHOD("get_collision_layer"), &VoxelInstanceLibraryMultiMeshItem::get_collision_layer);
ClassDB::bind_method(
D_METHOD("set_collision_mask", "collision_mask"), &VoxelInstanceLibraryMultiMeshItem::set_collision_mask);
ClassDB::bind_method(D_METHOD("get_collision_mask"), &VoxelInstanceLibraryMultiMeshItem::get_collision_mask);
ClassDB::bind_method(D_METHOD("set_collision_shapes", "shape_infos"),
&VoxelInstanceLibraryMultiMeshItem::_b_set_collision_shapes);
ClassDB::bind_method(D_METHOD("get_collision_shapes"), &VoxelInstanceLibraryMultiMeshItem::_b_get_collision_shapes);
ClassDB::bind_method(
D_METHOD("setup_from_template", "node"), &VoxelInstanceLibraryMultiMeshItem::setup_from_template);
// Used in editor only
ClassDB::bind_method(D_METHOD("_deserialize_multimesh_item_properties", "props"),
&VoxelInstanceLibraryMultiMeshItem::deserialize_multimesh_item_properties);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "_set_mesh_lod0",
"_get_mesh_lod0");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh_lod1", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "_set_mesh_lod1",
"_get_mesh_lod1");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh_lod2", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "_set_mesh_lod2",
"_get_mesh_lod2");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh_lod3", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "_set_mesh_lod3",
"_get_mesh_lod3");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_override", PROPERTY_HINT_RESOURCE_TYPE, "Material"),
"set_material_override", "get_material_override");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cast_shadow", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"),
"set_cast_shadows_setting", "get_cast_shadows_setting");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer",
"get_collision_layer");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask",
"get_collision_mask");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "collision_shapes"), "set_collision_shapes", "get_collision_shapes");
BIND_CONSTANT(MAX_MESH_LODS);
}
} // namespace zylann::voxel

View File

@ -0,0 +1,96 @@
#ifndef VOXEL_INSTANCE_LIBRARY_MULTIMESH_ITEM_H
#define VOXEL_INSTANCE_LIBRARY_MULTIMESH_ITEM_H
#include "voxel_instance_library_item.h"
#include <scene/resources/mesh.h>
#include <scene/resources/shape_3d.h>
namespace zylann::voxel {
// Settings for a model that can be used by VoxelInstancer
class VoxelInstanceLibraryMultiMeshItem : public VoxelInstanceLibraryItem {
GDCLASS(VoxelInstanceLibraryMultiMeshItem, VoxelInstanceLibraryItem)
public:
static const int MAX_MESH_LODS = 4;
struct CollisionShapeInfo {
Transform3D transform;
Ref<Shape3D> shape;
};
void set_mesh(Ref<Mesh> mesh, int mesh_lod_index);
Ref<Mesh> get_mesh(int mesh_lod_index) const;
int get_mesh_lod_count() const;
void set_material_override(Ref<Material> material);
Ref<Material> get_material_override() const;
void set_cast_shadows_setting(RenderingServer::ShadowCastingSetting mode);
RenderingServer::ShadowCastingSetting get_cast_shadows_setting() const;
void set_collision_layer(int collision_layer);
int get_collision_layer() const;
void set_collision_mask(int collision_mask);
int get_collision_mask() const;
void setup_from_template(Node *root);
// Internal
inline const Vector<CollisionShapeInfo> &get_collision_shapes() const {
return _collision_shapes;
}
Array serialize_multimesh_item_properties() const;
void deserialize_multimesh_item_properties(Array a);
private:
static void _bind_methods();
void _b_set_collision_shapes(Array shape_infos);
Array _b_get_collision_shapes() const;
Ref<Mesh> _b_get_mesh_lod0() const {
return get_mesh(0);
}
Ref<Mesh> _b_get_mesh_lod1() const {
return get_mesh(1);
}
Ref<Mesh> _b_get_mesh_lod2() const {
return get_mesh(2);
}
Ref<Mesh> _b_get_mesh_lod3() const {
return get_mesh(3);
}
void _b_set_mesh_lod0(Ref<Mesh> mesh) {
set_mesh(mesh, 0);
}
void _b_set_mesh_lod1(Ref<Mesh> mesh) {
set_mesh(mesh, 1);
}
void _b_set_mesh_lod2(Ref<Mesh> mesh) {
set_mesh(mesh, 2);
}
void _b_set_mesh_lod3(Ref<Mesh> mesh) {
set_mesh(mesh, 3);
}
FixedArray<Ref<Mesh>, MAX_MESH_LODS> _mesh_lods;
unsigned int _mesh_lod_count = 1;
// It is preferred to have materials on the mesh already,
// but this is in case OBJ meshes are used, which often dont have a material of their own
Ref<Material> _material_override;
RenderingServer::ShadowCastingSetting _shadow_casting_setting = RenderingServer::SHADOW_CASTING_SETTING_ON;
int _collision_mask = 1;
int _collision_layer = 1;
Vector<CollisionShapeInfo> _collision_shapes;
};
} // namespace zylann::voxel
#endif // VOXEL_INSTANCE_LIBRARY_MULTIMESH_ITEM_H

View File

@ -1,14 +1,14 @@
#ifndef VOXEL_INSTANCE_LIBRARY_SCENE_ITEM_H
#define VOXEL_INSTANCE_LIBRARY_SCENE_ITEM_H
#include "voxel_instance_library_item_base.h"
#include "voxel_instance_library_item.h"
#include <scene/resources/packed_scene.h>
namespace zylann::voxel {
class VoxelInstanceLibrarySceneItem : public VoxelInstanceLibraryItemBase {
GDCLASS(VoxelInstanceLibrarySceneItem, VoxelInstanceLibraryItemBase)
class VoxelInstanceLibrarySceneItem : public VoxelInstanceLibraryItem {
GDCLASS(VoxelInstanceLibrarySceneItem, VoxelInstanceLibraryItem)
public:
void set_scene(Ref<PackedScene> scene);
Ref<PackedScene> get_scene() const;

View File

@ -275,10 +275,10 @@ void VoxelInstancer::process_mesh_lods() {
for (auto it = _blocks.begin(); it != _blocks.end(); ++it) {
Block *block = *it;
const VoxelInstanceLibraryItemBase *item_base = _library->get_item_const(block->layer_id);
const VoxelInstanceLibraryItem *item_base = _library->get_item_const(block->layer_id);
ERR_CONTINUE(item_base == nullptr);
// TODO Optimization: would be nice to not need this cast by iterating only the same item types
const VoxelInstanceLibraryItem *item = Object::cast_to<VoxelInstanceLibraryItem>(item_base);
const VoxelInstanceLibraryMultiMeshItem *item = Object::cast_to<VoxelInstanceLibraryMultiMeshItem>(item_base);
if (item == nullptr) {
// Not a multimesh item
continue;
@ -294,7 +294,7 @@ void VoxelInstancer::process_mesh_lods() {
const int lod_index = item->get_lod_index();
#ifdef DEBUG_ENABLED
ERR_FAIL_COND(mesh_lod_count < VoxelInstanceLibraryItem::MAX_MESH_LODS);
ERR_FAIL_COND(mesh_lod_count < VoxelInstanceLibraryMultiMeshItem::MAX_MESH_LODS);
#endif
const Lod &lod = _lods[lod_index];
@ -379,7 +379,7 @@ void VoxelInstancer::set_library(Ref<VoxelInstanceLibrary> library) {
clear_layers();
if (_library.is_valid()) {
_library->for_each_item([this](int id, const VoxelInstanceLibraryItemBase &item) {
_library->for_each_item([this](int id, const VoxelInstanceLibraryItem &item) {
add_layer(id, item.get_lod_index());
if (_parent != nullptr && is_inside_tree()) {
regenerate_layer(id, true);
@ -407,7 +407,7 @@ void VoxelInstancer::regenerate_layer(uint16_t layer_id, bool regenerate_blocks)
Layer *layer = get_layer(layer_id);
CRASH_COND(layer == nullptr);
Ref<VoxelInstanceLibraryItemBase> item = _library->get_item(layer_id);
Ref<VoxelInstanceLibraryItem> item = _library->get_item(layer_id);
ERR_FAIL_COND(item.is_null());
if (item->get_generator().is_null()) {
return;
@ -522,9 +522,9 @@ void VoxelInstancer::regenerate_layer(uint16_t layer_id, bool regenerate_blocks)
}
void VoxelInstancer::update_layer_meshes(int layer_id) {
Ref<VoxelInstanceLibraryItemBase> item_base = _library->get_item(layer_id);
Ref<VoxelInstanceLibraryItem> item_base = _library->get_item(layer_id);
ERR_FAIL_COND(item_base.is_null());
VoxelInstanceLibraryItem *item = Object::cast_to<VoxelInstanceLibraryItem>(*item_base);
VoxelInstanceLibraryMultiMeshItem *item = Object::cast_to<VoxelInstanceLibraryMultiMeshItem>(*item_base);
ERR_FAIL_COND(item == nullptr);
for (auto it = _blocks.begin(); it != _blocks.end(); ++it) {
@ -548,7 +548,7 @@ void VoxelInstancer::update_layer_meshes(int layer_id) {
}
void VoxelInstancer::update_layer_scenes(int layer_id) {
Ref<VoxelInstanceLibraryItemBase> item_base = _library->get_item(layer_id);
Ref<VoxelInstanceLibraryItem> item_base = _library->get_item(layer_id);
ERR_FAIL_COND(item_base.is_null());
VoxelInstanceLibrarySceneItem *item = Object::cast_to<VoxelInstanceLibrarySceneItem>(*item_base);
ERR_FAIL_COND(item == nullptr);
@ -572,39 +572,39 @@ void VoxelInstancer::update_layer_scenes(int layer_id) {
}
}
void VoxelInstancer::on_library_item_changed(int item_id, VoxelInstanceLibraryItemBase::ChangeType change) {
void VoxelInstancer::on_library_item_changed(int item_id, VoxelInstanceLibraryItem::ChangeType change) {
ERR_FAIL_COND(_library.is_null());
// TODO It's unclear yet if some code paths do the right thing in case instances got edited
switch (change) {
case VoxelInstanceLibraryItemBase::CHANGE_ADDED: {
Ref<VoxelInstanceLibraryItemBase> item = _library->get_item(item_id);
case VoxelInstanceLibraryItem::CHANGE_ADDED: {
Ref<VoxelInstanceLibraryItem> item = _library->get_item(item_id);
ERR_FAIL_COND(item.is_null());
add_layer(item_id, item->get_lod_index());
regenerate_layer(item_id, true);
update_configuration_warnings();
} break;
case VoxelInstanceLibraryItemBase::CHANGE_REMOVED:
case VoxelInstanceLibraryItem::CHANGE_REMOVED:
remove_layer(item_id);
update_configuration_warnings();
break;
case VoxelInstanceLibraryItemBase::CHANGE_GENERATOR:
case VoxelInstanceLibraryItem::CHANGE_GENERATOR:
regenerate_layer(item_id, false);
break;
case VoxelInstanceLibraryItemBase::CHANGE_VISUAL:
case VoxelInstanceLibraryItem::CHANGE_VISUAL:
update_layer_meshes(item_id);
break;
case VoxelInstanceLibraryItemBase::CHANGE_SCENE:
case VoxelInstanceLibraryItem::CHANGE_SCENE:
update_layer_scenes(item_id);
break;
case VoxelInstanceLibraryItemBase::CHANGE_LOD_INDEX: {
Ref<VoxelInstanceLibraryItemBase> item = _library->get_item(item_id);
case VoxelInstanceLibraryItem::CHANGE_LOD_INDEX: {
Ref<VoxelInstanceLibraryItem> item = _library->get_item(item_id);
ERR_FAIL_COND(item.is_null());
clear_blocks_in_layer(item_id);
@ -814,7 +814,7 @@ int VoxelInstancer::create_block(Layer *layer, uint16_t layer_id, Vector3i grid_
}
void VoxelInstancer::update_block_from_transforms(int block_index, Span<const Transform3D> transforms,
Vector3i grid_position, Layer *layer, const VoxelInstanceLibraryItemBase *item_base, uint16_t layer_id,
Vector3i grid_position, Layer *layer, const VoxelInstanceLibraryItem *item_base, uint16_t layer_id,
World3D *world, const Transform3D &block_transform) {
VOXEL_PROFILE_SCOPE();
@ -831,7 +831,7 @@ void VoxelInstancer::update_block_from_transforms(int block_index, Span<const Tr
}
// Update multimesh
const VoxelInstanceLibraryItem *item = Object::cast_to<VoxelInstanceLibraryItem>(item_base);
const VoxelInstanceLibraryMultiMeshItem *item = Object::cast_to<VoxelInstanceLibraryMultiMeshItem>(item_base);
if (item != nullptr) {
if (transforms.size() == 0) {
if (block->multimesh_instance.is_valid()) {
@ -872,7 +872,8 @@ void VoxelInstancer::update_block_from_transforms(int block_index, Span<const Tr
}
// Update bodies
const Vector<VoxelInstanceLibraryItem::CollisionShapeInfo> &collision_shapes = item->get_collision_shapes();
const Vector<VoxelInstanceLibraryMultiMeshItem::CollisionShapeInfo> &collision_shapes =
item->get_collision_shapes();
if (collision_shapes.size() > 0) {
VOXEL_PROFILE_SCOPE_NAMED("Update multimesh bodies");
@ -897,7 +898,7 @@ void VoxelInstancer::update_block_from_transforms(int block_index, Span<const Tr
Vector3iUtil::from_floored(body_transform.origin) >> data_block_size_po2);
for (int i = 0; i < collision_shapes.size(); ++i) {
const VoxelInstanceLibraryItem::CollisionShapeInfo &shape_info = collision_shapes[i];
const VoxelInstanceLibraryMultiMeshItem::CollisionShapeInfo &shape_info = collision_shapes[i];
CollisionShape3D *cs = memnew(CollisionShape3D);
cs->set_shape(shape_info.shape);
cs->set_transform(shape_info.transform);
@ -1045,7 +1046,7 @@ void VoxelInstancer::create_render_blocks(Vector3i render_grid_position, int lod
}
}
const VoxelInstanceLibraryItemBase *item = _library->get_item(layer_id);
const VoxelInstanceLibraryItem *item = _library->get_item(layer_id);
CRASH_COND(item == nullptr);
// Generate the rest
@ -1099,7 +1100,7 @@ void VoxelInstancer::save_block(Vector3i data_grid_pos, int lod_index) const {
for (auto it = lod.layers.begin(); it != lod.layers.end(); ++it) {
const int layer_id = *it;
const VoxelInstanceLibraryItemBase *item = _library->get_item_const(layer_id);
const VoxelInstanceLibraryItem *item = _library->get_item_const(layer_id);
CRASH_COND(item == nullptr);
if (!item->is_persistent()) {
continue;

View File

@ -7,7 +7,7 @@
#include "../../util/math/box3i.h"
#include "voxel_instance_generator.h"
#include "voxel_instance_library.h"
#include "voxel_instance_library_item.h"
#include "voxel_instance_library_multimesh_item.h"
#ifdef TOOLS_ENABLED
#include "../../editor/voxel_debug.h"
@ -124,7 +124,7 @@ private:
unsigned int block_index, Transform3D transform, int data_block_size_po2);
void update_block_from_transforms(int block_index, Span<const Transform3D> transforms, Vector3i grid_position,
Layer *layer, const VoxelInstanceLibraryItemBase *item_base, uint16_t layer_id, World3D *world,
Layer *layer, const VoxelInstanceLibraryItem *item_base, uint16_t layer_id, World3D *world,
const Transform3D &block_transform);
void on_library_item_changed(int item_id, VoxelInstanceLibraryItem::ChangeType change) override;
@ -190,7 +190,7 @@ private:
// Can't use `HashMap` because it lacks move semantics.
std::unordered_map<Vector3i, std::unique_ptr<InstanceBlockData>> loaded_instances_data;
FixedArray<MeshLodDistances, VoxelInstanceLibraryItem::MAX_MESH_LODS> mesh_lod_distances;
FixedArray<MeshLodDistances, VoxelInstanceLibraryMultiMeshItem::MAX_MESH_LODS> mesh_lod_distances;
Lod() = default;
Lod(const Lod &) = delete; // non construction-copyable