Support custom AABB within MultiMesh resources

- Supporting custom AABB on the MultiMesh resource itself allows us to prevent costly runtime AABB recalculations.
- Should also help improve CPU Particle performance.
This commit is contained in:
Arman Elgudzhyan 2023-07-22 20:53:39 -07:00
parent a9bb8509f2
commit 7ac8365e11
13 changed files with 112 additions and 8 deletions

View File

@ -92,7 +92,11 @@
</member> </member>
<member name="color_array" type="PackedColorArray" setter="_set_color_array" getter="_get_color_array" deprecated="Use [method set_instance_color] instead."> <member name="color_array" type="PackedColorArray" setter="_set_color_array" getter="_get_color_array" deprecated="Use [method set_instance_color] instead.">
</member> </member>
<member name="custom_aabb" type="AABB" setter="set_custom_aabb" getter="get_custom_aabb" default="AABB(0, 0, 0, 0, 0, 0)">
Custom AABB for this MultiMesh resource. Setting this manually prevents costly runtime AABB recalculations.
</member>
<member name="custom_data_array" type="PackedColorArray" setter="_set_custom_data_array" getter="_get_custom_data_array" deprecated="Use [method set_instance_custom_data] instead."> <member name="custom_data_array" type="PackedColorArray" setter="_set_custom_data_array" getter="_get_custom_data_array" deprecated="Use [method set_instance_custom_data] instead.">
See [method set_instance_custom_data].
</member> </member>
<member name="instance_count" type="int" setter="set_instance_count" getter="get_instance_count" default="0"> <member name="instance_count" type="int" setter="set_instance_count" getter="get_instance_count" default="0">
Number of instances that will get drawn. This clears and (re)sizes the buffers. Setting data format or flags afterwards will have no effect. Number of instances that will get drawn. This clears and (re)sizes the buffers. Setting data format or flags afterwards will have no effect.

View File

@ -2332,6 +2332,13 @@
[b]Note:[/b] If the buffer is in the engine's internal cache, it will have to be fetched from GPU memory and possibly decompressed. This means [method multimesh_get_buffer] is potentially a slow operation and should be avoided whenever possible. [b]Note:[/b] If the buffer is in the engine's internal cache, it will have to be fetched from GPU memory and possibly decompressed. This means [method multimesh_get_buffer] is potentially a slow operation and should be avoided whenever possible.
</description> </description>
</method> </method>
<method name="multimesh_get_custom_aabb" qualifiers="const">
<return type="AABB" />
<param index="0" name="multimesh" type="RID" />
<description>
Returns the custom AABB defined for this MultiMesh resource.
</description>
</method>
<method name="multimesh_get_instance_count" qualifiers="const"> <method name="multimesh_get_instance_count" qualifiers="const">
<return type="int" /> <return type="int" />
<param index="0" name="multimesh" type="RID" /> <param index="0" name="multimesh" type="RID" />
@ -2442,6 +2449,14 @@
[/codeblock] [/codeblock]
</description> </description>
</method> </method>
<method name="multimesh_set_custom_aabb">
<return type="void" />
<param index="0" name="multimesh" type="RID" />
<param index="1" name="aabb" type="AABB" />
<description>
Sets the custom AABB for this MultiMesh resource.
</description>
</method>
<method name="multimesh_set_mesh"> <method name="multimesh_set_mesh">
<return type="void" /> <return type="void" />
<param index="0" name="multimesh" type="RID" /> <param index="0" name="multimesh" type="RID" />

View File

@ -1607,6 +1607,9 @@ void MeshStorage::_multimesh_mark_all_dirty(MultiMesh *multimesh, bool p_data, b
void MeshStorage::_multimesh_re_create_aabb(MultiMesh *multimesh, const float *p_data, int p_instances) { void MeshStorage::_multimesh_re_create_aabb(MultiMesh *multimesh, const float *p_data, int p_instances) {
ERR_FAIL_COND(multimesh->mesh.is_null()); ERR_FAIL_COND(multimesh->mesh.is_null());
if (multimesh->custom_aabb != AABB()) {
return;
}
AABB aabb; AABB aabb;
AABB mesh_aabb = mesh_get_aabb(multimesh->mesh); AABB mesh_aabb = mesh_get_aabb(multimesh->mesh);
for (int i = 0; i < p_instances; i++) { for (int i = 0; i < p_instances; i++) {
@ -1749,9 +1752,25 @@ RID MeshStorage::multimesh_get_mesh(RID p_multimesh) const {
return multimesh->mesh; return multimesh->mesh;
} }
void MeshStorage::multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
multimesh->custom_aabb = p_aabb;
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
}
AABB MeshStorage::multimesh_get_custom_aabb(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, AABB());
return multimesh->custom_aabb;
}
AABB MeshStorage::multimesh_get_aabb(RID p_multimesh) const { AABB MeshStorage::multimesh_get_aabb(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, AABB()); ERR_FAIL_NULL_V(multimesh, AABB());
if (multimesh->custom_aabb != AABB()) {
return multimesh->custom_aabb;
}
if (multimesh->aabb_dirty) { if (multimesh->aabb_dirty) {
const_cast<MeshStorage *>(this)->_update_dirty_multimeshes(); const_cast<MeshStorage *>(this)->_update_dirty_multimeshes();
} }
@ -1943,8 +1962,10 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b
//if we have a mesh set, we need to re-generate the AABB from the new data //if we have a mesh set, we need to re-generate the AABB from the new data
const float *data = p_buffer.ptr(); const float *data = p_buffer.ptr();
_multimesh_re_create_aabb(multimesh, data, multimesh->instances); if (multimesh->custom_aabb != AABB()) {
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); _multimesh_re_create_aabb(multimesh, data, multimesh->instances);
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
}
} }
} }
@ -2091,9 +2112,11 @@ void MeshStorage::_update_dirty_multimeshes() {
} }
if (multimesh->aabb_dirty && multimesh->mesh.is_valid()) { if (multimesh->aabb_dirty && multimesh->mesh.is_valid()) {
_multimesh_re_create_aabb(multimesh, data, visible_instances);
multimesh->aabb_dirty = false; multimesh->aabb_dirty = false;
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); if (multimesh->custom_aabb != AABB()) {
_multimesh_re_create_aabb(multimesh, data, visible_instances);
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
}
} }
} }

View File

@ -189,6 +189,7 @@ struct MultiMesh {
bool uses_custom_data = false; bool uses_custom_data = false;
int visible_instances = -1; int visible_instances = -1;
AABB aabb; AABB aabb;
AABB custom_aabb;
bool aabb_dirty = false; bool aabb_dirty = false;
bool buffer_set = false; bool buffer_set = false;
uint32_t stride_cache = 0; uint32_t stride_cache = 0;
@ -505,6 +506,8 @@ public:
virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override; virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override;
virtual RID multimesh_get_mesh(RID p_multimesh) const override; virtual RID multimesh_get_mesh(RID p_multimesh) const override;
virtual void multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override;
virtual AABB multimesh_get_custom_aabb(RID p_multimesh) const override;
virtual AABB multimesh_get_aabb(RID p_multimesh) const override; virtual AABB multimesh_get_aabb(RID p_multimesh) const override;
virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const override; virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const override;

View File

@ -269,6 +269,16 @@ Color MultiMesh::get_instance_custom_data(int p_instance) const {
return RenderingServer::get_singleton()->multimesh_instance_get_custom_data(multimesh, p_instance); return RenderingServer::get_singleton()->multimesh_instance_get_custom_data(multimesh, p_instance);
} }
void MultiMesh::set_custom_aabb(const AABB &p_custom) {
custom_aabb = p_custom;
RS::get_singleton()->multimesh_set_custom_aabb(multimesh, custom_aabb);
emit_changed();
}
AABB MultiMesh::get_custom_aabb() const {
return custom_aabb;
}
AABB MultiMesh::get_aabb() const { AABB MultiMesh::get_aabb() const {
return RenderingServer::get_singleton()->multimesh_get_aabb(multimesh); return RenderingServer::get_singleton()->multimesh_get_aabb(multimesh);
} }
@ -326,6 +336,8 @@ void MultiMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_instance_color", "instance"), &MultiMesh::get_instance_color); ClassDB::bind_method(D_METHOD("get_instance_color", "instance"), &MultiMesh::get_instance_color);
ClassDB::bind_method(D_METHOD("set_instance_custom_data", "instance", "custom_data"), &MultiMesh::set_instance_custom_data); ClassDB::bind_method(D_METHOD("set_instance_custom_data", "instance", "custom_data"), &MultiMesh::set_instance_custom_data);
ClassDB::bind_method(D_METHOD("get_instance_custom_data", "instance"), &MultiMesh::get_instance_custom_data); ClassDB::bind_method(D_METHOD("get_instance_custom_data", "instance"), &MultiMesh::get_instance_custom_data);
ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &MultiMesh::set_custom_aabb);
ClassDB::bind_method(D_METHOD("get_custom_aabb"), &MultiMesh::get_custom_aabb);
ClassDB::bind_method(D_METHOD("get_aabb"), &MultiMesh::get_aabb); ClassDB::bind_method(D_METHOD("get_aabb"), &MultiMesh::get_aabb);
ClassDB::bind_method(D_METHOD("get_buffer"), &MultiMesh::get_buffer); ClassDB::bind_method(D_METHOD("get_buffer"), &MultiMesh::get_buffer);
@ -334,6 +346,7 @@ void MultiMesh::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_format", PROPERTY_HINT_ENUM, "2D,3D"), "set_transform_format", "get_transform_format"); ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_format", PROPERTY_HINT_ENUM, "2D,3D"), "set_transform_format", "get_transform_format");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_colors"), "set_use_colors", "is_using_colors"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_colors"), "set_use_colors", "is_using_colors");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_custom_data"), "set_use_custom_data", "is_using_custom_data"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_custom_data"), "set_use_custom_data", "is_using_custom_data");
ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, "suffix:m"), "set_custom_aabb", "get_custom_aabb");
ADD_PROPERTY(PropertyInfo(Variant::INT, "instance_count", PROPERTY_HINT_RANGE, "0,16384,1,or_greater"), "set_instance_count", "get_instance_count"); ADD_PROPERTY(PropertyInfo(Variant::INT, "instance_count", PROPERTY_HINT_RANGE, "0,16384,1,or_greater"), "set_instance_count", "get_instance_count");
ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_instance_count", PROPERTY_HINT_RANGE, "-1,16384,1,or_greater"), "set_visible_instance_count", "get_visible_instance_count"); ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_instance_count", PROPERTY_HINT_RANGE, "-1,16384,1,or_greater"), "set_visible_instance_count", "get_visible_instance_count");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");

View File

@ -48,6 +48,7 @@ private:
Ref<Mesh> mesh; Ref<Mesh> mesh;
RID multimesh; RID multimesh;
TransformFormat transform_format = TRANSFORM_2D; TransformFormat transform_format = TRANSFORM_2D;
AABB custom_aabb;
bool use_colors = false; bool use_colors = false;
bool use_custom_data = false; bool use_custom_data = false;
int instance_count = 0; int instance_count = 0;
@ -103,6 +104,9 @@ public:
void set_instance_custom_data(int p_instance, const Color &p_custom_data); void set_instance_custom_data(int p_instance, const Color &p_custom_data);
Color get_instance_custom_data(int p_instance) const; Color get_instance_custom_data(int p_instance) const;
void set_custom_aabb(const AABB &p_custom);
AABB get_custom_aabb() const;
virtual AABB get_aabb() const; virtual AABB get_aabb() const;
virtual RID get_rid() const override; virtual RID get_rid() const override;

View File

@ -153,6 +153,9 @@ public:
virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override {} virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override {}
virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override {} virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override {}
virtual void multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override {}
virtual AABB multimesh_get_custom_aabb(RID p_multimesh) const override { return AABB(); }
virtual RID multimesh_get_mesh(RID p_multimesh) const override { return RID(); } virtual RID multimesh_get_mesh(RID p_multimesh) const override { return RID(); }
virtual AABB multimesh_get_aabb(RID p_multimesh) const override { return AABB(); } virtual AABB multimesh_get_aabb(RID p_multimesh) const override { return AABB(); }

View File

@ -1660,6 +1660,9 @@ void MeshStorage::_multimesh_mark_all_dirty(MultiMesh *multimesh, bool p_data, b
void MeshStorage::_multimesh_re_create_aabb(MultiMesh *multimesh, const float *p_data, int p_instances) { void MeshStorage::_multimesh_re_create_aabb(MultiMesh *multimesh, const float *p_data, int p_instances) {
ERR_FAIL_COND(multimesh->mesh.is_null()); ERR_FAIL_COND(multimesh->mesh.is_null());
if (multimesh->custom_aabb != AABB()) {
return;
}
AABB aabb; AABB aabb;
AABB mesh_aabb = mesh_get_aabb(multimesh->mesh); AABB mesh_aabb = mesh_get_aabb(multimesh->mesh);
for (int i = 0; i < p_instances; i++) { for (int i = 0; i < p_instances; i++) {
@ -1960,8 +1963,10 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b
//if we have a mesh set, we need to re-generate the AABB from the new data //if we have a mesh set, we need to re-generate the AABB from the new data
const float *data = p_buffer.ptr(); const float *data = p_buffer.ptr();
_multimesh_re_create_aabb(multimesh, data, multimesh->instances); if (multimesh->custom_aabb != AABB()) {
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); _multimesh_re_create_aabb(multimesh, data, multimesh->instances);
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
}
} }
} }
@ -2015,9 +2020,26 @@ int MeshStorage::multimesh_get_visible_instances(RID p_multimesh) const {
return multimesh->visible_instances; return multimesh->visible_instances;
} }
void MeshStorage::multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
multimesh->custom_aabb = p_aabb;
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
}
AABB MeshStorage::multimesh_get_custom_aabb(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, AABB());
return multimesh->custom_aabb;
}
AABB MeshStorage::multimesh_get_aabb(RID p_multimesh) const { AABB MeshStorage::multimesh_get_aabb(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, AABB()); ERR_FAIL_NULL_V(multimesh, AABB());
if (multimesh->custom_aabb != AABB()) {
return multimesh->custom_aabb;
}
if (multimesh->aabb_dirty) { if (multimesh->aabb_dirty) {
const_cast<MeshStorage *>(this)->_update_dirty_multimeshes(); const_cast<MeshStorage *>(this)->_update_dirty_multimeshes();
} }
@ -2064,9 +2086,11 @@ void MeshStorage::_update_dirty_multimeshes() {
if (multimesh->aabb_dirty) { if (multimesh->aabb_dirty) {
//aabb is dirty.. //aabb is dirty..
_multimesh_re_create_aabb(multimesh, data, visible_instances);
multimesh->aabb_dirty = false; multimesh->aabb_dirty = false;
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); if (multimesh->custom_aabb != AABB()) {
_multimesh_re_create_aabb(multimesh, data, visible_instances);
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
}
} }
} }

View File

@ -220,6 +220,7 @@ private:
bool uses_custom_data = false; bool uses_custom_data = false;
int visible_instances = -1; int visible_instances = -1;
AABB aabb; AABB aabb;
AABB custom_aabb;
bool aabb_dirty = false; bool aabb_dirty = false;
bool buffer_set = false; bool buffer_set = false;
bool motion_vectors_enabled = false; bool motion_vectors_enabled = false;
@ -646,6 +647,9 @@ public:
virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible) override; virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible) override;
virtual int multimesh_get_visible_instances(RID p_multimesh) const override; virtual int multimesh_get_visible_instances(RID p_multimesh) const override;
virtual void multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override;
virtual AABB multimesh_get_custom_aabb(RID p_multimesh) const override;
virtual AABB multimesh_get_aabb(RID p_multimesh) const override; virtual AABB multimesh_get_aabb(RID p_multimesh) const override;
void _update_dirty_multimeshes(); void _update_dirty_multimeshes();

View File

@ -335,6 +335,9 @@ public:
FUNC3(multimesh_instance_set_color, RID, int, const Color &) FUNC3(multimesh_instance_set_color, RID, int, const Color &)
FUNC3(multimesh_instance_set_custom_data, RID, int, const Color &) FUNC3(multimesh_instance_set_custom_data, RID, int, const Color &)
FUNC2(multimesh_set_custom_aabb, RID, const AABB &)
FUNC1RC(AABB, multimesh_get_custom_aabb, RID)
FUNC1RC(RID, multimesh_get_mesh, RID) FUNC1RC(RID, multimesh_get_mesh, RID)
FUNC1RC(AABB, multimesh_get_aabb, RID) FUNC1RC(AABB, multimesh_get_aabb, RID)

View File

@ -104,6 +104,9 @@ public:
virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) = 0; virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) = 0;
virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) = 0; virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) = 0;
virtual void multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) = 0;
virtual AABB multimesh_get_custom_aabb(RID p_multimesh) const = 0;
virtual RID multimesh_get_mesh(RID p_multimesh) const = 0; virtual RID multimesh_get_mesh(RID p_multimesh) const = 0;
virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const = 0; virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const = 0;

View File

@ -2427,6 +2427,8 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("multimesh_instance_set_custom_data", "multimesh", "index", "custom_data"), &RenderingServer::multimesh_instance_set_custom_data); ClassDB::bind_method(D_METHOD("multimesh_instance_set_custom_data", "multimesh", "index", "custom_data"), &RenderingServer::multimesh_instance_set_custom_data);
ClassDB::bind_method(D_METHOD("multimesh_get_mesh", "multimesh"), &RenderingServer::multimesh_get_mesh); ClassDB::bind_method(D_METHOD("multimesh_get_mesh", "multimesh"), &RenderingServer::multimesh_get_mesh);
ClassDB::bind_method(D_METHOD("multimesh_get_aabb", "multimesh"), &RenderingServer::multimesh_get_aabb); ClassDB::bind_method(D_METHOD("multimesh_get_aabb", "multimesh"), &RenderingServer::multimesh_get_aabb);
ClassDB::bind_method(D_METHOD("multimesh_set_custom_aabb", "multimesh", "aabb"), &RenderingServer::multimesh_set_custom_aabb);
ClassDB::bind_method(D_METHOD("multimesh_get_custom_aabb", "multimesh"), &RenderingServer::multimesh_get_custom_aabb);
ClassDB::bind_method(D_METHOD("multimesh_instance_get_transform", "multimesh", "index"), &RenderingServer::multimesh_instance_get_transform); ClassDB::bind_method(D_METHOD("multimesh_instance_get_transform", "multimesh", "index"), &RenderingServer::multimesh_instance_get_transform);
ClassDB::bind_method(D_METHOD("multimesh_instance_get_transform_2d", "multimesh", "index"), &RenderingServer::multimesh_instance_get_transform_2d); ClassDB::bind_method(D_METHOD("multimesh_instance_get_transform_2d", "multimesh", "index"), &RenderingServer::multimesh_instance_get_transform_2d);
ClassDB::bind_method(D_METHOD("multimesh_instance_get_color", "multimesh", "index"), &RenderingServer::multimesh_instance_get_color); ClassDB::bind_method(D_METHOD("multimesh_instance_get_color", "multimesh", "index"), &RenderingServer::multimesh_instance_get_color);

View File

@ -413,6 +413,9 @@ public:
virtual RID multimesh_get_mesh(RID p_multimesh) const = 0; virtual RID multimesh_get_mesh(RID p_multimesh) const = 0;
virtual AABB multimesh_get_aabb(RID p_multimesh) const = 0; virtual AABB multimesh_get_aabb(RID p_multimesh) const = 0;
virtual void multimesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) = 0;
virtual AABB multimesh_get_custom_aabb(RID p_mesh) const = 0;
virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const = 0; virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const = 0;
virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const = 0; virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const = 0;
virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const = 0; virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const = 0;