diff --git a/core/pooled_list.h b/core/pooled_list.h index ecc925c88165..19d38e0924fe 100644 --- a/core/pooled_list.h +++ b/core/pooled_list.h @@ -110,7 +110,7 @@ class PooledList { r_id = list.size(); list.resize(r_id + 1); - static_assert((!zero_on_first_request) || (__is_pod(T)), "zero_on_first_request requires trivial type"); + static_assert((!zero_on_first_request) || (std::is_trivially_destructible::value), "zero_on_first_request requires trivial type"); if (zero_on_first_request && __is_pod(T)) { list[r_id] = {}; } diff --git a/doc/classes/VisualServer.xml b/doc/classes/VisualServer.xml index ed49390e6d64..bf882923155c 100644 --- a/doc/classes/VisualServer.xml +++ b/doc/classes/VisualServer.xml @@ -1108,6 +1108,65 @@ [/codeblock] + + + + FTI ("fixed timestep interpolation") instance provides a convenience physics interpolation wrapper for users creating instances directly on the server rather than through Nodes. + FTI instance internally contains a regular instance, but provides extra functionality. It manages the lifetime of the regular instance for you (freeing it when the FTI instance is freed). + There are thus two [code]RIDs[/code] to be concerned with - the FTI instance [code]RID[/code], and the internal regular instance [code]RID[/code], which can be retrieved using [method fti_instance_get_instance]. + [codeblock] + # Create an FTI instance and link it to the regular instance. + var fti_instance = VisualServer.fti_instance_create() + var instance = VisualServer.fti_instance_get_instance(fti_instance) + + # Set the scenario from the world, this ensures it + # appears with the same objects as the scene. + var scenario = get_world().scenario + VisualServer.instance_set_scenario(instance, scenario) + + # Add an example mesh to it. + var mesh = load("res://test_model.obj") + VisualServer.instance_set_base(instance, mesh) + + # Use this function instead of instance_set_transform(). + VisualServer.fti_instance_set_transform(fti_instance, xform) + + # This function provides reset_physics_interpolation(). + VisualServer.fti_instance_reset(fti_instance) + + # Be sure to free RID on the server after use. + # Note that the fti_instance automatically frees + # the underlying regular instance, so you should not + # call free_rid(instance), as it would result + # in trying to free an already deleted object. + VisualServer.free_rid(fti_instance) + [/codeblock] + + + + + + + This function provides access to the internal regular instance [code]RID[/code], which you need in order to call regular non-FTI VisualServer functions. + [b]Note:[/b] There is no need to call free for this [code]RID[/code]. The lifetime will be managed for you by the FTI instance. + [b]Note:[/b] It is recommended to call this function only once during FTI instance creation, and keep a record of the instance [code]RID[/code] in your own code. This is because the function could potentially cause a stall reading back from the server, so it is important not to call it every frame. + + + + + + + Equivalent to [method Node.reset_physics_interpolation] for Nodes. + + + + + + + + Equivalent to [method instance_set_transform], but provides automatic physics interpolation. + + diff --git a/scene/3d/collision_object.cpp b/scene/3d/collision_object.cpp index 77abd234f844..2018bd37c87a 100644 --- a/scene/3d/collision_object.cpp +++ b/scene/3d/collision_object.cpp @@ -98,6 +98,20 @@ void CollisionObject::_notification(int p_what) { } } break; + case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: { + if (debug_shapes_count > 0 && is_inside_tree()) { + for (Map::Element *E = shapes.front(); E; E = E->next()) { + ShapeData &shapedata = E->get(); + const ShapeData::ShapeBase *shapes = shapedata.shapes.ptr(); + for (int i = 0; i < shapedata.shapes.size(); i++) { + if (shapes[i].fti_instance_debug_shape.is_valid()) { + VS::get_singleton()->fti_instance_reset(shapes[i].fti_instance_debug_shape); + } + } + } + } + + } break; } } @@ -237,14 +251,20 @@ void CollisionObject::_update_debug_shapes() { for (int i = 0; i < shapedata.shapes.size(); i++) { ShapeData::ShapeBase &s = shapes[i]; if (s.shape.is_null() || shapedata.disabled) { - if (s.debug_shape.is_valid()) { - VS::get_singleton()->free(s.debug_shape); + if (s.fti_instance_debug_shape.is_valid()) { + VS::get_singleton()->free(s.fti_instance_debug_shape); + s.fti_instance_debug_shape = RID(); s.debug_shape = RID(); --debug_shapes_count; } } - if (!s.debug_shape.is_valid()) { - s.debug_shape = RID_PRIME(VS::get_singleton()->instance_create()); + + bool needs_fti_reset = false; + + if (!s.fti_instance_debug_shape.is_valid()) { + needs_fti_reset = true; + s.fti_instance_debug_shape = RID_PRIME(VS::get_singleton()->fti_instance_create()); + s.debug_shape = VS::get_singleton()->fti_instance_get_instance(s.fti_instance_debug_shape); VS::get_singleton()->instance_set_scenario(s.debug_shape, get_world()->get_scenario()); if (!s.shape->is_connected("changed", this, "_shape_changed")) { @@ -256,8 +276,12 @@ void CollisionObject::_update_debug_shapes() { Ref mesh = s.shape->get_debug_mesh(); VS::get_singleton()->instance_set_base(s.debug_shape, mesh->get_rid()); - VS::get_singleton()->instance_set_transform(s.debug_shape, get_global_transform() * shapedata.xform); + VS::get_singleton()->fti_instance_set_transform(s.fti_instance_debug_shape, get_global_transform() * shapedata.xform); VS::get_singleton()->instance_set_portal_mode(s.debug_shape, VisualServer::INSTANCE_PORTAL_MODE_GLOBAL); + + if (needs_fti_reset) { + VS::get_singleton()->fti_instance_reset(s.fti_instance_debug_shape); + } } } } @@ -270,8 +294,9 @@ void CollisionObject::_clear_debug_shapes() { ShapeData::ShapeBase *shapes = shapedata.shapes.ptrw(); for (int i = 0; i < shapedata.shapes.size(); i++) { ShapeData::ShapeBase &s = shapes[i]; - if (s.debug_shape.is_valid()) { - VS::get_singleton()->free(s.debug_shape); + if (s.fti_instance_debug_shape.is_valid()) { + VS::get_singleton()->free(s.fti_instance_debug_shape); + s.fti_instance_debug_shape = RID(); s.debug_shape = RID(); if (s.shape.is_valid() && s.shape->is_connected("changed", this, "_shape_changed")) { s.shape->disconnect("changed", this, "_shape_changed"); @@ -290,7 +315,7 @@ void CollisionObject::_on_transform_changed() { ShapeData &shapedata = E->get(); const ShapeData::ShapeBase *shapes = shapedata.shapes.ptr(); for (int i = 0; i < shapedata.shapes.size(); i++) { - VS::get_singleton()->instance_set_transform(shapes[i].debug_shape, debug_shape_old_transform * shapedata.xform); + VS::get_singleton()->fti_instance_set_transform(shapes[i].fti_instance_debug_shape, debug_shape_old_transform * shapedata.xform); } } } @@ -496,8 +521,11 @@ void CollisionObject::shape_owner_remove_shape(uint32_t p_owner, int p_shape) { PhysicsServer::get_singleton()->body_remove_shape(rid, index_to_remove); } - if (s.debug_shape.is_valid()) { - VS::get_singleton()->free(s.debug_shape); + if (s.fti_instance_debug_shape.is_valid()) { + VS::get_singleton()->free(s.fti_instance_debug_shape); + s.fti_instance_debug_shape = RID(); + s.debug_shape = RID(); + if (s.shape.is_valid() && s.shape->is_connected("changed", this, "_shape_changed")) { s.shape->disconnect("changed", this, "_shape_changed"); } diff --git a/scene/3d/collision_object.h b/scene/3d/collision_object.h index ab0a83102489..bb6288be0d61 100644 --- a/scene/3d/collision_object.h +++ b/scene/3d/collision_object.h @@ -48,6 +48,7 @@ class CollisionObject : public Spatial { ObjectID owner_id; Transform xform; struct ShapeBase { + RID fti_instance_debug_shape; RID debug_shape; Ref shape; int index; diff --git a/servers/visual/fti_helper.cpp b/servers/visual/fti_helper.cpp new file mode 100644 index 000000000000..608db4ee9a69 --- /dev/null +++ b/servers/visual/fti_helper.cpp @@ -0,0 +1,183 @@ +/**************************************************************************/ +/* fti_helper.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 "fti_helper.h" + +#include "core/engine.h" +#include "core/math/transform_interpolator.h" +#include "servers/visual/visual_server_globals.h" +#include "servers/visual/visual_server_scene.h" +#include "servers/visual_server.h" + +FTIHelper::Handle FTIHelper::instance_create() { + Handle h; + Instance *i = _instances.request(h.id()); + i->clear(); + i->revision += 1; + h.set_revision(i->revision); + + i->instance = RID_PRIME(VSG::scene->instance_create()); + return h; +} + +RID FTIHelper::instance_get_instance(Handle h_instance) { + FTIHelper::Instance *instance = get_instance(h_instance); + ERR_FAIL_NULL_V(instance, RID()); + return instance->instance; +} + +bool FTIHelper::instance_free(Handle h_instance) { + FTIHelper::Instance *instance = get_instance(h_instance); + ERR_FAIL_NULL_V(instance, false); + + // Free the regular instance, as the lifetime of the regular instance + // is handled by the fti_instance automatically. + VisualServer::get_singleton()->free(instance->instance); + instance->instance = RID(); + + _instances.free(h_instance.id()); + + // Remove from all lists. + _instance_frame_list.erase_multiple_unordered(h_instance); + _instance_tick_list_curr->erase_multiple_unordered(h_instance); + _instance_tick_list_prev->erase_multiple_unordered(h_instance); + return true; +} + +FTIHelper::Instance *FTIHelper::get_instance(Handle h_instance) { + ERR_FAIL_COND_V(h_instance.id() >= _instances.reserved_size(), nullptr); + Instance &i = _instances[h_instance.id()]; + ERR_FAIL_COND_V(h_instance.revision() != i.revision, nullptr); + return &i; +} + +void FTIHelper::instance_changed(Instance &r_instance, Handle h_instance) { + // Add to tick list + if (!r_instance.on_tick_list) { + r_instance.on_tick_list = true; + _instance_tick_list_curr->push_back(h_instance); + } + + // Add to frame list. + if (!r_instance.on_frame_list) { + r_instance.on_frame_list = true; + _instance_frame_list.push_back(h_instance); + } +} + +void FTIHelper::instance_set_transform(Handle h_instance, const Transform &p_xform) { + Instance *i = get_instance(h_instance); + ERR_FAIL_NULL(i); + + if (_interpolation_enabled) { + i->curr = p_xform; + instance_changed(*i, h_instance); + } else { + // Pass through. + VisualServer::get_singleton()->instance_set_transform(i->instance, p_xform); + } +} + +void FTIHelper::instance_reset_physics_interpolation(Handle h_instance) { + if (!_interpolation_enabled) { + return; + } + Instance *i = get_instance(h_instance); + ERR_FAIL_NULL(i); + i->pump(); + instance_changed(*i, h_instance); +} + +void FTIHelper::set_interpolation_enabled(bool p_enabled) { + if (p_enabled == _interpolation_enabled) { + return; + } + + _interpolation_enabled = p_enabled; +} + +void FTIHelper::tick_update() { + LocalVector &curr = *_instance_tick_list_curr; + LocalVector &prev = *_instance_tick_list_prev; + + // First detect on the previous list but not on this tick list. + for (uint32_t n = 0; n < prev.size(); n++) { + const Handle &h = prev[n]; + Instance *i = get_instance(h); + if (i) { + if (!i->on_tick_list) { + // Needs a reset so jittering will stop. + i->pump(); + + // Remove from interpolation list. + if (i->on_frame_list) { + i->on_frame_list = false; + _instance_frame_list.erase_unordered(h); + } + } + } + } + + // Now pump all on the current list. + for (uint32_t n = 0; n < curr.size(); n++) { + const Handle &h = curr[n]; + Instance *i = get_instance(h); + if (i) { + // Reset, needs to be marked each tick. + i->on_tick_list = false; + } + + // Pump. + i->pump(); + } + + // Clear previous list and flip. + prev.clear(); + + SWAP(_instance_tick_list_curr, _instance_tick_list_prev); +} + +void FTIHelper::frame_update() { + float f = Engine::get_singleton()->get_physics_interpolation_fraction(); + + Transform x; + VisualServer *vs = VisualServer::get_singleton(); + + for (uint32_t n = 0; n < _instance_frame_list.size(); n++) { + const Handle &h = _instance_frame_list[n]; + Instance *i = get_instance(h); + if (i) { + TransformInterpolator::interpolate_transform(i->prev, i->curr, x, f); + + // Send to server. + vs->instance_set_transform(i->instance, x); + } + } +} diff --git a/servers/visual/fti_helper.h b/servers/visual/fti_helper.h new file mode 100644 index 000000000000..c4b1e30fe340 --- /dev/null +++ b/servers/visual/fti_helper.h @@ -0,0 +1,115 @@ +/**************************************************************************/ +/* fti_helper.h */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#ifndef FTI_HELPER_H +#define FTI_HELPER_H + +#include "core/math/transform.h" +#include "core/pooled_list.h" +#include "core/rid.h" + +#include + +class FTIHelper { + struct Handle { + static const uint64_t INVALID = UINT64_MAX; + + // conversion operator + operator uint64_t() const { return _data; } + Handle(uint64_t p_value) { _data = p_value; } + Handle() { _data = INVALID; } + + union { + struct + { + uint32_t _id; + uint32_t _revision; + }; + uint64_t _data; + }; + + void set_invalid() { _data = INVALID; } + bool is_invalid() const { return _data == INVALID; } + uint32_t id() const { return _id; } + uint32_t &id() { return _id; } + void set_id(uint32_t p_id) { _id = p_id; } + uint32_t revision() const { return _revision; } + void set_revision(uint32_t p_revision) { _revision = p_revision; } + + bool operator==(const Handle &p_h) const { return _data == p_h._data; } + bool operator!=(const Handle &p_h) const { return (*this == p_h) == false; } + }; + + struct Instance { + uint32_t revision = 0; + RID instance; + Transform curr; + Transform prev; + bool on_frame_list = false; + bool on_tick_list = false; + void clear() { + instance = RID(); + curr = Transform(); + prev = Transform(); + on_frame_list = false; + on_tick_list = false; + } + void pump() { + prev = curr; + } + }; + + PooledList _instances; + + LocalVector _instance_frame_list; + LocalVector _instance_tick_list[2]; + LocalVector *_instance_tick_list_curr = &_instance_tick_list[0]; + LocalVector *_instance_tick_list_prev = &_instance_tick_list[1]; + + bool _interpolation_enabled = false; + + // Check revisions etc. in case of user error. + Instance *get_instance(Handle h_instance); + void instance_changed(Instance &r_instance, Handle h_instance); + +public: + Handle instance_create(); + RID instance_get_instance(Handle h_instance); + bool instance_free(Handle h_instance); + void instance_set_transform(Handle h_instance, const Transform &p_xform); + void instance_reset_physics_interpolation(Handle h_instance); + + void tick_update(); + void frame_update(); + + void set_interpolation_enabled(bool p_enabled); +}; + +#endif // FTI_HELPER_H diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h index 4193f3fc61af..d24ad499261f 100644 --- a/servers/visual/visual_server_raster.h +++ b/servers/visual/visual_server_raster.h @@ -588,6 +588,11 @@ class VisualServerRaster : public VisualServer { BIND2(instance_set_extra_visibility_margin, RID, real_t) + BIND0R(RID, fti_instance_create) + BIND1R(RID, fti_instance_get_instance, RID) + BIND2(fti_instance_set_transform, RID, const Transform &) + BIND1(fti_instance_reset, RID) + /* BLOB SHADOWS */ BIND0R(RID, capsule_shadow_create) BIND5(capsule_shadow_update, RID, const Vector3 &, real_t, const Vector3 &, real_t) diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp index a9e1e31b3096..07c60b8ada90 100644 --- a/servers/visual/visual_server_scene.cpp +++ b/servers/visual/visual_server_scene.cpp @@ -32,6 +32,7 @@ #include "core/math/transform_interpolator.h" #include "core/os/os.h" +#include "servers/visual/fti_helper.h" #include "visual_server_globals.h" #include "visual_server_light_culler.h" #include "visual_server_raster.h" @@ -426,11 +427,16 @@ RID VisualServerScene::scenario_create() { void VisualServerScene::set_physics_interpolation_enabled(bool p_enabled) { _interpolation_data.interpolation_enabled = p_enabled; + DEV_ASSERT(_fti_helper); + _fti_helper->set_interpolation_enabled(p_enabled); } void VisualServerScene::tick() { if (_interpolation_data.interpolation_enabled) { update_interpolation_tick(true); + + DEV_ASSERT(_fti_helper); + _fti_helper->tick_update(); } } @@ -447,6 +453,11 @@ void VisualServerScene::pre_draw(bool p_will_draw) { light_culler->set_caster_culling_active(GLOBAL_GET("rendering/quality/shadows/caster_culling")); light_culler->set_light_culling_active(GLOBAL_GET("rendering/quality/shadows/light_culling")); } + + if (_interpolation_data.interpolation_enabled && p_will_draw) { + DEV_ASSERT(_fti_helper); + _fti_helper->frame_update(); + } } void VisualServerScene::scenario_set_debug(RID p_scenario, VS::ScenarioDebugMode p_debug_mode) { @@ -1086,6 +1097,38 @@ bool VisualServerScene::_instance_get_transformed_aabb(RID p_instance, AABB &r_a return true; } +RID VisualServerScene::fti_instance_create() { + FTIInstance *fti_hh = memnew(FTIInstance); + ERR_FAIL_COND_V(!fti_hh, RID()); + RID rid = fti_instance_owner.make_rid(fti_hh); + + DEV_ASSERT(_fti_helper); + fti_hh->handle = _fti_helper->instance_create(); + + return rid; +} + +RID VisualServerScene::fti_instance_get_instance(RID p_fti_instance) { + FTIInstance *fti_hh = fti_instance_owner.getornull(p_fti_instance); + ERR_FAIL_NULL_V(fti_hh, RID()); + DEV_ASSERT(_fti_helper); + return _fti_helper->instance_get_instance(fti_hh->handle); +} + +void VisualServerScene::fti_instance_set_transform(RID p_fti_instance, const Transform &p_transform) { + FTIInstance *fti_hh = fti_instance_owner.getornull(p_fti_instance); + ERR_FAIL_NULL(fti_hh); + DEV_ASSERT(_fti_helper); + _fti_helper->instance_set_transform(fti_hh->handle, p_transform); +} + +void VisualServerScene::fti_instance_reset(RID p_fti_instance) { + FTIInstance *fti_hh = fti_instance_owner.getornull(p_fti_instance); + ERR_FAIL_NULL(fti_hh); + DEV_ASSERT(_fti_helper); + _fti_helper->instance_reset_physics_interpolation(fti_hh->handle); +} + RID VisualServerScene::blob_light_create() { BlobLight *blob_light = memnew(BlobLight); ERR_FAIL_NULL_V(blob_light, RID()); @@ -4525,6 +4568,15 @@ bool VisualServerScene::free(RID p_rid) { _blob_shadows.delete_light(blob_light->handle); } memdelete(blob_light); + } else if (fti_instance_owner.owns(p_rid)) { + FTIInstance *fti_hh = fti_instance_owner.get(p_rid); + if (fti_hh->handle != UINT64_MAX) { + DEV_ASSERT(_fti_helper); + _fti_helper->instance_free(fti_hh->handle); + } + fti_instance_owner.free(p_rid); + memdelete(fti_hh); + return true; } else { return false; } @@ -4539,6 +4591,7 @@ VisualServerScene::VisualServerScene() { probe_bake_thread_exit = false; light_culler = memnew(VisualServerLightCuller); + _fti_helper = memnew(FTIHelper); render_pass = 1; singleton = this; @@ -4561,4 +4614,9 @@ VisualServerScene::~VisualServerScene() { memdelete(light_culler); light_culler = nullptr; } + + if (_fti_helper) { + memdelete(_fti_helper); + _fti_helper = nullptr; + } } diff --git a/servers/visual/visual_server_scene.h b/servers/visual/visual_server_scene.h index 6ac9c86b8e1b..b153a00202be 100644 --- a/servers/visual/visual_server_scene.h +++ b/servers/visual/visual_server_scene.h @@ -45,6 +45,7 @@ #include "visual_server_blob_shadows.h" class VisualServerLightCuller; +class FTIHelper; class VisualServerScene { public: @@ -59,6 +60,7 @@ class VisualServerScene { uint64_t render_pass; static VisualServerScene *singleton; + FTIHelper *_fti_helper; /* EVENT QUEUING */ @@ -676,6 +678,17 @@ class VisualServerScene { virtual void instance_set_extra_visibility_margin(RID p_instance, real_t p_margin); + /* FTI HELPER API */ + struct FTIInstance : RID_Data { + uint64_t handle = UINT64_MAX; + }; + RID_Owner fti_instance_owner; + + RID fti_instance_create(); + RID fti_instance_get_instance(RID p_fti_instance); + void fti_instance_set_transform(RID p_fti_instance, const Transform &p_transform); + void fti_instance_reset(RID p_fti_instance); + // Portals virtual void instance_set_portal_mode(RID p_instance, VisualServer::InstancePortalMode p_mode); bool _instance_get_transformed_aabb(RID p_instance, AABB &r_aabb); diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h index fdfae36af82e..7df6c5cc7ed3 100644 --- a/servers/visual/visual_server_wrap_mt.h +++ b/servers/visual/visual_server_wrap_mt.h @@ -496,6 +496,12 @@ class VisualServerWrapMT : public VisualServer { FUNC2(instance_set_extra_visibility_margin, RID, real_t) + /* FTI HELPER API */ + FUNCRID(fti_instance) + FUNC1R(RID, fti_instance_get_instance, RID) + FUNC2(fti_instance_set_transform, RID, const Transform &) + FUNC1(fti_instance_reset, RID) + /* BLOB SHADOWS */ FUNCRID(capsule_shadow) FUNC5(capsule_shadow_update, RID, const Vector3 &, real_t, const Vector3 &, real_t) diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp index db33faf8d0d2..2890b9952621 100644 --- a/servers/visual_server.cpp +++ b/servers/visual_server.cpp @@ -2192,11 +2192,16 @@ void VisualServer::_bind_methods() { ClassDB::bind_method(D_METHOD("instances_cull_ray", "from", "to", "scenario"), &VisualServer::_instances_cull_ray_bind, DEFVAL(RID())); ClassDB::bind_method(D_METHOD("instances_cull_convex", "convex", "scenario"), &VisualServer::_instances_cull_convex_bind, DEFVAL(RID())); + ClassDB::bind_method(D_METHOD("fti_instance_create"), &VisualServer::fti_instance_create); + ClassDB::bind_method(D_METHOD("fti_instance_get_instance", "fti_instance"), &VisualServer::fti_instance_get_instance); + ClassDB::bind_method(D_METHOD("fti_instance_set_transform", "fti_instance", "transform"), &VisualServer::fti_instance_set_transform); + ClassDB::bind_method(D_METHOD("fti_instance_reset", "fti_instance"), &VisualServer::fti_instance_reset); + ClassDB::bind_method(D_METHOD("blob_shadows_set_range", "range"), &VisualServer::blob_shadows_set_range); ClassDB::bind_method(D_METHOD("blob_shadows_set_gamma", "gamma"), &VisualServer::blob_shadows_set_gamma); ClassDB::bind_method(D_METHOD("blob_shadows_set_intensity", "intensity"), &VisualServer::blob_shadows_set_intensity); - #endif + ClassDB::bind_method(D_METHOD("canvas_create"), &VisualServer::canvas_create); ClassDB::bind_method(D_METHOD("canvas_set_item_mirroring", "canvas", "item", "mirroring"), &VisualServer::canvas_set_item_mirroring); ClassDB::bind_method(D_METHOD("canvas_set_modulate", "canvas", "color"), &VisualServer::canvas_set_modulate); diff --git a/servers/visual_server.h b/servers/visual_server.h index d6b516b06ff3..92f762c4d3eb 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -901,6 +901,12 @@ class VisualServer : public Object { virtual void instance_set_extra_visibility_margin(RID p_instance, real_t p_margin) = 0; + /* FTI HELPER API */ + virtual RID fti_instance_create() = 0; + virtual RID fti_instance_get_instance(RID p_fti_instance) = 0; + virtual void fti_instance_set_transform(RID p_fti_instance, const Transform &p_transform) = 0; + virtual void fti_instance_reset(RID p_fti_instance) = 0; + /* BLOB SHADOWS API */ virtual RID capsule_shadow_create() = 0; virtual void capsule_shadow_update(RID p_blob, const Vector3 &p_occluder_a_pos, real_t p_occluder_a_radius, const Vector3 &p_occluder_b_pos, real_t p_occluder_b_radius) = 0;