Trouble with Greedy Meshing Voxels

I want to optimize my terrain generation with Greedy Meshing (Read about it here Greedy Meshing Voxels)

It worked fine with parts that were 1 stud in volume. But I need custom sizes and I just can’t manage to make it work. I thought that by replacing all the 1 stud increments with the appropiate sizes would work, but the results are nowhere close.
image
image

This module uses the Grid module to optimize the parts in the grid

local Greedy = {}
Greedy.__index = Greedy

-- Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- Modules
local Parts = require(ReplicatedStorage.Utility.Parts)

function Greedy.new(grid, partWidth, partHeight)
    local self = setmetatable({}, Greedy)
    self.grid = grid
    self.partWidth = partWidth
    self.partHeight = partHeight
    return self
end

function Greedy:loop(axis, func)
    local xO, yO, zO = self.originPosition.X, self.originPosition.Y, self.originPosition.Z
	local xE, yE, zE = self.endPosition.X, self.endPosition.Y, self.endPosition.Z

    if axis == "X" then
        xO = xO + self.partWidth
    elseif axis == "Y" then
        yO = yO + self.partHeight
    elseif axis == "Z" then
        zO = zO + self.partWidth
    end

    for x = xO, xE, self.partWidth do
        for y = yO, yE, self.partHeight do
            for z = zO, zE, self.partWidth do
                local part = self.grid:get(x, y, z)
                local otherPart = nil

                if axis == "X" then
                    otherPart = self.grid:get(x - self.partWidth, y, z)
                elseif axis == "Y" then
                    otherPart = self.grid:get(x, y - self.partHeight, z)
                elseif axis == "Z" then
                    otherPart = self.grid:get(x, y, z - self.partWidth)
                end

                if not part or not otherPart then continue end
                if part:GetAttribute("TerrainType") ~= otherPart:GetAttribute("TerrainType") then continue end

                func(x, y, z, part, otherPart)
            end
        end
    end
end

function Greedy:blend(originPosition, endPosition)
    if typeof(originPosition) ~= "Vector3" then error("Origin position isn't a Vector3") end
    if typeof(endPosition) ~= "Vector3" then error("End position isn't a Vector3") end

    -- Parts.new(CFrame.new(originPosition), Vector3.one, "Strong Blue", "Void", workspace)
    -- Parts.new(CFrame.new(endPosition), Vector3.one, "Strong Blue", "Void", workspace)

    self.originPosition = originPosition
    self.endPosition = endPosition

    self:loop("Z", function(x, y, z, part, otherPart)
        -- Resize part
        local newSizeZ = part.Size.Z + self.partWidth
        part.Size = Vector3.new(part.Size.X, part.Size.Y, newSizeZ)
        -- Remove other part
        self.grid:remove(x, y, z - self.partWidth)
    end)

    self:loop("X", function(x, y, z, part, otherPart)
        if part.Size.Z == otherPart.Size.Z then
            local newSizeX = part.Size.X + self.partWidth
            part.Size = Vector3.new(newSizeX, part.Size.Y, part.Size.Z)
            self.grid:remove(x - self.partWidth, y, z)
        end
    end)

    self:loop("Y", function(x, y, z, part, otherPart)
        if part.Size.X == otherPart.Size.X and part.Size.Z == otherPart.Size.Z then
            local newSizeY = part.Size.Y + self.partHeight
            part.Size = Vector3.new(part.Size.X, newSizeY, part.Size.Z)
            self.grid:remove(x, y - self.partHeight, z)
        end
    end)
end

return Greedy

This is the Grid module, it just keeps count of the parts.

local Grid = {}
Grid.__index = Grid
    
function Grid.new()
    local self = setmetatable({}, Grid)

	self.data = {};

	return self
end

function Grid:get(x,y,z)
    if self.data[x] and self.data[x][y] then
        return self.data[x][y][z]
    end

    return nil
end

function Grid:set(x, y, z, part)
    if not self.data[x] then 
        self.data[x] = {
            [y] = {}
        }
    elseif not self.data[x][y] then
        self.data[x][y] = {}
    end

    self.data[x][y][z] = part
end

function Grid:remove(x, y, z)
    local entry = self.data[x][y][z] 
    if entry then entry:Destroy() end
    self.data[x][y][z] = nil
end

function Grid:clearY(x, y)
    self.data[x][y] = nil
end

function Grid:clearX(x)
    self.data[x] = nil
end

function Grid:clearAll()
    self.data = {}
end


return Grid

Do I need to rethink the algorithm completlly to handle custom sizes? If i can’t have custom sizes having cubes with sizes bigger than 1 would still suffice, because that doesn’t work either, somehow.

2 Likes

i dont understand what you mean by having “custom sizes” for your parts/grid:
is your grid a type of tree(octree, etc.) or does it have rectangular cells?

the whole point of greedy meshing is to have non-square sizes for faces(not sure if it works w/ parts in the first place)

I mean sizes that aren’t just 1 x 1 x 1. My attempt has two variables, partWidth and partHeight, i want it to work with those.

I’m still slightly confused by what you mean by “custom sizes”. Do all of the parts share this size? So like every part is now 2x1x1? Or is it more a case where every part can have a different measurement almost as though it’s already been partially calculated?

@CringeEngineer
My resource VoxelDestruct was made for voxel destruction physics but includes in it a module to greedy mesh groups of parts without relying on a uniform voxel size, perhaps this is what you meant by ‘custom sizes’ ? In the resource’s hierarchy there is a Mesh module, require it and use :MergeParts(parts) (passing a table of parts, use :GetChildren() on a model.) The other parameters wont be useful unless you use PartCache. Try this and see if it fits your project’s needs and mark this as a solution if so.

This should work for most models as long as the parts are not intersecting each other.

If you look at the code the parts all share the same partWidth and partHeight. So if i wanted the parts to all be partWidth = 4 ; partHeight = 2 all pats will be Size = Vector3.new(partWidth,partHeight,partWidth) If you look at the link i provided its implemented with 1 x1x1 parts only.

Haven’t been able to look at the code directly, but my first thought is to calculate it like a 1x1x1, then scale the model up after so each part is the right size.

2 Likes

Wow. This is the holy grail of voxels… I’ll look into it and see if it will help me. I’m very confident it is what i need, Thank you so much!

That, actually solves the issue, It will be a little messy but It will defenitly work, I don’t know why i didn’t think of it. Even if Salvatore gave me the most resources, I think this is the actual solution. Thanks a lot! I apolagize for using vague and confusing terms like “custom sizes” lol, i should’ve been more specific.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.