Fixed Timestep Interpolation: MultiMesh

Adds fixed timestep interpolation to multimeshes.

Co-authored-by: lawnjelly <lawnjelly@gmail.com>
This commit is contained in:
Ricardo Buring 2024-05-11 01:22:50 +02:00
parent a7598679cf
commit 1728f80e7c
19 changed files with 901 additions and 141 deletions

View File

@ -843,6 +843,6 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
_physics_interpolation_warning(FUNCTION_STR, __FILE__, __LINE__, m_object_id, m_string)
#define PHYSICS_INTERPOLATION_WARNING(m_string) \
_physics_interpolation_warning(FUNCTION_STR, __FILE__, __LINE__, UINT64_MAX, m_string)
_physics_interpolation_warning(FUNCTION_STR, __FILE__, __LINE__, ObjectID(UINT64_MAX), m_string)
#endif // ERROR_MACROS_H

View File

@ -51,6 +51,24 @@
Returns the [Transform2D] of a specific instance.
</description>
</method>
<method name="reset_instance_physics_interpolation">
<return type="void" />
<param index="0" name="instance" type="int" />
<description>
When using [i]physics interpolation[/i], this function allows you to prevent interpolation on an instance in the current physics tick.
This allows you to move instances instantaneously, and should usually be used when initially placing an instance such as a bullet to prevent graphical glitches.
</description>
</method>
<method name="set_buffer_interpolated">
<return type="void" />
<param index="0" name="buffer_curr" type="PackedFloat32Array" />
<param index="1" name="buffer_prev" type="PackedFloat32Array" />
<description>
An alternative to setting the [member buffer] property, which can be used with [i]physics interpolation[/i]. This method takes two arrays, and can set the data for the current and previous tick in one go. The renderer will automatically interpolate the data at each frame.
This is useful for situations where the order of instances may change from physics tick to tick, such as particle systems.
When the order of instances is coherent, the simpler alternative of setting [member buffer] can still be used with interpolation.
</description>
</method>
<method name="set_instance_color">
<return type="void" />
<param index="0" name="instance" type="int" />
@ -109,6 +127,11 @@
[Mesh] resource to be instanced.
The looks of the individual instances can be modified using [method set_instance_color] and [method set_instance_custom_data].
</member>
<member name="physics_interpolation_quality" type="int" setter="set_physics_interpolation_quality" getter="get_physics_interpolation_quality" enum="MultiMesh.PhysicsInterpolationQuality" default="0">
Choose whether to use an interpolation method that favors speed or quality.
When using low physics tick rates (typically below 20) or high rates of object rotation, you may get better results from the high quality setting.
[b]Note:[/b] Fast quality does not equate to low quality. Except in the special cases mentioned above, the quality should be comparable to high quality.
</member>
<member name="transform_2d_array" type="PackedVector2Array" setter="_set_transform_2d_array" getter="_get_transform_2d_array" deprecated="Accessing this property is very slow. Use [method set_instance_transform_2d] and [method get_instance_transform_2d] instead.">
Array containing each [Transform2D] value used by all instances of this mesh, as a [PackedVector2Array]. Each transform is divided into 3 [Vector2] values corresponding to the transforms' [code]x[/code], [code]y[/code], and [code]origin[/code].
</member>
@ -135,5 +158,11 @@
<constant name="TRANSFORM_3D" value="1" enum="TransformFormat">
Use this when using 3D transforms.
</constant>
<constant name="INTERP_QUALITY_FAST" value="0" enum="PhysicsInterpolationQuality">
Always interpolate using Basis lerping, which can produce warping artifacts in some situations.
</constant>
<constant name="INTERP_QUALITY_HIGH" value="1" enum="PhysicsInterpolationQuality">
Attempt to interpolate using Basis slerping (spherical linear interpolation) where possible, otherwise fall back to lerping.
</constant>
</constants>
</class>

View File

@ -2558,6 +2558,15 @@
Returns the [Transform2D] of the specified instance. For use when the multimesh is set to use 2D transforms.
</description>
</method>
<method name="multimesh_instance_reset_physics_interpolation">
<return type="void" />
<param index="0" name="multimesh" type="RID" />
<param index="1" name="index" type="int" />
<description>
Prevents physics interpolation for the specified instance during the current physics tick.
This is useful when moving an instance to a new location, to give an instantaneous change rather than interpolation from the previous location.
</description>
</method>
<method name="multimesh_instance_set_color">
<return type="void" />
<param index="0" name="multimesh" type="RID" />
@ -2615,6 +2624,16 @@
[/codeblock]
</description>
</method>
<method name="multimesh_set_buffer_interpolated">
<return type="void" />
<param index="0" name="multimesh" type="RID" />
<param index="1" name="buffer" type="PackedFloat32Array" />
<param index="2" name="buffer_previous" type="PackedFloat32Array" />
<description>
Alternative version of [method multimesh_set_buffer] for use with physics interpolation.
Takes both an array of current data and an array of data for the previous physics tick.
</description>
</method>
<method name="multimesh_set_custom_aabb">
<return type="void" />
<param index="0" name="multimesh" type="RID" />
@ -2631,6 +2650,23 @@
Sets the mesh to be drawn by the multimesh. Equivalent to [member MultiMesh.mesh].
</description>
</method>
<method name="multimesh_set_physics_interpolated">
<return type="void" />
<param index="0" name="multimesh" type="RID" />
<param index="1" name="interpolated" type="bool" />
<description>
Turns on and off physics interpolation for this MultiMesh resource.
</description>
</method>
<method name="multimesh_set_physics_interpolation_quality">
<return type="void" />
<param index="0" name="multimesh" type="RID" />
<param index="1" name="quality" type="int" enum="RenderingServer.MultimeshPhysicsInterpolationQuality" />
<description>
Sets the physics interpolation quality for the [MultiMesh].
A value of [constant MULTIMESH_INTERP_QUALITY_FAST] gives fast but low quality interpolation, a value of [constant MULTIMESH_INTERP_QUALITY_HIGH] gives slower but higher quality interpolation.
</description>
</method>
<method name="multimesh_set_visible_instances">
<return type="void" />
<param index="0" name="multimesh" type="RID" />
@ -4500,6 +4536,12 @@
<constant name="MULTIMESH_TRANSFORM_3D" value="1" enum="MultimeshTransformFormat">
Use [Transform3D] to store MultiMesh transform.
</constant>
<constant name="MULTIMESH_INTERP_QUALITY_FAST" value="0" enum="MultimeshPhysicsInterpolationQuality">
MultiMesh physics interpolation favors speed over quality.
</constant>
<constant name="MULTIMESH_INTERP_QUALITY_HIGH" value="1" enum="MultimeshPhysicsInterpolationQuality">
MultiMesh physics interpolation favors quality over speed.
</constant>
<constant name="LIGHT_PROJECTOR_FILTER_NEAREST" value="0" enum="LightProjectorFilter">
Nearest-neighbor filter for light projectors (use for pixel art light projectors). No mipmaps are used for rendering, which means light projectors at a distance will look sharp but grainy. This has roughly the same performance cost as using mipmaps.
</constant>

View File

@ -1432,15 +1432,17 @@ void MeshStorage::update_mesh_instances() {
/* MULTIMESH API */
RID MeshStorage::multimesh_allocate() {
RID MeshStorage::_multimesh_allocate() {
return multimesh_owner.allocate_rid();
}
void MeshStorage::multimesh_initialize(RID p_rid) {
void MeshStorage::_multimesh_initialize(RID p_rid) {
multimesh_owner.initialize_rid(p_rid, MultiMesh());
}
void MeshStorage::multimesh_free(RID p_rid) {
void MeshStorage::_multimesh_free(RID p_rid) {
// Remove from interpolator.
_interpolation_data.notify_free_multimesh(p_rid);
_update_dirty_multimeshes();
multimesh_allocate_data(p_rid, 0, RS::MULTIMESH_TRANSFORM_2D);
MultiMesh *multimesh = multimesh_owner.get_or_null(p_rid);
@ -1448,7 +1450,7 @@ void MeshStorage::multimesh_free(RID p_rid) {
multimesh_owner.free(p_rid);
}
void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) {
void MeshStorage::_multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
@ -1495,13 +1497,13 @@ void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH);
}
int MeshStorage::multimesh_get_instance_count(RID p_multimesh) const {
int MeshStorage::_multimesh_get_instance_count(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, 0);
return multimesh->instances;
}
void MeshStorage::multimesh_set_mesh(RID p_multimesh, RID p_mesh) {
void MeshStorage::_multimesh_set_mesh(RID p_multimesh, RID p_mesh) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
if (multimesh->mesh == p_mesh || p_mesh.is_null()) {
@ -1651,7 +1653,7 @@ void MeshStorage::_multimesh_re_create_aabb(MultiMesh *multimesh, const float *p
multimesh->aabb = aabb;
}
void MeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) {
void MeshStorage::_multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
ERR_FAIL_INDEX(p_index, multimesh->instances);
@ -1681,7 +1683,7 @@ void MeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index,
_multimesh_mark_dirty(multimesh, p_index, true);
}
void MeshStorage::multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) {
void MeshStorage::_multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
ERR_FAIL_INDEX(p_index, multimesh->instances);
@ -1707,7 +1709,7 @@ void MeshStorage::multimesh_instance_set_transform_2d(RID p_multimesh, int p_ind
_multimesh_mark_dirty(multimesh, p_index, true);
}
void MeshStorage::multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) {
void MeshStorage::_multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
ERR_FAIL_INDEX(p_index, multimesh->instances);
@ -1727,7 +1729,7 @@ void MeshStorage::multimesh_instance_set_color(RID p_multimesh, int p_index, con
_multimesh_mark_dirty(multimesh, p_index, false);
}
void MeshStorage::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) {
void MeshStorage::_multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
ERR_FAIL_INDEX(p_index, multimesh->instances);
@ -1746,27 +1748,27 @@ void MeshStorage::multimesh_instance_set_custom_data(RID p_multimesh, int p_inde
_multimesh_mark_dirty(multimesh, p_index, false);
}
RID MeshStorage::multimesh_get_mesh(RID p_multimesh) const {
RID MeshStorage::_multimesh_get_mesh(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, RID());
return multimesh->mesh;
}
void MeshStorage::multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) {
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 {
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);
ERR_FAIL_NULL_V(multimesh, AABB());
if (multimesh->custom_aabb != AABB()) {
@ -1778,7 +1780,7 @@ AABB MeshStorage::multimesh_get_aabb(RID p_multimesh) const {
return multimesh->aabb;
}
Transform3D MeshStorage::multimesh_instance_get_transform(RID p_multimesh, int p_index) const {
Transform3D MeshStorage::_multimesh_instance_get_transform(RID p_multimesh, int p_index) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, Transform3D());
ERR_FAIL_INDEX_V(p_index, multimesh->instances, Transform3D());
@ -1809,7 +1811,7 @@ Transform3D MeshStorage::multimesh_instance_get_transform(RID p_multimesh, int p
return t;
}
Transform2D MeshStorage::multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const {
Transform2D MeshStorage::_multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, Transform2D());
ERR_FAIL_INDEX_V(p_index, multimesh->instances, Transform2D());
@ -1834,7 +1836,7 @@ Transform2D MeshStorage::multimesh_instance_get_transform_2d(RID p_multimesh, in
return t;
}
Color MeshStorage::multimesh_instance_get_color(RID p_multimesh, int p_index) const {
Color MeshStorage::_multimesh_instance_get_color(RID p_multimesh, int p_index) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, Color());
ERR_FAIL_INDEX_V(p_index, multimesh->instances, Color());
@ -1858,7 +1860,7 @@ Color MeshStorage::multimesh_instance_get_color(RID p_multimesh, int p_index) co
return c;
}
Color MeshStorage::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const {
Color MeshStorage::_multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, Color());
ERR_FAIL_INDEX_V(p_index, multimesh->instances, Color());
@ -1882,7 +1884,7 @@ Color MeshStorage::multimesh_instance_get_custom_data(RID p_multimesh, int p_ind
return c;
}
void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) {
void MeshStorage::_multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
@ -1971,7 +1973,7 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b
}
}
Vector<float> MeshStorage::multimesh_get_buffer(RID p_multimesh) const {
Vector<float> MeshStorage::_multimesh_get_buffer(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, Vector<float>());
Vector<float> ret;
@ -2043,7 +2045,7 @@ Vector<float> MeshStorage::multimesh_get_buffer(RID p_multimesh) const {
}
}
void MeshStorage::multimesh_set_visible_instances(RID p_multimesh, int p_visible) {
void MeshStorage::_multimesh_set_visible_instances(RID p_multimesh, int p_visible) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
ERR_FAIL_COND(p_visible < -1 || p_visible > multimesh->instances);
@ -2065,12 +2067,19 @@ void MeshStorage::multimesh_set_visible_instances(RID p_multimesh, int p_visible
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES);
}
int MeshStorage::multimesh_get_visible_instances(RID p_multimesh) const {
int MeshStorage::_multimesh_get_visible_instances(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, 0);
return multimesh->visible_instances;
}
MeshStorage::MultiMeshInterpolator *MeshStorage::_multimesh_get_interpolator(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V_MSG(multimesh, nullptr, "Multimesh not found: " + itos(p_multimesh.get_id()));
return &multimesh->interpolator;
}
void MeshStorage::_update_dirty_multimeshes() {
while (multimesh_dirty_list) {
MultiMesh *multimesh = multimesh_dirty_list;

View File

@ -205,6 +205,8 @@ struct MultiMesh {
bool dirty = false;
MultiMesh *dirty_list = nullptr;
RendererMeshStorage::MultiMeshInterpolator interpolator;
Dependency dependency;
};
@ -493,32 +495,34 @@ public:
MultiMesh *get_multimesh(RID p_rid) { return multimesh_owner.get_or_null(p_rid); };
bool owns_multimesh(RID p_rid) { return multimesh_owner.owns(p_rid); };
virtual RID multimesh_allocate() override;
virtual void multimesh_initialize(RID p_rid) override;
virtual void multimesh_free(RID p_rid) override;
virtual void multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override;
virtual int multimesh_get_instance_count(RID p_multimesh) const override;
virtual RID _multimesh_allocate() override;
virtual void _multimesh_initialize(RID p_rid) override;
virtual void _multimesh_free(RID p_rid) override;
virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override;
virtual int _multimesh_get_instance_count(RID p_multimesh) const override;
virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh) override;
virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override;
virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) 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_set_mesh(RID p_multimesh, RID p_mesh) override;
virtual void _multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override;
virtual void _multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) 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 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 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 Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const override;
virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override;
virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const override;
virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override;
virtual void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override;
virtual Vector<float> multimesh_get_buffer(RID p_multimesh) const override;
virtual Transform3D _multimesh_instance_get_transform(RID p_multimesh, int p_index) const override;
virtual Transform2D _multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override;
virtual Color _multimesh_instance_get_color(RID p_multimesh, int p_index) const override;
virtual Color _multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override;
virtual void _multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override;
virtual Vector<float> _multimesh_get_buffer(RID p_multimesh) const 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 void _multimesh_set_visible_instances(RID p_multimesh, int p_visible) override;
virtual int _multimesh_get_visible_instances(RID p_multimesh) const override;
virtual MultiMeshInterpolator *_multimesh_get_interpolator(RID p_multimesh) const override;
void _update_dirty_multimeshes();

View File

@ -30,16 +30,35 @@
#include "multimesh_instance_3d.h"
void MultiMeshInstance3D::_refresh_interpolated() {
if (is_inside_tree() && multimesh.is_valid()) {
bool interpolated = is_physics_interpolated_and_enabled();
multimesh->set_physics_interpolated(interpolated);
}
}
void MultiMeshInstance3D::_physics_interpolated_changed() {
VisualInstance3D::_physics_interpolated_changed();
_refresh_interpolated();
}
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);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multimesh", PROPERTY_HINT_RESOURCE_TYPE, "MultiMesh"), "set_multimesh", "get_multimesh");
}
void MultiMeshInstance3D::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
_refresh_interpolated();
}
}
void MultiMeshInstance3D::set_multimesh(const Ref<MultiMesh> &p_multimesh) {
multimesh = p_multimesh;
if (multimesh.is_valid()) {
set_base(multimesh->get_rid());
_refresh_interpolated();
} else {
set_base(RID());
}

View File

@ -39,9 +39,12 @@ class MultiMeshInstance3D : public GeometryInstance3D {
Ref<MultiMesh> multimesh;
void _refresh_interpolated();
protected:
virtual void _physics_interpolated_changed() override;
static void _bind_methods();
// bind helpers
void _notification(int p_what);
public:
void set_multimesh(const Ref<MultiMesh> &p_multimesh);

View File

@ -202,6 +202,10 @@ Vector<float> MultiMesh::get_buffer() const {
return RS::get_singleton()->multimesh_get_buffer(multimesh);
}
void MultiMesh::set_buffer_interpolated(const Vector<float> &p_buffer_curr, const Vector<float> &p_buffer_prev) {
RS::get_singleton()->multimesh_set_buffer_interpolated(multimesh, p_buffer_curr, p_buffer_prev);
}
void MultiMesh::set_mesh(const Ref<Mesh> &p_mesh) {
mesh = p_mesh;
if (!mesh.is_null()) {
@ -236,6 +240,11 @@ int MultiMesh::get_visible_instance_count() const {
return visible_instance_count;
}
void MultiMesh::set_physics_interpolation_quality(PhysicsInterpolationQuality p_quality) {
_physics_interpolation_quality = p_quality;
RenderingServer::get_singleton()->multimesh_set_physics_interpolation_quality(multimesh, (RS::MultimeshPhysicsInterpolationQuality)p_quality);
}
void MultiMesh::set_instance_transform(int p_instance, const Transform3D &p_transform) {
RenderingServer::get_singleton()->multimesh_instance_set_transform(multimesh, p_instance, p_transform);
}
@ -269,6 +278,14 @@ Color MultiMesh::get_instance_custom_data(int p_instance) const {
return RenderingServer::get_singleton()->multimesh_instance_get_custom_data(multimesh, p_instance);
}
void MultiMesh::reset_instance_physics_interpolation(int p_instance) {
RenderingServer::get_singleton()->multimesh_instance_reset_physics_interpolation(multimesh, p_instance);
}
void MultiMesh::set_physics_interpolated(bool p_interpolated) {
RenderingServer::get_singleton()->multimesh_set_physics_interpolated(multimesh, p_interpolated);
}
void MultiMesh::set_custom_aabb(const AABB &p_custom) {
custom_aabb = p_custom;
RS::get_singleton()->multimesh_set_custom_aabb(multimesh, custom_aabb);
@ -328,6 +345,8 @@ void MultiMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_instance_count"), &MultiMesh::get_instance_count);
ClassDB::bind_method(D_METHOD("set_visible_instance_count", "count"), &MultiMesh::set_visible_instance_count);
ClassDB::bind_method(D_METHOD("get_visible_instance_count"), &MultiMesh::get_visible_instance_count);
ClassDB::bind_method(D_METHOD("set_physics_interpolation_quality", "quality"), &MultiMesh::set_physics_interpolation_quality);
ClassDB::bind_method(D_METHOD("get_physics_interpolation_quality"), &MultiMesh::get_physics_interpolation_quality);
ClassDB::bind_method(D_METHOD("set_instance_transform", "instance", "transform"), &MultiMesh::set_instance_transform);
ClassDB::bind_method(D_METHOD("set_instance_transform_2d", "instance", "transform"), &MultiMesh::set_instance_transform_2d);
ClassDB::bind_method(D_METHOD("get_instance_transform", "instance"), &MultiMesh::get_instance_transform);
@ -336,6 +355,7 @@ void MultiMesh::_bind_methods() {
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("get_instance_custom_data", "instance"), &MultiMesh::get_instance_custom_data);
ClassDB::bind_method(D_METHOD("reset_instance_physics_interpolation", "instance"), &MultiMesh::reset_instance_physics_interpolation);
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);
@ -343,6 +363,8 @@ void MultiMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_buffer"), &MultiMesh::get_buffer);
ClassDB::bind_method(D_METHOD("set_buffer", "buffer"), &MultiMesh::set_buffer);
ClassDB::bind_method(D_METHOD("set_buffer_interpolated", "buffer_curr", "buffer_prev"), &MultiMesh::set_buffer_interpolated);
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_custom_data"), "set_use_custom_data", "is_using_custom_data");
@ -369,8 +391,14 @@ void MultiMesh::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "custom_data_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_custom_data_array", "_get_custom_data_array");
#endif
ADD_GROUP("Physics Interpolation", "physics_interpolation");
ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_interpolation_quality", PROPERTY_HINT_ENUM, "Fast,High"), "set_physics_interpolation_quality", "get_physics_interpolation_quality");
BIND_ENUM_CONSTANT(TRANSFORM_2D);
BIND_ENUM_CONSTANT(TRANSFORM_3D);
BIND_ENUM_CONSTANT(INTERP_QUALITY_FAST);
BIND_ENUM_CONSTANT(INTERP_QUALITY_HIGH);
}
MultiMesh::MultiMesh() {

View File

@ -44,6 +44,11 @@ public:
TRANSFORM_3D = RS::MULTIMESH_TRANSFORM_3D
};
enum PhysicsInterpolationQuality {
INTERP_QUALITY_FAST,
INTERP_QUALITY_HIGH,
};
private:
Ref<Mesh> mesh;
RID multimesh;
@ -53,6 +58,7 @@ private:
bool use_custom_data = false;
int instance_count = 0;
int visible_instance_count = -1;
PhysicsInterpolationQuality _physics_interpolation_quality = INTERP_QUALITY_FAST;
protected:
static void _bind_methods();
@ -74,6 +80,8 @@ protected:
void set_buffer(const Vector<float> &p_buffer);
Vector<float> get_buffer() const;
void set_buffer_interpolated(const Vector<float> &p_buffer_curr, const Vector<float> &p_buffer_prev);
public:
void set_mesh(const Ref<Mesh> &p_mesh);
Ref<Mesh> get_mesh() const;
@ -93,6 +101,9 @@ public:
void set_visible_instance_count(int p_count);
int get_visible_instance_count() const;
void set_physics_interpolation_quality(PhysicsInterpolationQuality p_quality);
PhysicsInterpolationQuality get_physics_interpolation_quality() const { return _physics_interpolation_quality; }
void set_instance_transform(int p_instance, const Transform3D &p_transform);
void set_instance_transform_2d(int p_instance, const Transform2D &p_transform);
Transform3D get_instance_transform(int p_instance) const;
@ -104,6 +115,10 @@ public:
void set_instance_custom_data(int p_instance, const Color &p_custom_data);
Color get_instance_custom_data(int p_instance) const;
void reset_instance_physics_interpolation(int p_instance);
void set_physics_interpolated(bool p_interpolated);
void set_custom_aabb(const AABB &p_custom);
AABB get_custom_aabb() const;
@ -116,5 +131,6 @@ public:
};
VARIANT_ENUM_CAST(MultiMesh::TransformFormat);
VARIANT_ENUM_CAST(MultiMesh::PhysicsInterpolationQuality);
#endif // MULTIMESH_H

View File

@ -64,22 +64,22 @@ void MeshStorage::mesh_clear(RID p_mesh) {
m->surfaces.clear();
}
RID MeshStorage::multimesh_allocate() {
RID MeshStorage::_multimesh_allocate() {
return multimesh_owner.allocate_rid();
}
void MeshStorage::multimesh_initialize(RID p_rid) {
void MeshStorage::_multimesh_initialize(RID p_rid) {
multimesh_owner.initialize_rid(p_rid, DummyMultiMesh());
}
void MeshStorage::multimesh_free(RID p_rid) {
void MeshStorage::_multimesh_free(RID p_rid) {
DummyMultiMesh *multimesh = multimesh_owner.get_or_null(p_rid);
ERR_FAIL_NULL(multimesh);
multimesh_owner.free(p_rid);
}
void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) {
void MeshStorage::_multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) {
DummyMultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
multimesh->buffer.resize(p_buffer.size());
@ -87,7 +87,7 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b
memcpy(cache_data, p_buffer.ptr(), p_buffer.size() * sizeof(float));
}
Vector<float> MeshStorage::multimesh_get_buffer(RID p_multimesh) const {
Vector<float> MeshStorage::_multimesh_get_buffer(RID p_multimesh) const {
DummyMultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, Vector<float>());

View File

@ -146,34 +146,36 @@ public:
bool owns_multimesh(RID p_rid) { return multimesh_owner.owns(p_rid); }
virtual RID multimesh_allocate() override;
virtual void multimesh_initialize(RID p_rid) override;
virtual void multimesh_free(RID p_rid) override;
virtual RID _multimesh_allocate() override;
virtual void _multimesh_initialize(RID p_rid) override;
virtual void _multimesh_free(RID p_rid) override;
virtual void multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override {}
virtual int multimesh_get_instance_count(RID p_multimesh) const override { return 0; }
virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override {}
virtual int _multimesh_get_instance_count(RID p_multimesh) const override { return 0; }
virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh) override {}
virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override {}
virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) 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_set_mesh(RID p_multimesh, RID p_mesh) override {}
virtual void _multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override {}
virtual void _multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) 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_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 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 AABB multimesh_get_aabb(RID p_multimesh) const override { return AABB(); }
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 Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const override { return Transform3D(); }
virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override { return Transform2D(); }
virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const override { return Color(); }
virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override { return Color(); }
virtual void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override;
virtual Vector<float> multimesh_get_buffer(RID p_multimesh) const override;
virtual Transform3D _multimesh_instance_get_transform(RID p_multimesh, int p_index) const override { return Transform3D(); }
virtual Transform2D _multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override { return Transform2D(); }
virtual Color _multimesh_instance_get_color(RID p_multimesh, int p_index) const override { return Color(); }
virtual Color _multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override { return Color(); }
virtual void _multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override;
virtual Vector<float> _multimesh_get_buffer(RID p_multimesh) const 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 { return 0; }
virtual void _multimesh_set_visible_instances(RID p_multimesh, int p_visible) override {}
virtual int _multimesh_get_visible_instances(RID p_multimesh) const override { return 0; }
MultiMeshInterpolator *_multimesh_get_interpolator(RID p_multimesh) const override { return nullptr; }
/* SKELETON API */

View File

@ -1380,14 +1380,16 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
////////////////// MULTIMESH
RID MeshStorage::multimesh_allocate() {
RID MeshStorage::_multimesh_allocate() {
return multimesh_owner.allocate_rid();
}
void MeshStorage::multimesh_initialize(RID p_rid) {
void MeshStorage::_multimesh_initialize(RID p_rid) {
multimesh_owner.initialize_rid(p_rid, MultiMesh());
}
void MeshStorage::multimesh_free(RID p_rid) {
void MeshStorage::_multimesh_free(RID p_rid) {
// Remove from interpolator.
_interpolation_data.notify_free_multimesh(p_rid);
_update_dirty_multimeshes();
multimesh_allocate_data(p_rid, 0, RS::MULTIMESH_TRANSFORM_2D);
MultiMesh *multimesh = multimesh_owner.get_or_null(p_rid);
@ -1395,7 +1397,7 @@ void MeshStorage::multimesh_free(RID p_rid) {
multimesh_owner.free(p_rid);
}
void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) {
void MeshStorage::_multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
@ -1505,13 +1507,13 @@ bool MeshStorage::_multimesh_uses_motion_vectors_offsets(RID p_multimesh) {
return _multimesh_uses_motion_vectors(multimesh);
}
int MeshStorage::multimesh_get_instance_count(RID p_multimesh) const {
int MeshStorage::_multimesh_get_instance_count(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, 0);
return multimesh->instances;
}
void MeshStorage::multimesh_set_mesh(RID p_multimesh, RID p_mesh) {
void MeshStorage::_multimesh_set_mesh(RID p_multimesh, RID p_mesh) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
if (multimesh->mesh == p_mesh) {
@ -1701,7 +1703,7 @@ void MeshStorage::_multimesh_re_create_aabb(MultiMesh *multimesh, const float *p
multimesh->aabb = aabb;
}
void MeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) {
void MeshStorage::_multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
ERR_FAIL_INDEX(p_index, multimesh->instances);
@ -1738,7 +1740,7 @@ void MeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index,
_multimesh_mark_dirty(multimesh, p_index, true);
}
void MeshStorage::multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) {
void MeshStorage::_multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
ERR_FAIL_INDEX(p_index, multimesh->instances);
@ -1765,7 +1767,7 @@ void MeshStorage::multimesh_instance_set_transform_2d(RID p_multimesh, int p_ind
_multimesh_mark_dirty(multimesh, p_index, true);
}
void MeshStorage::multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) {
void MeshStorage::_multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
ERR_FAIL_INDEX(p_index, multimesh->instances);
@ -1788,7 +1790,7 @@ void MeshStorage::multimesh_instance_set_color(RID p_multimesh, int p_index, con
_multimesh_mark_dirty(multimesh, p_index, false);
}
void MeshStorage::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) {
void MeshStorage::_multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
ERR_FAIL_INDEX(p_index, multimesh->instances);
@ -1811,7 +1813,7 @@ void MeshStorage::multimesh_instance_set_custom_data(RID p_multimesh, int p_inde
_multimesh_mark_dirty(multimesh, p_index, false);
}
RID MeshStorage::multimesh_get_mesh(RID p_multimesh) const {
RID MeshStorage::_multimesh_get_mesh(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, RID());
@ -1825,7 +1827,7 @@ Dependency *MeshStorage::multimesh_get_dependency(RID p_multimesh) const {
return &multimesh->dependency;
}
Transform3D MeshStorage::multimesh_instance_get_transform(RID p_multimesh, int p_index) const {
Transform3D MeshStorage::_multimesh_instance_get_transform(RID p_multimesh, int p_index) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, Transform3D());
ERR_FAIL_INDEX_V(p_index, multimesh->instances, Transform3D());
@ -1856,7 +1858,7 @@ Transform3D MeshStorage::multimesh_instance_get_transform(RID p_multimesh, int p
return t;
}
Transform2D MeshStorage::multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const {
Transform2D MeshStorage::_multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, Transform2D());
ERR_FAIL_INDEX_V(p_index, multimesh->instances, Transform2D());
@ -1881,7 +1883,7 @@ Transform2D MeshStorage::multimesh_instance_get_transform_2d(RID p_multimesh, in
return t;
}
Color MeshStorage::multimesh_instance_get_color(RID p_multimesh, int p_index) const {
Color MeshStorage::_multimesh_instance_get_color(RID p_multimesh, int p_index) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, Color());
ERR_FAIL_INDEX_V(p_index, multimesh->instances, Color());
@ -1904,7 +1906,7 @@ Color MeshStorage::multimesh_instance_get_color(RID p_multimesh, int p_index) co
return c;
}
Color MeshStorage::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const {
Color MeshStorage::_multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, Color());
ERR_FAIL_INDEX_V(p_index, multimesh->instances, Color());
@ -1927,7 +1929,7 @@ Color MeshStorage::multimesh_instance_get_custom_data(RID p_multimesh, int p_ind
return c;
}
void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) {
void MeshStorage::_multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
ERR_FAIL_COND(p_buffer.size() != (multimesh->instances * (int)multimesh->stride_cache));
@ -1974,7 +1976,7 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b
}
}
Vector<float> MeshStorage::multimesh_get_buffer(RID p_multimesh) const {
Vector<float> MeshStorage::_multimesh_get_buffer(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, Vector<float>());
if (multimesh->buffer.is_null()) {
@ -1996,7 +1998,7 @@ Vector<float> MeshStorage::multimesh_get_buffer(RID p_multimesh) const {
}
}
void MeshStorage::multimesh_set_visible_instances(RID p_multimesh, int p_visible) {
void MeshStorage::_multimesh_set_visible_instances(RID p_multimesh, int p_visible) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
ERR_FAIL_COND(p_visible < -1 || p_visible > multimesh->instances);
@ -2018,26 +2020,26 @@ void MeshStorage::multimesh_set_visible_instances(RID p_multimesh, int p_visible
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES);
}
int MeshStorage::multimesh_get_visible_instances(RID p_multimesh) const {
int MeshStorage::_multimesh_get_visible_instances(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, 0);
return multimesh->visible_instances;
}
void MeshStorage::multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) {
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 {
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);
ERR_FAIL_NULL_V(multimesh, AABB());
if (multimesh->custom_aabb != AABB()) {
@ -2050,6 +2052,13 @@ AABB MeshStorage::multimesh_get_aabb(RID p_multimesh) const {
return multimesh->aabb;
}
MeshStorage::MultiMeshInterpolator *MeshStorage::_multimesh_get_interpolator(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V_MSG(multimesh, nullptr, "Multimesh not found: " + itos(p_multimesh.get_id()));
return &multimesh->interpolator;
}
void MeshStorage::_update_dirty_multimeshes() {
while (multimesh_dirty_list) {
MultiMesh *multimesh = multimesh_dirty_list;

View File

@ -244,6 +244,8 @@ private:
bool dirty = false;
MultiMesh *dirty_list = nullptr;
RendererMeshStorage::MultiMeshInterpolator interpolator;
Dependency dependency;
};
@ -621,36 +623,38 @@ public:
bool owns_multimesh(RID p_rid) { return multimesh_owner.owns(p_rid); };
virtual RID multimesh_allocate() override;
virtual void multimesh_initialize(RID p_multimesh) override;
virtual void multimesh_free(RID p_rid) override;
virtual RID _multimesh_allocate() override;
virtual void _multimesh_initialize(RID p_multimesh) override;
virtual void _multimesh_free(RID p_rid) override;
virtual void multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override;
virtual int multimesh_get_instance_count(RID p_multimesh) const override;
virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override;
virtual int _multimesh_get_instance_count(RID p_multimesh) const override;
virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh) override;
virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override;
virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) 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_set_mesh(RID p_multimesh, RID p_mesh) override;
virtual void _multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override;
virtual void _multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) 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 RID multimesh_get_mesh(RID p_multimesh) const override;
virtual RID _multimesh_get_mesh(RID p_multimesh) const override;
virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const override;
virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override;
virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const override;
virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override;
virtual Transform3D _multimesh_instance_get_transform(RID p_multimesh, int p_index) const override;
virtual Transform2D _multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override;
virtual Color _multimesh_instance_get_color(RID p_multimesh, int p_index) const override;
virtual Color _multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override;
virtual void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override;
virtual Vector<float> multimesh_get_buffer(RID p_multimesh) const override;
virtual void _multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override;
virtual Vector<float> _multimesh_get_buffer(RID p_multimesh) const 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 void _multimesh_set_visible_instances(RID p_multimesh, int p_visible) 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 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 MultiMeshInterpolator *_multimesh_get_interpolator(RID p_multimesh) const override;
void _update_dirty_multimeshes();
void _multimesh_get_motion_vectors_offsets(RID p_multimesh, uint32_t &r_current_offset, uint32_t &r_prev_offset);

View File

@ -4392,7 +4392,8 @@ void RendererSceneCull::set_scene_render(RendererSceneRender *p_scene_render) {
/* INTERPOLATION API */
void RendererSceneCull::update_interpolation_tick(bool p_process) {
// TODO (MultiMesh): Update interpolation in storage.
// MultiMesh: Update interpolation in storage.
RSG::mesh_storage->update_interpolation_tick(p_process);
// INSTANCES
@ -4450,7 +4451,8 @@ void RendererSceneCull::update_interpolation_tick(bool p_process) {
}
void RendererSceneCull::update_interpolation_frame(bool p_process) {
// TODO (MultiMesh): Update interpolation in storage.
// MultiMesh: Update interpolation in storage.
RSG::mesh_storage->update_interpolation_frame(p_process);
if (p_process) {
real_t f = Engine::get_singleton()->get_physics_interpolation_fraction();

View File

@ -344,6 +344,11 @@ public:
FUNC2(multimesh_set_buffer, RID, const Vector<float> &)
FUNC1RC(Vector<float>, multimesh_get_buffer, RID)
FUNC3(multimesh_set_buffer_interpolated, RID, const Vector<float> &, const Vector<float> &)
FUNC2(multimesh_set_physics_interpolated, RID, bool)
FUNC2(multimesh_set_physics_interpolation_quality, RID, MultimeshPhysicsInterpolationQuality)
FUNC2(multimesh_instance_reset_physics_interpolation, RID, int)
FUNC2(multimesh_set_visible_instances, RID, int)
FUNC1RC(int, multimesh_get_visible_instances, RID)

View File

@ -0,0 +1,485 @@
/**************************************************************************/
/* mesh_storage.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "mesh_storage.h"
#include "core/math/transform_interpolator.h"
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
#include "core/config/project_settings.h"
#endif
RID RendererMeshStorage::multimesh_allocate() {
return _multimesh_allocate();
}
void RendererMeshStorage::multimesh_initialize(RID p_rid) {
_multimesh_initialize(p_rid);
}
void RendererMeshStorage::multimesh_free(RID p_rid) {
_multimesh_free(p_rid);
}
void RendererMeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) {
MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh);
if (mmi) {
mmi->_transform_format = p_transform_format;
mmi->_use_colors = p_use_colors;
mmi->_use_custom_data = p_use_custom_data;
mmi->_num_instances = p_instances;
mmi->_vf_size_xform = p_transform_format == RS::MULTIMESH_TRANSFORM_2D ? 8 : 12;
mmi->_vf_size_color = p_use_colors ? 4 : 0;
mmi->_vf_size_data = p_use_custom_data ? 4 : 0;
mmi->_stride = mmi->_vf_size_xform + mmi->_vf_size_color + mmi->_vf_size_data;
int size_in_floats = p_instances * mmi->_stride;
mmi->_data_curr.resize_zeroed(size_in_floats);
mmi->_data_prev.resize_zeroed(size_in_floats);
mmi->_data_interpolated.resize_zeroed(size_in_floats);
}
_multimesh_allocate_data(p_multimesh, p_instances, p_transform_format, p_use_colors, p_use_custom_data);
}
int RendererMeshStorage::multimesh_get_instance_count(RID p_multimesh) const {
return _multimesh_get_instance_count(p_multimesh);
}
void RendererMeshStorage::multimesh_set_mesh(RID p_multimesh, RID p_mesh) {
_multimesh_set_mesh(p_multimesh, p_mesh);
}
void RendererMeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) {
MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh);
if (mmi && mmi->interpolated) {
ERR_FAIL_COND(p_index >= mmi->_num_instances);
ERR_FAIL_COND(mmi->_vf_size_xform != 12);
int start = p_index * mmi->_stride;
float *ptr = mmi->_data_curr.ptrw();
ptr += start;
const Transform3D &t = p_transform;
ptr[0] = t.basis.rows[0][0];
ptr[1] = t.basis.rows[0][1];
ptr[2] = t.basis.rows[0][2];
ptr[3] = t.origin.x;
ptr[4] = t.basis.rows[1][0];
ptr[5] = t.basis.rows[1][1];
ptr[6] = t.basis.rows[1][2];
ptr[7] = t.origin.y;
ptr[8] = t.basis.rows[2][0];
ptr[9] = t.basis.rows[2][1];
ptr[10] = t.basis.rows[2][2];
ptr[11] = t.origin.z;
_multimesh_add_to_interpolation_lists(p_multimesh, *mmi);
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
if (!Engine::get_singleton()->is_in_physics_frame()) {
PHYSICS_INTERPOLATION_WARNING("MultiMesh interpolation is being triggered from outside physics process, this might lead to issues");
}
#endif
return;
}
_multimesh_instance_set_transform(p_multimesh, p_index, p_transform);
}
void RendererMeshStorage::multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) {
_multimesh_instance_set_transform_2d(p_multimesh, p_index, p_transform);
}
void RendererMeshStorage::multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) {
MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh);
if (mmi && mmi->interpolated) {
ERR_FAIL_COND(p_index >= mmi->_num_instances);
ERR_FAIL_COND(mmi->_vf_size_color == 0);
int start = (p_index * mmi->_stride) + mmi->_vf_size_xform;
float *ptr = mmi->_data_curr.ptrw();
ptr += start;
if (mmi->_vf_size_color == 4) {
for (int n = 0; n < 4; n++) {
ptr[n] = p_color.components[n];
}
} else {
#ifdef DEV_ENABLED
// The options are currently 4 or zero, but just in case this changes in future...
ERR_FAIL_COND(mmi->_vf_size_color != 0);
#endif
}
_multimesh_add_to_interpolation_lists(p_multimesh, *mmi);
return;
}
_multimesh_instance_set_color(p_multimesh, p_index, p_color);
}
void RendererMeshStorage::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) {
MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh);
if (mmi && mmi->interpolated) {
ERR_FAIL_COND(p_index >= mmi->_num_instances);
ERR_FAIL_COND(mmi->_vf_size_data == 0);
int start = (p_index * mmi->_stride) + mmi->_vf_size_xform + mmi->_vf_size_color;
float *ptr = mmi->_data_curr.ptrw();
ptr += start;
if (mmi->_vf_size_data == 4) {
for (int n = 0; n < 4; n++) {
ptr[n] = p_color.components[n];
}
} else {
#ifdef DEV_ENABLED
// The options are currently 4 or zero, but just in case this changes in future...
ERR_FAIL_COND(mmi->_vf_size_data != 0);
#endif
}
_multimesh_add_to_interpolation_lists(p_multimesh, *mmi);
return;
}
_multimesh_instance_set_custom_data(p_multimesh, p_index, p_color);
}
void RendererMeshStorage::multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) {
_multimesh_set_custom_aabb(p_multimesh, p_aabb);
}
AABB RendererMeshStorage::multimesh_get_custom_aabb(RID p_multimesh) const {
return _multimesh_get_custom_aabb(p_multimesh);
}
RID RendererMeshStorage::multimesh_get_mesh(RID p_multimesh) const {
return _multimesh_get_mesh(p_multimesh);
}
Transform3D RendererMeshStorage::multimesh_instance_get_transform(RID p_multimesh, int p_index) const {
return _multimesh_instance_get_transform(p_multimesh, p_index);
}
Transform2D RendererMeshStorage::multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const {
return _multimesh_instance_get_transform_2d(p_multimesh, p_index);
}
Color RendererMeshStorage::multimesh_instance_get_color(RID p_multimesh, int p_index) const {
return _multimesh_instance_get_color(p_multimesh, p_index);
}
Color RendererMeshStorage::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const {
return _multimesh_instance_get_custom_data(p_multimesh, p_index);
}
void RendererMeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) {
MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh);
if (mmi && mmi->interpolated) {
ERR_FAIL_COND_MSG(p_buffer.size() != mmi->_data_curr.size(), vformat("Buffer should have %d elements, got %d instead.", mmi->_data_curr.size(), p_buffer.size()));
mmi->_data_curr = p_buffer;
_multimesh_add_to_interpolation_lists(p_multimesh, *mmi);
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
if (!Engine::get_singleton()->is_in_physics_frame()) {
PHYSICS_INTERPOLATION_WARNING("MultiMesh interpolation is being triggered from outside physics process, this might lead to issues");
}
#endif
return;
}
_multimesh_set_buffer(p_multimesh, p_buffer);
}
Vector<float> RendererMeshStorage::multimesh_get_buffer(RID p_multimesh) const {
return _multimesh_get_buffer(p_multimesh);
}
void RendererMeshStorage::multimesh_set_buffer_interpolated(RID p_multimesh, const Vector<float> &p_buffer, const Vector<float> &p_buffer_prev) {
MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh);
if (mmi) {
ERR_FAIL_COND_MSG(p_buffer.size() != mmi->_data_curr.size(), vformat("Buffer for current frame should have %d elements, got %d instead.", mmi->_data_curr.size(), p_buffer.size()));
ERR_FAIL_COND_MSG(p_buffer_prev.size() != mmi->_data_prev.size(), vformat("Buffer for previous frame should have %d elements, got %d instead.", mmi->_data_prev.size(), p_buffer_prev.size()));
// We are assuming that mmi->interpolated is the case. (Can possibly assert this?)
// Even if this flag hasn't been set - just calling this function suggests interpolation is desired.
mmi->_data_prev = p_buffer_prev;
mmi->_data_curr = p_buffer;
_multimesh_add_to_interpolation_lists(p_multimesh, *mmi);
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
if (!Engine::get_singleton()->is_in_physics_frame()) {
PHYSICS_INTERPOLATION_WARNING("MultiMesh interpolation is being triggered from outside physics process, this might lead to issues");
}
#endif
}
}
void RendererMeshStorage::multimesh_set_physics_interpolated(RID p_multimesh, bool p_interpolated) {
MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh);
if (mmi) {
mmi->interpolated = p_interpolated;
}
}
void RendererMeshStorage::multimesh_set_physics_interpolation_quality(RID p_multimesh, RS::MultimeshPhysicsInterpolationQuality p_quality) {
ERR_FAIL_COND((p_quality < 0) || (p_quality > 1));
MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh);
if (mmi) {
mmi->quality = (int)p_quality;
}
}
void RendererMeshStorage::multimesh_instance_reset_physics_interpolation(RID p_multimesh, int p_index) {
MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh);
if (mmi) {
ERR_FAIL_INDEX(p_index, mmi->_num_instances);
float *w = mmi->_data_prev.ptrw();
const float *r = mmi->_data_curr.ptr();
int start = p_index * mmi->_stride;
for (int n = 0; n < mmi->_stride; n++) {
w[start + n] = r[start + n];
}
}
}
void RendererMeshStorage::multimesh_set_visible_instances(RID p_multimesh, int p_visible) {
return _multimesh_set_visible_instances(p_multimesh, p_visible);
}
int RendererMeshStorage::multimesh_get_visible_instances(RID p_multimesh) const {
return _multimesh_get_visible_instances(p_multimesh);
}
AABB RendererMeshStorage::multimesh_get_aabb(RID p_multimesh) const {
return _multimesh_get_aabb(p_multimesh);
}
void RendererMeshStorage::_multimesh_add_to_interpolation_lists(RID p_multimesh, MultiMeshInterpolator &r_mmi) {
if (!r_mmi.on_interpolate_update_list) {
r_mmi.on_interpolate_update_list = true;
_interpolation_data.multimesh_interpolate_update_list.push_back(p_multimesh);
}
if (!r_mmi.on_transform_update_list) {
r_mmi.on_transform_update_list = true;
_interpolation_data.multimesh_transform_update_list_curr->push_back(p_multimesh);
}
}
void RendererMeshStorage::InterpolationData::notify_free_multimesh(RID p_rid) {
// If the instance was on any of the lists, remove.
multimesh_interpolate_update_list.erase_multiple_unordered(p_rid);
multimesh_transform_update_lists[0].erase_multiple_unordered(p_rid);
multimesh_transform_update_lists[1].erase_multiple_unordered(p_rid);
}
void RendererMeshStorage::update_interpolation_tick(bool p_process) {
// Detect any that were on the previous transform list that are no longer active,
// we should remove them from the interpolate list.
for (unsigned int n = 0; n < _interpolation_data.multimesh_transform_update_list_prev->size(); n++) {
const RID &rid = (*_interpolation_data.multimesh_transform_update_list_prev)[n];
bool active = true;
// No longer active? (Either the instance deleted or no longer being transformed.)
MultiMeshInterpolator *mmi = _multimesh_get_interpolator(rid);
if (mmi && !mmi->on_transform_update_list) {
active = false;
mmi->on_interpolate_update_list = false;
// Make sure the most recent transform is set...
mmi->_data_interpolated = mmi->_data_curr; // TODO: Copy data rather than use Packed = function?
// ... and that both prev and current are the same, just in case of any interpolations.
mmi->_data_prev = mmi->_data_curr;
}
if (!mmi) {
active = false;
}
if (!active) {
_interpolation_data.multimesh_interpolate_update_list.erase(rid);
}
}
if (p_process) {
for (unsigned int i = 0; i < _interpolation_data.multimesh_transform_update_list_curr->size(); i++) {
const RID &rid = (*_interpolation_data.multimesh_transform_update_list_curr)[i];
MultiMeshInterpolator *mmi = _multimesh_get_interpolator(rid);
if (mmi) {
// Reset for next tick.
mmi->on_transform_update_list = false;
mmi->_data_prev = mmi->_data_curr;
}
}
}
// If any have left the transform list, remove from the interpolate list.
// We maintain a mirror list for the transform updates, so we can detect when an instance
// is no longer being transformed, and remove it from the interpolate list.
SWAP(_interpolation_data.multimesh_transform_update_list_curr, _interpolation_data.multimesh_transform_update_list_prev);
// Prepare for the next iteration.
_interpolation_data.multimesh_transform_update_list_curr->clear();
}
void RendererMeshStorage::update_interpolation_frame(bool p_process) {
if (p_process) {
// Only need 32 bits for interpolation, don't use real_t.
float f = Engine::get_singleton()->get_physics_interpolation_fraction();
for (unsigned int c = 0; c < _interpolation_data.multimesh_interpolate_update_list.size(); c++) {
const RID &rid = _interpolation_data.multimesh_interpolate_update_list[c];
// We could use the TransformInterpolator here to slerp transforms, but that might be too expensive,
// so just using a Basis lerp for now.
MultiMeshInterpolator *mmi = _multimesh_get_interpolator(rid);
if (mmi) {
// Make sure arrays are the correct size.
DEV_ASSERT(mmi->_data_prev.size() == mmi->_data_curr.size());
if (mmi->_data_interpolated.size() < mmi->_data_curr.size()) {
mmi->_data_interpolated.resize(mmi->_data_curr.size());
}
DEV_ASSERT(mmi->_data_interpolated.size() >= mmi->_data_curr.size());
DEV_ASSERT((mmi->_data_curr.size() % mmi->_stride) == 0);
int num = mmi->_data_curr.size() / mmi->_stride;
const float *pf_prev = mmi->_data_prev.ptr();
const float *pf_curr = mmi->_data_curr.ptr();
float *pf_int = mmi->_data_interpolated.ptrw();
bool use_lerp = mmi->quality == 0;
// Temporary transform (needed for swizzling).
Transform3D tp, tc, tr; // (transform prev, curr and result)
// Test for cache friendliness versus doing branchless.
for (int n = 0; n < num; n++) {
// Transform.
if (use_lerp) {
for (int i = 0; i < mmi->_vf_size_xform; i++) {
pf_int[i] = Math::lerp(pf_prev[i], pf_curr[i], f);
}
} else {
// Silly swizzling, this will slow things down.
// No idea why it is using this format...
// ... maybe due to the shader.
tp.basis.rows[0][0] = pf_prev[0];
tp.basis.rows[0][1] = pf_prev[1];
tp.basis.rows[0][2] = pf_prev[2];
tp.basis.rows[1][0] = pf_prev[4];
tp.basis.rows[1][1] = pf_prev[5];
tp.basis.rows[1][2] = pf_prev[6];
tp.basis.rows[2][0] = pf_prev[8];
tp.basis.rows[2][1] = pf_prev[9];
tp.basis.rows[2][2] = pf_prev[10];
tp.origin.x = pf_prev[3];
tp.origin.y = pf_prev[7];
tp.origin.z = pf_prev[11];
tc.basis.rows[0][0] = pf_curr[0];
tc.basis.rows[0][1] = pf_curr[1];
tc.basis.rows[0][2] = pf_curr[2];
tc.basis.rows[1][0] = pf_curr[4];
tc.basis.rows[1][1] = pf_curr[5];
tc.basis.rows[1][2] = pf_curr[6];
tc.basis.rows[2][0] = pf_curr[8];
tc.basis.rows[2][1] = pf_curr[9];
tc.basis.rows[2][2] = pf_curr[10];
tc.origin.x = pf_curr[3];
tc.origin.y = pf_curr[7];
tc.origin.z = pf_curr[11];
TransformInterpolator::interpolate_transform_3d(tp, tc, tr, f);
pf_int[0] = tr.basis.rows[0][0];
pf_int[1] = tr.basis.rows[0][1];
pf_int[2] = tr.basis.rows[0][2];
pf_int[4] = tr.basis.rows[1][0];
pf_int[5] = tr.basis.rows[1][1];
pf_int[6] = tr.basis.rows[1][2];
pf_int[8] = tr.basis.rows[2][0];
pf_int[9] = tr.basis.rows[2][1];
pf_int[10] = tr.basis.rows[2][2];
pf_int[3] = tr.origin.x;
pf_int[7] = tr.origin.y;
pf_int[11] = tr.origin.z;
}
pf_prev += mmi->_vf_size_xform;
pf_curr += mmi->_vf_size_xform;
pf_int += mmi->_vf_size_xform;
// Color.
if (mmi->_vf_size_color == 4) {
for (int i = 0; i < 4; i++) {
pf_int[i] = Math::lerp(pf_prev[i], pf_curr[i], f);
}
pf_prev += 4;
pf_curr += 4;
pf_int += 4;
}
// Custom data.
if (mmi->_vf_size_data == 4) {
for (int i = 0; i < 4; i++) {
pf_int[i] = Math::lerp(pf_prev[i], pf_curr[i], f);
}
pf_prev += 4;
pf_curr += 4;
pf_int += 4;
}
}
_multimesh_set_buffer(rid, mmi->_data_interpolated);
// TODO: Make sure AABBs are constantly up to date through the interpolation?
// NYI.
}
}
}
}

View File

@ -89,39 +89,110 @@ public:
virtual void update_mesh_instances() = 0;
/* MULTIMESH API */
struct MultiMeshInterpolator {
RS::MultimeshTransformFormat _transform_format = RS::MULTIMESH_TRANSFORM_3D;
bool _use_colors = false;
bool _use_custom_data = false;
virtual RID multimesh_allocate() = 0;
virtual void multimesh_initialize(RID p_rid) = 0;
virtual void multimesh_free(RID p_rid) = 0;
// The stride of the buffer in floats.
int _stride = 0;
virtual void multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) = 0;
// Vertex format sizes in floats.
int _vf_size_xform = 0;
int _vf_size_color = 0;
int _vf_size_data = 0;
virtual int multimesh_get_instance_count(RID p_multimesh) const = 0;
// Set by allocate, can be used to prevent indexing out of range.
int _num_instances = 0;
virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh) = 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;
virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) = 0;
// Quality determines whether to use lerp or slerp etc.
int quality = 0;
bool interpolated = false;
bool on_interpolate_update_list = false;
bool on_transform_update_list = false;
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;
Vector<float> _data_prev;
Vector<float> _data_curr;
Vector<float> _data_interpolated;
};
virtual RID multimesh_get_mesh(RID p_multimesh) const = 0;
virtual RID multimesh_allocate();
virtual void multimesh_initialize(RID p_rid);
virtual void multimesh_free(RID p_rid);
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 Color multimesh_instance_get_color(RID p_multimesh, int p_index) const = 0;
virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const = 0;
virtual void multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false);
virtual void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) = 0;
virtual Vector<float> multimesh_get_buffer(RID p_multimesh) const = 0;
virtual int multimesh_get_instance_count(RID p_multimesh) const;
virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible) = 0;
virtual int multimesh_get_visible_instances(RID p_multimesh) const = 0;
virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh);
virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform);
virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform);
virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color);
virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color);
virtual AABB multimesh_get_aabb(RID p_multimesh) const = 0;
virtual void multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb);
virtual AABB multimesh_get_custom_aabb(RID p_multimesh) const;
virtual RID multimesh_get_mesh(RID p_multimesh) const;
virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const;
virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const;
virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const;
virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const;
virtual void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer);
virtual Vector<float> multimesh_get_buffer(RID p_multimesh) const;
virtual void multimesh_set_buffer_interpolated(RID p_multimesh, const Vector<float> &p_buffer, const Vector<float> &p_buffer_prev);
virtual void multimesh_set_physics_interpolated(RID p_multimesh, bool p_interpolated);
virtual void multimesh_set_physics_interpolation_quality(RID p_multimesh, RS::MultimeshPhysicsInterpolationQuality p_quality);
virtual void multimesh_instance_reset_physics_interpolation(RID p_multimesh, int p_index);
virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible);
virtual int multimesh_get_visible_instances(RID p_multimesh) const;
virtual AABB multimesh_get_aabb(RID p_multimesh) const;
virtual RID _multimesh_allocate() = 0;
virtual void _multimesh_initialize(RID p_rid) = 0;
virtual void _multimesh_free(RID p_rid) = 0;
virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) = 0;
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_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;
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 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 Color _multimesh_instance_get_color(RID p_multimesh, int p_index) const = 0;
virtual Color _multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const = 0;
virtual void _multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) = 0;
virtual Vector<float> _multimesh_get_buffer(RID p_multimesh) const = 0;
virtual void _multimesh_set_visible_instances(RID p_multimesh, int p_visible) = 0;
virtual int _multimesh_get_visible_instances(RID p_multimesh) const = 0;
virtual AABB _multimesh_get_aabb(RID p_multimesh) const = 0;
// Multimesh is responsible for allocating / destroying a MultiMeshInterpolator object.
// This allows shared functionality for interpolation across backends.
virtual MultiMeshInterpolator *_multimesh_get_interpolator(RID p_multimesh) const = 0;
private:
void _multimesh_add_to_interpolation_lists(RID p_multimesh, MultiMeshInterpolator &r_mmi);
public:
/* SKELETON API */
virtual RID skeleton_allocate() = 0;
@ -137,6 +208,19 @@ public:
virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) = 0;
virtual void skeleton_update_dependency(RID p_base, DependencyTracker *p_instance) = 0;
/* INTERPOLATION */
struct InterpolationData {
void notify_free_multimesh(RID p_rid);
LocalVector<RID> multimesh_interpolate_update_list;
LocalVector<RID> multimesh_transform_update_lists[2];
LocalVector<RID> *multimesh_transform_update_list_curr = &multimesh_transform_update_lists[0];
LocalVector<RID> *multimesh_transform_update_list_prev = &multimesh_transform_update_lists[1];
} _interpolation_data;
void update_interpolation_tick(bool p_process = true);
void update_interpolation_frame(bool p_process = true);
};
#endif // MESH_STORAGE_H

View File

@ -2439,8 +2439,15 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("multimesh_set_buffer", "multimesh", "buffer"), &RenderingServer::multimesh_set_buffer);
ClassDB::bind_method(D_METHOD("multimesh_get_buffer", "multimesh"), &RenderingServer::multimesh_get_buffer);
ClassDB::bind_method(D_METHOD("multimesh_set_buffer_interpolated", "multimesh", "buffer", "buffer_previous"), &RenderingServer::multimesh_set_buffer_interpolated);
ClassDB::bind_method(D_METHOD("multimesh_set_physics_interpolated", "multimesh", "interpolated"), &RenderingServer::multimesh_set_physics_interpolated);
ClassDB::bind_method(D_METHOD("multimesh_set_physics_interpolation_quality", "multimesh", "quality"), &RenderingServer::multimesh_set_physics_interpolation_quality);
ClassDB::bind_method(D_METHOD("multimesh_instance_reset_physics_interpolation", "multimesh", "index"), &RenderingServer::multimesh_instance_reset_physics_interpolation);
BIND_ENUM_CONSTANT(MULTIMESH_TRANSFORM_2D);
BIND_ENUM_CONSTANT(MULTIMESH_TRANSFORM_3D);
BIND_ENUM_CONSTANT(MULTIMESH_INTERP_QUALITY_FAST);
BIND_ENUM_CONSTANT(MULTIMESH_INTERP_QUALITY_HIGH);
/* SKELETON API */

View File

@ -426,6 +426,11 @@ public:
MULTIMESH_TRANSFORM_3D,
};
enum MultimeshPhysicsInterpolationQuality {
MULTIMESH_INTERP_QUALITY_FAST,
MULTIMESH_INTERP_QUALITY_HIGH,
};
virtual void multimesh_allocate_data(RID p_multimesh, int p_instances, MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) = 0;
virtual int multimesh_get_instance_count(RID p_multimesh) const = 0;
@ -449,6 +454,12 @@ public:
virtual void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) = 0;
virtual Vector<float> multimesh_get_buffer(RID p_multimesh) const = 0;
// Interpolation.
virtual void multimesh_set_buffer_interpolated(RID p_multimesh, const Vector<float> &p_buffer_curr, const Vector<float> &p_buffer_prev) = 0;
virtual void multimesh_set_physics_interpolated(RID p_multimesh, bool p_interpolated) = 0;
virtual void multimesh_set_physics_interpolation_quality(RID p_multimesh, MultimeshPhysicsInterpolationQuality p_quality) = 0;
virtual void multimesh_instance_reset_physics_interpolation(RID p_multimesh, int p_index) = 0;
virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible) = 0;
virtual int multimesh_get_visible_instances(RID p_multimesh) const = 0;
@ -1788,6 +1799,7 @@ VARIANT_ENUM_CAST(RenderingServer::ArrayCustomFormat);
VARIANT_ENUM_CAST(RenderingServer::PrimitiveType);
VARIANT_ENUM_CAST(RenderingServer::BlendShapeMode);
VARIANT_ENUM_CAST(RenderingServer::MultimeshTransformFormat);
VARIANT_ENUM_CAST(RenderingServer::MultimeshPhysicsInterpolationQuality);
VARIANT_ENUM_CAST(RenderingServer::LightType);
VARIANT_ENUM_CAST(RenderingServer::LightParam);
VARIANT_ENUM_CAST(RenderingServer::LightBakeMode);