1/**************************************************************************/
2/* godot_collision_object_3d.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 "godot_collision_object_3d.h"
32
33#include "godot_physics_server_3d.h"
34#include "godot_space_3d.h"
35
36void GodotCollisionObject3D::add_shape(GodotShape3D *p_shape, const Transform3D &p_transform, bool p_disabled) {
37 Shape s;
38 s.shape = p_shape;
39 s.xform = p_transform;
40 s.xform_inv = s.xform.affine_inverse();
41 s.bpid = 0; //needs update
42 s.disabled = p_disabled;
43 shapes.push_back(s);
44 p_shape->add_owner(this);
45
46 if (!pending_shape_update_list.in_list()) {
47 GodotPhysicsServer3D::godot_singleton->pending_shape_update_list.add(&pending_shape_update_list);
48 }
49}
50
51void GodotCollisionObject3D::set_shape(int p_index, GodotShape3D *p_shape) {
52 ERR_FAIL_INDEX(p_index, shapes.size());
53 shapes[p_index].shape->remove_owner(this);
54 shapes.write[p_index].shape = p_shape;
55
56 p_shape->add_owner(this);
57 if (!pending_shape_update_list.in_list()) {
58 GodotPhysicsServer3D::godot_singleton->pending_shape_update_list.add(&pending_shape_update_list);
59 }
60}
61
62void GodotCollisionObject3D::set_shape_transform(int p_index, const Transform3D &p_transform) {
63 ERR_FAIL_INDEX(p_index, shapes.size());
64
65 shapes.write[p_index].xform = p_transform;
66 shapes.write[p_index].xform_inv = p_transform.affine_inverse();
67 if (!pending_shape_update_list.in_list()) {
68 GodotPhysicsServer3D::godot_singleton->pending_shape_update_list.add(&pending_shape_update_list);
69 }
70}
71
72void GodotCollisionObject3D::set_shape_disabled(int p_idx, bool p_disabled) {
73 ERR_FAIL_INDEX(p_idx, shapes.size());
74
75 GodotCollisionObject3D::Shape &shape = shapes.write[p_idx];
76 if (shape.disabled == p_disabled) {
77 return;
78 }
79
80 shape.disabled = p_disabled;
81
82 if (!space) {
83 return;
84 }
85
86 if (p_disabled && shape.bpid != 0) {
87 space->get_broadphase()->remove(shape.bpid);
88 shape.bpid = 0;
89 if (!pending_shape_update_list.in_list()) {
90 GodotPhysicsServer3D::godot_singleton->pending_shape_update_list.add(&pending_shape_update_list);
91 }
92 } else if (!p_disabled && shape.bpid == 0) {
93 if (!pending_shape_update_list.in_list()) {
94 GodotPhysicsServer3D::godot_singleton->pending_shape_update_list.add(&pending_shape_update_list);
95 }
96 }
97}
98
99void GodotCollisionObject3D::remove_shape(GodotShape3D *p_shape) {
100 //remove a shape, all the times it appears
101 for (int i = 0; i < shapes.size(); i++) {
102 if (shapes[i].shape == p_shape) {
103 remove_shape(i);
104 i--;
105 }
106 }
107}
108
109void GodotCollisionObject3D::remove_shape(int p_index) {
110 //remove anything from shape to be erased to end, so subindices don't change
111 ERR_FAIL_INDEX(p_index, shapes.size());
112 for (int i = p_index; i < shapes.size(); i++) {
113 if (shapes[i].bpid == 0) {
114 continue;
115 }
116 //should never get here with a null owner
117 space->get_broadphase()->remove(shapes[i].bpid);
118 shapes.write[i].bpid = 0;
119 }
120 shapes[p_index].shape->remove_owner(this);
121 shapes.remove_at(p_index);
122
123 if (!pending_shape_update_list.in_list()) {
124 GodotPhysicsServer3D::godot_singleton->pending_shape_update_list.add(&pending_shape_update_list);
125 }
126}
127
128void GodotCollisionObject3D::_set_static(bool p_static) {
129 if (_static == p_static) {
130 return;
131 }
132 _static = p_static;
133
134 if (!space) {
135 return;
136 }
137 for (int i = 0; i < get_shape_count(); i++) {
138 const Shape &s = shapes[i];
139 if (s.bpid > 0) {
140 space->get_broadphase()->set_static(s.bpid, _static);
141 }
142 }
143}
144
145void GodotCollisionObject3D::_unregister_shapes() {
146 for (int i = 0; i < shapes.size(); i++) {
147 Shape &s = shapes.write[i];
148 if (s.bpid > 0) {
149 space->get_broadphase()->remove(s.bpid);
150 s.bpid = 0;
151 }
152 }
153}
154
155void GodotCollisionObject3D::_update_shapes() {
156 if (!space) {
157 return;
158 }
159
160 for (int i = 0; i < shapes.size(); i++) {
161 Shape &s = shapes.write[i];
162 if (s.disabled) {
163 continue;
164 }
165
166 //not quite correct, should compute the next matrix..
167 AABB shape_aabb = s.shape->get_aabb();
168 Transform3D xform = transform * s.xform;
169 shape_aabb = xform.xform(shape_aabb);
170 shape_aabb.grow_by((s.aabb_cache.size.x + s.aabb_cache.size.y) * 0.5 * 0.05);
171 s.aabb_cache = shape_aabb;
172
173 Vector3 scale = xform.get_basis().get_scale();
174 s.area_cache = s.shape->get_volume() * scale.x * scale.y * scale.z;
175
176 if (s.bpid == 0) {
177 s.bpid = space->get_broadphase()->create(this, i, shape_aabb, _static);
178 space->get_broadphase()->set_static(s.bpid, _static);
179 }
180
181 space->get_broadphase()->move(s.bpid, shape_aabb);
182 }
183}
184
185void GodotCollisionObject3D::_update_shapes_with_motion(const Vector3 &p_motion) {
186 if (!space) {
187 return;
188 }
189
190 for (int i = 0; i < shapes.size(); i++) {
191 Shape &s = shapes.write[i];
192 if (s.disabled) {
193 continue;
194 }
195
196 //not quite correct, should compute the next matrix..
197 AABB shape_aabb = s.shape->get_aabb();
198 Transform3D xform = transform * s.xform;
199 shape_aabb = xform.xform(shape_aabb);
200 shape_aabb.merge_with(AABB(shape_aabb.position + p_motion, shape_aabb.size)); //use motion
201 s.aabb_cache = shape_aabb;
202
203 if (s.bpid == 0) {
204 s.bpid = space->get_broadphase()->create(this, i, shape_aabb, _static);
205 space->get_broadphase()->set_static(s.bpid, _static);
206 }
207
208 space->get_broadphase()->move(s.bpid, shape_aabb);
209 }
210}
211
212void GodotCollisionObject3D::_set_space(GodotSpace3D *p_space) {
213 if (space) {
214 space->remove_object(this);
215
216 for (int i = 0; i < shapes.size(); i++) {
217 Shape &s = shapes.write[i];
218 if (s.bpid) {
219 space->get_broadphase()->remove(s.bpid);
220 s.bpid = 0;
221 }
222 }
223 }
224
225 space = p_space;
226
227 if (space) {
228 space->add_object(this);
229 _update_shapes();
230 }
231}
232
233void GodotCollisionObject3D::_shape_changed() {
234 _update_shapes();
235 _shapes_changed();
236}
237
238GodotCollisionObject3D::GodotCollisionObject3D(Type p_type) :
239 pending_shape_update_list(this) {
240 type = p_type;
241}
242