1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-22 15:06:45 +00:00
Files
godot/scene/resources/3d/joint_limitation_cone_3d.cpp
Silc Lizard (Tokage) Renew bf22eb25e3 Add IKModifier3D
2025-11-04 02:38:48 +09:00

183 lines
7.1 KiB
C++

/**************************************************************************/
/* 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