diff --git a/scene/3d/multimesh_instance_3d.cpp b/scene/3d/multimesh_instance_3d.cpp index 2eef1dbbf4dc..baa048b3bbb2 100644 --- a/scene/3d/multimesh_instance_3d.cpp +++ b/scene/3d/multimesh_instance_3d.cpp @@ -42,9 +42,66 @@ void MultiMeshInstance3D::_physics_interpolated_changed() { _refresh_interpolated(); } +bool MultiMeshInstance3D::_set(const StringName &p_name, const Variant &p_value) { + //this is not _too_ bad performance wise, really. it only arrives here if the property was not set anywhere else. + //add to it that it's probably found on first call to _set anyway. + + if (!get_instance().is_valid()) { + return false; + } + + if (p_name.operator String().begins_with("surface_material_override/")) { + int idx = p_name.operator String().get_slicec('/', 1).to_int(); + + if (idx >= surface_override_materials.size() || idx < 0) { + return false; + } + + set_surface_override_material(idx, p_value); + return true; + } + + return false; +} + +bool MultiMeshInstance3D::_get(const StringName &p_name, Variant &r_ret) const { + if (!get_instance().is_valid()) { + return false; + } + + if (p_name.operator String().begins_with("surface_material_override/")) { + int idx = p_name.operator String().get_slicec('/', 1).to_int(); + if (idx >= surface_override_materials.size() || idx < 0) { + return false; + } + r_ret = surface_override_materials[idx]; + return true; + } + return false; +} + +void MultiMeshInstance3D::_get_property_list(List *p_list) const { + if (multimesh.is_valid()) { + Ref mesh = multimesh->get_mesh(); + if (mesh.is_valid()) { + for (int i = 0; i < mesh->get_surface_count(); i++) { + p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("%s/%d", PNAME("surface_material_override"), i), PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial", PROPERTY_USAGE_DEFAULT)); + } + } + } + + +} + void MultiMeshInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_multimesh", "multimesh"), &MultiMeshInstance3D::set_multimesh); ClassDB::bind_method(D_METHOD("get_multimesh"), &MultiMeshInstance3D::get_multimesh); + + ClassDB::bind_method(D_METHOD("get_surface_override_material_count"), &MultiMeshInstance3D::get_surface_override_material_count); + ClassDB::bind_method(D_METHOD("set_surface_override_material", "surface", "material"), &MultiMeshInstance3D::set_surface_override_material); + ClassDB::bind_method(D_METHOD("get_surface_override_material", "surface"), &MultiMeshInstance3D::get_surface_override_material); + ClassDB::bind_method(D_METHOD("get_active_material", "surface"), &MultiMeshInstance3D::get_active_material); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multimesh", PROPERTY_HINT_RESOURCE_TYPE, "MultiMesh"), "set_multimesh", "get_multimesh"); } @@ -54,14 +111,86 @@ void MultiMeshInstance3D::_notification(int p_what) { } } +void MultiMeshInstance3D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "multimesh") { + WARN_PRINT_ED("Multimesh validated."); + } +} + +void MultiMeshInstance3D::_multimesh_changed() { + + ERR_FAIL_COND(multimesh.is_null()); + + Ref mesh = multimesh->get_mesh(); + WARN_PRINT_ED("Multimesh changed."); + + if (mesh.is_valid()) { + surface_override_materials.resize(mesh->get_surface_count()); + + int surface_count = mesh->get_surface_count(); + for (int surface_index = 0; surface_index < surface_count; ++surface_index) { + if (surface_override_materials[surface_index].is_valid()) { + RS::get_singleton()->multimesh_set_surface_override_material(get_instance(), surface_index, surface_override_materials[surface_index]->get_rid()); + } + } + } else { + surface_override_materials.resize(0); + } + + //notify_property_list_changed(); +} + void MultiMeshInstance3D::set_multimesh(const Ref &p_multimesh) { - multimesh = p_multimesh; + if (multimesh == p_multimesh) { + return; + } + if (multimesh.is_valid()) { + multimesh->disconnect_changed(callable_mp(this, &MultiMeshInstance3D::_multimesh_changed)); + } + + multimesh = p_multimesh; + + if (multimesh.is_valid()) { set_base(multimesh->get_rid()); + + multimesh->connect_changed(callable_mp(this, &MultiMeshInstance3D::_multimesh_changed)); + _multimesh_changed(); + _refresh_interpolated(); + } else { set_base(RID()); } + + notify_property_list_changed(); + + // multimesh = p_multimesh; + + // if (multimesh.is_valid()) { + // set_base(multimesh->get_rid()); + + // // Adapted from MeshInstance3D::_mesh_changed() and inlined. + // Ref mesh = multimesh->get_mesh(); + // if (mesh.is_valid()) { + // surface_override_materials.resize(mesh->get_surface_count()); + + // int surface_count = mesh->get_surface_count(); + // for (int surface_index = 0; surface_index < surface_count; ++surface_index) { + // if (surface_override_materials[surface_index].is_valid()) { + // RS::get_singleton()->multimesh_set_surface_override_material(get_instance(), surface_index, surface_override_materials[surface_index]->get_rid()); + // } + // } + // } else { + // surface_override_materials.resize(0); + // } + + // _refresh_interpolated(); + // } else { + // set_base(RID()); + // } + + // notify_property_list_changed(); } Ref MultiMeshInstance3D::get_multimesh() const { @@ -88,6 +217,48 @@ Array MultiMeshInstance3D::get_meshes() const { return results; } +int MultiMeshInstance3D::get_surface_override_material_count() const { + return surface_override_materials.size(); +} + +void MultiMeshInstance3D::set_surface_override_material(int p_surface, const Ref &p_material) { + ERR_FAIL_INDEX(p_surface, surface_override_materials.size()); + + surface_override_materials.write[p_surface] = p_material; + + if (surface_override_materials[p_surface].is_valid()) { + RS::get_singleton()->multimesh_set_surface_override_material(get_instance(), p_surface, surface_override_materials[p_surface]->get_rid()); + } else { + RS::get_singleton()->multimesh_set_surface_override_material(get_instance(), p_surface, RID()); + } +} + +Ref MultiMeshInstance3D::get_surface_override_material(int p_surface) const { + ERR_FAIL_INDEX_V(p_surface, surface_override_materials.size(), Ref()); + + return surface_override_materials[p_surface]; +} + +Ref MultiMeshInstance3D::get_active_material(int p_surface) const { + Ref mat_override = get_material_override(); + if (mat_override.is_valid()) { + return mat_override; + } + + Ref surface_material = get_surface_override_material(p_surface); + if (surface_material.is_valid()) { + return surface_material; + } + + // Ref m = get_mesh(); + Ref m = multimesh.is_valid() ? multimesh->get_mesh() : Ref(); + if (m.is_valid()) { + return m->surface_get_material(p_surface); + } + + return Ref(); +} + AABB MultiMeshInstance3D::get_aabb() const { if (multimesh.is_null()) { return AABB(); diff --git a/scene/3d/multimesh_instance_3d.h b/scene/3d/multimesh_instance_3d.h index c9507b1047e3..90a2901e9dcf 100644 --- a/scene/3d/multimesh_instance_3d.h +++ b/scene/3d/multimesh_instance_3d.h @@ -42,9 +42,18 @@ class MultiMeshInstance3D : public GeometryInstance3D { void _refresh_interpolated(); protected: + void _multimesh_changed(); + + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + virtual void _physics_interpolated_changed() override; + void _get_property_list(List *p_list) const; static void _bind_methods(); void _notification(int p_what); + void _validate_property(PropertyInfo &p_property) const; + + Vector> surface_override_materials; public: void set_multimesh(const Ref &p_multimesh); @@ -52,6 +61,11 @@ class MultiMeshInstance3D : public GeometryInstance3D { Array get_meshes() const; + int get_surface_override_material_count() const; + void set_surface_override_material(int p_surface, const Ref &p_material); + Ref get_surface_override_material(int p_surface) const; + Ref get_active_material(int p_surface) const; + virtual AABB get_aabb() const override; MultiMeshInstance3D(); diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index 1d25dec633b5..7eced209629b 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -1097,6 +1097,26 @@ void RendererSceneCull::instance_set_surface_override_material(RID p_instance, i _instance_queue_update(instance, false, true); } +void RendererSceneCull::multimesh_set_surface_override_material(RID p_instance, int p_surface, RID p_material) { + Instance *instance = instance_owner.get_or_null(p_instance); + ERR_FAIL_NULL(instance); + + if (instance->base_type == RS::INSTANCE_MULTIMESH) { + RID mesh = RSG::mesh_storage->multimesh_get_mesh(instance->base); + //if (mesh.is_valid()) { + { + //may not have been updated yet, may also have not been set yet. When updated will be correcte, worst case + instance->materials.resize(MAX(p_surface + 1, RSG::mesh_storage->mesh_get_surface_count(mesh))); + } + } + + ERR_FAIL_INDEX(p_surface, instance->materials.size()); + + instance->materials.write[p_surface] = p_material; + + _instance_queue_update(instance, false, true); +} + void RendererSceneCull::instance_set_visible(RID p_instance, bool p_visible) { Instance *instance = instance_owner.get_or_null(p_instance); ERR_FAIL_NULL(instance); @@ -4125,8 +4145,11 @@ void RendererSceneCull::_update_dirty_instance(Instance *p_instance) { bool cast_shadows = false; int sc = RSG::mesh_storage->mesh_get_surface_count(mesh); + // int mc = p_instance->materials.size(); + // sc = MIN(sc,mc); for (int i = 0; i < sc; i++) { RID mat = RSG::mesh_storage->mesh_surface_get_material(mesh, i); + //RID mat = p_instance->materials[i].is_valid() ? p_instance->materials[i] : RSG::mesh_storage->mesh_surface_get_material(mesh, i); if (!mat.is_valid()) { cast_shadows = true; diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h index 972f66d325ff..40d172748580 100644 --- a/servers/rendering/renderer_scene_cull.h +++ b/servers/rendering/renderer_scene_cull.h @@ -1064,6 +1064,8 @@ class RendererSceneCull : public RenderingMethod { virtual void instance_attach_object_instance_id(RID p_instance, ObjectID p_id); virtual void instance_set_blend_shape_weight(RID p_instance, int p_shape, float p_weight); virtual void instance_set_surface_override_material(RID p_instance, int p_surface, RID p_material); + virtual void multimesh_set_surface_override_material(RID p_instance, int p_surface, RID p_material); + virtual void instance_set_visible(RID p_instance, bool p_visible); virtual void instance_geometry_set_transparency(RID p_instance, float p_transparency); diff --git a/servers/rendering/rendering_method.h b/servers/rendering/rendering_method.h index f6212faf085d..660666384147 100644 --- a/servers/rendering/rendering_method.h +++ b/servers/rendering/rendering_method.h @@ -88,6 +88,7 @@ class RenderingMethod { virtual void instance_attach_object_instance_id(RID p_instance, ObjectID p_id) = 0; virtual void instance_set_blend_shape_weight(RID p_instance, int p_shape, float p_weight) = 0; virtual void instance_set_surface_override_material(RID p_instance, int p_surface, RID p_material) = 0; + virtual void multimesh_set_surface_override_material(RID p_instance, int p_surface, RID p_material) = 0; virtual void instance_set_visible(RID p_instance, bool p_visible) = 0; virtual void instance_geometry_set_transparency(RID p_instance, float p_transparency) = 0; diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 60fa546e164c..35a4f70c2598 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -325,6 +325,9 @@ class RenderingServerDefault : public RenderingServer { FUNC1RC(int, multimesh_get_instance_count, RID) FUNC2(multimesh_set_mesh, RID, RID) + + // FUNC3(multimesh_set_surface_override_material, RID, int, RID) + FUNC3(multimesh_instance_set_transform, RID, int, const Transform3D &) FUNC3(multimesh_instance_set_transform_2d, RID, int, const Transform2D &) FUNC3(multimesh_instance_set_color, RID, int, const Color &) @@ -814,6 +817,7 @@ class RenderingServerDefault : public RenderingServer { FUNC2(instance_attach_object_instance_id, RID, ObjectID) FUNC3(instance_set_blend_shape_weight, RID, int, float) FUNC3(instance_set_surface_override_material, RID, int, RID) + FUNC3(multimesh_set_surface_override_material, RID, int, RID) FUNC2(instance_set_visible, RID, bool) FUNC2(instance_set_custom_aabb, RID, AABB) diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 92f0f0dbc0c9..32ad879e6b3e 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2422,6 +2422,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("multimesh_allocate_data", "multimesh", "instances", "transform_format", "color_format", "custom_data_format"), &RenderingServer::multimesh_allocate_data, DEFVAL(false), DEFVAL(false)); ClassDB::bind_method(D_METHOD("multimesh_get_instance_count", "multimesh"), &RenderingServer::multimesh_get_instance_count); ClassDB::bind_method(D_METHOD("multimesh_set_mesh", "multimesh", "mesh"), &RenderingServer::multimesh_set_mesh); + ClassDB::bind_method(D_METHOD("multimesh_set_surface_override_material", "multimesh", "surface", "material"), &RenderingServer::multimesh_set_surface_override_material); ClassDB::bind_method(D_METHOD("multimesh_instance_set_transform", "multimesh", "index", "transform"), &RenderingServer::multimesh_instance_set_transform); ClassDB::bind_method(D_METHOD("multimesh_instance_set_transform_2d", "multimesh", "index", "transform"), &RenderingServer::multimesh_instance_set_transform_2d); ClassDB::bind_method(D_METHOD("multimesh_instance_set_color", "multimesh", "index", "color"), &RenderingServer::multimesh_instance_set_color); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index d8b665183337..aec067af2005 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -435,6 +435,7 @@ class RenderingServer : public Object { virtual int multimesh_get_instance_count(RID p_multimesh) const = 0; virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh) = 0; + virtual void multimesh_set_surface_override_material(RID p_multimesh, int p_surface, RID p_material) = 0; virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) = 0; virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) = 0; virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) = 0;