Advertisement
AstIPT

VoxelObject

Jun 1st, 2024
461
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 6.96 KB | None | 0 0
  1. local AABB = require(script.Parent.AABB)
  2. local SDF = require(script.Parent.SDF)
  3. local Cache = require(script.Parent.Cache)
  4. local gizmo = require(script.Parent.gizmo)
  5.  
  6. local LOD_MULTIPLIERS = {
  7.     {5, 1/2};
  8.     {25, 1};
  9.     {100, 2};
  10.     {math.huge, 4};
  11. }
  12.  
  13. local VoxelObject = {}
  14. VoxelObject.__index = VoxelObject
  15.  
  16. function VoxelObject.new(part: BasePart, cellSize: number, voxelContainer: Instance)
  17.     --[[
  18.     A dude explained me how to do this a long time ago which is why I even knew how to do this so credit to them
  19.     (I don't recall the name :sob:)
  20.     ]]
  21.     local self = setmetatable({}, VoxelObject)
  22.    
  23.     local sdf: (point: Vector3) -> number -- took 2 tries to do this but i gotta impress 4th <3
  24.    
  25.     if part:IsA("Part") then -- i hate ifs with my life but idk how to do this better
  26.         if part.Shape == Enum.PartType.Ball then
  27.             local radius = part.Size.X/2
  28.             sdf =  function(point)
  29.                 return SDF.Sphere(point, self._cframe.Position, radius)
  30.             end
  31.         elseif part.Shape == Enum.PartType.Cylinder then
  32.             local height = part.Size.X
  33.             local radius = part.Size.Y/2
  34.             sdf = function(point)
  35.                 return SDF.CappedCylinderCached(point, self._cframeInv, height, radius)
  36.             end
  37.         end
  38.     elseif part:IsA("WedgePart") then
  39.         sdf = function(point)
  40.             return SDF.Wedge(point, self._cframe, self._size)
  41.         end
  42.     end
  43.     if not sdf then
  44.         sdf = function(point)
  45.             return SDF.BoxCached(point, self._cframeInv, self._halfSize)
  46.         end
  47.     end
  48.    
  49.     -- Static
  50.     self._part = part
  51.     self._baseCellSize = cellSize
  52.     self._size = part.Size
  53.     self._halfSize = part.Size/2
  54.     self._cache = Cache.new(part, voxelContainer)
  55.     self._sdf = sdf
  56.  
  57.     -- Dynamic
  58.     self._voxelSet = nil
  59.     self._cframe = nil
  60.     self._cframeInv = nil
  61.     self._aabb = nil
  62.     self._cellSize = nil
  63.     self._neighborOffsets = nil
  64.    
  65.     return self
  66. end
  67.  
  68. function VoxelObject:_markVoxel(pos: Vector3)
  69.     self._voxelSet[pos] = true
  70. end
  71.  
  72. function VoxelObject:_voxelizeAABB(cellSizes: {number}, aabb: AABB.AABB?, depth: number?)
  73.     aabb = aabb or AABB.ToGrid(
  74.         AABB.Box(self._cframe, self._size),
  75.         1/cellSizes[1]
  76.     )  
  77.     depth = depth or 1
  78.     assert(aabb)
  79.     assert(depth)
  80.  
  81.     local cellSize = cellSizes[depth]
  82.     local cellCenterOffset = Vector3.one*(cellSize/2)
  83.     local min = aabb.Min
  84.     local max = aabb.Max
  85.  
  86.     for x = min.X, max.X - cellSize, cellSize do
  87.         for y = min.Y, max.Y - cellSize, cellSize do
  88.             for z = min.Z, max.Z - cellSize, cellSize do
  89.                 local corner = Vector3.new(x, y, z)
  90.                 local center = corner + cellCenterOffset
  91.  
  92.                 local dist = self._sdf(center)
  93.                 if depth == #cellSizes then
  94.                     if math.abs(dist) <= cellSize/2 then
  95.                         self:_markVoxel(corner)
  96.                     end
  97.                 else
  98.                     if math.abs(dist) <= cellSize then
  99.                         local subAABB = AABB.fromMinMax(corner, corner + Vector3.one*cellSize)
  100.                         self:_voxelizeAABB(cellSizes, subAABB, depth + 1)
  101.                     end
  102.                 end
  103.             end
  104.         end
  105.     end
  106. end
  107.  
  108. -- TODO: dis could be smarter
  109. function VoxelObject:_voxelizeNeighborRecurse(origin, seen)
  110.     self:_markVoxel(origin)
  111.     local cellSize = self._cellSize
  112.     for _, offset in self._neighborOffsets do
  113.         local neighbor = origin + offset
  114.         if not seen[neighbor] then
  115.             local dist = self._sdf(neighbor)
  116.             seen[neighbor] = true
  117.             if math.abs(dist) <= cellSize/2 then
  118.                 self:_voxelizeNeighborRecurse(neighbor, seen)
  119.             end
  120.         end
  121.     end
  122. end
  123.  
  124. local function snapToGrid(pos, cellSize): Vector3
  125.     return Vector3.new(
  126.         math.floor(pos.X/cellSize)*cellSize,
  127.         math.floor(pos.Y/cellSize)*cellSize,
  128.         math.floor(pos.Z/cellSize)*cellSize
  129.     )
  130. end
  131.  
  132. -- TODO: dis sucks and seems to fail frequently (but we bal)
  133. function VoxelObject:_getStartVoxel(): Vector3
  134.     local cellSize = self._cellSize
  135.     local aabbSize = self._aabb.Size
  136.     local neighborOffset = Vector3.xAxis*cellSize
  137.    
  138.     local center = snapToGrid(self._cframe.Position, self._cellSize)
  139.     center += Vector3.one*self._cellSize/2
  140.     local points = {
  141.         center;
  142.         center + Vector3.new(0, cellSize, 0);
  143.         center + Vector3.new(0, 0, cellSize);
  144.         center + Vector3.new(0, cellSize, cellSize);
  145.     }
  146.    
  147.     local tries = 0
  148.     local surfacePoint
  149.     repeat
  150.         tries += 1
  151.         local minDist = math.huge
  152.         for i, point in points do
  153.             local dist = math.abs(self._sdf(point))
  154.             if dist < minDist then
  155.                 minDist = dist
  156.                 surfacePoint = point
  157.             end
  158.             points[i] += neighborOffset
  159.         end
  160.     until minDist <= cellSize/2 or tries > 50
  161.  
  162.     return surfacePoint
  163. end
  164.  
  165. function VoxelObject:_voxelizeNeighbor()
  166.     debug.profilebegin("VoxelizeNeighbor")
  167.     local start = self:_getStartVoxel()
  168.     self:_voxelizeNeighborRecurse(start, {[start] = true})
  169.     debug.profileend()
  170. end
  171.  
  172. function VoxelObject:Voxelize(): boolean
  173.     if self._cframe == self._part.CFrame then
  174.         return false
  175.     end
  176.    
  177.     local cameraPos = workspace.CurrentCamera.CFrame.Position
  178.     local cameraLook = workspace.CurrentCamera.CFrame.LookVector
  179.     local aabb = AABB.ToGrid(AABB.Box(self._part.CFrame, self._size), 1/self._baseCellSize)
  180.     if (aabb.Min - cameraPos):Dot(cameraLook) < 0 and (aabb.Max - cameraPos):Dot(cameraLook) < 0 then
  181.         return false
  182.     end
  183.    
  184.     self._cframe = self._part.CFrame
  185.     self._cframeInv = self._cframe:Inverse()
  186.    
  187.     local distToCamera = SDF.BoxCached(cameraPos, self._cframeInv, self._halfSize)
  188.     local cellSize
  189.     for _, multiplier in LOD_MULTIPLIERS do
  190.         if distToCamera < multiplier[1] then
  191.             cellSize = self._baseCellSize*multiplier[2]
  192.             break
  193.         end
  194.     end
  195.    
  196.     self._voxelSet = {}
  197.     self._aabb = aabb
  198.     if cellSize ~= self._cellSize then
  199.         self._cellSize = cellSize
  200.         self._neighborOffsets = {
  201.             -- Edges
  202.             Vector3.new(0, cellSize, 0);
  203.             Vector3.new(0, -cellSize, 0);
  204.             Vector3.new(cellSize, 0, 0);
  205.             Vector3.new(-cellSize, 0, 0);
  206.             Vector3.new(0, 0, cellSize);
  207.             Vector3.new(0, 0, -cellSize);
  208.            
  209.             -- Corners
  210.             Vector3.new( cellSize,  cellSize,  cellSize);
  211.             Vector3.new(-cellSize,  cellSize,  cellSize);
  212.             Vector3.new( cellSize, -cellSize,  cellSize);
  213.             Vector3.new( cellSize,  cellSize, -cellSize);
  214.             Vector3.new(-cellSize, -cellSize,  cellSize);
  215.             Vector3.new( cellSize, -cellSize, -cellSize);
  216.             Vector3.new(-cellSize,  cellSize, -cellSize);
  217.             Vector3.new(-cellSize, -cellSize, -cellSize);
  218.         }
  219.     end
  220.  
  221.     self:_voxelizeNeighbor()
  222.    
  223.     return true
  224. end
  225.  
  226. function VoxelObject:UpdateParts()
  227.     debug.profilebegin("UpdateParts")
  228.     local cellSize = self._cellSize
  229.    
  230.     debug.profilebegin("Get parts")
  231.     local parts = {}
  232.     local bulkMoveToParts = {}
  233.     local bulkMoveToCFrames = {}
  234.     for pos in self._voxelSet do
  235.         local part, shouldCFrame = self._cache:Get(pos)
  236.         parts[pos] = part
  237.         if shouldCFrame then
  238.             table.insert(bulkMoveToParts, part)
  239.             table.insert(bulkMoveToCFrames, CFrame.new(pos))
  240.         end
  241.     end
  242.     debug.profileend()
  243.    
  244.     debug.profilebegin("Resize")
  245.     local partSize = Vector3.one*cellSize
  246.     for _, part in parts do
  247.         if part.Size.X ~= cellSize then
  248.             part.Size = partSize
  249.         end
  250.     end
  251.     debug.profileend()
  252.    
  253.     workspace:BulkMoveTo(bulkMoveToParts, bulkMoveToCFrames, Enum.BulkMoveMode.FireCFrameChanged)
  254.     self._cache:ClearTemporaryCache()
  255.     self._cache:SetTemporaryCache(parts)
  256.  
  257.     debug.profileend()
  258. end
  259.  
  260. return VoxelObject
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement