47 unsigned char *octree_collision;
51@interface Octree (Private)
53#ifndef OODEBUGLDRAWING_DISABLE
55- (void) drawOctreeFromLocation:(uint32_t)loc :(GLfloat)scale :(Vector)offset;
56- (void) drawOctreeCollisionFromLocation:(uint32_t)loc :(GLfloat)scale :(Vector)offset;
59- (void) setHasCollision:(BOOL)value;
68static const int change_oct[] = { 0, 1, 2, 4, 3, 5, 6, 7 };
71static BOOL
isHitByLine(
const int *octbuffer,
unsigned char *collbuffer,
int level, GLfloat rad, Vector v0, Vector v1, Vector off,
int face_hit);
84 [NSException raise:NSInternalInconsistencyException format:@"Call of invalid initializer %s", __FUNCTION__];
90- (id) initWithData:(NSData *)data
91 radius:(GLfloat)radius
93 if ((
self = [super init]))
98 NSUInteger nodeCount = [_data length] /
sizeof *_octree;
99 NSParameterAssert(nodeCount < UINT32_MAX);
100 _nodeCount = (uint32_t)nodeCount;
102 _octree = [_data bytes];
104 _collisionOctree = calloc(1, _nodeCount);
105 if (_octree == NULL || _collisionOctree == NULL)
116- (id) initWithDictionary:(NSDictionary *)dict
118 NSData *data = [dict objectForKey:@"octree"];
119 if (![data isKindOfClass:[NSData
class]] || ([data length] %
sizeof (
int)) != 0)
126 return [
self initWithData:data radius:[dict oo_floatForKey:@"radius"]];
133 free(_collisionOctree);
272- (void) drawOctreeCollisions
280 [
self drawOctreeCollisionFromLocation:0 :_radius :kZeroVector];
289- (void) drawOctreeCollisionFromLocation:(uint32_t)loc :(GLfloat)scale :(Vector)offset
291 if (_octree[loc] == 0)
296 if ((_octree[loc] != 0)&&(_collisionOctree[loc] != (
unsigned char)0))
300 GLfloat red = (GLfloat)(_collisionOctree[loc])/255.0;
301 glColor4f(1.0, 0.0, 0.0, red);
304 _collisionOctree[loc]--;
307 OOGL(glDisable(GL_CULL_FACE));
309 OOGL(glDisable(GL_TEXTURE_2D));
341 OOGL(glEnable(GL_CULL_FACE));
343 if (_octree[loc] > 0)
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)];
359static BOOL
isHitByLineSub(
const int *octbuffer,
unsigned char *collbuffer,
int nextLevel, GLfloat rad, GLfloat rd2, Vector v0, Vector v1,
int octantMask)
361 if (octbuffer[nextLevel + octantMask])
363 Vector moveLine = offsetForOctant(octantMask, rad);
364 return isHitByLine(octbuffer, collbuffer, nextLevel + octantMask, rd2, v0, v1, moveLine, 0);
372static BOOL
isHitByLine(
const int *octbuffer,
unsigned char *collbuffer,
int level, GLfloat rad, Vector v0, Vector v1, Vector off,
int face_hit)
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);
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);
381 if (octbuffer[level] == 0)
387 if (octbuffer[level] == -1)
390 collbuffer[level] = 2;
391 hit_dist = sqrt(u0.x * u0.x + u0.y * u0.y + u0.z * u0.z);
395 int faces = face_hit;
405 int octantIntersected = 0;
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);
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);
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);
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);
431 OctreeDebugLog(
@"----> inside cube of radius %.2f octant:%d", rad, octantIntersected);
436 collbuffer[level] = 1;
440 int nextLevel = level + octbuffer[level];
442 GLfloat rd2 = 0.5f * rad;
445 int oct0, oct1, oct2, oct3;
446 oct0 = octantIntersected;
452 if (isHitByLineSub(octbuffer, collbuffer, nextLevel, rad, rd2, u0, u1, oct0))
return YES;
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;
458 if (isHitByLineSub(octbuffer, collbuffer, nextLevel, rad, rd2, u0, u1, oct2))
return YES;
459 if (isHitByLineSub(octbuffer, collbuffer, nextLevel, rad, rd2, u0, u1, oct3))
return YES;
463 oct0 ^= 0x07; oct1 ^= 0x07; oct2 ^= 0x07; oct3 ^= 0x07;
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;
467 if (isHitByLineSub(octbuffer, collbuffer, nextLevel, rad, rd2, u0, u1, oct2))
return YES;
468 if (isHitByLineSub(octbuffer, collbuffer, nextLevel, rad, rd2, u0, u1, oct3))
return YES;
472 if (isHitByLineSub(octbuffer, collbuffer, nextLevel, rad, rd2, u0, u1, oct0))
return YES;
498 Octree_details otherDetails, Vector otherPosition, Triangle other_ijk)
500 const int *axialBuffer = axialDetails.
octree;
501 const int *otherBuffer = otherDetails.
octree;
503 if (axialBuffer[0] == 0)
515 if (otherBuffer[0] == 0)
521 GLfloat axialRadius = axialDetails.
radius;
522 GLfloat otherRadius = otherDetails.
radius;
524 if (otherRadius < axialRadius)
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))
531 OctreeDebugLog(
@"%@",
@"----> Other sphere does not intersect axial cube");
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))
543 OctreeDebugLog(
@"%@",
@"----> Axial sphere does not intersect other cube");
551 if (axialBuffer[0] == -1)
554 if (otherBuffer[0] == -1)
557 axialCollisionBuffer[0] = (
unsigned char)255;
558 otherCollisionBuffer[0] = (
unsigned char)255;
571 int nearest_oct = ((otherPosition.x > 0.0)? 0:4)|((otherPosition.y > 0.0)? 0:2)|((otherPosition.z > 0.0)? 0:1);
573 int nextLevel = otherBuffer[0];
574 const int *nextBuffer = &otherBuffer[nextLevel];
575 unsigned char *nextCollisionBuffer = &otherCollisionBuffer[nextLevel];
577 Vector voff, nextPosition;
580 nextDetails.
radius = 0.5f * otherRadius;
581 for (i = 0; i < 8; i++)
586 nextDetails.
octree = &nextBuffer[oct];
589 voff = resolveVectorInIJK(offsetForOctant(oct, otherRadius), other_ijk);
590 nextPosition = vector_subtract(otherPosition, voff);
591 if (
isHitByOctree(axialDetails, nextDetails, nextPosition, other_ijk))
607 int nearest_oct = ((otherPosition.x > 0.0)? 4:0)|((otherPosition.y > 0.0)? 2:0)|((otherPosition.z > 0.0)? 1:0);
609 int nextLevel = axialBuffer[0];
610 const int *nextBuffer = &axialBuffer[nextLevel];
611 unsigned char *nextCollisionBuffer = &axialCollisionBuffer[nextLevel];
616 nextDetails.
radius = 0.5f * axialRadius;
617 for (i = 0; i < 8; i++)
622 nextDetails.
octree = &nextBuffer[oct];
625 nextPosition = vector_add(otherPosition, offsetForOctant(oct, axialRadius));
626 if (
isHitByOctree(nextDetails, otherDetails, nextPosition, other_ijk))
639 if (other ==
nil)
return NO;
641 BOOL hit =
isHitByOctree([
self octreeDetails], [other octreeDetails], v0, ijk);
643 _hasCollision = _hasCollision || hit;
650- (BOOL)
isHitByOctree:(
Octree *)other withOrigin:(Vector)v0 andIJK:(Triangle)ijk andScales:(GLfloat) s1 :(GLfloat)s2
660 _hasCollision = _hasCollision || hit;
667- (NSDictionary *) dictionaryRepresentation
669 return [NSDictionary dictionaryWithObjectsAndKeys:
671 [NSNumber numberWithFloat:_radius], @"radius",
818 NSCAssert(State(
self)->remaining > 0,
@"Attempt to add node to a full parent in octree builder.");
819 State(
self)->remaining--;
821 SetNode(
self, State(
self)->insertionPoint++, value);
827 if ((
self = [super init]))
830 _octree = malloc(_capacity *
sizeof *_octree);
842 State(
self)->remaining = 1;
856- (
Octree *) buildOctreeWithRadius:(GLfloat)radius
858 NSAssert(State(
self)->remaining == 0 && _level == 0,
@"Attempt to produce octree from an octree builder in an incomplete state.");
860 size_t dataSize = _nodeCount *
sizeof *_octree;
861 int *resized = realloc(_octree, dataSize);
862 if (resized == NULL) resized = _octree;
867 NSData *data = [NSData dataWithBytesNoCopy:resized
875 return [[[
Octree alloc] initWithData:data radius:radius] autorelease];
881 InsertNode(
self, -1);
891- (void) beginInnerNode
893 NSAssert(_level <
kMaxOctreeDepth,
@"Attempt to build octree exceeding maximum depth.");
896 uint32_t newInsertionPoint = _nodeCount;
897 InsertNode(
self, (
int)_nodeCount - State(
self)->insertionPoint);
910 State(
self)->insertionPoint = newInsertionPoint;
911 State(
self)->remaining = 8;