raytrace shit

This commit is contained in:
RandomityGuy 2021-06-17 22:01:18 +05:30
parent 38e93ea561
commit cbc0257509
4 changed files with 73 additions and 98 deletions

View file

@ -25,6 +25,7 @@ class BoxCollisionEntity extends CollisionEntity {
public override function isIntersectedByRay(rayOrigin:Vector, rayDirection:Vector, intersectionPoint:Vector):Bool { public override function isIntersectedByRay(rayOrigin:Vector, rayDirection:Vector, intersectionPoint:Vector):Bool {
// TEMP cause bruh // TEMP cause bruh
return false;
return boundingBox.rayIntersection(Ray.fromValues(rayOrigin.x, rayOrigin.y, rayOrigin.z, rayDirection.x, rayDirection.y, rayDirection.z), true) != -1; return boundingBox.rayIntersection(Ray.fromValues(rayOrigin.x, rayOrigin.y, rayOrigin.z, rayDirection.x, rayDirection.y, rayDirection.z), true) != -1;
} }

View file

@ -86,7 +86,7 @@ class CollisionWorld {
} }
public function rayCast(rayStart:Vector, rayDirection:Vector) { public function rayCast(rayStart:Vector, rayDirection:Vector) {
return []; // return [];
return this.octree.raycast(rayStart, rayDirection); return this.octree.raycast(rayStart, rayDirection);
} }

View file

@ -14,8 +14,9 @@ class Octree {
public function new() { public function new() {
this.root = new OctreeNode(this, 0); this.root = new OctreeNode(this, 0);
// Init the octree to a 1x1x1 cube // Init the octree to a 1x1x1 cube
this.root.min.set(0, 0, 0); this.root.bounds = new Bounds();
this.root.size = 1; this.root.bounds.xMin = this.root.bounds.yMin = this.root.bounds.zMin = 0;
this.root.bounds.xMax = this.root.bounds.yMax = this.root.bounds.zMax = 1;
this.objectToNode = new Map(); this.objectToNode = new Map();
} }
@ -25,6 +26,8 @@ class Octree {
return; // Don't insert if already contained in the tree return; // Don't insert if already contained in the tree
while (!this.root.largerThan(object) || !this.root.containsCenter(object)) { while (!this.root.largerThan(object) || !this.root.containsCenter(object)) {
// The root node does not fit the object; we need to grow the tree. // The root node does not fit the object; we need to grow the tree.
var a = this.root.largerThan(object);
var b = this.root.containsCenter(object);
if (this.root.depth == -32) { if (this.root.depth == -32) {
return; return;
} }
@ -68,18 +71,20 @@ class Octree {
} }
averagePoint = averagePoint.multiply(1 / count); // count should be greater than 0, because that's why we're growing in the first place. averagePoint = averagePoint.multiply(1 / count); // count should be greater than 0, because that's why we're growing in the first place.
// Determine the direction from the root center to the determined point // Determine the direction from the root center to the determined point
var rootCenter = this.root.min.clone().add(new Vector(this.root.size / 2, this.root.size / 2, this.root.size / 2)); var rootCenter = this.root.bounds.getCenter().toVector();
var direction = averagePoint.sub(rootCenter); // Determine the "direction of growth" var direction = averagePoint.sub(rootCenter); // Determine the "direction of growth"
// Create a new root. The current root will become a quadrant in this new root. // Create a new root. The current root will become a quadrant in this new root.
var newRoot = new OctreeNode(this, this.root.depth - 1); var newRoot = new OctreeNode(this, this.root.depth - 1);
newRoot.min = this.root.min.clone(); newRoot.bounds = this.root.bounds.clone();
newRoot.size = this.root.size * 2; newRoot.bounds.xSize *= 2;
newRoot.bounds.ySize *= 2;
newRoot.bounds.zSize *= 2;
if (direction.x < 0) if (direction.x < 0)
newRoot.min.x -= this.root.size; newRoot.bounds.xMin -= this.root.bounds.xSize;
if (direction.y < 0) if (direction.y < 0)
newRoot.min.y -= this.root.size; newRoot.bounds.yMin -= this.root.bounds.ySize;
if (direction.z < 0) if (direction.z < 0)
newRoot.min.z -= this.root.size; newRoot.bounds.zMin -= this.root.bounds.zSize;
if (this.root.count > 0) { if (this.root.count > 0) {
var octantIndex = ((direction.x < 0) ? 1 : 0) + ((direction.y < 0) ? 2 : 0) + ((direction.z < 0) ? 4 : 0); var octantIndex = ((direction.x < 0) ? 1 : 0) + ((direction.y < 0) ? 2 : 0) + ((direction.z < 0) ? 4 : 0);
newRoot.createOctants(); newRoot.createOctants();
@ -93,12 +98,12 @@ class Octree {
/** Tries to shrink the octree if large parts of the octree are empty. */ /** Tries to shrink the octree if large parts of the octree are empty. */
public function shrink() { public function shrink() {
if (this.root.size < 1 || this.root.objects.length > 0) if (this.root.bounds.xSize < 1 || this.root.bounds.ySize < 1 || this.root.bounds.zSize < 1 || this.root.objects.length > 0)
return; return;
if (this.root.count == 0) { if (this.root.count == 0) {
// Reset to default empty octree // Reset to default empty octree
this.root.min.set(0, 0, 0); this.root.bounds.xMin = this.root.bounds.yMin = this.root.bounds.zMin = 0;
this.root.size = 1; this.root.bounds.xMax = this.root.bounds.yMax = this.root.bounds.zMin = 1;
this.root.depth = 0; this.root.depth = 0;
return; return;
} }

View file

@ -13,11 +13,9 @@ class OctreeNode implements IOctreeElement {
public var position:Int; public var position:Int;
/** The min corner of the bounding box. */ /** The min corner of the bounding box. */
public var min = new Vector(); public var bounds:Bounds;
/** The size of the bounding box on all three axes. This forces the bounding box to be a cube. */ /** The size of the bounding box on all three axes. This forces the bounding box to be a cube. */
public var size:Float;
public var octants:Array<OctreeNode> = null; public var octants:Array<OctreeNode> = null;
/** A list of objects contained in this node. Note that the node doesn't need to be a leaf node for this set to be non-empty; since this is an octree of bounding boxes, some volumes cannot fit into an octant and therefore need to be stored in the node itself. */ /** A list of objects contained in this node. Note that the node doesn't need to be a leaf node for this set to be non-empty; since this is an octree of bounding boxes, some volumes cannot fit into an octant and therefore need to be stored in the node itself. */
@ -36,18 +34,13 @@ class OctreeNode implements IOctreeElement {
public function insert(object:IOctreeObject) { public function insert(object:IOctreeObject) {
this.count++; this.count++;
if (this.octants != null) { if (this.octants != null) {
// First we check if the object can fit into any of the octants (they all have the same size, so checking only one suffices)
if (this.octants[0].largerThan(object)) {
// Try to insert the object into one of the octants...
for (i in 0...8) { for (i in 0...8) {
var octant = this.octants[i]; var octant = this.octants[i];
if (octant.containsCenter(object)) { if (octant.largerThan(object) && octant.containsCenter(object)) {
octant.insert(object); octant.insert(object);
return; return;
} }
} }
}
// No octant fit the object, so add it to the list of objects instead
this.objects.push(object); this.objects.push(object);
this.octree.objectToNode.set(object, this); this.octree.objectToNode.set(object, this);
} else { } else {
@ -63,13 +56,12 @@ class OctreeNode implements IOctreeElement {
this.createOctants(); this.createOctants();
// Put the objects into the correct octants. Note that all objects that couldn't fit into any octant will remain in the set. // Put the objects into the correct octants. Note that all objects that couldn't fit into any octant will remain in the set.
for (object in this.objects) { for (object in this.objects) {
if (this.octants[0].largerThan(object)) {
for (j in 0...8) { for (j in 0...8) {
var octant = this.octants[j]; var octant = this.octants[j];
if (octant.containsCenter(object)) { if (octant.largerThan(object) && octant.containsCenter(object)) {
octant.insert(object); octant.insert(object);
this.objects.remove(object); this.objects.remove(object);
} break;
} }
} }
} }
@ -84,14 +76,16 @@ class OctreeNode implements IOctreeElement {
for (i in 0...8) { for (i in 0...8) {
var newNode = new OctreeNode(this.octree, this.depth + 1); var newNode = new OctreeNode(this.octree, this.depth + 1);
newNode.parent = this; newNode.parent = this;
newNode.size = this.size / 2; var newSize = this.bounds.getSize().multiply(1 / 2);
newNode.min.set(this.min.x newNode.bounds = this.bounds.clone();
+ newNode.size * ((i & 1) >> 0), // The x coordinate changes every index newNode.bounds.setMin(new Point(this.bounds.xMin
this.min.y + newSize.x * ((i & 1) >> 0), this.bounds.yMin
+ newNode.size * ((i & 2) >> 1), // The y coordinate changes every 2 indices + newSize.y * ((i & 2) >> 1),
this.min.z this.bounds.zMin
+ newNode.size * ((i & 4) >> 2) // The z coordinate changes every 4 indices + newSize.z * ((i & 4) >> 2)));
); newNode.bounds.xSize = newSize.x;
newNode.bounds.ySize = newSize.y;
newNode.bounds.zSize = newSize.z;
this.octants.push(newNode); this.octants.push(newNode);
} }
} }
@ -125,71 +119,46 @@ class OctreeNode implements IOctreeElement {
} }
public function largerThan(object:IOctreeObject) { public function largerThan(object:IOctreeObject) {
var box = object.boundingBox; return this.bounds.containsBounds(object.boundingBox);
var bb = new Bounds();
bb.setMin(this.min.toPoint());
bb.xMax = bb.xMin + this.size;
bb.yMax = bb.yMin + this.size;
bb.zMax = bb.zMin + this.size;
return bb.containsBounds(box);
// return this.size > (box.xMax - box.xMin) && this.size > (box.yMax - box.yMin) && this.size > (box.zMax - box.zMin); // return this.size > (box.xMax - box.xMin) && this.size > (box.yMax - box.yMin) && this.size > (box.zMax - box.zMin);
} }
public function containsCenter(object:IOctreeObject) { public function containsCenter(object:IOctreeObject) {
var box = object.boundingBox; return this.bounds.contains(object.boundingBox.getCenter());
var x = box.xMin + (box.xMax - box.xMin) / 2;
var y = box.yMin + (box.yMax - box.yMin) / 2;
var z = box.zMin + (box.zMax - box.zMin) / 2;
return this.min.x <= x && x < (this.min.x + this.size) && this.min.y <= y && y < (this.min.y + this.size) && this.min.z <= z
&& z < (this.min.z + this.size);
} }
public function containsPoint(point:Vector) { public function containsPoint(point:Vector) {
var x = point.x; return this.bounds.contains(point.toPoint());
var y = point.y;
var z = point.z;
return this.min.x <= x && x < (this.min.x + this.size) && this.min.y <= y && y < (this.min.y + this.size) && this.min.z <= z
&& z < (this.min.z + this.size);
} }
public function raycast(rayOrigin:Vector, rayDirection:Vector, intersections:Array<OctreeIntersection>) { public function raycast(rayOrigin:Vector, rayDirection:Vector, intersections:Array<OctreeIntersection>) {
var ray = Ray.fromValues(rayOrigin.x, rayOrigin.y, rayOrigin.z, rayDirection.x, rayDirection.y, rayDirection.z);
// Construct the loose bounding box of this node (2x in size, with the regular bounding box in the center) // Construct the loose bounding box of this node (2x in size, with the regular bounding box in the center)
var looseBoundingBox = this.octree.tempBox;
looseBoundingBox.xMin += this.min.x + (-this.size / 2); if (this.bounds.rayIntersection(ray, true) == -1)
looseBoundingBox.yMin += this.min.y + (-this.size / 2); return;
looseBoundingBox.zMin += this.min.z + (-this.size / 2);
looseBoundingBox.xMax += this.min.x + (this.size * 3 / 2); for (obj in this.objects) {
looseBoundingBox.yMax += this.min.y + (this.size * 3 / 2); var iSec = new Vector();
looseBoundingBox.zMax += this.min.z + (this.size * 3 / 2); if (obj.isIntersectedByRay(rayOrigin, rayDirection, iSec)) {
if (looseBoundingBox.rayIntersection(Ray.fromValues(rayOrigin.x, rayOrigin.y, rayOrigin.z, rayDirection.x, rayDirection.y, rayDirection.z), var intersectionData = new OctreeIntersection();
true) == -1) intersectionData.distance = rayOrigin.distance(iSec);
return; // The ray doesn't hit the node's loose bounding box; we can stop intersectionData.object = obj;
var vec = new Vector(); intersectionData.point = iSec;
// Test all objects for intersection intersections.push(intersectionData);
if (this.objects.length > 0)
for (object in this.objects) {
if (object.isIntersectedByRay(rayOrigin, rayDirection, vec)) {
var intersection:OctreeIntersection = new OctreeIntersection();
intersection.object = object;
intersection.point = vec;
intersection.distance = rayOrigin.distance(vec);
intersections.push(intersection);
vec = new Vector();
} }
} }
// Recurse into the octants
if (this.octants != null) if (this.octants != null) {
for (i in 0...8) { for (i in 0...8) {
var octant = this.octants[i]; var octant = this.octants[i];
octant.raycast(rayOrigin, rayDirection, intersections); octant.raycast(rayOrigin, rayDirection, intersections);
} }
} }
}
public function boundingSearch(bounds:Bounds, intersections:Array<IOctreeElement>) { public function boundingSearch(bounds:Bounds, intersections:Array<IOctreeElement>) {
var thisBounds = new Bounds(); if (this.bounds.collide(bounds)) {
thisBounds.setMin(this.min.toPoint());
thisBounds.xSize = thisBounds.ySize = thisBounds.zSize = this.size;
if (thisBounds.collide(bounds)) {
for (obj in this.objects) { for (obj in this.objects) {
if (obj.boundingBox.collide(bounds)) if (obj.boundingBox.collide(bounds))
intersections.push(obj); intersections.push(obj);
@ -203,24 +172,24 @@ class OctreeNode implements IOctreeElement {
public function getClosestPoint(point:Vector) { public function getClosestPoint(point:Vector) {
var closest = new Vector(); var closest = new Vector();
if (this.min.x > point.x) if (this.bounds.xMin > point.x)
closest.x = this.min.x; closest.x = this.bounds.xMin;
else if (this.min.x + this.size < point.x) else if (this.bounds.xMax < point.x)
closest.x = this.min.x + this.size; closest.x = this.bounds.xMax;
else else
closest.x = point.x; closest.x = point.x;
if (this.min.y > point.y) if (this.bounds.yMin > point.y)
closest.y = this.min.y; closest.y = this.bounds.yMin;
else if (this.min.y + this.size < point.y) else if (this.bounds.yMax < point.y)
closest.y = this.min.y + this.size; closest.y = this.bounds.yMax;
else else
closest.y = point.y; closest.y = point.y;
if (this.min.z > point.z) if (this.bounds.zMin > point.z)
closest.z = this.min.z; closest.z = this.bounds.zMin;
else if (this.min.z + this.size < point.z) else if (this.bounds.zMax < point.z)
closest.z = this.min.z + this.size; closest.z = this.bounds.zMax;
else else
closest.z = point.z; closest.z = point.z;