Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/godot_physics_2d/godot_shape_2d.cpp
10277 views
1
/**************************************************************************/
2
/* godot_shape_2d.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_shape_2d.h"
32
33
#include "core/math/geometry_2d.h"
34
#include "core/templates/sort_array.h"
35
36
void GodotShape2D::configure(const Rect2 &p_aabb) {
37
aabb = p_aabb;
38
configured = true;
39
for (const KeyValue<GodotShapeOwner2D *, int> &E : owners) {
40
E.key->_shape_changed();
41
}
42
}
43
44
Vector2 GodotShape2D::get_support(const Vector2 &p_normal) const {
45
Vector2 res[2];
46
int amnt;
47
get_supports(p_normal, res, amnt);
48
return res[0];
49
}
50
51
void GodotShape2D::add_owner(GodotShapeOwner2D *p_owner) {
52
HashMap<GodotShapeOwner2D *, int>::Iterator E = owners.find(p_owner);
53
if (E) {
54
E->value++;
55
} else {
56
owners[p_owner] = 1;
57
}
58
}
59
60
void GodotShape2D::remove_owner(GodotShapeOwner2D *p_owner) {
61
HashMap<GodotShapeOwner2D *, int>::Iterator E = owners.find(p_owner);
62
ERR_FAIL_COND(!E);
63
E->value--;
64
if (E->value == 0) {
65
owners.remove(E);
66
}
67
}
68
69
bool GodotShape2D::is_owner(GodotShapeOwner2D *p_owner) const {
70
return owners.has(p_owner);
71
}
72
73
const HashMap<GodotShapeOwner2D *, int> &GodotShape2D::get_owners() const {
74
return owners;
75
}
76
77
GodotShape2D::~GodotShape2D() {
78
ERR_FAIL_COND(owners.size());
79
}
80
81
/*********************************************************/
82
/*********************************************************/
83
/*********************************************************/
84
85
void GodotWorldBoundaryShape2D::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
86
r_amount = 0;
87
}
88
89
bool GodotWorldBoundaryShape2D::contains_point(const Vector2 &p_point) const {
90
return normal.dot(p_point) < d;
91
}
92
93
bool GodotWorldBoundaryShape2D::intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const {
94
Vector2 segment = p_begin - p_end;
95
real_t den = normal.dot(segment);
96
97
//printf("den is %i\n",den);
98
if (Math::abs(den) <= CMP_EPSILON) {
99
return false;
100
}
101
102
real_t dist = (normal.dot(p_begin) - d) / den;
103
//printf("dist is %i\n",dist);
104
105
if (dist < -CMP_EPSILON || dist > (1.0 + CMP_EPSILON)) {
106
return false;
107
}
108
109
r_point = p_begin + segment * -dist;
110
r_normal = normal;
111
112
return true;
113
}
114
115
real_t GodotWorldBoundaryShape2D::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const {
116
return 0;
117
}
118
119
void GodotWorldBoundaryShape2D::set_data(const Variant &p_data) {
120
ERR_FAIL_COND(p_data.get_type() != Variant::ARRAY);
121
Array arr = p_data;
122
ERR_FAIL_COND(arr.size() != 2);
123
normal = arr[0];
124
d = arr[1];
125
configure(Rect2(Vector2(-1e15, -1e15), Vector2(1e15 * 2, 1e15 * 2)));
126
}
127
128
Variant GodotWorldBoundaryShape2D::get_data() const {
129
Array arr;
130
arr.resize(2);
131
arr[0] = normal;
132
arr[1] = d;
133
return arr;
134
}
135
136
/*********************************************************/
137
/*********************************************************/
138
/*********************************************************/
139
140
void GodotSeparationRayShape2D::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
141
r_amount = 1;
142
143
if (p_normal.y > 0) {
144
*r_supports = Vector2(0, length);
145
} else {
146
*r_supports = Vector2();
147
}
148
}
149
150
bool GodotSeparationRayShape2D::contains_point(const Vector2 &p_point) const {
151
return false;
152
}
153
154
bool GodotSeparationRayShape2D::intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const {
155
return false; //rays can't be intersected
156
}
157
158
real_t GodotSeparationRayShape2D::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const {
159
return 0; //rays are mass-less
160
}
161
162
void GodotSeparationRayShape2D::set_data(const Variant &p_data) {
163
Dictionary d = p_data;
164
length = d["length"];
165
slide_on_slope = d["slide_on_slope"];
166
configure(Rect2(0, 0, 0.001, length));
167
}
168
169
Variant GodotSeparationRayShape2D::get_data() const {
170
Dictionary d;
171
d["length"] = length;
172
d["slide_on_slope"] = slide_on_slope;
173
return d;
174
}
175
176
/*********************************************************/
177
/*********************************************************/
178
/*********************************************************/
179
180
void GodotSegmentShape2D::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
181
if (Math::abs(p_normal.dot(n)) > segment_is_valid_support_threshold) {
182
r_supports[0] = a;
183
r_supports[1] = b;
184
r_amount = 2;
185
return;
186
}
187
188
real_t dp = p_normal.dot(b - a);
189
if (dp > 0) {
190
*r_supports = b;
191
} else {
192
*r_supports = a;
193
}
194
r_amount = 1;
195
}
196
197
bool GodotSegmentShape2D::contains_point(const Vector2 &p_point) const {
198
return false;
199
}
200
201
bool GodotSegmentShape2D::intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const {
202
if (!Geometry2D::segment_intersects_segment(p_begin, p_end, a, b, &r_point)) {
203
return false;
204
}
205
206
if (n.dot(p_begin) > n.dot(a)) {
207
r_normal = n;
208
} else {
209
r_normal = -n;
210
}
211
212
return true;
213
}
214
215
real_t GodotSegmentShape2D::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const {
216
return p_mass * ((a * p_scale).distance_squared_to(b * p_scale)) / 12;
217
}
218
219
void GodotSegmentShape2D::set_data(const Variant &p_data) {
220
ERR_FAIL_COND(p_data.get_type() != Variant::RECT2);
221
222
Rect2 r = p_data;
223
a = r.position;
224
b = r.size;
225
n = (b - a).orthogonal();
226
227
Rect2 aabb_new;
228
aabb_new.position = a;
229
aabb_new.expand_to(b);
230
if (aabb_new.size.x == 0) {
231
aabb_new.size.x = 0.001;
232
}
233
if (aabb_new.size.y == 0) {
234
aabb_new.size.y = 0.001;
235
}
236
configure(aabb_new);
237
}
238
239
Variant GodotSegmentShape2D::get_data() const {
240
Rect2 r;
241
r.position = a;
242
r.size = b;
243
return r;
244
}
245
246
/*********************************************************/
247
/*********************************************************/
248
/*********************************************************/
249
250
void GodotCircleShape2D::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
251
r_amount = 1;
252
*r_supports = p_normal * radius;
253
}
254
255
bool GodotCircleShape2D::contains_point(const Vector2 &p_point) const {
256
return p_point.length_squared() < radius * radius;
257
}
258
259
bool GodotCircleShape2D::intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const {
260
Vector2 line_vec = p_end - p_begin;
261
262
real_t a, b, c;
263
264
a = line_vec.dot(line_vec);
265
b = 2 * p_begin.dot(line_vec);
266
c = p_begin.dot(p_begin) - radius * radius;
267
268
real_t sqrtterm = b * b - 4 * a * c;
269
270
if (sqrtterm < 0) {
271
return false;
272
}
273
sqrtterm = Math::sqrt(sqrtterm);
274
real_t res = (-b - sqrtterm) / (2 * a);
275
276
if (res < 0 || res > 1 + CMP_EPSILON) {
277
return false;
278
}
279
280
r_point = p_begin + line_vec * res;
281
r_normal = r_point.normalized();
282
return true;
283
}
284
285
real_t GodotCircleShape2D::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const {
286
real_t a = radius * p_scale.x;
287
real_t b = radius * p_scale.y;
288
return p_mass * (a * a + b * b) / 4;
289
}
290
291
void GodotCircleShape2D::set_data(const Variant &p_data) {
292
ERR_FAIL_COND(!p_data.is_num());
293
radius = p_data;
294
configure(Rect2(-radius, -radius, radius * 2, radius * 2));
295
}
296
297
Variant GodotCircleShape2D::get_data() const {
298
return radius;
299
}
300
301
/*********************************************************/
302
/*********************************************************/
303
/*********************************************************/
304
305
void GodotRectangleShape2D::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
306
for (int i = 0; i < 2; i++) {
307
Vector2 ag;
308
ag[i] = 1.0;
309
real_t dp = ag.dot(p_normal);
310
if (Math::abs(dp) <= segment_is_valid_support_threshold) {
311
continue;
312
}
313
314
real_t sgn = dp > 0 ? 1.0 : -1.0;
315
316
r_amount = 2;
317
318
r_supports[0][i] = half_extents[i] * sgn;
319
r_supports[0][i ^ 1] = half_extents[i ^ 1];
320
321
r_supports[1][i] = half_extents[i] * sgn;
322
r_supports[1][i ^ 1] = -half_extents[i ^ 1];
323
324
return;
325
}
326
327
/* USE POINT */
328
329
r_amount = 1;
330
r_supports[0] = Vector2(
331
(p_normal.x < 0) ? -half_extents.x : half_extents.x,
332
(p_normal.y < 0) ? -half_extents.y : half_extents.y);
333
}
334
335
bool GodotRectangleShape2D::contains_point(const Vector2 &p_point) const {
336
real_t x = p_point.x;
337
real_t y = p_point.y;
338
real_t edge_x = half_extents.x;
339
real_t edge_y = half_extents.y;
340
return (x >= -edge_x) && (x < edge_x) && (y >= -edge_y) && (y < edge_y);
341
}
342
343
bool GodotRectangleShape2D::intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const {
344
return get_aabb().intersects_segment(p_begin, p_end, &r_point, &r_normal);
345
}
346
347
real_t GodotRectangleShape2D::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const {
348
Vector2 he2 = half_extents * 2 * p_scale;
349
return p_mass * he2.dot(he2) / 12.0;
350
}
351
352
void GodotRectangleShape2D::set_data(const Variant &p_data) {
353
ERR_FAIL_COND(p_data.get_type() != Variant::VECTOR2);
354
355
half_extents = p_data;
356
configure(Rect2(-half_extents, half_extents * 2.0));
357
}
358
359
Variant GodotRectangleShape2D::get_data() const {
360
return half_extents;
361
}
362
363
/*********************************************************/
364
/*********************************************************/
365
/*********************************************************/
366
367
void GodotCapsuleShape2D::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
368
Vector2 n = p_normal;
369
370
real_t h = height * 0.5 - radius; // half-height of the rectangle part
371
372
if (h > 0 && Math::abs(n.x) > segment_is_valid_support_threshold) {
373
// make it flat
374
n.y = 0.0;
375
n.x = SIGN(n.x) * radius;
376
377
r_amount = 2;
378
r_supports[0] = n;
379
r_supports[0].y += h;
380
r_supports[1] = n;
381
r_supports[1].y -= h;
382
} else {
383
n *= radius;
384
n.y += (n.y > 0) ? h : -h;
385
r_amount = 1;
386
*r_supports = n;
387
}
388
}
389
390
bool GodotCapsuleShape2D::contains_point(const Vector2 &p_point) const {
391
Vector2 p = p_point;
392
p.y = Math::abs(p.y);
393
p.y -= height * 0.5 - radius;
394
if (p.y < 0) {
395
p.y = 0;
396
}
397
398
return p.length_squared() < radius * radius;
399
}
400
401
bool GodotCapsuleShape2D::intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const {
402
real_t d = 1e10;
403
Vector2 n = (p_end - p_begin).normalized();
404
bool collided = false;
405
406
//try spheres
407
for (int i = 0; i < 2; i++) {
408
Vector2 begin = p_begin;
409
Vector2 end = p_end;
410
real_t ofs = (i == 0) ? -height * 0.5 + radius : height * 0.5 - radius;
411
begin.y += ofs;
412
end.y += ofs;
413
414
Vector2 line_vec = end - begin;
415
416
real_t a, b, c;
417
418
a = line_vec.dot(line_vec);
419
b = 2 * begin.dot(line_vec);
420
c = begin.dot(begin) - radius * radius;
421
422
real_t sqrtterm = b * b - 4 * a * c;
423
424
if (sqrtterm < 0) {
425
continue;
426
}
427
428
sqrtterm = Math::sqrt(sqrtterm);
429
real_t res = (-b - sqrtterm) / (2 * a);
430
431
if (res < 0 || res > 1 + CMP_EPSILON) {
432
continue;
433
}
434
435
Vector2 point = begin + line_vec * res;
436
Vector2 pointf(point.x, point.y - ofs);
437
real_t pd = n.dot(pointf);
438
if (pd < d) {
439
r_point = pointf;
440
r_normal = point.normalized();
441
d = pd;
442
collided = true;
443
}
444
}
445
446
Vector2 rpos, rnorm;
447
if (Rect2(Point2(-radius, -height * 0.5 + radius), Size2(radius * 2.0, height - radius * 2)).intersects_segment(p_begin, p_end, &rpos, &rnorm)) {
448
real_t pd = n.dot(rpos);
449
if (pd < d) {
450
r_point = rpos;
451
r_normal = rnorm;
452
d = pd;
453
collided = true;
454
}
455
}
456
457
//return get_aabb().intersects_segment(p_begin,p_end,&r_point,&r_normal);
458
return collided; //todo
459
}
460
461
real_t GodotCapsuleShape2D::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const {
462
Vector2 he2 = Vector2(radius * 2, height) * p_scale;
463
return p_mass * he2.dot(he2) / 12.0;
464
}
465
466
void GodotCapsuleShape2D::set_data(const Variant &p_data) {
467
ERR_FAIL_COND(p_data.get_type() != Variant::ARRAY && p_data.get_type() != Variant::VECTOR2);
468
469
if (p_data.get_type() == Variant::ARRAY) {
470
Array arr = p_data;
471
ERR_FAIL_COND(arr.size() != 2);
472
height = arr[0];
473
radius = arr[1];
474
} else {
475
Point2 p = p_data;
476
radius = p.x;
477
height = p.y;
478
}
479
480
Point2 he(radius, height * 0.5);
481
configure(Rect2(-he, he * 2));
482
}
483
484
Variant GodotCapsuleShape2D::get_data() const {
485
return Point2(height, radius);
486
}
487
488
/*********************************************************/
489
/*********************************************************/
490
/*********************************************************/
491
492
void GodotConvexPolygonShape2D::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
493
int support_idx = -1;
494
real_t d = -1e10;
495
r_amount = 0;
496
497
for (int i = 0; i < point_count; i++) {
498
//test point
499
real_t ld = p_normal.dot(points[i].pos);
500
if (ld > d) {
501
support_idx = i;
502
d = ld;
503
}
504
505
//test segment
506
if (points[i].normal.dot(p_normal) > segment_is_valid_support_threshold) {
507
r_amount = 2;
508
r_supports[0] = points[i].pos;
509
r_supports[1] = points[(i + 1) % point_count].pos;
510
return;
511
}
512
}
513
514
ERR_FAIL_COND_MSG(support_idx == -1, "Convex polygon shape support not found.");
515
516
r_amount = 1;
517
r_supports[0] = points[support_idx].pos;
518
}
519
520
bool GodotConvexPolygonShape2D::contains_point(const Vector2 &p_point) const {
521
bool out = false;
522
bool in = false;
523
524
for (int i = 0; i < point_count; i++) {
525
real_t d = points[i].normal.dot(p_point) - points[i].normal.dot(points[i].pos);
526
if (d > 0) {
527
out = true;
528
} else {
529
in = true;
530
}
531
}
532
533
return in != out;
534
}
535
536
bool GodotConvexPolygonShape2D::intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const {
537
Vector2 n = (p_end - p_begin).normalized();
538
real_t d = 1e10;
539
bool inters = false;
540
541
for (int i = 0; i < point_count; i++) {
542
Vector2 res;
543
544
if (!Geometry2D::segment_intersects_segment(p_begin, p_end, points[i].pos, points[(i + 1) % point_count].pos, &res)) {
545
continue;
546
}
547
548
real_t nd = n.dot(res);
549
if (nd < d) {
550
d = nd;
551
r_point = res;
552
r_normal = points[i].normal;
553
inters = true;
554
}
555
}
556
557
return inters;
558
}
559
560
real_t GodotConvexPolygonShape2D::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const {
561
ERR_FAIL_COND_V_MSG(point_count == 0, 0, "Convex polygon shape has no points.");
562
Rect2 aabb_new;
563
aabb_new.position = points[0].pos * p_scale;
564
for (int i = 0; i < point_count; i++) {
565
aabb_new.expand_to(points[i].pos * p_scale);
566
}
567
568
return p_mass * aabb_new.size.dot(aabb_new.size) / 12.0;
569
}
570
571
void GodotConvexPolygonShape2D::set_data(const Variant &p_data) {
572
#ifdef REAL_T_IS_DOUBLE
573
ERR_FAIL_COND(p_data.get_type() != Variant::PACKED_VECTOR2_ARRAY && p_data.get_type() != Variant::PACKED_FLOAT64_ARRAY);
574
#else
575
ERR_FAIL_COND(p_data.get_type() != Variant::PACKED_VECTOR2_ARRAY && p_data.get_type() != Variant::PACKED_FLOAT32_ARRAY);
576
#endif
577
578
if (points) {
579
memdelete_arr(points);
580
}
581
points = nullptr;
582
point_count = 0;
583
584
if (p_data.get_type() == Variant::PACKED_VECTOR2_ARRAY) {
585
Vector<Vector2> arr = p_data;
586
ERR_FAIL_COND(arr.is_empty());
587
point_count = arr.size();
588
points = memnew_arr(Point, point_count);
589
const Vector2 *r = arr.ptr();
590
591
for (int i = 0; i < point_count; i++) {
592
points[i].pos = r[i];
593
}
594
595
for (int i = 0; i < point_count; i++) {
596
Vector2 p = points[i].pos;
597
Vector2 pn = points[(i + 1) % point_count].pos;
598
points[i].normal = (pn - p).orthogonal().normalized();
599
}
600
} else {
601
Vector<real_t> dvr = p_data;
602
point_count = dvr.size() / 4;
603
ERR_FAIL_COND(point_count == 0);
604
605
points = memnew_arr(Point, point_count);
606
const real_t *r = dvr.ptr();
607
608
for (int i = 0; i < point_count; i++) {
609
int idx = i << 2;
610
points[i].pos.x = r[idx + 0];
611
points[i].pos.y = r[idx + 1];
612
points[i].normal.x = r[idx + 2];
613
points[i].normal.y = r[idx + 3];
614
}
615
}
616
617
ERR_FAIL_COND(point_count == 0);
618
Rect2 aabb_new;
619
aabb_new.position = points[0].pos;
620
for (int i = 1; i < point_count; i++) {
621
aabb_new.expand_to(points[i].pos);
622
}
623
624
configure(aabb_new);
625
}
626
627
Variant GodotConvexPolygonShape2D::get_data() const {
628
Vector<Vector2> dvr;
629
630
dvr.resize(point_count);
631
632
for (int i = 0; i < point_count; i++) {
633
dvr.set(i, points[i].pos);
634
}
635
636
return dvr;
637
}
638
639
GodotConvexPolygonShape2D::~GodotConvexPolygonShape2D() {
640
if (points) {
641
memdelete_arr(points);
642
}
643
}
644
645
//////////////////////////////////////////////////
646
647
void GodotConcavePolygonShape2D::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
648
real_t d = -1e10;
649
int idx = -1;
650
for (int i = 0; i < points.size(); i++) {
651
real_t ld = p_normal.dot(points[i]);
652
if (ld > d) {
653
d = ld;
654
idx = i;
655
}
656
}
657
658
r_amount = 1;
659
ERR_FAIL_COND(idx == -1);
660
*r_supports = points[idx];
661
}
662
663
bool GodotConcavePolygonShape2D::contains_point(const Vector2 &p_point) const {
664
return false; //sorry
665
}
666
667
bool GodotConcavePolygonShape2D::intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const {
668
if (segments.is_empty() || points.is_empty()) {
669
return false;
670
}
671
672
uint32_t *stack = (uint32_t *)alloca(sizeof(int) * bvh_depth);
673
674
enum {
675
TEST_AABB_BIT = 0,
676
VISIT_LEFT_BIT = 1,
677
VISIT_RIGHT_BIT = 2,
678
VISIT_DONE_BIT = 3,
679
VISITED_BIT_SHIFT = 29,
680
NODE_IDX_MASK = (1 << VISITED_BIT_SHIFT) - 1,
681
VISITED_BIT_MASK = ~NODE_IDX_MASK,
682
683
};
684
685
Vector2 n = (p_end - p_begin).normalized();
686
real_t d = 1e10;
687
bool inters = false;
688
689
/*
690
for(int i=0;i<bvh_depth;i++)
691
stack[i]=0;
692
*/
693
694
int level = 0;
695
696
const Segment *segmentptr = &segments[0];
697
const Vector2 *pointptr = &points[0];
698
const BVH *bvhptr = &bvh[0];
699
700
stack[0] = 0;
701
while (true) {
702
uint32_t node = stack[level] & NODE_IDX_MASK;
703
const BVH &bvh2 = bvhptr[node];
704
bool done = false;
705
706
switch (stack[level] >> VISITED_BIT_SHIFT) {
707
case TEST_AABB_BIT: {
708
bool valid = bvh2.aabb.intersects_segment(p_begin, p_end);
709
if (!valid) {
710
stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
711
712
} else {
713
if (bvh2.left < 0) {
714
const Segment &s = segmentptr[bvh2.right];
715
Vector2 a = pointptr[s.points[0]];
716
Vector2 b = pointptr[s.points[1]];
717
718
Vector2 res;
719
720
if (Geometry2D::segment_intersects_segment(p_begin, p_end, a, b, &res)) {
721
real_t nd = n.dot(res);
722
if (nd < d) {
723
d = nd;
724
r_point = res;
725
r_normal = (b - a).orthogonal().normalized();
726
inters = true;
727
}
728
}
729
730
stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
731
732
} else {
733
stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node;
734
}
735
}
736
}
737
continue;
738
case VISIT_LEFT_BIT: {
739
stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node;
740
stack[level + 1] = bvh2.left | TEST_AABB_BIT;
741
level++;
742
}
743
continue;
744
case VISIT_RIGHT_BIT: {
745
stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
746
stack[level + 1] = bvh2.right | TEST_AABB_BIT;
747
level++;
748
}
749
continue;
750
case VISIT_DONE_BIT: {
751
if (level == 0) {
752
done = true;
753
break;
754
} else {
755
level--;
756
}
757
}
758
continue;
759
}
760
761
if (done) {
762
break;
763
}
764
}
765
766
if (inters) {
767
if (n.dot(r_normal) > 0) {
768
r_normal = -r_normal;
769
}
770
}
771
772
return inters;
773
}
774
775
int GodotConcavePolygonShape2D::_generate_bvh(BVH *p_bvh, int p_len, int p_depth) {
776
if (p_len == 1) {
777
bvh_depth = MAX(p_depth, bvh_depth);
778
bvh.push_back(*p_bvh);
779
return bvh.size() - 1;
780
}
781
782
//else sort best
783
784
Rect2 global_aabb = p_bvh[0].aabb;
785
for (int i = 1; i < p_len; i++) {
786
global_aabb = global_aabb.merge(p_bvh[i].aabb);
787
}
788
789
if (global_aabb.size.x > global_aabb.size.y) {
790
SortArray<BVH, BVH_CompareX> sort;
791
sort.sort(p_bvh, p_len);
792
793
} else {
794
SortArray<BVH, BVH_CompareY> sort;
795
sort.sort(p_bvh, p_len);
796
}
797
798
int median = p_len / 2;
799
800
BVH node;
801
node.aabb = global_aabb;
802
int node_idx = bvh.size();
803
bvh.push_back(node);
804
805
int l = _generate_bvh(p_bvh, median, p_depth + 1);
806
int r = _generate_bvh(&p_bvh[median], p_len - median, p_depth + 1);
807
bvh.write[node_idx].left = l;
808
bvh.write[node_idx].right = r;
809
810
return node_idx;
811
}
812
813
void GodotConcavePolygonShape2D::set_data(const Variant &p_data) {
814
#ifdef REAL_T_IS_DOUBLE
815
ERR_FAIL_COND(p_data.get_type() != Variant::PACKED_VECTOR2_ARRAY && p_data.get_type() != Variant::PACKED_FLOAT64_ARRAY);
816
#else
817
ERR_FAIL_COND(p_data.get_type() != Variant::PACKED_VECTOR2_ARRAY && p_data.get_type() != Variant::PACKED_FLOAT32_ARRAY);
818
#endif
819
820
Rect2 aabb_new;
821
822
if (p_data.get_type() == Variant::PACKED_VECTOR2_ARRAY) {
823
Vector<Vector2> p2arr = p_data;
824
int len = p2arr.size();
825
ERR_FAIL_COND(len % 2);
826
827
segments.clear();
828
points.clear();
829
bvh.clear();
830
bvh_depth = 1;
831
832
if (len == 0) {
833
configure(aabb_new);
834
return;
835
}
836
837
const Vector2 *arr = p2arr.ptr();
838
839
HashMap<Point2, int> pointmap;
840
for (int i = 0; i < len; i += 2) {
841
Point2 p1 = arr[i];
842
Point2 p2 = arr[i + 1];
843
int idx_p1, idx_p2;
844
845
if (pointmap.has(p1)) {
846
idx_p1 = pointmap[p1];
847
} else {
848
idx_p1 = pointmap.size();
849
pointmap[p1] = idx_p1;
850
}
851
852
if (pointmap.has(p2)) {
853
idx_p2 = pointmap[p2];
854
} else {
855
idx_p2 = pointmap.size();
856
pointmap[p2] = idx_p2;
857
}
858
859
Segment s;
860
s.points[0] = idx_p1;
861
s.points[1] = idx_p2;
862
segments.push_back(s);
863
}
864
865
points.resize(pointmap.size());
866
aabb_new.position = pointmap.begin()->key;
867
for (const KeyValue<Point2, int> &E : pointmap) {
868
aabb_new.expand_to(E.key);
869
points.write[E.value] = E.key;
870
}
871
872
Vector<BVH> main_vbh;
873
main_vbh.resize(segments.size());
874
for (int i = 0; i < main_vbh.size(); i++) {
875
main_vbh.write[i].aabb.position = points[segments[i].points[0]];
876
main_vbh.write[i].aabb.expand_to(points[segments[i].points[1]]);
877
main_vbh.write[i].left = -1;
878
main_vbh.write[i].right = i;
879
}
880
881
_generate_bvh(main_vbh.ptrw(), main_vbh.size(), 1);
882
883
} else {
884
//dictionary with arrays
885
}
886
887
configure(aabb_new);
888
}
889
890
Variant GodotConcavePolygonShape2D::get_data() const {
891
Vector<Vector2> rsegments;
892
int len = segments.size();
893
rsegments.resize(len * 2);
894
Vector2 *w = rsegments.ptrw();
895
for (int i = 0; i < len; i++) {
896
w[(i << 1) + 0] = points[segments[i].points[0]];
897
w[(i << 1) + 1] = points[segments[i].points[1]];
898
}
899
900
return rsegments;
901
}
902
903
void GodotConcavePolygonShape2D::cull(const Rect2 &p_local_aabb, QueryCallback p_callback, void *p_userdata) const {
904
uint32_t *stack = (uint32_t *)alloca(sizeof(int) * bvh_depth);
905
906
enum {
907
TEST_AABB_BIT = 0,
908
VISIT_LEFT_BIT = 1,
909
VISIT_RIGHT_BIT = 2,
910
VISIT_DONE_BIT = 3,
911
VISITED_BIT_SHIFT = 29,
912
NODE_IDX_MASK = (1 << VISITED_BIT_SHIFT) - 1,
913
VISITED_BIT_MASK = ~NODE_IDX_MASK,
914
915
};
916
917
/*
918
for(int i=0;i<bvh_depth;i++)
919
stack[i]=0;
920
*/
921
922
if (segments.is_empty() || points.is_empty() || bvh.is_empty()) {
923
return;
924
}
925
926
int level = 0;
927
928
const Segment *segmentptr = &segments[0];
929
const Vector2 *pointptr = &points[0];
930
const BVH *bvhptr = &bvh[0];
931
932
stack[0] = 0;
933
while (true) {
934
uint32_t node = stack[level] & NODE_IDX_MASK;
935
const BVH &bvh2 = bvhptr[node];
936
937
switch (stack[level] >> VISITED_BIT_SHIFT) {
938
case TEST_AABB_BIT: {
939
bool valid = p_local_aabb.intersects(bvh2.aabb);
940
if (!valid) {
941
stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
942
943
} else {
944
if (bvh2.left < 0) {
945
const Segment &s = segmentptr[bvh2.right];
946
Vector2 a = pointptr[s.points[0]];
947
Vector2 b = pointptr[s.points[1]];
948
949
GodotSegmentShape2D ss(a, b, (b - a).orthogonal().normalized());
950
951
if (p_callback(p_userdata, &ss)) {
952
return;
953
}
954
stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
955
956
} else {
957
stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node;
958
}
959
}
960
}
961
continue;
962
case VISIT_LEFT_BIT: {
963
stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node;
964
stack[level + 1] = bvh2.left | TEST_AABB_BIT;
965
level++;
966
}
967
continue;
968
case VISIT_RIGHT_BIT: {
969
stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
970
stack[level + 1] = bvh2.right | TEST_AABB_BIT;
971
level++;
972
}
973
continue;
974
case VISIT_DONE_BIT: {
975
if (level == 0) {
976
return;
977
} else {
978
level--;
979
}
980
}
981
continue;
982
}
983
}
984
}
985
986