mirror of
https://github.com/SashLilac/cambridge.git
synced 2024-11-22 13:29:03 -06:00
558 lines
16 KiB
Lua
558 lines
16 KiB
Lua
|
-- https://github.com/benraziel/bvh-tree
|
||
|
|
||
|
--- BVH Tree
|
||
|
-- @module bvh
|
||
|
|
||
|
local modules = (...):gsub('%.[^%.]+$', '') .. "."
|
||
|
local intersect = require(modules .. "intersect")
|
||
|
local vec3 = require(modules .. "vec3")
|
||
|
local EPSILON = 1e-6
|
||
|
local BVH = {}
|
||
|
local BVHNode = {}
|
||
|
local Node
|
||
|
|
||
|
BVH.__index = BVH
|
||
|
BVHNode.__index = BVHNode
|
||
|
|
||
|
local function new(triangles, maxTrianglesPerNode)
|
||
|
local tree = setmetatable({}, BVH)
|
||
|
local trianglesArray = {}
|
||
|
|
||
|
for _, triangle in ipairs(triangles) do
|
||
|
local p1 = triangle[1]
|
||
|
local p2 = triangle[2]
|
||
|
local p3 = triangle[3]
|
||
|
|
||
|
table.insert(trianglesArray, p1.x or p1[1])
|
||
|
table.insert(trianglesArray, p1.y or p1[2])
|
||
|
table.insert(trianglesArray, p1.z or p1[3])
|
||
|
|
||
|
table.insert(trianglesArray, p2.x or p2[1])
|
||
|
table.insert(trianglesArray, p2.y or p2[2])
|
||
|
table.insert(trianglesArray, p2.z or p2[3])
|
||
|
|
||
|
table.insert(trianglesArray, p3.x or p3[1])
|
||
|
table.insert(trianglesArray, p3.y or p3[2])
|
||
|
table.insert(trianglesArray, p3.z or p3[3])
|
||
|
end
|
||
|
|
||
|
tree._trianglesArray = trianglesArray
|
||
|
tree._maxTrianglesPerNode = maxTrianglesPerNode or 10
|
||
|
tree._bboxArray = tree.calcBoundingBoxes(trianglesArray)
|
||
|
|
||
|
-- clone a helper array
|
||
|
tree._bboxHelper = {}
|
||
|
for _, bbox in ipairs(tree._bboxArray) do
|
||
|
table.insert(tree._bboxHelper, bbox)
|
||
|
end
|
||
|
|
||
|
-- create the root node, add all the triangles to it
|
||
|
local triangleCount = #triangles
|
||
|
local extents = tree:calcExtents(1, triangleCount, EPSILON)
|
||
|
tree._rootNode = Node(extents[1], extents[2], 1, triangleCount, 1)
|
||
|
|
||
|
tree._nodes_to_split = { tree._rootNode }
|
||
|
while #tree._nodes_to_split > 0 do
|
||
|
local node = table.remove(tree._nodes_to_split)
|
||
|
tree:splitNode(node)
|
||
|
end
|
||
|
return tree
|
||
|
end
|
||
|
|
||
|
function BVH:intersectAABB(aabb)
|
||
|
local nodesToIntersect = { self._rootNode }
|
||
|
local trianglesInIntersectingNodes = {} -- a list of nodes that intersect the ray (according to their bounding box)
|
||
|
local intersectingTriangles = {}
|
||
|
|
||
|
-- go over the BVH tree, and extract the list of triangles that lie in nodes that intersect the box.
|
||
|
-- note: these triangles may not intersect the box themselves
|
||
|
while #nodesToIntersect > 0 do
|
||
|
local node = table.remove(nodesToIntersect)
|
||
|
|
||
|
local node_aabb = {
|
||
|
min = node._extentsMin,
|
||
|
max = node._extentsMax
|
||
|
}
|
||
|
|
||
|
if intersect.aabb_aabb(aabb, node_aabb) then
|
||
|
if node._node0 then
|
||
|
table.insert(nodesToIntersect, node._node0)
|
||
|
end
|
||
|
|
||
|
if node._node1 then
|
||
|
table.insert(nodesToIntersect, node._node1)
|
||
|
end
|
||
|
|
||
|
for i=node._startIndex, node._endIndex do
|
||
|
table.insert(trianglesInIntersectingNodes, self._bboxArray[1+(i-1)*7])
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- insert all node triangles, don't bother being more specific yet.
|
||
|
local triangle = { vec3(), vec3(), vec3() }
|
||
|
|
||
|
for i=1, #trianglesInIntersectingNodes do
|
||
|
local triIndex = trianglesInIntersectingNodes[i]
|
||
|
|
||
|
-- print(triIndex, #self._trianglesArray)
|
||
|
triangle[1].x = self._trianglesArray[1+(triIndex-1)*9]
|
||
|
triangle[1].y = self._trianglesArray[1+(triIndex-1)*9+1]
|
||
|
triangle[1].z = self._trianglesArray[1+(triIndex-1)*9+2]
|
||
|
triangle[2].x = self._trianglesArray[1+(triIndex-1)*9+3]
|
||
|
triangle[2].y = self._trianglesArray[1+(triIndex-1)*9+4]
|
||
|
triangle[2].z = self._trianglesArray[1+(triIndex-1)*9+5]
|
||
|
triangle[3].x = self._trianglesArray[1+(triIndex-1)*9+6]
|
||
|
triangle[3].y = self._trianglesArray[1+(triIndex-1)*9+7]
|
||
|
triangle[3].z = self._trianglesArray[1+(triIndex-1)*9+8]
|
||
|
|
||
|
table.insert(intersectingTriangles, {
|
||
|
triangle = { triangle[1]:clone(), triangle[2]:clone(), triangle[3]:clone() },
|
||
|
triangleIndex = triIndex
|
||
|
})
|
||
|
end
|
||
|
|
||
|
return intersectingTriangles
|
||
|
end
|
||
|
|
||
|
function BVH:intersectRay(rayOrigin, rayDirection, backfaceCulling)
|
||
|
local nodesToIntersect = { self._rootNode }
|
||
|
local trianglesInIntersectingNodes = {} -- a list of nodes that intersect the ray (according to their bounding box)
|
||
|
local intersectingTriangles = {}
|
||
|
|
||
|
local invRayDirection = vec3(
|
||
|
1 / rayDirection.x,
|
||
|
1 / rayDirection.y,
|
||
|
1 / rayDirection.z
|
||
|
)
|
||
|
|
||
|
-- go over the BVH tree, and extract the list of triangles that lie in nodes that intersect the ray.
|
||
|
-- note: these triangles may not intersect the ray themselves
|
||
|
while #nodesToIntersect > 0 do
|
||
|
local node = table.remove(nodesToIntersect)
|
||
|
|
||
|
if BVH.intersectNodeBox(rayOrigin, invRayDirection, node) then
|
||
|
if node._node0 then
|
||
|
table.insert(nodesToIntersect, node._node0)
|
||
|
end
|
||
|
|
||
|
if node._node1 then
|
||
|
table.insert(nodesToIntersect, node._node1)
|
||
|
end
|
||
|
|
||
|
for i=node._startIndex, node._endIndex do
|
||
|
table.insert(trianglesInIntersectingNodes, self._bboxArray[1+(i-1)*7])
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- go over the list of candidate triangles, and check each of them using ray triangle intersection
|
||
|
local triangle = { vec3(), vec3(), vec3() }
|
||
|
local ray = {
|
||
|
position = vec3(rayOrigin.x, rayOrigin.y, rayOrigin.z),
|
||
|
direction = vec3(rayDirection.x, rayDirection.y, rayDirection.z)
|
||
|
}
|
||
|
|
||
|
for i=1, #trianglesInIntersectingNodes do
|
||
|
local triIndex = trianglesInIntersectingNodes[i]
|
||
|
|
||
|
-- print(triIndex, #self._trianglesArray)
|
||
|
triangle[1].x = self._trianglesArray[1+(triIndex-1)*9]
|
||
|
triangle[1].y = self._trianglesArray[1+(triIndex-1)*9+1]
|
||
|
triangle[1].z = self._trianglesArray[1+(triIndex-1)*9+2]
|
||
|
triangle[2].x = self._trianglesArray[1+(triIndex-1)*9+3]
|
||
|
triangle[2].y = self._trianglesArray[1+(triIndex-1)*9+4]
|
||
|
triangle[2].z = self._trianglesArray[1+(triIndex-1)*9+5]
|
||
|
triangle[3].x = self._trianglesArray[1+(triIndex-1)*9+6]
|
||
|
triangle[3].y = self._trianglesArray[1+(triIndex-1)*9+7]
|
||
|
triangle[3].z = self._trianglesArray[1+(triIndex-1)*9+8]
|
||
|
|
||
|
local intersectionPoint, intersectionDistance = intersect.ray_triangle(ray, triangle, backfaceCulling)
|
||
|
|
||
|
if intersectionPoint then
|
||
|
table.insert(intersectingTriangles, {
|
||
|
triangle = { triangle[1]:clone(), triangle[2]:clone(), triangle[3]:clone() },
|
||
|
triangleIndex = triIndex,
|
||
|
intersectionPoint = intersectionPoint,
|
||
|
intersectionDistance = intersectionDistance
|
||
|
})
|
||
|
end
|
||
|
end
|
||
|
|
||
|
return intersectingTriangles
|
||
|
end
|
||
|
|
||
|
function BVH.calcBoundingBoxes(trianglesArray)
|
||
|
local p1x, p1y, p1z
|
||
|
local p2x, p2y, p2z
|
||
|
local p3x, p3y, p3z
|
||
|
local minX, minY, minZ
|
||
|
local maxX, maxY, maxZ
|
||
|
|
||
|
local bboxArray = {}
|
||
|
|
||
|
for i=1, #trianglesArray / 9 do
|
||
|
p1x = trianglesArray[1+(i-1)*9]
|
||
|
p1y = trianglesArray[1+(i-1)*9+1]
|
||
|
p1z = trianglesArray[1+(i-1)*9+2]
|
||
|
p2x = trianglesArray[1+(i-1)*9+3]
|
||
|
p2y = trianglesArray[1+(i-1)*9+4]
|
||
|
p2z = trianglesArray[1+(i-1)*9+5]
|
||
|
p3x = trianglesArray[1+(i-1)*9+6]
|
||
|
p3y = trianglesArray[1+(i-1)*9+7]
|
||
|
p3z = trianglesArray[1+(i-1)*9+8]
|
||
|
|
||
|
minX = math.min(p1x, p2x, p3x)
|
||
|
minY = math.min(p1y, p2y, p3y)
|
||
|
minZ = math.min(p1z, p2z, p3z)
|
||
|
maxX = math.max(p1x, p2x, p3x)
|
||
|
maxY = math.max(p1y, p2y, p3y)
|
||
|
maxZ = math.max(p1z, p2z, p3z)
|
||
|
|
||
|
BVH.setBox(bboxArray, i, i, minX, minY, minZ, maxX, maxY, maxZ)
|
||
|
end
|
||
|
|
||
|
return bboxArray
|
||
|
end
|
||
|
|
||
|
function BVH:calcExtents(startIndex, endIndex, expandBy)
|
||
|
expandBy = expandBy or 0
|
||
|
|
||
|
if startIndex > endIndex then
|
||
|
return { vec3(), vec3() }
|
||
|
end
|
||
|
|
||
|
local minX = math.huge
|
||
|
local minY = math.huge
|
||
|
local minZ = math.huge
|
||
|
local maxX = -math.huge
|
||
|
local maxY = -math.huge
|
||
|
local maxZ = -math.huge
|
||
|
|
||
|
for i=startIndex, endIndex do
|
||
|
minX = math.min(self._bboxArray[1+(i-1)*7+1], minX)
|
||
|
minY = math.min(self._bboxArray[1+(i-1)*7+2], minY)
|
||
|
minZ = math.min(self._bboxArray[1+(i-1)*7+3], minZ)
|
||
|
maxX = math.max(self._bboxArray[1+(i-1)*7+4], maxX)
|
||
|
maxY = math.max(self._bboxArray[1+(i-1)*7+5], maxY)
|
||
|
maxZ = math.max(self._bboxArray[1+(i-1)*7+6], maxZ)
|
||
|
end
|
||
|
|
||
|
return {
|
||
|
vec3(minX - expandBy, minY - expandBy, minZ - expandBy),
|
||
|
vec3(maxX + expandBy, maxY + expandBy, maxZ + expandBy)
|
||
|
}
|
||
|
end
|
||
|
|
||
|
function BVH:splitNode(node)
|
||
|
local num_elements = node:elementCount()
|
||
|
if (num_elements <= self._maxTrianglesPerNode) or (num_elements <= 0) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local startIndex = node._startIndex
|
||
|
local endIndex = node._endIndex
|
||
|
|
||
|
local leftNode = { {},{},{} }
|
||
|
local rightNode = { {},{},{} }
|
||
|
local extentCenters = { node:centerX(), node:centerY(), node:centerZ() }
|
||
|
|
||
|
local extentsLength = {
|
||
|
node._extentsMax.x - node._extentsMin.x,
|
||
|
node._extentsMax.y - node._extentsMin.y,
|
||
|
node._extentsMax.z - node._extentsMin.z
|
||
|
}
|
||
|
|
||
|
local objectCenter = {}
|
||
|
for i=startIndex, endIndex do
|
||
|
objectCenter[1] = (self._bboxArray[1+(i-1)*7+1] + self._bboxArray[1+(i-1)*7+4]) * 0.5 -- center = (min + max) / 2
|
||
|
objectCenter[2] = (self._bboxArray[1+(i-1)*7+2] + self._bboxArray[1+(i-1)*7+5]) * 0.5 -- center = (min + max) / 2
|
||
|
objectCenter[3] = (self._bboxArray[1+(i-1)*7+3] + self._bboxArray[1+(i-1)*7+6]) * 0.5 -- center = (min + max) / 2
|
||
|
|
||
|
for j=1, 3 do
|
||
|
if objectCenter[j] < extentCenters[j] then
|
||
|
table.insert(leftNode[j], i)
|
||
|
else
|
||
|
table.insert(rightNode[j], i)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- check if we couldn't split the node by any of the axes (x, y or z). halt
|
||
|
-- here, dont try to split any more (cause it will always fail, and we'll
|
||
|
-- enter an infinite loop
|
||
|
local splitFailed = {
|
||
|
#leftNode[1] == 0 or #rightNode[1] == 0,
|
||
|
#leftNode[2] == 0 or #rightNode[2] == 0,
|
||
|
#leftNode[3] == 0 or #rightNode[3] == 0
|
||
|
}
|
||
|
|
||
|
if splitFailed[1] and splitFailed[2] and splitFailed[3] then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
-- choose the longest split axis. if we can't split by it, choose next best one.
|
||
|
local splitOrder = { 1, 2, 3 }
|
||
|
table.sort(splitOrder, function(a, b)
|
||
|
return extentsLength[a] > extentsLength[b]
|
||
|
end)
|
||
|
|
||
|
local leftElements
|
||
|
local rightElements
|
||
|
|
||
|
for i=1, 3 do
|
||
|
local candidateIndex = splitOrder[i]
|
||
|
if not splitFailed[candidateIndex] then
|
||
|
leftElements = leftNode[candidateIndex]
|
||
|
rightElements = rightNode[candidateIndex]
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- sort the elements in range (startIndex, endIndex) according to which node they should be at
|
||
|
local node0Start = startIndex
|
||
|
local node1Start = node0Start + #leftElements
|
||
|
local node0End = node1Start - 1
|
||
|
local node1End = endIndex
|
||
|
local currElement
|
||
|
|
||
|
local helperPos = node._startIndex
|
||
|
local concatenatedElements = {}
|
||
|
|
||
|
for _, element in ipairs(leftElements) do
|
||
|
table.insert(concatenatedElements, element)
|
||
|
end
|
||
|
|
||
|
for _, element in ipairs(rightElements) do
|
||
|
table.insert(concatenatedElements, element)
|
||
|
end
|
||
|
|
||
|
-- print(#leftElements, #rightElements, #concatenatedElements)
|
||
|
|
||
|
for i=1, #concatenatedElements do
|
||
|
currElement = concatenatedElements[i]
|
||
|
BVH.copyBox(self._bboxArray, currElement, self._bboxHelper, helperPos)
|
||
|
helperPos = helperPos + 1
|
||
|
end
|
||
|
|
||
|
-- copy results back to main array
|
||
|
for i=1+(node._startIndex-1)*7, node._endIndex*7 do
|
||
|
self._bboxArray[i] = self._bboxHelper[i]
|
||
|
end
|
||
|
|
||
|
-- create 2 new nodes for the node we just split, and add links to them from the parent node
|
||
|
local node0Extents = self:calcExtents(node0Start, node0End, EPSILON)
|
||
|
local node1Extents = self:calcExtents(node1Start, node1End, EPSILON)
|
||
|
|
||
|
local node0 = Node(node0Extents[1], node0Extents[2], node0Start, node0End, node._level + 1)
|
||
|
local node1 = Node(node1Extents[1], node1Extents[2], node1Start, node1End, node._level + 1)
|
||
|
|
||
|
node._node0 = node0
|
||
|
node._node1 = node1
|
||
|
node:clearShapes()
|
||
|
|
||
|
-- add new nodes to the split queue
|
||
|
table.insert(self._nodes_to_split, node0)
|
||
|
table.insert(self._nodes_to_split, node1)
|
||
|
end
|
||
|
|
||
|
function BVH._calcTValues(minVal, maxVal, rayOriginCoord, invdir)
|
||
|
local res = { min=0, max=0 }
|
||
|
|
||
|
if invdir >= 0 then
|
||
|
res.min = ( minVal - rayOriginCoord ) * invdir
|
||
|
res.max = ( maxVal - rayOriginCoord ) * invdir
|
||
|
else
|
||
|
res.min = ( maxVal - rayOriginCoord ) * invdir
|
||
|
res.max = ( minVal - rayOriginCoord ) * invdir
|
||
|
end
|
||
|
|
||
|
return res
|
||
|
end
|
||
|
|
||
|
function BVH.intersectNodeBox(rayOrigin, invRayDirection, node)
|
||
|
local t = BVH._calcTValues(node._extentsMin.x, node._extentsMax.x, rayOrigin.x, invRayDirection.x)
|
||
|
local ty = BVH._calcTValues(node._extentsMin.y, node._extentsMax.y, rayOrigin.y, invRayDirection.y)
|
||
|
|
||
|
if t.min > ty.max or ty.min > t.max then
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
-- These lines also handle the case where tmin or tmax is NaN
|
||
|
-- (result of 0 * Infinity). x !== x returns true if x is NaN
|
||
|
if ty.min > t.min or t.min ~= t.min then
|
||
|
t.min = ty.min
|
||
|
end
|
||
|
|
||
|
if ty.max < t.max or t.max ~= t.max then
|
||
|
t.max = ty.max
|
||
|
end
|
||
|
|
||
|
local tz = BVH._calcTValues(node._extentsMin.z, node._extentsMax.z, rayOrigin.z, invRayDirection.z)
|
||
|
|
||
|
if t.min > tz.max or tz.min > t.max then
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
if tz.min > t.min or t.min ~= t.min then
|
||
|
t.min = tz.min
|
||
|
end
|
||
|
|
||
|
if tz.max < t.max or t.max ~= t.max then
|
||
|
t.max = tz.max
|
||
|
end
|
||
|
|
||
|
--return point closest to the ray (positive side)
|
||
|
if t.max < 0 then
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
function BVH.setBox(bboxArray, pos, triangleId, minX, minY, minZ, maxX, maxY, maxZ)
|
||
|
bboxArray[1+(pos-1)*7] = triangleId
|
||
|
bboxArray[1+(pos-1)*7+1] = minX
|
||
|
bboxArray[1+(pos-1)*7+2] = minY
|
||
|
bboxArray[1+(pos-1)*7+3] = minZ
|
||
|
bboxArray[1+(pos-1)*7+4] = maxX
|
||
|
bboxArray[1+(pos-1)*7+5] = maxY
|
||
|
bboxArray[1+(pos-1)*7+6] = maxZ
|
||
|
end
|
||
|
|
||
|
function BVH.copyBox(sourceArray, sourcePos, destArray, destPos)
|
||
|
destArray[1+(destPos-1)*7] = sourceArray[1+(sourcePos-1)*7]
|
||
|
destArray[1+(destPos-1)*7+1] = sourceArray[1+(sourcePos-1)*7+1]
|
||
|
destArray[1+(destPos-1)*7+2] = sourceArray[1+(sourcePos-1)*7+2]
|
||
|
destArray[1+(destPos-1)*7+3] = sourceArray[1+(sourcePos-1)*7+3]
|
||
|
destArray[1+(destPos-1)*7+4] = sourceArray[1+(sourcePos-1)*7+4]
|
||
|
destArray[1+(destPos-1)*7+5] = sourceArray[1+(sourcePos-1)*7+5]
|
||
|
destArray[1+(destPos-1)*7+6] = sourceArray[1+(sourcePos-1)*7+6]
|
||
|
end
|
||
|
|
||
|
function BVH.getBox(bboxArray, pos, outputBox)
|
||
|
outputBox.triangleId = bboxArray[1+(pos-1)*7]
|
||
|
outputBox.minX = bboxArray[1+(pos-1)*7+1]
|
||
|
outputBox.minY = bboxArray[1+(pos-1)*7+2]
|
||
|
outputBox.minZ = bboxArray[1+(pos-1)*7+3]
|
||
|
outputBox.maxX = bboxArray[1+(pos-1)*7+4]
|
||
|
outputBox.maxY = bboxArray[1+(pos-1)*7+5]
|
||
|
outputBox.maxZ = bboxArray[1+(pos-1)*7+6]
|
||
|
end
|
||
|
|
||
|
local function new_node(extentsMin, extentsMax, startIndex, endIndex, level)
|
||
|
return setmetatable({
|
||
|
_extentsMin = extentsMin,
|
||
|
_extentsMax = extentsMax,
|
||
|
_startIndex = startIndex,
|
||
|
_endIndex = endIndex,
|
||
|
_level = level
|
||
|
--_node0 = nil
|
||
|
--_node1 = nil
|
||
|
}, BVHNode)
|
||
|
end
|
||
|
|
||
|
function BVHNode:elementCount()
|
||
|
return (self._endIndex + 1) - self._startIndex
|
||
|
end
|
||
|
|
||
|
function BVHNode:centerX()
|
||
|
return (self._extentsMin.x + self._extentsMax.x) * 0.5
|
||
|
end
|
||
|
|
||
|
function BVHNode:centerY()
|
||
|
return (self._extentsMin.y + self._extentsMax.y) * 0.5
|
||
|
end
|
||
|
|
||
|
function BVHNode:centerZ()
|
||
|
return (self._extentsMin.z + self._extentsMax.z) * 0.5
|
||
|
end
|
||
|
|
||
|
function BVHNode:clearShapes()
|
||
|
self._startIndex = 0
|
||
|
self._endIndex = -1
|
||
|
end
|
||
|
|
||
|
function BVHNode.ngSphereRadius(extentsMin, extentsMax)
|
||
|
local centerX = (extentsMin.x + extentsMax.x) * 0.5
|
||
|
local centerY = (extentsMin.y + extentsMax.y) * 0.5
|
||
|
local centerZ = (extentsMin.z + extentsMax.z) * 0.5
|
||
|
|
||
|
local extentsMinDistSqr =
|
||
|
(centerX - extentsMin.x) * (centerX - extentsMin.x) +
|
||
|
(centerY - extentsMin.y) * (centerY - extentsMin.y) +
|
||
|
(centerZ - extentsMin.z) * (centerZ - extentsMin.z)
|
||
|
|
||
|
local extentsMaxDistSqr =
|
||
|
(centerX - extentsMax.x) * (centerX - extentsMax.x) +
|
||
|
(centerY - extentsMax.y) * (centerY - extentsMax.y) +
|
||
|
(centerZ - extentsMax.z) * (centerZ - extentsMax.z)
|
||
|
|
||
|
return math.sqrt(math.max(extentsMinDistSqr, extentsMaxDistSqr))
|
||
|
end
|
||
|
|
||
|
--[[
|
||
|
|
||
|
--- Draws node boundaries visually for debugging.
|
||
|
-- @param cube Cube model to draw
|
||
|
-- @param depth Used for recurcive calls to self method
|
||
|
function OctreeNode:draw_bounds(cube, depth)
|
||
|
depth = depth or 0
|
||
|
local tint = depth / 7 -- Will eventually get values > 1. Color rounds to 1 automatically
|
||
|
|
||
|
love.graphics.setColor(tint * 255, 0, (1 - tint) * 255)
|
||
|
local m = mat4()
|
||
|
:translate(self.center)
|
||
|
:scale(vec3(self.adjLength, self.adjLength, self.adjLength))
|
||
|
|
||
|
love.graphics.updateMatrix("transform", m)
|
||
|
love.graphics.setWireframe(true)
|
||
|
love.graphics.draw(cube)
|
||
|
love.graphics.setWireframe(false)
|
||
|
|
||
|
for _, child in ipairs(self.children) do
|
||
|
child:draw_bounds(cube, depth + 1)
|
||
|
end
|
||
|
|
||
|
love.graphics.setColor(255, 255, 255)
|
||
|
end
|
||
|
|
||
|
--- Draws the bounds of all objects in the tree visually for debugging.
|
||
|
-- @param cube Cube model to draw
|
||
|
-- @param filter a function returning true or false to determine visibility.
|
||
|
function OctreeNode:draw_objects(cube, filter)
|
||
|
local tint = self.baseLength / 20
|
||
|
love.graphics.setColor(0, (1 - tint) * 255, tint * 255, 63)
|
||
|
|
||
|
for _, object in ipairs(self.objects) do
|
||
|
if filter and filter(object.data) or not filter then
|
||
|
local m = mat4()
|
||
|
:translate(object.bounds.center)
|
||
|
:scale(object.bounds.size)
|
||
|
|
||
|
love.graphics.updateMatrix("transform", m)
|
||
|
love.graphics.draw(cube)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
for _, child in ipairs(self.children) do
|
||
|
child:draw_objects(cube, filter)
|
||
|
end
|
||
|
|
||
|
love.graphics.setColor(255, 255, 255)
|
||
|
end
|
||
|
|
||
|
--]]
|
||
|
|
||
|
Node = setmetatable({
|
||
|
new = new_node
|
||
|
}, {
|
||
|
__call = function(_, ...) return new_node(...) end
|
||
|
})
|
||
|
|
||
|
return setmetatable({
|
||
|
new = new
|
||
|
}, {
|
||
|
__call = function(_, ...) return new(...) end
|
||
|
})
|