Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local AABB = require(script.Parent.AABB)
- local SDF = require(script.Parent.SDF)
- local Cache = require(script.Parent.Cache)
- local gizmo = require(script.Parent.gizmo)
- local LOD_MULTIPLIERS = {
- {5, 1/2};
- {25, 1};
- {100, 2};
- {math.huge, 4};
- }
- local VoxelObject = {}
- VoxelObject.__index = VoxelObject
- function VoxelObject.new(part: BasePart, cellSize: number, voxelContainer: Instance)
- --[[
- 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
- (I don't recall the name :sob:)
- ]]
- local self = setmetatable({}, VoxelObject)
- local sdf: (point: Vector3) -> number -- took 2 tries to do this but i gotta impress 4th <3
- if part:IsA("Part") then -- i hate ifs with my life but idk how to do this better
- if part.Shape == Enum.PartType.Ball then
- local radius = part.Size.X/2
- sdf = function(point)
- return SDF.Sphere(point, self._cframe.Position, radius)
- end
- elseif part.Shape == Enum.PartType.Cylinder then
- local height = part.Size.X
- local radius = part.Size.Y/2
- sdf = function(point)
- return SDF.CappedCylinderCached(point, self._cframeInv, height, radius)
- end
- end
- elseif part:IsA("WedgePart") then
- sdf = function(point)
- return SDF.Wedge(point, self._cframe, self._size)
- end
- end
- if not sdf then
- sdf = function(point)
- return SDF.BoxCached(point, self._cframeInv, self._halfSize)
- end
- end
- -- Static
- self._part = part
- self._baseCellSize = cellSize
- self._size = part.Size
- self._halfSize = part.Size/2
- self._cache = Cache.new(part, voxelContainer)
- self._sdf = sdf
- -- Dynamic
- self._voxelSet = nil
- self._cframe = nil
- self._cframeInv = nil
- self._aabb = nil
- self._cellSize = nil
- self._neighborOffsets = nil
- return self
- end
- function VoxelObject:_markVoxel(pos: Vector3)
- self._voxelSet[pos] = true
- end
- function VoxelObject:_voxelizeAABB(cellSizes: {number}, aabb: AABB.AABB?, depth: number?)
- aabb = aabb or AABB.ToGrid(
- AABB.Box(self._cframe, self._size),
- 1/cellSizes[1]
- )
- depth = depth or 1
- assert(aabb)
- assert(depth)
- local cellSize = cellSizes[depth]
- local cellCenterOffset = Vector3.one*(cellSize/2)
- local min = aabb.Min
- local max = aabb.Max
- for x = min.X, max.X - cellSize, cellSize do
- for y = min.Y, max.Y - cellSize, cellSize do
- for z = min.Z, max.Z - cellSize, cellSize do
- local corner = Vector3.new(x, y, z)
- local center = corner + cellCenterOffset
- local dist = self._sdf(center)
- if depth == #cellSizes then
- if math.abs(dist) <= cellSize/2 then
- self:_markVoxel(corner)
- end
- else
- if math.abs(dist) <= cellSize then
- local subAABB = AABB.fromMinMax(corner, corner + Vector3.one*cellSize)
- self:_voxelizeAABB(cellSizes, subAABB, depth + 1)
- end
- end
- end
- end
- end
- end
- -- TODO: dis could be smarter
- function VoxelObject:_voxelizeNeighborRecurse(origin, seen)
- self:_markVoxel(origin)
- local cellSize = self._cellSize
- for _, offset in self._neighborOffsets do
- local neighbor = origin + offset
- if not seen[neighbor] then
- local dist = self._sdf(neighbor)
- seen[neighbor] = true
- if math.abs(dist) <= cellSize/2 then
- self:_voxelizeNeighborRecurse(neighbor, seen)
- end
- end
- end
- end
- local function snapToGrid(pos, cellSize): Vector3
- return Vector3.new(
- math.floor(pos.X/cellSize)*cellSize,
- math.floor(pos.Y/cellSize)*cellSize,
- math.floor(pos.Z/cellSize)*cellSize
- )
- end
- -- TODO: dis sucks and seems to fail frequently (but we bal)
- function VoxelObject:_getStartVoxel(): Vector3
- local cellSize = self._cellSize
- local aabbSize = self._aabb.Size
- local neighborOffset = Vector3.xAxis*cellSize
- local center = snapToGrid(self._cframe.Position, self._cellSize)
- center += Vector3.one*self._cellSize/2
- local points = {
- center;
- center + Vector3.new(0, cellSize, 0);
- center + Vector3.new(0, 0, cellSize);
- center + Vector3.new(0, cellSize, cellSize);
- }
- local tries = 0
- local surfacePoint
- repeat
- tries += 1
- local minDist = math.huge
- for i, point in points do
- local dist = math.abs(self._sdf(point))
- if dist < minDist then
- minDist = dist
- surfacePoint = point
- end
- points[i] += neighborOffset
- end
- until minDist <= cellSize/2 or tries > 50
- return surfacePoint
- end
- function VoxelObject:_voxelizeNeighbor()
- debug.profilebegin("VoxelizeNeighbor")
- local start = self:_getStartVoxel()
- self:_voxelizeNeighborRecurse(start, {[start] = true})
- debug.profileend()
- end
- function VoxelObject:Voxelize(): boolean
- if self._cframe == self._part.CFrame then
- return false
- end
- local cameraPos = workspace.CurrentCamera.CFrame.Position
- local cameraLook = workspace.CurrentCamera.CFrame.LookVector
- local aabb = AABB.ToGrid(AABB.Box(self._part.CFrame, self._size), 1/self._baseCellSize)
- if (aabb.Min - cameraPos):Dot(cameraLook) < 0 and (aabb.Max - cameraPos):Dot(cameraLook) < 0 then
- return false
- end
- self._cframe = self._part.CFrame
- self._cframeInv = self._cframe:Inverse()
- local distToCamera = SDF.BoxCached(cameraPos, self._cframeInv, self._halfSize)
- local cellSize
- for _, multiplier in LOD_MULTIPLIERS do
- if distToCamera < multiplier[1] then
- cellSize = self._baseCellSize*multiplier[2]
- break
- end
- end
- self._voxelSet = {}
- self._aabb = aabb
- if cellSize ~= self._cellSize then
- self._cellSize = cellSize
- self._neighborOffsets = {
- -- Edges
- Vector3.new(0, cellSize, 0);
- Vector3.new(0, -cellSize, 0);
- Vector3.new(cellSize, 0, 0);
- Vector3.new(-cellSize, 0, 0);
- Vector3.new(0, 0, cellSize);
- Vector3.new(0, 0, -cellSize);
- -- Corners
- Vector3.new( cellSize, cellSize, cellSize);
- Vector3.new(-cellSize, cellSize, cellSize);
- Vector3.new( cellSize, -cellSize, cellSize);
- Vector3.new( cellSize, cellSize, -cellSize);
- Vector3.new(-cellSize, -cellSize, cellSize);
- Vector3.new( cellSize, -cellSize, -cellSize);
- Vector3.new(-cellSize, cellSize, -cellSize);
- Vector3.new(-cellSize, -cellSize, -cellSize);
- }
- end
- self:_voxelizeNeighbor()
- return true
- end
- function VoxelObject:UpdateParts()
- debug.profilebegin("UpdateParts")
- local cellSize = self._cellSize
- debug.profilebegin("Get parts")
- local parts = {}
- local bulkMoveToParts = {}
- local bulkMoveToCFrames = {}
- for pos in self._voxelSet do
- local part, shouldCFrame = self._cache:Get(pos)
- parts[pos] = part
- if shouldCFrame then
- table.insert(bulkMoveToParts, part)
- table.insert(bulkMoveToCFrames, CFrame.new(pos))
- end
- end
- debug.profileend()
- debug.profilebegin("Resize")
- local partSize = Vector3.one*cellSize
- for _, part in parts do
- if part.Size.X ~= cellSize then
- part.Size = partSize
- end
- end
- debug.profileend()
- workspace:BulkMoveTo(bulkMoveToParts, bulkMoveToCFrames, Enum.BulkMoveMode.FireCFrameChanged)
- self._cache:ClearTemporaryCache()
- self._cache:SetTemporaryCache(parts)
- debug.profileend()
- end
- return VoxelObject
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement