I’ve been having issues with my voxel destruction system, I just implemented basic greedy meshing based of of this video: https://www.youtube.com/watch?v=L6P86i5O9iU and made it work in 3d. It has greatly improved the performance but for some reason after creating the parts they are always offset by a small amount from the position of the original part. This issue get worse on bigger parts. I don’t know what’s causing it but if someone notices an issue in my code that is causing this please let me know and your help would be greatly appreciated.
External MediaHere is the code for my module script:
local Destruction = {}
--Functions
local function CalculateBlockCount(vec, op)
return Vector3.new(op(vec.X), op(vec.Y), op(vec.Z))
end
function GetClosestPointOnBox(collider, boxCFrame, boxSize)
local transform = boxCFrame:PointToObjectSpace(collider.Position)
local halfSize = boxSize/2
local position = boxCFrame * Vector3.new(math.clamp(transform.X, -halfSize.X, halfSize.X), math.clamp(transform.Y, -halfSize.Y, halfSize.Y), math.clamp(transform.Y, -halfSize.Y, halfSize.Y))
return position
end
function Destruction:SplitPart(targetPart: BasePart, collider: BasePart, voxelSize: number)
local regionPos = {}
local boundParams = OverlapParams.new()
boundParams.FilterDescendantsInstances = {targetPart}
boundParams.FilterType = Enum.RaycastFilterType.Exclude
local blockCount = CalculateBlockCount(targetPart.Size, function(l) return math.floor(l / voxelSize) end)
local blockSize = targetPart.Size / blockCount
for x = 1, blockCount.X do
regionPos[x] = {}
for y = 1, blockCount.Y do
regionPos[x][y] = {}
for z = 1, blockCount.Z do
local offset = (targetPart.Size + blockSize) / 2
local cframe = targetPart.CFrame:ToWorldSpace(CFrame.new(Vector3.new(x,y,z) * blockSize - offset))
local closestPointOnBox = GetClosestPointOnBox(collider, cframe, blockSize)
local distance = (collider.Position - closestPointOnBox).Magnitude
if distance > collider.Size.Y/2 then
regionPos[x][y][z] = {["X"] = 1, ["Y"] = 1, ["Z"] = 1, ["Position"] = cframe.Position}
end
end
end
end
for x, row in pairs(regionPos) do
for y, column in pairs(row) do
for z, size in pairs(column) do
if not regionPos[x][y][z] then continue end
if regionPos[x][y][z - 1] == nil then continue end
regionPos[x][y][z].Z += regionPos[x][y][z - 1].Z
regionPos[x][y][z - 1] = nil
end
end
end
for x = 2, blockCount.X do
for y = 1, blockCount.Y do
for z, size in pairs(regionPos[x]) do
if not regionPos[x][y][z] then continue end
if regionPos[x - 1][y][z] == nil then continue end
if size.Z ~= regionPos[x - 1][y][z].Z then continue end
size.X += regionPos[x - 1][y][z].X
regionPos[x - 1][y][z] = nil
end
end
end
for x, row in pairs(regionPos) do
for y = 2, blockCount.Y do
for z, size in pairs(regionPos[x][y]) do
if not regionPos[x][y][z] then continue end
if regionPos[x][y - 1][z] == nil then continue end
if size.Z ~= regionPos[x][y - 1][z].Z then continue end
if size.X ~= regionPos[x][y - 1][z].X then continue end
size.Y += regionPos[x][y - 1][z].Y
regionPos[x][y - 1][z] = nil
end
end
end
for x, row in pairs(regionPos) do
for y, column in pairs(row) do
for z, size in pairs(column) do
local position = size.Position
local part = Instance.new("Part")
part.Anchored = true
part.Size = Vector3.new(size.X, size.Y, size.Z) * blockSize
part.Position = Vector3.new(position.X - size.X / 2, position.Y - size.Y / 2, position.Z - size.Z / 2) * blockSize
part.Color = targetPart.Color
part.Material = targetPart.Material
part.TopSurface = targetPart.TopSurface
part.BottomSurface = targetPart.BottomSurface
part.Parent = workspace
end
end
end
targetPart:Destroy()
end
return Destruction
And this is the code for my local script:
local UIS = game:GetService("UserInputService")
local destructionModuleV2 = require(script:WaitForChild("DestructionV2"))
local rayParams = RaycastParams.new()
rayParams.FilterDescendantsInstances = {game.Players.LocalPlayer.Character}
rayParams.FilterType = Enum.RaycastFilterType.Exclude
local camera = workspace.CurrentCamera
local voxelSize = 1
UIS.InputBegan:Connect(function(input, gd)
if input.KeyCode == Enum.KeyCode.F then
local ray = workspace:Raycast(camera.CFrame.Position, camera.CFrame.LookVector * 1000, rayParams)
if ray and ray.Instance then
local part = Instance.new("Part")
part.Anchored = true
part.Transparency = .5
part.BrickColor = BrickColor.Red()
part.CanCollide = false
part.Position = ray.Position
part.Size = Vector3.new(4, 4, 4) * 2
part.Shape = Enum.PartType.Ball
part.Parent = workspace
local startTick = tick()
local partsInCollider = workspace:GetPartsInPart(part)
for i, p in pairs(partsInCollider) do
destructionModuleV2:SplitPart(p, part, voxelSize)
end
part:Destroy()
print(tick() - startTick, "Total time")
end
end
end)