vengi icon indicating copy to clipboard operation
vengi copied to clipboard

VOXELGENERATOR: convert the tree generators into lua code

Open mgerhardy opened this issue 1 year ago • 1 comments

  • [x] Create bindings for ShapeGenerator.h functions
  • [ ] Convert the code in TreeGenerator.h into lua code

mgerhardy avatar Aug 28 '23 16:08 mgerhardy

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

mgerhardy avatar Sep 19 '23 16:09 mgerhardy