mirror of
https://github.com/godotengine/godot.git
synced 2026-05-12 22:35:35 +00:00
Merge pull request #113284 from TokageItLab/twist-disperser
Add BoneTwistDisperser3D to propagate IK target's twist
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="BoneTwistDisperser3D" inherits="SkeletonModifier3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<brief_description>
|
||||
A node that propagates and disperses the child bone's twist to the parent bones.
|
||||
</brief_description>
|
||||
<description>
|
||||
This [BoneTwistDisperser3D] allows for smooth twist interpolation between multiple bones by dispersing the end bone's twist to the parents. This only changes the twist without changing the global position of each joint.
|
||||
This is useful for smoothly twisting bones in combination with [CopyTransformModifier3D] and IK.
|
||||
[b]Note:[/b] If an extracted twist is greater than 180 degrees, flipping occurs. This is similar to [ConvertTransformModifier3D].
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="clear_settings">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Clears all settings.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_damping_curve" qualifiers="const">
|
||||
<return type="Curve" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the damping curve when [method get_disperse_mode] is [constant DISPERSE_MODE_CUSTOM].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_disperse_mode" qualifiers="const">
|
||||
<return type="int" enum="BoneTwistDisperser3D.DisperseMode" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns whether to use automatic amount assignment or to allow manual assignment.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_end_bone" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the end bone index of the bone chain.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_end_bone_direction" qualifiers="const">
|
||||
<return type="int" enum="SkeletonModifier3D.BoneDirection" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the tail direction of the end bone of the bone chain when [method is_end_bone_extended] is [code]true[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_end_bone_name" qualifiers="const">
|
||||
<return type="String" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the end bone name of the bone chain.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_joint_bone" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="joint" type="int" />
|
||||
<description>
|
||||
Returns the bone index at [param joint] in the bone chain's joint list.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_joint_bone_name" qualifiers="const">
|
||||
<return type="String" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="joint" type="int" />
|
||||
<description>
|
||||
Returns the bone name at [param joint] in the bone chain's joint list.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_joint_count" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the joint count of the bone chain's joint list.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_joint_twist_amount" qualifiers="const">
|
||||
<return type="float" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="joint" type="int" />
|
||||
<description>
|
||||
Returns the twist amount at [param joint] in the bone chain's joint list when [method get_disperse_mode] is [constant DISPERSE_MODE_CUSTOM].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_reference_bone" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the reference bone to extract twist of the setting at [param index].
|
||||
This bone is either the end of the chain or its parent, depending on [method is_end_bone_extended].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_reference_bone_name" qualifiers="const">
|
||||
<return type="String" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the reference bone name to extract twist of the setting at [param index].
|
||||
This bone is either the end of the chain or its parent, depending on [method is_end_bone_extended].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_root_bone" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the root bone index of the bone chain.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_root_bone_name" qualifiers="const">
|
||||
<return type="String" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the root bone name of the bone chain.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_twist_from" qualifiers="const">
|
||||
<return type="Quaternion" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the rotation to an arbitrary state before twisting for the current bone pose to extract the twist when [method is_twist_from_rest] is [code]false[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_weight_position" qualifiers="const">
|
||||
<return type="float" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the position at which to divide the segment between joints for weight assignment when [method get_disperse_mode] is [constant DISPERSE_MODE_WEIGHTED].
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_end_bone_extended" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the end bone is extended to have a tail.
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_twist_from_rest" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns [code]true[/code] if extracting the twist amount from the difference between the bone rest and the current bone pose.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_damping_curve">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="curve" type="Curve" />
|
||||
<description>
|
||||
Sets the damping curve when [method get_disperse_mode] is [constant DISPERSE_MODE_CUSTOM].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_disperse_mode">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="disperse_mode" type="int" enum="BoneTwistDisperser3D.DisperseMode" />
|
||||
<description>
|
||||
Sets whether to use automatic amount assignment or to allow manual assignment.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_end_bone">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="bone" type="int" />
|
||||
<description>
|
||||
Sets the end bone index of the bone chain.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_end_bone_direction">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="bone_direction" type="int" enum="SkeletonModifier3D.BoneDirection" />
|
||||
<description>
|
||||
Sets the end bone tail direction of the bone chain when [method is_end_bone_extended] is [code]true[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_end_bone_name">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="bone_name" type="String" />
|
||||
<description>
|
||||
Sets the end bone name of the bone chain.
|
||||
[b]Note:[/b] The end bone must be a child of the root bone.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_extend_end_bone">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="enabled" type="bool" />
|
||||
<description>
|
||||
If [param enabled] is [code]true[/code], the end bone is extended to have a tail.
|
||||
If [param enabled] is [code]false[/code], [method get_reference_bone] becomes a parent of the end bone and it uses the vector to the end bone as a twist axis.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_joint_twist_amount">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="joint" type="int" />
|
||||
<param index="2" name="twist_amount" type="float" />
|
||||
<description>
|
||||
Sets the twist amount at [param joint] in the bone chain's joint list when [method get_disperse_mode] is [constant DISPERSE_MODE_CUSTOM].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_root_bone">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="bone" type="int" />
|
||||
<description>
|
||||
Sets the root bone index of the bone chain.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_root_bone_name">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="bone_name" type="String" />
|
||||
<description>
|
||||
Sets the root bone name of the bone chain.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_twist_from">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="from" type="Quaternion" />
|
||||
<description>
|
||||
Sets the rotation to an arbitrary state before twisting for the current bone pose to extract the twist when [method is_twist_from_rest] is [code]false[/code].
|
||||
In other words, by calling [method set_twist_from] by [signal SkeletonModifier3D.modification_processed] of a specific [SkeletonModifier3D], you can extract only the twists generated by modifiers processed after that but before this [BoneTwistDisperser3D].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_twist_from_rest">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="enabled" type="bool" />
|
||||
<description>
|
||||
If [param enabled] is [code]true[/code], it extracts the twist amount from the difference between the bone rest and the current bone pose.
|
||||
If [param enabled] is [code]false[/code], it extracts the twist amount from the difference between [method get_twist_from] and the current bone pose. See also [method set_twist_from].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_weight_position">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="weight_position" type="float" />
|
||||
<description>
|
||||
Sets the position at which to divide the segment between joints for weight assignment when [method get_disperse_mode] is [constant DISPERSE_MODE_WEIGHTED].
|
||||
For example, when [param weight_position] is [code]0.5[/code], if two bone segments with a length of [code]1.0[/code] exist between three joints, weights are assigned to each joint from root to end at ratios of [code]0.5[/code], [code]1.0[/code], and [code]0.5[/code]. Then amounts become [code]0.25[/code], [code]0.75[/code], and [code]1.0[/code] respectively.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="mutable_bone_axes" type="bool" setter="set_mutable_bone_axes" getter="are_bone_axes_mutable" default="true">
|
||||
If [code]true[/code], the solver retrieves the bone axis from the bone pose every frame.
|
||||
If [code]false[/code], the solver retrieves the bone axis from the bone rest and caches it.
|
||||
</member>
|
||||
<member name="setting_count" type="int" setter="set_setting_count" getter="get_setting_count" default="0">
|
||||
The number of settings.
|
||||
</member>
|
||||
</members>
|
||||
<constants>
|
||||
<constant name="DISPERSE_MODE_EVEN" value="0" enum="DisperseMode">
|
||||
Assign amounts so that they monotonically increase from [code]0.0[/code] to [code]1.0[/code], ensuring all weights are equal. For example, with five joints, the amounts would be [code]0.2[/code], [code]0.4[/code], [code]0.6[/code], [code]0.8[/code], and [code]1.0[/code] starting from the root bone.
|
||||
</constant>
|
||||
<constant name="DISPERSE_MODE_WEIGHTED" value="1" enum="DisperseMode">
|
||||
Assign amounts so that they monotonically increase from [code]0.0[/code] to [code]1.0[/code], based on the length of the bones between joint segments. See also [method set_weight_position].
|
||||
</constant>
|
||||
<constant name="DISPERSE_MODE_CUSTOM" value="2" enum="DisperseMode">
|
||||
You can assign arbitrary amounts to the joint list. See also [method set_joint_twist_amount].
|
||||
When [method is_end_bone_extended] is [code]false[/code], a child of the reference bone exists solely to determine the twist axis, so its custom amount has absolutely no effect at all.
|
||||
</constant>
|
||||
</constants>
|
||||
</class>
|
||||
@@ -15,6 +15,7 @@
|
||||
[b]Not Relative + Not Additive:[/b]
|
||||
- Extract reference pose absolutely and the apply bone's pose is replaced with it.
|
||||
[b]Note:[/b] Relative option is available only in the case [method BoneConstraint3D.get_reference_type] is [constant BoneConstraint3D.REFERENCE_TYPE_BONE]. See also [enum BoneConstraint3D.ReferenceType].
|
||||
[b]Note:[/b] If there is a rotation greater than [code]180[/code] degrees with constrained axes, flipping may occur.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#fc7f7f" d="M1.344 11.332v1.668l1.654-.49v.373c-.784.156-1.292.92-1.134 1.705.158.783.921 1.291 1.705 1.135.212-.045.414-.135.588-.266.638.482 1.545.354 2.028-.281.481-.641.355-1.547-.283-2.029-.173-.131-.375-.223-.588-.264v-1.061l1.654-.49v-1.664zM1.344 8v1.666l.003.001 5.622-1.667v-1.668zM5.314 3.49v-.371c.786-.156 1.294-.92 1.136-1.703-.158-.785-.923-1.295-1.707-1.139-.212.045-.413.137-.587.268-.638-.482-1.546-.356-2.029.283-.481.637-.355 1.545.284 2.027.174.131.375.221.587.264v1.057l-1.654.49v1.666l5.625-1.666v-1.666zM10.656 15v-10h-2l3-4 3 4h-2v10z"/><path fill="#b56d6d" d="m4.156 5.5-2.812.832 2.813.836 2.813-.836zm0 3.332-2.812.836 2.812.832 2.813-.832z"/></svg>
|
||||
|
After Width: | Height: | Size: 753 B |
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,163 @@
|
||||
/**************************************************************************/
|
||||
/* bone_twist_disperser_3d.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/3d/skeleton_modifier_3d.h"
|
||||
|
||||
class BoneTwistDisperser3D : public SkeletonModifier3D {
|
||||
GDCLASS(BoneTwistDisperser3D, SkeletonModifier3D);
|
||||
|
||||
bool mutable_bone_axes = true;
|
||||
|
||||
public:
|
||||
enum DisperseMode {
|
||||
DISPERSE_MODE_EVEN,
|
||||
DISPERSE_MODE_WEIGHTED,
|
||||
DISPERSE_MODE_CUSTOM,
|
||||
};
|
||||
|
||||
struct BoneJoint {
|
||||
StringName name;
|
||||
int bone = -1;
|
||||
};
|
||||
|
||||
struct DisperseJointSetting {
|
||||
BoneJoint joint;
|
||||
double custom_amount = 1.0;
|
||||
// For processing.
|
||||
double amount = 1.0;
|
||||
Vector3 axis;
|
||||
};
|
||||
|
||||
struct BoneTwistDisperser3DSetting {
|
||||
bool joints_dirty = false;
|
||||
|
||||
DisperseMode disperse_mode = DISPERSE_MODE_EVEN;
|
||||
bool twist_from_rest = true;
|
||||
Quaternion twist_from;
|
||||
|
||||
BoneJoint root_bone;
|
||||
BoneJoint end_bone;
|
||||
LocalVector<DisperseJointSetting> joints;
|
||||
|
||||
bool extend_end_bone = false;
|
||||
BoneDirection end_bone_direction = BONE_DIRECTION_FROM_PARENT;
|
||||
|
||||
float weight_position = 0.5;
|
||||
Ref<Curve> damping_curve;
|
||||
|
||||
BoneJoint reference_bone; // To cache.
|
||||
|
||||
~BoneTwistDisperser3DSetting() {
|
||||
joints.clear();
|
||||
damping_curve.unref();
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
LocalVector<BoneTwistDisperser3DSetting *> settings;
|
||||
|
||||
bool _get(const StringName &p_path, Variant &r_ret) const;
|
||||
bool _set(const StringName &p_path, const Variant &p_value);
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
void _validate_dynamic_prop(PropertyInfo &p_property) const;
|
||||
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
virtual void _set_active(bool p_active) override;
|
||||
virtual void _skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) override;
|
||||
virtual void _validate_bone_names() override;
|
||||
|
||||
void _make_all_joints_dirty();
|
||||
|
||||
void _make_joints_dirty(int p_index);
|
||||
void _update_joints(int p_index);
|
||||
void _update_reference_bone(int p_index);
|
||||
void _update_curve(int p_index);
|
||||
|
||||
virtual void _process_modification(double p_delta) override;
|
||||
|
||||
public:
|
||||
void set_mutable_bone_axes(bool p_enabled);
|
||||
bool are_bone_axes_mutable() const;
|
||||
|
||||
int get_setting_count() const;
|
||||
void set_setting_count(int p_count);
|
||||
void clear_settings();
|
||||
|
||||
// Setting.
|
||||
void set_root_bone_name(int p_index, const String &p_bone_name);
|
||||
String get_root_bone_name(int p_index) const;
|
||||
void set_root_bone(int p_index, int p_bone);
|
||||
int get_root_bone(int p_index) const;
|
||||
|
||||
void set_end_bone_name(int p_index, const String &p_bone_name);
|
||||
String get_end_bone_name(int p_index) const;
|
||||
void set_end_bone(int p_index, int p_bone);
|
||||
int get_end_bone(int p_index) const;
|
||||
|
||||
void set_extend_end_bone(int p_index, bool p_enabled);
|
||||
bool is_end_bone_extended(int p_index) const;
|
||||
void set_end_bone_direction(int p_index, BoneDirection p_bone_direction);
|
||||
BoneDirection get_end_bone_direction(int p_index) const;
|
||||
|
||||
void set_twist_from_rest(int p_index, bool p_enabled);
|
||||
bool is_twist_from_rest(int p_index) const;
|
||||
void set_twist_from(int p_index, const Quaternion &p_from);
|
||||
Quaternion get_twist_from(int p_index) const;
|
||||
|
||||
String get_reference_bone_name(int p_index) const;
|
||||
int get_reference_bone(int p_index) const;
|
||||
|
||||
void set_disperse_mode(int p_index, DisperseMode p_disperse_mode);
|
||||
DisperseMode get_disperse_mode(int p_index) const;
|
||||
void set_weight_position(int p_index, float p_position);
|
||||
float get_weight_position(int p_index) const;
|
||||
void set_damping_curve(int p_index, const Ref<Curve> &p_damping_curve);
|
||||
Ref<Curve> get_damping_curve(int p_index) const;
|
||||
|
||||
// Individual joints.
|
||||
void set_joint_bone_name(int p_index, int p_joint, const String &p_bone_name);
|
||||
String get_joint_bone_name(int p_index, int p_joint) const;
|
||||
void set_joint_bone(int p_index, int p_joint, int p_bone);
|
||||
int get_joint_bone(int p_index, int p_joint) const;
|
||||
|
||||
void set_joint_twist_amount(int p_index, int p_joint, float p_amount);
|
||||
float get_joint_twist_amount(int p_index, int p_joint) const;
|
||||
|
||||
void set_joint_count(int p_index, int p_count);
|
||||
int get_joint_count(int p_index) const;
|
||||
|
||||
~BoneTwistDisperser3D();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(BoneTwistDisperser3D::DisperseMode);
|
||||
@@ -220,6 +220,7 @@
|
||||
#include "scene/3d/audio_stream_player_3d.h"
|
||||
#include "scene/3d/bone_attachment_3d.h"
|
||||
#include "scene/3d/bone_constraint_3d.h"
|
||||
#include "scene/3d/bone_twist_disperser_3d.h"
|
||||
#include "scene/3d/camera_3d.h"
|
||||
#include "scene/3d/ccd_ik_3d.h"
|
||||
#include "scene/3d/chain_ik_3d.h"
|
||||
@@ -684,6 +685,7 @@ void register_scene_types() {
|
||||
GDREGISTER_CLASS(CCDIK3D);
|
||||
GDREGISTER_CLASS(JacobianIK3D);
|
||||
GDREGISTER_CLASS(LimitAngularVelocityModifier3D);
|
||||
GDREGISTER_CLASS(BoneTwistDisperser3D);
|
||||
|
||||
#ifndef XR_DISABLED
|
||||
GDREGISTER_CLASS(XRCamera3D);
|
||||
|
||||
Reference in New Issue
Block a user