vengi-voxel / vengi

free and open source voxel art tools - editor, thumbnailer and format converter
http://vengi-voxel.github.io/vengi/
Other
1.08k stars 90 forks source link

VOXELGENERATOR: convert the tree generators into lua code #321

Open mgerhardy opened 1 year ago

mgerhardy commented 1 year ago
mgerhardy commented 1 year ago
local module = {}

-- Define the Spiral class in Lua
local Spiral = {}
Spiral.__index = Spiral

-- Constructor for Spiral
function Spiral.new()
    local self = setmetatable({}, Spiral)
    self._layer = 1
    self._leg = 0
    self._x = 0
    self._z = 0
    return self
end

-- Move to the next position in the spiral
function Spiral:next(amount)
    amount = amount or 1
    for i = 1, amount do
        if self._leg == 0 then
            self._x = self._x + 1
            if self._x == self._layer then
                self._leg = 1
            end
        elseif self._leg == 1 then
            self._z = self._z + 1
            if self._z == self._layer then
                self._leg = 2
            end
        elseif self._leg == 2 then
            self._x = self._x - 1
            if -self._x == self._layer then
                self._leg = 3
            end
        elseif self._leg == 3 then
            self._z = self._z - 1
            if -self._z == self._layer then
                self._leg = 0
                self._layer = self._layer + 1
            end
        end
    end
end

-- Get the current x-coordinate
function Spiral:x()
    return self._x
end

-- Get the current z-coordinate
function Spiral:z()
    return self._z
end

function module.createTrunk(volume, ctx, voxel)
    local endPos = g_ivec3.new(
        ctx.pos.x,
        ctx.pos.y + ctx.trunkHeight,
        ctx.pos.z
    )

    g_shape.line(volume, ctx.pos, endPos, voxel, ctx.trunkStrength)
end

function module.createBezierTrunk(volume, ctx, voxel)
    local trunkTop = g_ivec3.new(ctx.pos.x, ctx.pos.y + ctx.trunkHeight, ctx.pos.z)
    local shiftX = ctx.trunkWidth
    local shiftZ = ctx.trunkDepth
    local endPos = g_ivec3.new(trunkTop.x + shiftX, trunkTop.y, trunkTop.z + shiftZ)
    local trunkSize = ctx.trunkStrength
    local control = g_ivec3.new(ctx.pos.x, ctx.pos.y + ctx.trunkControlOffset, ctx.pos.z)

    g_shape.bezier(volume, ctx.pos, endPos, control, voxel,
        function(vol, last, pos, voxel)
            -- TODO: this should be some kind of polygon - not a line - we want a flat trunk
            g_shape.line(vol, pos, last, voxel, math.max(1, math.ceil(trunkSize)))
            trunkSize = trunkSize * ctx.trunkFactor
        end,
        ctx.trunkHeight
    )

    endPos.y = endPos.y - 1
    return endPos
end

function module.createTreePalm(volume, ctx, trunkVoxel, leavesVoxel)
    local start = module.createBezierTrunk(volume, ctx, trunkVoxel)
    local stepWidth = math.rad(360.0 / ctx.branches)
    local w = ctx.leavesWidth
    local angle = math.random(0.0, 2.0 * math.pi)

    for b = 1, ctx.branches do
        local branchSize = ctx.branchSize
        local x = math.cos(angle)
        local z = math.sin(angle)
        local randomLength = math.random(ctx.leavesHeight, ctx.leavesHeight + ctx.randomLeavesHeightOffset)
        local control = g_ivec3.new(
            start.x - x * (w / 2.0),
            start.y + ctx.branchControlOffset,
            start.z - z * (w / 2.0)
        )
        local endPos = g_ivec3.new(
            start.x - x * w,
            start.y - randomLength,
            start.z - z * w
        )

        g_shape.BezierFunc(volume, start, endPos, control, leavesVoxel,
            function(vol, last, pos, voxel)
                -- TODO: this should be some kind of polygon - not a line - we want a flat leaf
                g_shape.line(vol, pos, last, voxel, math.max(1, math.ceil(branchSize)))
                branchSize = branchSize * ctx.branchFactor
            end,
            ctx.leavesHeight / 4
        )

        angle = angle + stepWidth
    end
end

-- Create a tree with ellipsis-shaped leaves
function module.createTreeEllipsis(volume, ctx, trunkVoxel, leavesVoxel)
    module.createTrunk(volume, ctx, trunkVoxel)
    local leavesCenter = g_ivec3.new(
        ctx.pos.x,
        ctx.pos.y + ctx.trunkHeight + ctx.leavesHeight / 2,
        ctx.pos.zg_ivec
    )
    g_shape.ellipse(volume, leavesCenter, "y", ctx.leavesWidth, ctx.leavesHeight, ctx.leavesDepth,
        leavesVoxel)
end

-- Create a tree with cone-shaped leaves
function module.createTreeCone(volume, ctx, trunkVoxel, leavesVoxel)
    module.createTrunk(volume, ctx, trunkVoxel)
    local leavesCenter = g_ivec3.new(
        ctx.pos.x,
        ctx.pos.y + ctx.trunkHeight + ctx.leavesHeight / 2,
        ctx.pos.z
    )
    g_shape.cone(volume, leavesCenter, "y", false, ctx.leavesHeight, ctx.leavesWidth, ctx.leavesDepth,
        leavesVoxel)
end

-- Create a tree with fir-like leaves
function module.createTreeFir(volume, ctx, trunkVoxel, leavesVoxel)
    module.createTrunk(volume, ctx, trunkVoxel)
    local stepWidth = math.rad(360.0 / ctx.branches)
    local angle = math.random(0.0, 2.0 * math.pi)
    local leafesPos = g_ivec3.new(ctx.pos.x, ctx.pos.y + ctx.trunkHeight, ctx.pos.z)
    local halfHeight = ((ctx.amount - 1) * ctx.stepHeight) / 2
    local center = g_ivec3.new(ctx.pos.x, ctx.pos.y + ctx.trunkHeight - halfHeight, ctx.pos.z)

    g_shape.cube(volume, center, ctx.trunkStrength, halfHeight * 2, ctx.trunkStrength, leavesVoxel)

    local w = ctx.w

    for n = 1, ctx.amount do
        for b = 1, ctx.branches do
            local start = leafesPos
            local endPos = start
            local x = math.cos(angle)
            local z = math.sin(angle)
            local randomZ = math.random(4, 8)
            endPos.y = endPos.y - randomZ
            endPos.x = endPos.x - x * w
            endPos.z = endPos.z - z * w
            g_shape.line(volume, start, endPos, leavesVoxel, ctx.branchStrength)
            local end2 = endPos
            end2.y = end2.y - ctx.branchDownwardOffset
            end2.x = end2.x - x * w * ctx.branchPositionFactor
            end2.z = end2.z - z * w * ctx.branchPositionFactor
            g_shape.line(volume, endPos, end2, leavesVoxel, ctx.branchStrength)
            angle = angle + stepWidth
            w = w + 1.0 / b
        end
        leafesPos.y = leafesPos.y - ctx.stepHeight
    end
end

-- Create a tree with a dome of leaves
function module.createTreeDome(volume, ctx, trunkVoxel, leavesVoxel)
    module.createTrunk(volume, ctx, trunkVoxel)
    local leavesCenter = g_ivec3.new(
        ctx.pos.x,
        ctx.pos.y + ctx.trunkHeight + ctx.leavesHeight / 2,
        ctx.pos.z
    )
    g_shape.dome(volume, leavesCenter, "y", false, ctx.leavesWidth, ctx.leavesHeight, ctx.leavesDepth,
        leavesVoxel)
end

-- Create a tree with a dome of leaves and hanging branches
function module.createTreeDomeHangingLeaves(volume, ctx, trunkVoxel, leavesVoxel)
    module.createTrunk(volume, ctx, trunkVoxel)
    local leavesCenter = g_ivec3.new(
        ctx.pos.x,
        ctx.pos.y + ctx.trunkHeight + ctx.leavesHeight / 2,
        ctx.pos.z
    )
    g_shape.dome(volume, leavesCenter, "y", false, ctx.leavesWidth, ctx.leavesHeight, ctx.leavesDepth,
        leavesVoxel)
    local stepWidth = math.rad(360.0 / ctx.branches)
    local angle = math.randomf(0.0, 2 * math.pi)
    local y = ctx.pos.y + ctx.trunkHeight + 1

    for b = 1, ctx.branches do
        local x = math.cos(angle)
        local z = math.sin(angle)
        local randomLength = math.random(ctx.hangingLeavesLengthMin, ctx.hangingLeavesLengthMax)
        local start = g_ivec3.new(
            math.round(ctx.pos.x - x * (ctx.leavesWidth - 1.0) / 2.0),
            y,
            math.round(ctx.pos.z - z * (ctx.leavesDepth - 1.0) / 2.0)
        )
        local endPos = { x = start.x, y = start.y - randomLength, z = start.z }
        g_shape.line(volume, start, endPos, leavesVoxel, ctx.hangingLeavesThickness)
        angle = angle + stepWidth
    end
end

-- Create a tree with cube-shaped leaves
function module.createTreeCube(volume, ctx, trunkVoxel, leavesVoxel)
    module.createTrunk(volume, ctx, trunkVoxel)
    local leavesCenter = g_ivec3.new(
        ctx.pos.x,
        ctx.pos.y + ctx.trunkHeight + ctx.leavesHeight / 2,
        ctx.pos.z
    )
    g_shape.cube(volume, leavesCenter, ctx.leavesWidth, ctx.leavesHeight, ctx.leavesDepth, leavesVoxel)
    g_shape.cube(volume, leavesCenter, ctx.leavesWidth + 2, ctx.leavesHeight - 2, ctx.leavesDepth - 2, leavesVoxel)
    g_shape.cube(volume, leavesCenter, ctx.leavesWidth - 2, ctx.leavesHeight + 2, ctx.leavesDepth - 2, leavesVoxel)
    g_shape.cube(volume, leavesCenter, ctx.leavesWidth - 2, ctx.leavesHeight - 2, ctx.leavesDepth + 2, leavesVoxel)
end

-- Create a tree with cube-shaped leaves and side ellipsis-shaped leaves
function module.createTreeCubeSideCubes(volume, ctx, trunkVoxel, leavesVoxel)
    module.createTrunk(volume, ctx, trunkVoxel)
    local leafesPos = g_ivec3.new(ctx.pos.x, ctx.pos.y + ctx.trunkHeight + ctx.leavesHeight / 2, ctx.pos.z)
    g_shape.cube(volume, leafesPos, ctx.leavesWidth, ctx.leavesHeight, ctx.leavesDepth, leavesVoxel)
    g_shape.cube(volume, leafesPos, ctx.leavesWidth + 2, ctx.leavesHeight - 2, ctx.leavesDepth - 2, leavesVoxel)
    g_shape.cube(volume, leafesPos, ctx.leavesWidth - 2, ctx.leavesHeight + 2, ctx.leavesDepth - 2, leavesVoxel)
    g_shape.cube(volume, leafesPos, ctx.leavesWidth - 2, ctx.leavesHeight - 2, ctx.leavesDepth + 2, leavesVoxel)

    local o = Spiral.new()
    o:next(1)
    local halfWidth = ctx.leavesWidth / 2
    local halfHeight = ctx.leavesHeight / 2
    local halfDepth = ctx.leavesDepth / 2

    for i = 1, 4 do
        local leafesPos2 = leafesPos
        leafesPos2.x = leafesPos2.x + o:x() * halfWidth
        leafesPos2.z = leafesPos2.z + o:z() * halfDepth
        g_shape.ellipse(volume, leafesPos2, "y", halfWidth, halfHeight, halfDepth, leavesVoxel)
        o:next(2)
    end
end

return module