Merge pull request #110120 from TokageItLab/ik-modifier-3d

Add `SkeletonModifier3D` IKs as `IKModifier3D`
This commit is contained in:
Thaddeus Crews 2025-11-04 16:32:36 -06:00
commit 3fb8961b00
No known key found for this signature in database
GPG key ID: 8C6E5FEB5FC03CCC
69 changed files with 6066 additions and 179 deletions

View file

@ -5,6 +5,8 @@ Import("env")
env.add_source_files(env.scene_sources, "fog_material.cpp")
env.add_source_files(env.scene_sources, "importer_mesh.cpp")
env.add_source_files(env.scene_sources, "joint_limitation_3d.cpp")
env.add_source_files(env.scene_sources, "joint_limitation_cone_3d.cpp")
env.add_source_files(env.scene_sources, "mesh_library.cpp")
env.add_source_files(env.scene_sources, "primitive_meshes.cpp")
env.add_source_files(env.scene_sources, "skin.cpp")

View file

@ -0,0 +1,65 @@
/**************************************************************************/
/* joint_limitation_3d.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 "joint_limitation_3d.h"
#include "scene/3d/skeleton_modifier_3d.h"
Quaternion JointLimitation3D::make_space(const Vector3 &p_local_forward_vector, const Vector3 &p_local_right_vector, const Quaternion &p_rotation_offset) const {
const double ALMOST_ONE = 1.0 - CMP_EPSILON;
// The default is to interpret the forward vector as the +Y axis.
Vector3 axis_y = p_local_forward_vector.normalized();
Vector3 axis_x = p_local_right_vector.normalized();
if (axis_x.is_zero_approx() || Math::abs(axis_x.dot(axis_y)) > ALMOST_ONE) {
return (Quaternion(Vector3(0, 1, 0), axis_y) * p_rotation_offset.normalized()).normalized();
}
// Prior X axis.
Vector3 axis_z = axis_x.cross(axis_y);
axis_z.normalize();
axis_x = axis_y.cross(axis_z);
axis_x.normalize();
return (Basis(axis_x, axis_y, axis_z).get_rotation_quaternion() * p_rotation_offset.normalized()).normalized();
}
Vector3 JointLimitation3D::_solve(const Vector3 &p_direction) const {
return p_direction;
}
Vector3 JointLimitation3D::solve(const Vector3 &p_local_forward_vector, const Vector3 &p_local_right_vector, const Quaternion &p_rotation_offset, const Vector3 &p_local_current_vector) const {
Quaternion space = make_space(p_local_forward_vector, p_local_right_vector, p_rotation_offset);
Vector3 dir = p_local_current_vector.normalized();
return space.xform(_solve(space.xform_inv(dir)));
}
#ifdef TOOLS_ENABLED
void JointLimitation3D::draw_shape(Ref<SurfaceTool> &p_surface_tool, const Transform3D &p_transform, float p_bone_length, const Color &p_color) const {
//
}
#endif // TOOLS_ENABLED

View file

@ -0,0 +1,55 @@
/**************************************************************************/
/* joint_limitation_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 "core/io/resource.h"
#ifdef TOOLS_ENABLED
#include "scene/resources/surface_tool.h"
#endif // TOOLS_ENABLED
class JointLimitation3D : public Resource {
GDCLASS(JointLimitation3D, Resource);
protected:
// Directions are normalized vector from Vector(0, 0, 0). Space is defined by _make_space(), must return normalized vector.
virtual Vector3 _solve(const Vector3 &p_direction) const;
public:
// Define temporary space based on rest and forward vector.
virtual Quaternion make_space(const Vector3 &p_local_forward_vector, const Vector3 &p_local_right_vector, const Quaternion &p_rotation_offset) const;
Vector3 solve(const Vector3 &p_local_forward_vector, const Vector3 &p_local_right_vector, const Quaternion &p_rotation_offset, const Vector3 &p_local_current_vector) const;
#ifdef TOOLS_ENABLED
virtual void draw_shape(Ref<SurfaceTool> &p_surface_tool, const Transform3D &p_transform, float p_bone_length, const Color &p_color) const; // For drawing gizmo.
#endif // TOOLS_ENABLED
};

View file

@ -0,0 +1,182 @@
/**************************************************************************/
/* joint_limitation_cone_3d.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 "joint_limitation_cone_3d.h"
void JointLimitationCone3D::set_radius_range(real_t p_radius_range) {
radius_range = p_radius_range;
emit_changed();
}
real_t JointLimitationCone3D::get_radius_range() const {
return radius_range;
}
void JointLimitationCone3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_radius_range", "radius_range"), &JointLimitationCone3D::set_radius_range);
ClassDB::bind_method(D_METHOD("get_radius_range"), &JointLimitationCone3D::get_radius_range);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius_range", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_radius_range", "get_radius_range");
}
Vector3 JointLimitationCone3D::_solve(const Vector3 &p_direction) const {
// Assume the central (forward of the cone) axis is the +Y.
// This is based on the coordinate system set by JointLimitation3D::_make_space().
Vector3 center_axis = Vector3(0, 1, 0);
// Apply the limitation if the angle exceeds radius_range * PI.
real_t angle = p_direction.angle_to(center_axis);
real_t max_angle = radius_range * Math::PI;
if (angle <= max_angle) {
// If within the limitation range, return the new direction as is.
return p_direction;
}
// If outside the limitation range, calculate the closest direction within the range.
// Define a plane using the central axis and the new direction vector.
Vector3 plane_normal;
// Special handling for when the new direction vector is completely opposite to the central axis.
if (Math::is_equal_approx((double)angle, Math::PI)) {
// Select an arbitrary perpendicular axis
plane_normal = center_axis.get_any_perpendicular();
} else {
plane_normal = center_axis.cross(p_direction).normalized();
}
// Calculate a vector rotated by the maximum angle from the central axis on the plane.
Quaternion rotation = Quaternion(plane_normal, max_angle);
Vector3 limited_dir = rotation.xform(center_axis);
// Return the vector within the limitation range that is closest to p_direction.
// This preserves the directionality of p_direction as much as possible.
Vector3 projection = p_direction - center_axis * p_direction.dot(center_axis);
if (projection.length_squared() > CMP_EPSILON) {
Vector3 side_dir = projection.normalized();
Quaternion side_rotation = Quaternion(center_axis.cross(side_dir).normalized(), max_angle);
limited_dir = side_rotation.xform(center_axis);
}
return limited_dir.normalized();
}
#ifdef TOOLS_ENABLED
void JointLimitationCone3D::draw_shape(Ref<SurfaceTool> &p_surface_tool, const Transform3D &p_transform, float p_bone_length, const Color &p_color) const {
static const int N = 16;
static const real_t DP = Math::TAU / (real_t)N;
real_t sphere_r = p_bone_length * (real_t)0.25;
if (sphere_r <= CMP_EPSILON) {
return;
}
real_t alpha = CLAMP((real_t)radius_range, (real_t)0.0, (real_t)1.0) * Math::PI;
real_t y_cap = sphere_r * Math::cos(alpha);
real_t r_cap = sphere_r * Math::sin(alpha);
LocalVector<Vector3> vts;
// Cone bottom.
if (r_cap > CMP_EPSILON) {
for (int i = 0; i < N; i++) {
real_t a0 = (real_t)i * DP;
real_t a1 = (real_t)((i + 1) % N) * DP;
Vector3 p0 = Vector3(r_cap * Math::cos(a0), y_cap, r_cap * Math::sin(a0));
Vector3 p1 = Vector3(r_cap * Math::cos(a1), y_cap, r_cap * Math::sin(a1));
vts.push_back(p0);
vts.push_back(p1);
}
}
// Rotate arcs around Y-axis.
real_t t_start;
real_t arc_len;
if (alpha <= (real_t)1e-6) {
t_start = (real_t)0.5 * Math::PI;
arc_len = Math::PI;
} else {
t_start = (real_t)0.5 * Math::PI + alpha;
arc_len = Math::PI - alpha;
}
real_t dt = arc_len / (real_t)N;
for (int k = 0; k < N; k++) {
Basis ry(Vector3(0, 1, 0), (real_t)k * DP);
Vector3 prev = ry.xform(Vector3(sphere_r * Math::cos(t_start), sphere_r * Math::sin(t_start), 0));
for (int s = 1; s <= N; s++) {
real_t t = t_start + dt * (real_t)s;
Vector3 cur = ry.xform(Vector3(sphere_r * Math::cos(t), sphere_r * Math::sin(t), 0));
vts.push_back(prev);
vts.push_back(cur);
prev = cur;
}
Vector3 mouth = ry.xform(Vector3(sphere_r * Math::cos(t_start), sphere_r * Math::sin(t_start), 0));
Vector3 center = Vector3();
vts.push_back(center);
vts.push_back(mouth);
}
// Stack rings.
for (int i = 1; i <= 3; i++) {
for (int sgn = -1; sgn <= 1; sgn += 2) {
real_t y = (real_t)sgn * sphere_r * ((real_t)i / (real_t)4.0);
if (y >= y_cap - CMP_EPSILON) {
continue;
}
real_t ring_r2 = sphere_r * sphere_r - y * y;
if (ring_r2 <= (real_t)0.0) {
continue;
}
real_t ring_r = Math::sqrt(ring_r2);
for (int j = 0; j < N; j++) {
real_t a0 = (real_t)j * DP;
real_t a1 = (real_t)((j + 1) % N) * DP;
Vector3 p0 = Vector3(ring_r * Math::cos(a0), y, ring_r * Math::sin(a0));
Vector3 p1 = Vector3(ring_r * Math::cos(a1), y, ring_r * Math::sin(a1));
vts.push_back(p0);
vts.push_back(p1);
}
}
}
for (int64_t i = 0; i < vts.size(); i++) {
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex(p_transform.xform(vts[i]));
}
}
#endif // TOOLS_ENABLED

View file

@ -0,0 +1,52 @@
/**************************************************************************/
/* joint_limitation_cone_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/resources/3d/joint_limitation_3d.h"
class JointLimitationCone3D : public JointLimitation3D {
GDCLASS(JointLimitationCone3D, JointLimitation3D);
real_t radius_range = 0.25;
protected:
static void _bind_methods();
virtual Vector3 _solve(const Vector3 &p_direction) const override;
public:
void set_radius_range(real_t p_radius_range);
real_t get_radius_range() const;
#ifdef TOOLS_ENABLED
virtual void draw_shape(Ref<SurfaceTool> &p_surface_tool, const Transform3D &p_transform, float p_bone_length, const Color &p_color) const override;
#endif // TOOLS_ENABLED
};

View file

@ -2127,6 +2127,14 @@ PackedVector3Array Curve3D::get_baked_up_vectors() const {
return baked_up_vector_cache;
}
Vector<real_t> Curve3D::get_baked_dist_cache() const {
if (baked_cache_dirty) {
_bake();
}
return baked_dist_cache;
}
Vector3 Curve3D::get_closest_point(const Vector3 &p_to_point) const {
// Brute force method.

View file

@ -353,6 +353,7 @@ public:
PackedVector3Array get_baked_points() const; // Useful for going through.
Vector<real_t> get_baked_tilts() const; //useful for going through
PackedVector3Array get_baked_up_vectors() const;
Vector<real_t> get_baked_dist_cache() const;
Vector3 get_closest_point(const Vector3 &p_to_point) const;
real_t get_closest_offset(const Vector3 &p_to_point) const;
PackedVector3Array get_points() const;