Renamed VoxelInstanceLibraryItem => VoxelInstanceLibraryMultiMeshItem
parent
1fbe9c4731
commit
9454759b02
|
@ -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();
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue