1/**************************************************************************/
2/* gltf_physics_body.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 "gltf_physics_body.h"
32
33#include "scene/3d/area_3d.h"
34#include "scene/3d/vehicle_body_3d.h"
35
36void GLTFPhysicsBody::_bind_methods() {
37 ClassDB::bind_static_method("GLTFPhysicsBody", D_METHOD("from_node", "body_node"), &GLTFPhysicsBody::from_node);
38 ClassDB::bind_method(D_METHOD("to_node"), &GLTFPhysicsBody::to_node);
39
40 ClassDB::bind_static_method("GLTFPhysicsBody", D_METHOD("from_dictionary", "dictionary"), &GLTFPhysicsBody::from_dictionary);
41 ClassDB::bind_method(D_METHOD("to_dictionary"), &GLTFPhysicsBody::to_dictionary);
42
43 ClassDB::bind_method(D_METHOD("get_body_type"), &GLTFPhysicsBody::get_body_type);
44 ClassDB::bind_method(D_METHOD("set_body_type", "body_type"), &GLTFPhysicsBody::set_body_type);
45 ClassDB::bind_method(D_METHOD("get_mass"), &GLTFPhysicsBody::get_mass);
46 ClassDB::bind_method(D_METHOD("set_mass", "mass"), &GLTFPhysicsBody::set_mass);
47 ClassDB::bind_method(D_METHOD("get_linear_velocity"), &GLTFPhysicsBody::get_linear_velocity);
48 ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &GLTFPhysicsBody::set_linear_velocity);
49 ClassDB::bind_method(D_METHOD("get_angular_velocity"), &GLTFPhysicsBody::get_angular_velocity);
50 ClassDB::bind_method(D_METHOD("set_angular_velocity", "angular_velocity"), &GLTFPhysicsBody::set_angular_velocity);
51 ClassDB::bind_method(D_METHOD("get_center_of_mass"), &GLTFPhysicsBody::get_center_of_mass);
52 ClassDB::bind_method(D_METHOD("set_center_of_mass", "center_of_mass"), &GLTFPhysicsBody::set_center_of_mass);
53 ClassDB::bind_method(D_METHOD("get_inertia_tensor"), &GLTFPhysicsBody::get_inertia_tensor);
54 ClassDB::bind_method(D_METHOD("set_inertia_tensor", "inertia_tensor"), &GLTFPhysicsBody::set_inertia_tensor);
55
56 ADD_PROPERTY(PropertyInfo(Variant::STRING, "body_type"), "set_body_type", "get_body_type");
57 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass"), "set_mass", "get_mass");
58 ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity"), "set_linear_velocity", "get_linear_velocity");
59 ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "angular_velocity"), "set_angular_velocity", "get_angular_velocity");
60 ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_of_mass"), "set_center_of_mass", "get_center_of_mass");
61 ADD_PROPERTY(PropertyInfo(Variant::BASIS, "inertia_tensor"), "set_inertia_tensor", "get_inertia_tensor");
62}
63
64String GLTFPhysicsBody::get_body_type() const {
65 return body_type;
66}
67
68void GLTFPhysicsBody::set_body_type(String p_body_type) {
69 body_type = p_body_type;
70}
71
72real_t GLTFPhysicsBody::get_mass() const {
73 return mass;
74}
75
76void GLTFPhysicsBody::set_mass(real_t p_mass) {
77 mass = p_mass;
78}
79
80Vector3 GLTFPhysicsBody::get_linear_velocity() const {
81 return linear_velocity;
82}
83
84void GLTFPhysicsBody::set_linear_velocity(Vector3 p_linear_velocity) {
85 linear_velocity = p_linear_velocity;
86}
87
88Vector3 GLTFPhysicsBody::get_angular_velocity() const {
89 return angular_velocity;
90}
91
92void GLTFPhysicsBody::set_angular_velocity(Vector3 p_angular_velocity) {
93 angular_velocity = p_angular_velocity;
94}
95
96Vector3 GLTFPhysicsBody::get_center_of_mass() const {
97 return center_of_mass;
98}
99
100void GLTFPhysicsBody::set_center_of_mass(const Vector3 &p_center_of_mass) {
101 center_of_mass = p_center_of_mass;
102}
103
104Basis GLTFPhysicsBody::get_inertia_tensor() const {
105 return inertia_tensor;
106}
107
108void GLTFPhysicsBody::set_inertia_tensor(Basis p_inertia_tensor) {
109 inertia_tensor = p_inertia_tensor;
110}
111
112Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_node(const CollisionObject3D *p_body_node) {
113 Ref<GLTFPhysicsBody> physics_body;
114 physics_body.instantiate();
115 ERR_FAIL_COND_V_MSG(!p_body_node, physics_body, "Tried to create a GLTFPhysicsBody from a CollisionObject3D node, but the given node was null.");
116 if (cast_to<CharacterBody3D>(p_body_node)) {
117 physics_body->body_type = "character";
118 } else if (cast_to<AnimatableBody3D>(p_body_node)) {
119 physics_body->body_type = "kinematic";
120 } else if (cast_to<RigidBody3D>(p_body_node)) {
121 const RigidBody3D *body = cast_to<const RigidBody3D>(p_body_node);
122 physics_body->mass = body->get_mass();
123 physics_body->linear_velocity = body->get_linear_velocity();
124 physics_body->angular_velocity = body->get_angular_velocity();
125 physics_body->center_of_mass = body->get_center_of_mass();
126 Vector3 inertia_diagonal = body->get_inertia();
127 physics_body->inertia_tensor = Basis(inertia_diagonal.x, 0, 0, 0, inertia_diagonal.y, 0, 0, 0, inertia_diagonal.z);
128 if (body->get_center_of_mass() != Vector3()) {
129 WARN_PRINT("GLTFPhysicsBody: This rigid body has a center of mass offset from the origin, which will be ignored when exporting to GLTF.");
130 }
131 if (cast_to<VehicleBody3D>(p_body_node)) {
132 physics_body->body_type = "vehicle";
133 } else {
134 physics_body->body_type = "rigid";
135 }
136 } else if (cast_to<StaticBody3D>(p_body_node)) {
137 physics_body->body_type = "static";
138 } else if (cast_to<Area3D>(p_body_node)) {
139 physics_body->body_type = "trigger";
140 }
141 return physics_body;
142}
143
144CollisionObject3D *GLTFPhysicsBody::to_node() const {
145 if (body_type == "character") {
146 CharacterBody3D *body = memnew(CharacterBody3D);
147 return body;
148 }
149 if (body_type == "kinematic") {
150 AnimatableBody3D *body = memnew(AnimatableBody3D);
151 return body;
152 }
153 if (body_type == "vehicle") {
154 VehicleBody3D *body = memnew(VehicleBody3D);
155 body->set_mass(mass);
156 body->set_linear_velocity(linear_velocity);
157 body->set_angular_velocity(angular_velocity);
158 body->set_inertia(inertia_tensor.get_main_diagonal());
159 body->set_center_of_mass_mode(RigidBody3D::CENTER_OF_MASS_MODE_CUSTOM);
160 body->set_center_of_mass(center_of_mass);
161 return body;
162 }
163 if (body_type == "rigid") {
164 RigidBody3D *body = memnew(RigidBody3D);
165 body->set_mass(mass);
166 body->set_linear_velocity(linear_velocity);
167 body->set_angular_velocity(angular_velocity);
168 body->set_inertia(inertia_tensor.get_main_diagonal());
169 body->set_center_of_mass_mode(RigidBody3D::CENTER_OF_MASS_MODE_CUSTOM);
170 body->set_center_of_mass(center_of_mass);
171 return body;
172 }
173 if (body_type == "static") {
174 StaticBody3D *body = memnew(StaticBody3D);
175 return body;
176 }
177 if (body_type == "trigger") {
178 Area3D *body = memnew(Area3D);
179 return body;
180 }
181 ERR_FAIL_V_MSG(nullptr, "Error converting GLTFPhysicsBody to a node: Body type '" + body_type + "' is unknown.");
182}
183
184Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_dictionary(const Dictionary p_dictionary) {
185 Ref<GLTFPhysicsBody> physics_body;
186 physics_body.instantiate();
187 ERR_FAIL_COND_V_MSG(!p_dictionary.has("type"), physics_body, "Failed to parse GLTF physics body, missing required field 'type'.");
188 const String &body_type = p_dictionary["type"];
189 physics_body->body_type = body_type;
190
191 if (p_dictionary.has("mass")) {
192 physics_body->mass = p_dictionary["mass"];
193 }
194 if (p_dictionary.has("linearVelocity")) {
195 const Array &arr = p_dictionary["linearVelocity"];
196 if (arr.size() == 3) {
197 physics_body->set_linear_velocity(Vector3(arr[0], arr[1], arr[2]));
198 } else {
199 ERR_PRINT("Error parsing GLTF physics body: The linear velocity vector must have exactly 3 numbers.");
200 }
201 }
202 if (p_dictionary.has("angularVelocity")) {
203 const Array &arr = p_dictionary["angularVelocity"];
204 if (arr.size() == 3) {
205 physics_body->set_angular_velocity(Vector3(arr[0], arr[1], arr[2]));
206 } else {
207 ERR_PRINT("Error parsing GLTF physics body: The angular velocity vector must have exactly 3 numbers.");
208 }
209 }
210 if (p_dictionary.has("centerOfMass")) {
211 const Array &arr = p_dictionary["centerOfMass"];
212 if (arr.size() == 3) {
213 physics_body->set_center_of_mass(Vector3(arr[0], arr[1], arr[2]));
214 } else {
215 ERR_PRINT("Error parsing GLTF physics body: The center of mass vector must have exactly 3 numbers.");
216 }
217 }
218 if (p_dictionary.has("inertiaTensor")) {
219 const Array &arr = p_dictionary["inertiaTensor"];
220 if (arr.size() == 9) {
221 // Only use the diagonal elements of the inertia tensor matrix (principal axes).
222 physics_body->set_inertia_tensor(Basis(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7], arr[8]));
223 } else {
224 ERR_PRINT("Error parsing GLTF physics body: The inertia tensor must be a 3x3 matrix (9 number array).");
225 }
226 }
227 if (body_type != "character" && body_type != "kinematic" && body_type != "rigid" && body_type != "static" && body_type != "trigger" && body_type != "vehicle") {
228 ERR_PRINT("Error parsing GLTF physics body: Body type '" + body_type + "' is unknown.");
229 }
230 return physics_body;
231}
232
233Dictionary GLTFPhysicsBody::to_dictionary() const {
234 Dictionary d;
235 d["type"] = body_type;
236 if (mass != 1.0) {
237 d["mass"] = mass;
238 }
239 if (linear_velocity != Vector3()) {
240 Array velocity_array;
241 velocity_array.resize(3);
242 velocity_array[0] = linear_velocity.x;
243 velocity_array[1] = linear_velocity.y;
244 velocity_array[2] = linear_velocity.z;
245 d["linearVelocity"] = velocity_array;
246 }
247 if (angular_velocity != Vector3()) {
248 Array velocity_array;
249 velocity_array.resize(3);
250 velocity_array[0] = angular_velocity.x;
251 velocity_array[1] = angular_velocity.y;
252 velocity_array[2] = angular_velocity.z;
253 d["angularVelocity"] = velocity_array;
254 }
255 if (center_of_mass != Vector3()) {
256 Array center_of_mass_array;
257 center_of_mass_array.resize(3);
258 center_of_mass_array[0] = center_of_mass.x;
259 center_of_mass_array[1] = center_of_mass.y;
260 center_of_mass_array[2] = center_of_mass.z;
261 d["centerOfMass"] = center_of_mass_array;
262 }
263 if (inertia_tensor != Basis(0, 0, 0, 0, 0, 0, 0, 0, 0)) {
264 Array inertia_array;
265 inertia_array.resize(9);
266 inertia_array.fill(0.0);
267 inertia_array[0] = inertia_tensor[0][0];
268 inertia_array[1] = inertia_tensor[0][1];
269 inertia_array[2] = inertia_tensor[0][2];
270 inertia_array[3] = inertia_tensor[1][0];
271 inertia_array[4] = inertia_tensor[1][1];
272 inertia_array[5] = inertia_tensor[1][2];
273 inertia_array[6] = inertia_tensor[2][0];
274 inertia_array[7] = inertia_tensor[2][1];
275 inertia_array[8] = inertia_tensor[2][2];
276 d["inertiaTensor"] = inertia_array;
277 }
278 return d;
279}
280