Oolite 1.91.0.7604-240417-a536cbe
Loading...
Searching...
No Matches
Octree.m
Go to the documentation of this file.
1/*
2
3Octree.m
4
5Oolite
6Copyright (C) 2004-2013 Giles C Williams and contributors
7
8This program is free software; you can redistribute it and/or
9modify it under the terms of the GNU General Public License
10as published by the Free Software Foundation; either version 2
11of the License, or (at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program; if not, write to the Free Software
20Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21MA 02110-1301, USA.
22
23*/
24
25#import "Octree.h"
26#import "OOMaths.h"
27#import "Entity.h"
28#import "OOOpenGL.h"
29#import "OODebugGLDrawing.h"
30#import "OOMacroOpenGL.h"
31#import "OODebugFlags.h"
34
35
36#ifndef NDEBUG
37#define OctreeDebugLog(format, ...) do { if (EXPECT_NOT(gDebugFlags & DEBUG_OCTREE_LOGGING)) OOLog(@"octree.debug", format, ## __VA_ARGS__); } while (0)
38#else
39#define OctreeDebugLog(...) do {} while (0)
40#endif
41
42
43typedef struct
44{
45 GLfloat radius;
46 const int *octree;
47 unsigned char *octree_collision;
49
50
51@interface Octree (Private)
52
53#ifndef OODEBUGLDRAWING_DISABLE
54
55- (void) drawOctreeFromLocation:(uint32_t)loc :(GLfloat)scale :(Vector)offset;
56- (void) drawOctreeCollisionFromLocation:(uint32_t)loc :(GLfloat)scale :(Vector)offset;
57
58- (BOOL) hasCollision;
59- (void) setHasCollision:(BOOL)value;
60
61#endif
62
64
65@end
66
67
68static const int change_oct[] = { 0, 1, 2, 4, 3, 5, 6, 7 }; // used to move from nearest to furthest octant
69
70
71static BOOL isHitByLine(const int *octbuffer, unsigned char *collbuffer, int level, GLfloat rad, Vector v0, Vector v1, Vector off, int face_hit);
72static GLfloat volumeOfOctree(Octree_details octree_details, unsigned depthLimit);
73static Vector randomFullNodeFrom(Octree_details details, Vector offset);
74
75static BOOL isHitByOctree(Octree_details axialDetails, Octree_details otherDetails, Vector delta, Triangle other_ijk);
76
77
78@implementation Octree
79
80- (id) init
81{
82 // -init makes no sense, since octrees are immutable.
83 [self release];
84 [NSException raise:NSInternalInconsistencyException format:@"Call of invalid initializer %s", __FUNCTION__];
85 return nil;
86}
87
88
89// Designated initializer.
90- (id) initWithData:(NSData *)data
91 radius:(GLfloat)radius
92{
93 if ((self = [super init]))
94 {
95 _data = [data copy];
96 _radius = radius;
97
98 NSUInteger nodeCount = [_data length] / sizeof *_octree;
99 NSParameterAssert(nodeCount < UINT32_MAX);
100 _nodeCount = (uint32_t)nodeCount;
101
102 _octree = [_data bytes];
103
104 _collisionOctree = calloc(1, _nodeCount);
105 if (_octree == NULL || _collisionOctree == NULL)
106 {
107 [self release];
108 return nil;
109 }
110 }
111
112 return self;
113}
114
115
116- (id) initWithDictionary:(NSDictionary *)dict
117{
118 NSData *data = [dict objectForKey:@"octree"];
119 if (![data isKindOfClass:[NSData class]] || ([data length] % sizeof (int)) != 0)
120 {
121 // Invalid representation.
122 [self release];
123 return nil;
124 }
125
126 return [self initWithData:data radius:[dict oo_floatForKey:@"radius"]];
127}
128
129
130- (void) dealloc
131{
132 DESTROY(_data);
133 free(_collisionOctree);
134
135 [super dealloc];
136}
137
138
139- (BOOL) hasCollision
140{
141 return _hasCollision;
142}
143
144
145- (void) setHasCollision:(BOOL)value
146{
147 _hasCollision = !!value;
148}
149
150
152{
153 Octree_details details =
154 {
155 .octree = _octree,
156 .radius = _radius,
157 .octree_collision = _collisionOctree
158 };
159 return details;
160}
161
162
163- (Octree *) octreeScaledBy:(GLfloat)factor
164{
165 // Since octree data is immutable, we can share.
166 return [[[Octree alloc] initWithData:_data radius:_radius * factor] autorelease];
167}
168
169
170static Vector offsetForOctant(int oct, GLfloat r)
171{
172 return make_vector((0.5f - (GLfloat)((oct >> 2) & 1)) * r, (0.5f - (GLfloat)((oct >> 1) & 1)) * r, (0.5f - (GLfloat)(oct & 1)) * r);
173}
174
175
176#ifndef OODEBUGLDRAWING_DISABLE
177
178- (void) drawOctree
179{
181
183 OOGL(glEnable(GL_BLEND));
184 OOGLBEGIN(GL_LINES);
185 glColor4f(0.4f, 0.4f, 0.4f, 0.5f);
186
187 // it's a series of cubes
188 [self drawOctreeFromLocation:0 :_radius : kZeroVector];
189
190 OOGLEND();
191
192 OODebugEndWireframe(state);
193 OOCheckOpenGLErrors(@"Octree after drawing %@", self);
194}
195
196
197#if 0
198#define OCTREE_COLOR(r, g, b, a) glColor4f(r, g, b, a)
199#else
200#define OCTREE_COLOR(r, g, b, a) do {} while (0)
201#endif
202
203
204- (void) drawOctreeFromLocation:(uint32_t)loc :(GLfloat)scale :(Vector)offset
205{
206 if (_octree[loc] == 0)
207 {
208 return;
209 }
210
212
213 if (_octree[loc] == -1) // full
214 {
215 // draw a cube
216 glVertex3f(-scale + offset.x, -scale + offset.y, -scale + offset.z);
217 glVertex3f(-scale + offset.x, scale + offset.y, -scale + offset.z);
218 glVertex3f(-scale + offset.x, scale + offset.y, -scale + offset.z);
219 glVertex3f(scale + offset.x, scale + offset.y, -scale + offset.z);
220 glVertex3f(scale + offset.x, scale + offset.y, -scale + offset.z);
221 glVertex3f(scale + offset.x, -scale + offset.y, -scale + offset.z);
222 glVertex3f(scale + offset.x, -scale + offset.y, -scale + offset.z);
223 glVertex3f(-scale + offset.x, -scale + offset.y, -scale + offset.z);
224
225
226 glVertex3f(-scale + offset.x, -scale + offset.y, scale + offset.z);
227 glVertex3f(-scale + offset.x, scale + offset.y, scale + offset.z);
228 glVertex3f(-scale + offset.x, scale + offset.y, scale + offset.z);
229 glVertex3f(scale + offset.x, scale + offset.y, scale + offset.z);
230 glVertex3f(scale + offset.x, scale + offset.y, scale + offset.z);
231 glVertex3f(scale + offset.x, -scale + offset.y, scale + offset.z);
232 glVertex3f(scale + offset.x, -scale + offset.y, scale + offset.z);
233 glVertex3f(-scale + offset.x, -scale + offset.y, scale + offset.z);
234
235 glVertex3f(-scale + offset.x, -scale + offset.y, -scale + offset.z);
236 glVertex3f(-scale + offset.x, -scale + offset.y, scale + offset.z);
237
238 glVertex3f(-scale + offset.x, scale + offset.y, -scale + offset.z);
239 glVertex3f(-scale + offset.x, scale + offset.y, scale + offset.z);
240
241 glVertex3f(scale + offset.x, scale + offset.y, -scale + offset.z);
242 glVertex3f(scale + offset.x, scale + offset.y, scale + offset.z);
243
244 glVertex3f(scale + offset.x, -scale + offset.y, -scale + offset.z);
245 glVertex3f(scale + offset.x, -scale + offset.y, scale + offset.z);
246 }
247 else if (_octree[loc] > 0)
248 {
249 GLfloat sc = 0.5f * scale;
250 OCTREE_COLOR(0.4f, 0.4f, 0.4f, 0.5f);
251 [self drawOctreeFromLocation:loc + _octree[loc] + 0 :sc :make_vector(offset.x - sc, offset.y - sc, offset.z - sc)];
252 OCTREE_COLOR(0.0f, 0.0f, 1.0f, 0.5f);
253 [self drawOctreeFromLocation:loc + _octree[loc] + 1 :sc :make_vector(offset.x - sc, offset.y - sc, offset.z + sc)];
254 OCTREE_COLOR(0.0f, 1.0f, 0.0f, 0.5f);
255 [self drawOctreeFromLocation:loc + _octree[loc] + 2 :sc :make_vector(offset.x - sc, offset.y + sc, offset.z - sc)];
256 OCTREE_COLOR(0.0f, 1.0f, 1.0f, 0.5f);
257 [self drawOctreeFromLocation:loc + _octree[loc] + 3 :sc :make_vector(offset.x - sc, offset.y + sc, offset.z + sc)];
258 OCTREE_COLOR(1.0f, 0.0f, 0.0f, 0.5f);
259 [self drawOctreeFromLocation:loc + _octree[loc] + 4 :sc :make_vector(offset.x + sc, offset.y - sc, offset.z - sc)];
260 OCTREE_COLOR(1.0f, 0.0f, 1.0f, 0.5f);
261 [self drawOctreeFromLocation:loc + _octree[loc] + 5 :sc :make_vector(offset.x + sc, offset.y - sc, offset.z + sc)];
262 OCTREE_COLOR(1.0f, 1.0f, 0.0f, 0.5f);
263 [self drawOctreeFromLocation:loc + _octree[loc] + 6 :sc :make_vector(offset.x + sc, offset.y + sc, offset.z - sc)];
264 OCTREE_COLOR(1.0f, 1.0f, 1.0f, 0.5f);
265 [self drawOctreeFromLocation:loc + _octree[loc] + 7 :sc :make_vector(offset.x + sc, offset.y + sc, offset.z + sc)];
266 }
267}
268
269
271
272- (void) drawOctreeCollisions
273{
275
276 // it's a series of cubes
278 if (_hasCollision)
279 {
280 [self drawOctreeCollisionFromLocation:0 :_radius :kZeroVector];
281 }
282 _hasCollision = drawTestForCollisions;
283
284 OODebugEndWireframe(state);
285 OOCheckOpenGLErrors(@"Octree after drawing collisions for %@", self);
286}
287
288
289- (void) drawOctreeCollisionFromLocation:(uint32_t)loc :(GLfloat)scale :(Vector)offset
290{
291 if (_octree[loc] == 0)
292 {
293 return;
294 }
295
296 if ((_octree[loc] != 0)&&(_collisionOctree[loc] != (unsigned char)0)) // full - draw
297 {
299
300 GLfloat red = (GLfloat)(_collisionOctree[loc])/255.0;
301 glColor4f(1.0, 0.0, 0.0, red); // 50% translucent
302
304 _collisionOctree[loc]--;
305
306 // draw a cube
307 OOGL(glDisable(GL_CULL_FACE)); // face culling
308
309 OOGL(glDisable(GL_TEXTURE_2D));
310
311 OOGLBEGIN(GL_LINE_STRIP);
312 glVertex3f(-scale + offset.x, -scale + offset.y, -scale + offset.z);
313 glVertex3f(-scale + offset.x, scale + offset.y, -scale + offset.z);
314 glVertex3f(scale + offset.x, scale + offset.y, -scale + offset.z);
315 glVertex3f(scale + offset.x, -scale + offset.y, -scale + offset.z);
316 glVertex3f(-scale + offset.x, -scale + offset.y, -scale + offset.z);
317 OOGLEND();
318
319 OOGLBEGIN(GL_LINE_STRIP);
320 glVertex3f(-scale + offset.x, -scale + offset.y, scale + offset.z);
321 glVertex3f(-scale + offset.x, scale + offset.y, scale + offset.z);
322 glVertex3f(scale + offset.x, scale + offset.y, scale + offset.z);
323 glVertex3f(scale + offset.x, -scale + offset.y, scale + offset.z);
324 glVertex3f(-scale + offset.x, -scale + offset.y, scale + offset.z);
325 OOGLEND();
326
327 OOGLBEGIN(GL_LINES);
328 glVertex3f(-scale + offset.x, -scale + offset.y, -scale + offset.z);
329 glVertex3f(-scale + offset.x, -scale + offset.y, scale + offset.z);
330
331 glVertex3f(-scale + offset.x, scale + offset.y, -scale + offset.z);
332 glVertex3f(-scale + offset.x, scale + offset.y, scale + offset.z);
333
334 glVertex3f(scale + offset.x, scale + offset.y, -scale + offset.z);
335 glVertex3f(scale + offset.x, scale + offset.y, scale + offset.z);
336
337 glVertex3f(scale + offset.x, -scale + offset.y, -scale + offset.z);
338 glVertex3f(scale + offset.x, -scale + offset.y, scale + offset.z);
339 OOGLEND();
340
341 OOGL(glEnable(GL_CULL_FACE)); // face culling
342 }
343 if (_octree[loc] > 0)
344 {
345 GLfloat sc = 0.5f * scale;
346 [self drawOctreeCollisionFromLocation:loc + _octree[loc] + 0 :sc :make_vector(offset.x - sc, offset.y - sc, offset.z - sc)];
347 [self drawOctreeCollisionFromLocation:loc + _octree[loc] + 1 :sc :make_vector(offset.x - sc, offset.y - sc, offset.z + sc)];
348 [self drawOctreeCollisionFromLocation:loc + _octree[loc] + 2 :sc :make_vector(offset.x - sc, offset.y + sc, offset.z - sc)];
349 [self drawOctreeCollisionFromLocation:loc + _octree[loc] + 3 :sc :make_vector(offset.x - sc, offset.y + sc, offset.z + sc)];
350 [self drawOctreeCollisionFromLocation:loc + _octree[loc] + 4 :sc :make_vector(offset.x + sc, offset.y - sc, offset.z - sc)];
351 [self drawOctreeCollisionFromLocation:loc + _octree[loc] + 5 :sc :make_vector(offset.x + sc, offset.y - sc, offset.z + sc)];
352 [self drawOctreeCollisionFromLocation:loc + _octree[loc] + 6 :sc :make_vector(offset.x + sc, offset.y + sc, offset.z - sc)];
353 [self drawOctreeCollisionFromLocation:loc + _octree[loc] + 7 :sc :make_vector(offset.x + sc, offset.y + sc, offset.z + sc)];
354 }
355}
356#endif // OODEBUGLDRAWING_DISABLE
357
358
359static BOOL isHitByLineSub(const int *octbuffer, unsigned char *collbuffer, int nextLevel, GLfloat rad, GLfloat rd2, Vector v0, Vector v1, int octantMask)
360{
361 if (octbuffer[nextLevel + octantMask])
362 {
363 Vector moveLine = offsetForOctant(octantMask, rad);
364 return isHitByLine(octbuffer, collbuffer, nextLevel + octantMask, rd2, v0, v1, moveLine, 0);
365 }
366 else return NO;
367}
368
369
370static BOOL hasCollided = NO;
371static GLfloat hit_dist = 0.0;
372static BOOL isHitByLine(const int *octbuffer, unsigned char *collbuffer, int level, GLfloat rad, Vector v0, Vector v1, Vector off, int face_hit)
373{
374 // displace the line by the offset
375 Vector u0 = make_vector(v0.x + off.x, v0.y + off.y, v0.z + off.z);
376 Vector u1 = make_vector(v1.x + off.x, v1.y + off.y, v1.z + off.z);
377
378 OctreeDebugLog(@"DEBUG octant: [%d] radius: %.2f vs. line: (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f)",
379 level, rad, u0.x, u0.y, u0.z, u1.x, u1.y, u1.z);
380
381 if (octbuffer[level] == 0)
382 {
383 OctreeDebugLog(@"DEBUG Hit an empty octant: [%d]", level);
384 return NO;
385 }
386
387 if (octbuffer[level] == -1)
388 {
389 OctreeDebugLog(@"DEBUG Hit a solid octant: [%d]", level);
390 collbuffer[level] = 2; // green
391 hit_dist = sqrt(u0.x * u0.x + u0.y * u0.y + u0.z * u0.z);
392 return YES;
393 }
394
395 int faces = face_hit;
396 if (faces == 0)
397 faces = lineCubeIntersection(u0, u1, rad);
398
399 if (faces == 0)
400 {
401 OctreeDebugLog(@"----> Line misses octant: [%d].", level);
402 return NO;
403 }
404
405 int octantIntersected = 0;
406
407 if (faces > 0)
408 {
409 Vector vi = lineIntersectionWithFace(u0, u1, faces, rad);
410
411 if (CUBE_FACE_FRONT & faces)
412 octantIntersected = ((vi.x < 0.0)? 1: 5) + ((vi.y < 0.0)? 0: 2);
413 if (CUBE_FACE_BACK & faces)
414 octantIntersected = ((vi.x < 0.0)? 0: 4) + ((vi.y < 0.0)? 0: 2);
415
416 if (CUBE_FACE_RIGHT & faces)
417 octantIntersected = ((vi.y < 0.0)? 4: 6) + ((vi.z < 0.0)? 0: 1);
418 if (CUBE_FACE_LEFT & faces)
419 octantIntersected = ((vi.y < 0.0)? 0: 2) + ((vi.z < 0.0)? 0: 1);
420
421 if (CUBE_FACE_TOP & faces)
422 octantIntersected = ((vi.x < 0.0)? 2: 6) + ((vi.z < 0.0)? 0: 1);
423 if (CUBE_FACE_BOTTOM & faces)
424 octantIntersected = ((vi.x < 0.0)? 0: 4) + ((vi.z < 0.0)? 0: 1);
425
426 OctreeDebugLog(@"----> found intersection with face 0x%2x of cube of radius %.2f at (%.2f, %.2f, %.2f) octant:%d",
427 faces, rad, vi.x, vi.y, vi.z, octantIntersected);
428 }
429 else
430 {
431 OctreeDebugLog(@"----> inside cube of radius %.2f octant:%d", rad, octantIntersected);
432 }
433
434 hasCollided = YES;
435
436 collbuffer[level] = 1; // red
437
438 OctreeDebugLog(@"%@", @"----> testing octants...");
439
440 int nextLevel = level + octbuffer[level];
441
442 GLfloat rd2 = 0.5f * rad;
443
444 // first test the intersected octant, then the three adjacent, then the next three adjacent, the finally the diagonal octant
445 int oct0, oct1, oct2, oct3; // test oct0 then oct1, oct2, oct3 then (7 - oct1), (7 - oct2), (7 - oct3) then (7 - oct0)
446 oct0 = octantIntersected;
447 oct1 = oct0 ^ 0x01; // adjacent x
448 oct2 = oct0 ^ 0x02; // adjacent y
449 oct3 = oct0 ^ 0x04; // adjacent z
450
451 OctreeDebugLog(@"----> testing first octant hit [+%d]", oct0);
452 if (isHitByLineSub(octbuffer, collbuffer, nextLevel, rad, rd2, u0, u1, oct0)) return YES; // first octant
453
454 // test the three adjacent octants
455
456 OctreeDebugLog(@"----> testing next three octants [+%d] [+%d] [+%d]", oct1, oct2, oct3);
457 if (isHitByLineSub(octbuffer, collbuffer, nextLevel, rad, rd2, u0, u1, oct1)) return YES; // second octant
458 if (isHitByLineSub(octbuffer, collbuffer, nextLevel, rad, rd2, u0, u1, oct2)) return YES; // third octant
459 if (isHitByLineSub(octbuffer, collbuffer, nextLevel, rad, rd2, u0, u1, oct3)) return YES; // fourth octant
460
461 // go to the next four octants
462
463 oct0 ^= 0x07; oct1 ^= 0x07; oct2 ^= 0x07; oct3 ^= 0x07;
464
465 OctreeDebugLog(@"----> testing back three octants [+%d] [+%d] [+%d]", oct1, oct2, oct3);
466 if (isHitByLineSub(octbuffer, collbuffer, nextLevel, rad, rd2, u0, u1, oct1)) return YES; // fifth octant
467 if (isHitByLineSub(octbuffer, collbuffer, nextLevel, rad, rd2, u0, u1, oct2)) return YES; // sixth octant
468 if (isHitByLineSub(octbuffer, collbuffer, nextLevel, rad, rd2, u0, u1, oct3)) return YES; // seventh octant
469
470 // and check the last octant
471 OctreeDebugLog(@"----> testing final octant [+%d]", oct0);
472 if (isHitByLineSub(octbuffer, collbuffer, nextLevel, rad, rd2, u0, u1, oct0)) return YES; // last octant
473
474 return NO;
475}
476
477- (GLfloat) isHitByLine:(Vector)v0 :(Vector)v1
478{
479 memset(_collisionOctree, 0, _nodeCount * sizeof *_collisionOctree);
480 hasCollided = NO;
481
482 if (isHitByLine(_octree, _collisionOctree, 0, _radius, v0, v1, kZeroVector, 0))
483 {
484 OctreeDebugLog(@"DEBUG Hit at distance %.2f", hit_dist);
485 _hasCollision = hasCollided;
486 return hit_dist;
487 }
488 else
489 {
490 OctreeDebugLog(@"%@", @"DEBUG Missed!");
491 _hasCollision = hasCollided;
492 return 0.0f;
493 }
494}
495
496
497static BOOL isHitByOctree(Octree_details axialDetails,
498 Octree_details otherDetails, Vector otherPosition, Triangle other_ijk)
499{
500 const int *axialBuffer = axialDetails.octree;
501 const int *otherBuffer = otherDetails.octree;
502
503 if (axialBuffer[0] == 0)
504 {
505 OctreeDebugLog(@"%@", @"DEBUG Axial octree is empty.");
506 return NO;
507 }
508
509 if (!otherBuffer)
510 {
511 OctreeDebugLog(@"%@", @"DEBUG Other octree is undefined.");
512 return NO;
513 }
514
515 if (otherBuffer[0] == 0)
516 {
517 OctreeDebugLog(@"%@", @"DEBUG Other octree is empty.");
518 return NO;
519 }
520
521 GLfloat axialRadius = axialDetails.radius;
522 GLfloat otherRadius = otherDetails.radius;
523
524 if (otherRadius < axialRadius) // test axial cube against other sphere
525 {
526 // 'crude and simple' - test sphere against cube...
527 if ((otherPosition.x + otherRadius < -axialRadius)||(otherPosition.x - otherRadius > axialRadius)||
528 (otherPosition.y + otherRadius < -axialRadius)||(otherPosition.y - otherRadius > axialRadius)||
529 (otherPosition.z + otherRadius < -axialRadius)||(otherPosition.z - otherRadius > axialRadius))
530 {
531 OctreeDebugLog(@"%@", @"----> Other sphere does not intersect axial cube");
532 return NO;
533 }
534 }
535 else // test axial sphere against other cube
536 {
537 Vector d2 = vector_flip(otherPosition);
538 Vector axialPosition = resolveVectorInIJK(d2, other_ijk);
539 if ((axialPosition.x + axialRadius < -otherRadius)||(axialPosition.x - axialRadius > otherRadius)||
540 (axialPosition.y + axialRadius < -otherRadius)||(axialPosition.y - axialRadius > otherRadius)||
541 (axialPosition.z + axialRadius < -otherRadius)||(axialPosition.z - axialRadius > otherRadius))
542 {
543 OctreeDebugLog(@"%@", @"----> Axial sphere does not intersect other cube");
544 return NO;
545 }
546 }
547
548 // from here on, this Octree and the other Octree are considered to be intersecting
549 unsigned char *axialCollisionBuffer = axialDetails.octree_collision;
550 unsigned char *otherCollisionBuffer = otherDetails.octree_collision;
551 if (axialBuffer[0] == -1)
552 {
553 // we are SOLID - is the other octree?
554 if (otherBuffer[0] == -1)
555 {
556 // YES so octrees collide
557 axialCollisionBuffer[0] = (unsigned char)255; // mark
558 otherCollisionBuffer[0] = (unsigned char)255; // mark
559
560 OctreeDebugLog(@"%@", @"DEBUG Octrees collide!");
561 return YES;
562 }
563 // the other octree must be decomposed
564 // and each of its octants tested against the axial octree
565 // if any of them collides with this octant
566 // then we have a solid collision
567
568 OctreeDebugLog(@"%@", @"----> testing other octants...");
569
570 // work out the nearest octant to the axial octree
571 int nearest_oct = ((otherPosition.x > 0.0)? 0:4)|((otherPosition.y > 0.0)? 0:2)|((otherPosition.z > 0.0)? 0:1);
572
573 int nextLevel = otherBuffer[0];
574 const int *nextBuffer = &otherBuffer[nextLevel];
575 unsigned char *nextCollisionBuffer = &otherCollisionBuffer[nextLevel];
576 Octree_details nextDetails;
577 Vector voff, nextPosition;
578 unsigned i, oct;
579
580 nextDetails.radius = 0.5f * otherRadius;
581 for (i = 0; i < 8; i++)
582 {
583 oct = nearest_oct ^ change_oct[i]; // work from nearest to furthest
584 if (nextBuffer[oct]) // don't test empty octants
585 {
586 nextDetails.octree = &nextBuffer[oct];
587 nextDetails.octree_collision = &nextCollisionBuffer[oct];
588
589 voff = resolveVectorInIJK(offsetForOctant(oct, otherRadius), other_ijk);
590 nextPosition = vector_subtract(otherPosition, voff);
591 if (isHitByOctree(axialDetails, nextDetails, nextPosition, other_ijk)) // test octant
592 return YES;
593 }
594 }
595
596 // otherwise
597 return NO;
598 }
599 // we are not solid
600 // we must test each of our octants against
601 // the other octree, if any of them collide
602 // we have a solid collision
603
604 OctreeDebugLog(@"%@", @"----> testing axial octants...");
605
606 // work out the nearest octant to the other octree
607 int nearest_oct = ((otherPosition.x > 0.0)? 4:0)|((otherPosition.y > 0.0)? 2:0)|((otherPosition.z > 0.0)? 1:0);
608
609 int nextLevel = axialBuffer[0];
610 const int *nextBuffer = &axialBuffer[nextLevel];
611 unsigned char *nextCollisionBuffer = &axialCollisionBuffer[nextLevel];
612 Vector nextPosition;
613 Octree_details nextDetails;
614 unsigned i, oct;
615
616 nextDetails.radius = 0.5f * axialRadius;
617 for (i = 0; i < 8; i++)
618 {
619 oct = nearest_oct ^ change_oct[i]; // work from nearest to furthest
620 if (nextBuffer[oct]) // don't test empty octants
621 {
622 nextDetails.octree = &nextBuffer[oct];
623 nextDetails.octree_collision = &nextCollisionBuffer[oct];
624
625 nextPosition = vector_add(otherPosition, offsetForOctant(oct, axialRadius));
626 if (isHitByOctree(nextDetails, otherDetails, nextPosition, other_ijk))
627 {
628 return YES; // test octant
629 }
630 }
631 }
632 // otherwise we're done!
633 return NO;
634}
635
636
637- (BOOL) isHitByOctree:(Octree *)other withOrigin:(Vector)v0 andIJK:(Triangle)ijk
638{
639 if (other == nil) return NO;
640
641 BOOL hit = isHitByOctree([self octreeDetails], [other octreeDetails], v0, ijk);
642
643 _hasCollision = _hasCollision || hit;
644 [other setHasCollision: [other hasCollision] || hit];
645
646 return hit;
647}
648
649
650- (BOOL) isHitByOctree:(Octree *)other withOrigin:(Vector)v0 andIJK:(Triangle)ijk andScales:(GLfloat) s1 :(GLfloat)s2
651{
652 Octree_details details1 = [self octreeDetails];
653 Octree_details details2 = [other octreeDetails];
654
655 details1.radius *= s1;
656 details2.radius *= s2;
657
658 BOOL hit = isHitByOctree(details1, details2, v0, ijk);
659
660 _hasCollision = _hasCollision || hit;
661 [other setHasCollision: [other hasCollision] || hit];
662
663 return hit;
664}
665
666
667- (NSDictionary *) dictionaryRepresentation
668{
669 return [NSDictionary dictionaryWithObjectsAndKeys:
670 _data, @"octree",
671 [NSNumber numberWithFloat:_radius], @"radius",
672 nil];
673}
674
675
676static GLfloat volumeOfOctree(Octree_details octree_details, unsigned depthLimit)
677{
678 const int *octBuffer = octree_details.octree;
679 GLfloat octRadius = octree_details.radius;
680
681 if (octBuffer[0] == 0)
682 {
683 return 0.0f;
684 }
685
686 if (octBuffer[0] == -1 || depthLimit == 0)
687 {
688 return octRadius * octRadius * octRadius;
689 }
690 depthLimit--;
691
692 // We are not empty or solid.
693 // We sum the volume of each of our octants.
694 GLfloat sumVolume = 0.0f;
695 Octree_details nextDetails;
696 int nextLevel = octBuffer[0];
697 const int *nextBuffer = &octBuffer[nextLevel];
698 unsigned i;
699
700 nextDetails.radius = 0.5f * octRadius;
701 nextDetails.octree_collision = NULL; // Placate static analyzer
702
703 for (i = 0; i < 8; i++)
704 {
705 if (nextBuffer[i]) // don't test empty octants
706 {
707 nextDetails.octree = &nextBuffer[i];
708 sumVolume += volumeOfOctree(nextDetails, depthLimit);
709 }
710 }
711 return sumVolume;
712}
713
714
715- (GLfloat) volume
716{
717 /* For backwards compatibility, limit octree iteration for volume
718 calculation to five levels. Raising the limit means lower calculated
719 volumes for large ships.
720
721 EMMSTRAN: consider raising the limit but fudging mass lock calculations
722 to compensate. See http://aegidian.org/bb/viewtopic.php?f=3&t=9176 .
723 Then again, five levels of iteration might be fine.
724 -- Ahruman 2011-03-10
725 */
726 return volumeOfOctree([self octreeDetails], 5);
727}
728
729
730static Vector randomFullNodeFrom(Octree_details details, Vector offset)
731{
732 const int *octBuffer = details.octree;
733 GLfloat octRadius = details.radius;
734
735 if (octBuffer[0] == 0) return offset;
736 if (octBuffer[0] == -1) return offset;
737
738 // We are not empty or solid.
739 // Pick a location from a random octant.
740 Octree_details nextDetails;
741 int nextLevel = octBuffer[0];
742 const int *nextBuffer = &octBuffer[nextLevel];
743 unsigned i, oct;
744
745 nextDetails.radius = 0.5f * octRadius;
746 nextDetails.octree_collision = NULL;
747 oct = Ranrot() & 7;
748
749 for (i = 0; i < 8; i++)
750 {
751 int octant = oct ^ i;
752 if (nextBuffer[octant]) // don't test empty octants
753 {
754 nextDetails.octree = &nextBuffer[octant];
755 Vector voff = vector_add(offset, offsetForOctant(octant, octRadius));
756 return randomFullNodeFrom(nextDetails, voff);
757 }
758 }
759 return offset;
760}
761
762
763- (Vector) randomPoint
764{
765 return randomFullNodeFrom([self octreeDetails], kZeroVector);
766}
767
768
769#ifndef NDEBUG
770- (size_t) totalSize
771{
772 return [self oo_objectSize] + _nodeCount * [_data oo_objectSize] + [_data length] + _nodeCount * sizeof *_collisionOctree;
773}
774#endif
775
776@end
777
778
779enum
780{
781 /*
782 Initial capacity of OOOctreeBuilder. 16 KibiEntries is enough to handle
783 all but 2 built-in models; I expect similar results for OXPs, since
784 geometry detail has limited effect on the octree. Large models with
785 higher depth are more likely to need growth.
786 */
787 kMinimumBuilderCapacity = 16 << 10
789
790
791@implementation OOOctreeBuilder
792
793// Extract top of state stack.
795{
796 return &self->_stateStack[self->_level];
797}
798
799
800// SetNode(): set the value of an entry in _octree, making space if needed.
801static void SetNode_slow(OOOctreeBuilder *self, uint32_t index, int value) NO_INLINE_FUNC;
802OOINLINE void SetNode(OOOctreeBuilder *self, uint32_t index, int value)
803{
804 if (index < self->_capacity)
805 {
806 self->_octree[index] = value;
807 }
808 else
809 {
810 SetNode_slow(self, index, value);
811 }
812}
813
814
815// InsertNode(): set the node at the current insertion point, and increment insertion point.
816OOINLINE void InsertNode(OOOctreeBuilder *self, int value)
817{
818 NSCAssert(State(self)->remaining > 0, @"Attempt to add node to a full parent in octree builder.");
819 State(self)->remaining--;
820
821 SetNode(self, State(self)->insertionPoint++, value);
822}
823
824
825- (id) init
826{
827 if ((self = [super init]))
828 {
829 _capacity = kMinimumBuilderCapacity;
830 _octree = malloc(_capacity * sizeof *_octree);
831 if (_octree == NULL)
832 {
833 [self release];
834 return nil;
835 }
836
837 /*
838 We're initially inserting into the root slot, which must exist and
839 takes a single node.
840 */
841 _nodeCount = 1;
842 State(self)->remaining = 1;
843 }
844 return self;
845}
846
847
848- (void) dealloc
849{
850 free(_octree);
851
852 [super dealloc];
853}
854
855
856- (Octree *) buildOctreeWithRadius:(GLfloat)radius
857{
858 NSAssert(State(self)->remaining == 0 && _level == 0, @"Attempt to produce octree from an octree builder in an incomplete state.");
859
860 size_t dataSize = _nodeCount * sizeof *_octree;
861 int *resized = realloc(_octree, dataSize);
862 if (resized == NULL) resized = _octree;
863
864 /* Hand over the bytes to the data object, which will be used directly by
865 the Octree.
866 */
867 NSData *data = [NSData dataWithBytesNoCopy:resized
868 length:dataSize
869 freeWhenDone:YES];
870
871 _octree = NULL;
872 _nodeCount = 0;
873 _capacity = 0;
874
875 return [[[Octree alloc] initWithData:data radius:radius] autorelease];
876}
877
878
879- (void) writeSolid
880{
881 InsertNode(self, -1);
882}
883
884
885- (void) writeEmpty
886{
887 InsertNode(self, 0);
888}
889
890
891- (void) beginInnerNode
892{
893 NSAssert(_level < kMaxOctreeDepth, @"Attempt to build octree exceeding maximum depth.");
894
895 // Insert relative offset to next free space.
896 uint32_t newInsertionPoint = _nodeCount;
897 InsertNode(self, (int)_nodeCount - State(self)->insertionPoint);
898
899 /*
900 Leave space for eight nodes.
901
902 NOTE: this may leave memory uninitialized or leave _nodeCount pointing
903 past the end of the buffer. A valid sequence of eight child insertions
904 will fix both these problems by writing to all of the new slots.
905 */
906 _nodeCount += 8;
907
908 // Push state and set up new "stack frame".
909 _level++;
910 State(self)->insertionPoint = newInsertionPoint;
911 State(self)->remaining = 8;
912}
913
914
915- (void) endInnerNode
916{
917 NSAssert(State(self)->remaining == 0, @"Attempt to end an inner octree node with fewer than eight children.");
918 NSAssert1(_level > 0, @"Unbalanced call to %s", __FUNCTION__);
919
920 _level--;
921
922 /*
923 Check if the last eight nodes are solid. If so, we just inserted an
924 entirely solid subtree and can fold it into a single solid node. An
925 entirely solid subtree will always use the last eight nodes (any solid
926 subtrees within a child node will have been folded already), so this
927 simple approach to folding will produce an optimal result.
928
929 We could do the same for empty nodes, but OOMeshToOctreeConverter will
930 never recurse into an empty subtree.
931 */
932
933 NSAssert(_nodeCount > 8, @"After ending an inner node, there must be at least eight nodes in buffer.");
934 for (uint_fast32_t node = _nodeCount - 8; node < _nodeCount; node++)
935 {
936 if (_octree[node] != -1) return;
937 }
938
939 // If we got here, subtree is solid; fold it into a solid node.
940 _octree[State(self)->insertionPoint - 1] = -1;
941 _nodeCount -= 8;
942}
943
944
945// Slow path for SetNode() when writing beyond _capacity: expand, then perform set.
946static void SetNode_slow(OOOctreeBuilder *self, uint32_t index, int value)
947{
948 uint32_t newCapacity = MAX(self->_capacity * 2, (uint32_t)kMinimumBuilderCapacity);
949 newCapacity = MAX(newCapacity, index + 1);
950
951 int *newBuffer = realloc(self->_octree, newCapacity * sizeof *newBuffer);
952 if (EXPECT_NOT(newBuffer == NULL))
953 {
954 [NSException raise:NSMallocException format:@"Failed to allocate memory for octree."];
955 }
956
957 self->_octree = newBuffer;
958 self->_capacity = newCapacity;
959 self->_octree[index] = value;
960}
961
962
963#ifndef NDEBUG
964/* This method exists purely to suppress Clang static analyzer warnings that
965 these ivars are unused (but may be used by categories, which they are).
966*/
967- (BOOL) suppressClangStuff
968{
969 return &_stateStack && 0;
970}
971#endif
972
973@end
#define DESTROY(x)
Definition OOCocoa.h:77
void OODebugEndWireframe(OODebugWFState state)
OODebugWFState OODebugBeginWireframe(BOOL ignoreZ)
#define NO_INLINE_FUNC
#define EXPECT_NOT(x)
#define OOINLINE
#define OO_ENTER_OPENGL()
#define MAX(A, B)
Definition OOMaths.h:114
#define OOGLBEGIN
Definition OOOpenGL.h:253
BOOL OOCheckOpenGLErrors(NSString *format,...)
Definition OOOpenGL.m:39
#define OOGL(statement)
Definition OOOpenGL.h:251
#define OOGLEND
Definition OOOpenGL.h:254
return self
return nil
const Vector kZeroVector
Definition OOVector.m:28
Vector lineIntersectionWithFace(Vector p1, Vector p2, long mask, GLfloat rd)
Definition OOVoxel.m:77
int lineCubeIntersection(Vector v0, Vector v1, GLfloat rd)
Definition OOVoxel.m:140
@ kMaxOctreeDepth
Definition Octree.h:88
static BOOL drawTestForCollisions
Definition Octree.m:270
static GLfloat hit_dist
Definition Octree.m:371
#define OctreeDebugLog(format,...)
Definition Octree.m:37
static const int change_oct[]
Definition Octree.m:68
@ kMinimumBuilderCapacity
Definition Octree.m:787
static BOOL isHitByLine(const int *octbuffer, unsigned char *collbuffer, int level, GLfloat rad, Vector v0, Vector v1, Vector off, int face_hit)
static GLfloat volumeOfOctree(Octree_details octree_details, unsigned depthLimit)
static BOOL isHitByOctree(Octree_details axialDetails, Octree_details otherDetails, Vector delta, Triangle other_ijk)
static BOOL hasCollided
Definition Octree.m:370
#define OCTREE_COLOR(r, g, b, a)
Definition Octree.m:200
static Vector randomFullNodeFrom(Octree_details details, Vector offset)
BOOL hasCollision()
Definition Octree.m:139
Octree_details octreeDetails()
Definition Octree.m:151
OOINLINE struct OOOctreeBuildState * State(OOOctreeBuilder *self)
Definition Octree.m:794
uint_fast8_t _level
Definition Octree.h:102
OOINLINE void SetNode(OOOctreeBuilder *self, uint32_t index, int value)
Definition Octree.m:802
Octree_details octreeDetails()
Definition Octree.m:151
void setHasCollision:(BOOL value)
Definition Octree.m:145
static BOOL isHitByLine(const int *octbuffer, unsigned char *collbuffer, int level, GLfloat rad, Vector v0, Vector v1, Vector off, int face_hit)
Definition Octree.m:372
static BOOL isHitByLineSub(const int *octbuffer, unsigned char *collbuffer, int nextLevel, GLfloat rad, GLfloat rd2, Vector v0, Vector v1, int octantMask)
Definition Octree.m:359
static Vector randomFullNodeFrom(Octree_details details, Vector offset)
Definition Octree.m:730
BOOL hasCollision()
Definition Octree.m:139
voidpf uLong offset
Definition ioapi.h:140
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
unsigned Ranrot(void)
unsigned char * octree_collision
Definition Octree.m:47
GLfloat radius
Definition Octree.m:45
const int * octree
Definition Octree.m:46