1/**************************************************************************/
2/* gizmo_3d_helper.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "gizmo_3d_helper.h"
32
33#include "editor/editor_undo_redo_manager.h"
34#include "editor/plugins/node_3d_editor_plugin.h"
35#include "scene/3d/camera_3d.h"
36
37void Gizmo3DHelper::initialize_handle_action(const Variant &p_initial_value, const Transform3D &p_initial_transform) {
38 initial_value = p_initial_value;
39 initial_transform = p_initial_transform;
40}
41
42void Gizmo3DHelper::get_segment(Camera3D *p_camera, const Point2 &p_point, Vector3 *r_segment) {
43 Transform3D gt = initial_transform;
44 Transform3D gi = gt.affine_inverse();
45
46 Vector3 ray_from = p_camera->project_ray_origin(p_point);
47 Vector3 ray_dir = p_camera->project_ray_normal(p_point);
48
49 r_segment[0] = gi.xform(ray_from);
50 r_segment[1] = gi.xform(ray_from + ray_dir * 4096);
51}
52
53Vector<Vector3> Gizmo3DHelper::box_get_handles(const Vector3 &p_box_size) {
54 Vector<Vector3> handles;
55 for (int i = 0; i < 3; i++) {
56 Vector3 ax;
57 ax[i] = p_box_size[i] / 2;
58 handles.push_back(ax);
59 handles.push_back(-ax);
60 }
61 return handles;
62}
63
64String Gizmo3DHelper::box_get_handle_name(int p_id) const {
65 switch (p_id) {
66 case 0:
67 case 1:
68 return "Size X";
69 case 2:
70 case 3:
71 return "Size Y";
72 case 4:
73 case 5:
74 return "Size Z";
75 }
76 return "";
77}
78
79void Gizmo3DHelper::box_set_handle(const Vector3 p_segment[2], int p_id, Vector3 &r_box_size, Vector3 &r_box_position) {
80 Vector3 axis;
81 axis[p_id / 2] = 1.0;
82 Vector3 ra, rb;
83 int sign = p_id % 2 * -2 + 1;
84 Vector3 initial_size = initial_value;
85
86 Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096 * sign, p_segment[0], p_segment[1], ra, rb);
87 if (ra[p_id / 2] == 0) {
88 // Point before half of the shape. Needs to be calculated in opposite direction.
89 Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096 * -sign, p_segment[0], p_segment[1], ra, rb);
90 }
91
92 float d = ra[p_id / 2] * sign;
93
94 Vector3 he = r_box_size;
95 he[p_id / 2] = d * 2;
96 if (Node3DEditor::get_singleton()->is_snap_enabled()) {
97 he[p_id / 2] = Math::snapped(he[p_id / 2], Node3DEditor::get_singleton()->get_translate_snap());
98 }
99
100 if (Input::get_singleton()->is_key_pressed(Key::ALT)) {
101 he[p_id / 2] = MAX(he[p_id / 2], 0.001);
102 r_box_size = he;
103 r_box_position = initial_transform.get_origin();
104 } else {
105 he[p_id / 2] = MAX(he[p_id / 2], -initial_size[p_id / 2] + 0.002);
106 r_box_size = (initial_size + (he - initial_size) * 0.5).abs();
107 Vector3 pos = initial_transform.affine_inverse().xform(initial_transform.get_origin());
108 pos += (r_box_size - initial_size) * 0.5 * sign;
109 r_box_position = initial_transform.xform(pos);
110 }
111}
112
113void Gizmo3DHelper::box_commit_handle(const String &p_action_name, bool p_cancel, Object *p_position_object, Object *p_size_object, const StringName &p_position_property, const StringName &p_size_property) {
114 if (!p_size_object) {
115 p_size_object = p_position_object;
116 }
117
118 if (p_cancel) {
119 p_size_object->set(p_size_property, initial_value);
120 p_position_object->set(p_position_property, initial_transform.get_origin());
121 return;
122 }
123
124 EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
125 ur->create_action(p_action_name);
126 ur->add_do_property(p_size_object, p_size_property, p_size_object->get(p_size_property));
127 ur->add_do_property(p_position_object, p_position_property, p_position_object->get(p_position_property));
128 ur->add_undo_property(p_size_object, p_size_property, initial_value);
129 ur->add_undo_property(p_position_object, p_position_property, initial_transform.get_origin());
130 ur->commit_action();
131}
132