Path: blob/master/scene/resources/3d/joint_limitation_cone_3d.cpp
21345 views
/**************************************************************************/1/* joint_limitation_cone_3d.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "joint_limitation_cone_3d.h"3132#ifndef DISABLE_DEPRECATED33bool JointLimitationCone3D::_set(const StringName &p_path, const Variant &p_value) {34// To keep compatibility between 4.6.beta2 and beta3.35if (p_path == SNAME("radius_range")) {36set_angle((float)p_value * Math::TAU);37} else {38return false;39}40return true;41}42#endif // DISABLE_DEPRECATED4344void JointLimitationCone3D::set_angle(real_t p_angle) {45angle = p_angle;46emit_changed();47}4849real_t JointLimitationCone3D::get_angle() const {50return angle;51}5253void JointLimitationCone3D::_bind_methods() {54ClassDB::bind_method(D_METHOD("set_angle", "angle"), &JointLimitationCone3D::set_angle);55ClassDB::bind_method(D_METHOD("get_angle"), &JointLimitationCone3D::get_angle);5657ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angle", PROPERTY_HINT_RANGE, "0,360,0.01,radians_as_degrees"), "set_angle", "get_angle");58}5960Vector3 JointLimitationCone3D::_solve(const Vector3 &p_direction) const {61// Assume the central (forward of the cone) axis is the +Y.62// This is based on the coordinate system set by JointLimitation3D::_make_space().63Vector3 center_axis = Vector3(0, 1, 0);6465// Apply the limitation if the angle exceeds radius_range * PI.66real_t current_angle = p_direction.angle_to(center_axis);67real_t max_angle = angle * 0.5;6869if (current_angle <= max_angle) {70// If within the limitation range, return the new direction as is.71return p_direction;72}7374// If outside the limitation range, calculate the closest direction within the range.75// Define a plane using the central axis and the new direction vector.76Vector3 plane_normal;7778// Special handling for when the new direction vector is completely opposite to the central axis.79if (Math::is_equal_approx((double)current_angle, Math::PI)) {80// Select an arbitrary perpendicular axis81plane_normal = center_axis.get_any_perpendicular();82} else {83plane_normal = center_axis.cross(p_direction).normalized();84}8586// Calculate a vector rotated by the maximum angle from the central axis on the plane.87Quaternion rotation = Quaternion(plane_normal, max_angle);88Vector3 limited_dir = rotation.xform(center_axis);8990// Return the vector within the limitation range that is closest to p_direction.91// This preserves the directionality of p_direction as much as possible.92Vector3 projection = p_direction - center_axis * p_direction.dot(center_axis);93if (projection.length_squared() > CMP_EPSILON) {94Vector3 side_dir = projection.normalized();95Quaternion side_rotation = Quaternion(center_axis.cross(side_dir).normalized(), max_angle);96limited_dir = side_rotation.xform(center_axis);97}9899return limited_dir.normalized();100}101102#ifdef TOOLS_ENABLED103void JointLimitationCone3D::draw_shape(Ref<SurfaceTool> &p_surface_tool, const Transform3D &p_transform, float p_bone_length, const Color &p_color) const {104static const int N = 16;105static const real_t DP = Math::TAU / (real_t)N;106107real_t sphere_r = p_bone_length * (real_t)0.25;108if (sphere_r <= CMP_EPSILON) {109return;110}111real_t alpha = CLAMP((real_t)angle, (real_t)0.0, (real_t)Math::TAU) * 0.5;112real_t y_cap = sphere_r * Math::cos(alpha);113real_t r_cap = sphere_r * Math::sin(alpha);114115LocalVector<Vector3> vts;116117// Cone bottom.118if (r_cap > CMP_EPSILON) {119for (int i = 0; i < N; i++) {120real_t a0 = (real_t)i * DP;121real_t a1 = (real_t)((i + 1) % N) * DP;122Vector3 p0 = Vector3(r_cap * Math::cos(a0), y_cap, r_cap * Math::sin(a0));123Vector3 p1 = Vector3(r_cap * Math::cos(a1), y_cap, r_cap * Math::sin(a1));124vts.push_back(p0);125vts.push_back(p1);126}127}128129// Rotate arcs around Y-axis.130real_t t_start;131real_t arc_len;132if (alpha <= (real_t)1e-6) {133t_start = (real_t)0.5 * Math::PI;134arc_len = Math::PI;135} else {136t_start = (real_t)0.5 * Math::PI + alpha;137arc_len = Math::PI - alpha;138}139real_t dt = arc_len / (real_t)N;140141for (int k = 0; k < N; k++) {142Basis ry(Vector3(0, 1, 0), (real_t)k * DP);143144Vector3 prev = ry.xform(Vector3(sphere_r * Math::cos(t_start), sphere_r * Math::sin(t_start), 0));145146for (int s = 1; s <= N; s++) {147real_t t = t_start + dt * (real_t)s;148Vector3 cur = ry.xform(Vector3(sphere_r * Math::cos(t), sphere_r * Math::sin(t), 0));149150vts.push_back(prev);151vts.push_back(cur);152153prev = cur;154}155156Vector3 mouth = ry.xform(Vector3(sphere_r * Math::cos(t_start), sphere_r * Math::sin(t_start), 0));157Vector3 center = Vector3();158159vts.push_back(center);160vts.push_back(mouth);161}162163// Stack rings.164for (int i = 1; i <= 3; i++) {165for (int sgn = -1; sgn <= 1; sgn += 2) {166real_t y = (real_t)sgn * sphere_r * ((real_t)i / (real_t)4.0);167if (y >= y_cap - CMP_EPSILON) {168continue;169}170real_t ring_r2 = sphere_r * sphere_r - y * y;171if (ring_r2 <= (real_t)0.0) {172continue;173}174real_t ring_r = Math::sqrt(ring_r2);175176for (int j = 0; j < N; j++) {177real_t a0 = (real_t)j * DP;178real_t a1 = (real_t)((j + 1) % N) * DP;179Vector3 p0 = Vector3(ring_r * Math::cos(a0), y, ring_r * Math::sin(a0));180Vector3 p1 = Vector3(ring_r * Math::cos(a1), y, ring_r * Math::sin(a1));181182vts.push_back(p0);183vts.push_back(p1);184}185}186}187188for (int64_t i = 0; i < vts.size(); i++) {189p_surface_tool->set_color(p_color);190p_surface_tool->add_vertex(p_transform.xform(vts[i]));191}192}193#endif // TOOLS_ENABLED194195196