Path: blob/master/scene/resources/3d/joint_limitation_cone_3d.cpp
14710 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"3132void JointLimitationCone3D::set_radius_range(real_t p_radius_range) {33radius_range = p_radius_range;34emit_changed();35}3637real_t JointLimitationCone3D::get_radius_range() const {38return radius_range;39}4041void JointLimitationCone3D::_bind_methods() {42ClassDB::bind_method(D_METHOD("set_radius_range", "radius_range"), &JointLimitationCone3D::set_radius_range);43ClassDB::bind_method(D_METHOD("get_radius_range"), &JointLimitationCone3D::get_radius_range);4445ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius_range", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_radius_range", "get_radius_range");46}4748Vector3 JointLimitationCone3D::_solve(const Vector3 &p_direction) const {49// Assume the central (forward of the cone) axis is the +Y.50// This is based on the coordinate system set by JointLimitation3D::_make_space().51Vector3 center_axis = Vector3(0, 1, 0);5253// Apply the limitation if the angle exceeds radius_range * PI.54real_t angle = p_direction.angle_to(center_axis);55real_t max_angle = radius_range * Math::PI;5657if (angle <= max_angle) {58// If within the limitation range, return the new direction as is.59return p_direction;60}6162// If outside the limitation range, calculate the closest direction within the range.63// Define a plane using the central axis and the new direction vector.64Vector3 plane_normal;6566// Special handling for when the new direction vector is completely opposite to the central axis.67if (Math::is_equal_approx((double)angle, Math::PI)) {68// Select an arbitrary perpendicular axis69plane_normal = center_axis.get_any_perpendicular();70} else {71plane_normal = center_axis.cross(p_direction).normalized();72}7374// Calculate a vector rotated by the maximum angle from the central axis on the plane.75Quaternion rotation = Quaternion(plane_normal, max_angle);76Vector3 limited_dir = rotation.xform(center_axis);7778// Return the vector within the limitation range that is closest to p_direction.79// This preserves the directionality of p_direction as much as possible.80Vector3 projection = p_direction - center_axis * p_direction.dot(center_axis);81if (projection.length_squared() > CMP_EPSILON) {82Vector3 side_dir = projection.normalized();83Quaternion side_rotation = Quaternion(center_axis.cross(side_dir).normalized(), max_angle);84limited_dir = side_rotation.xform(center_axis);85}8687return limited_dir.normalized();88}8990#ifdef TOOLS_ENABLED91void JointLimitationCone3D::draw_shape(Ref<SurfaceTool> &p_surface_tool, const Transform3D &p_transform, float p_bone_length, const Color &p_color) const {92static const int N = 16;93static const real_t DP = Math::TAU / (real_t)N;9495real_t sphere_r = p_bone_length * (real_t)0.25;96if (sphere_r <= CMP_EPSILON) {97return;98}99real_t alpha = CLAMP((real_t)radius_range, (real_t)0.0, (real_t)1.0) * Math::PI;100real_t y_cap = sphere_r * Math::cos(alpha);101real_t r_cap = sphere_r * Math::sin(alpha);102103LocalVector<Vector3> vts;104105// Cone bottom.106if (r_cap > CMP_EPSILON) {107for (int i = 0; i < N; i++) {108real_t a0 = (real_t)i * DP;109real_t a1 = (real_t)((i + 1) % N) * DP;110Vector3 p0 = Vector3(r_cap * Math::cos(a0), y_cap, r_cap * Math::sin(a0));111Vector3 p1 = Vector3(r_cap * Math::cos(a1), y_cap, r_cap * Math::sin(a1));112vts.push_back(p0);113vts.push_back(p1);114}115}116117// Rotate arcs around Y-axis.118real_t t_start;119real_t arc_len;120if (alpha <= (real_t)1e-6) {121t_start = (real_t)0.5 * Math::PI;122arc_len = Math::PI;123} else {124t_start = (real_t)0.5 * Math::PI + alpha;125arc_len = Math::PI - alpha;126}127real_t dt = arc_len / (real_t)N;128129for (int k = 0; k < N; k++) {130Basis ry(Vector3(0, 1, 0), (real_t)k * DP);131132Vector3 prev = ry.xform(Vector3(sphere_r * Math::cos(t_start), sphere_r * Math::sin(t_start), 0));133134for (int s = 1; s <= N; s++) {135real_t t = t_start + dt * (real_t)s;136Vector3 cur = ry.xform(Vector3(sphere_r * Math::cos(t), sphere_r * Math::sin(t), 0));137138vts.push_back(prev);139vts.push_back(cur);140141prev = cur;142}143144Vector3 mouth = ry.xform(Vector3(sphere_r * Math::cos(t_start), sphere_r * Math::sin(t_start), 0));145Vector3 center = Vector3();146147vts.push_back(center);148vts.push_back(mouth);149}150151// Stack rings.152for (int i = 1; i <= 3; i++) {153for (int sgn = -1; sgn <= 1; sgn += 2) {154real_t y = (real_t)sgn * sphere_r * ((real_t)i / (real_t)4.0);155if (y >= y_cap - CMP_EPSILON) {156continue;157}158real_t ring_r2 = sphere_r * sphere_r - y * y;159if (ring_r2 <= (real_t)0.0) {160continue;161}162real_t ring_r = Math::sqrt(ring_r2);163164for (int j = 0; j < N; j++) {165real_t a0 = (real_t)j * DP;166real_t a1 = (real_t)((j + 1) % N) * DP;167Vector3 p0 = Vector3(ring_r * Math::cos(a0), y, ring_r * Math::sin(a0));168Vector3 p1 = Vector3(ring_r * Math::cos(a1), y, ring_r * Math::sin(a1));169170vts.push_back(p0);171vts.push_back(p1);172}173}174}175176for (int64_t i = 0; i < vts.size(); i++) {177p_surface_tool->set_color(p_color);178p_surface_tool->add_vertex(p_transform.xform(vts[i]));179}180}181#endif // TOOLS_ENABLED182183184