How to allow vertical grid block placements

Hello there, I was following this tutorial for the grid placement: Creating A Furniture Placement System - #182 by DAmCOLIE

Is there a way to modify this part of the code, to be able to allow for vertical placement in the z axis too?
I have tried looping through all the parts in the canvas (the grid) and seeing which ones are in the same X and Y position as the model, but it does not work.
Any ideas?

Snippit of code calculating the cframe for the model render:

function Placement:CalcPlacementCFrame(model, position, rotation)
	-- use other method to get info about the surface
	local cf, size = self:CalcCanvas()

	-- rotate the size so that we can properly constrain to the surface
	local modelSize = CFrame.fromEulerAnglesYXZ(0, rotation, 0) * model.Size
	modelSize = Vector3.new(math.abs(modelSize.x), math.abs(modelSize.y), math.abs(modelSize.z))

	-- get the position relative to the surface's CFrame
	local lpos = cf:pointToObjectSpace(position);
	-- the max bounds the model can be from the surface's center
	local size2 = (size - Vector2.new(modelSize.x, modelSize.z))/2

	-- constrain the position using size2
	local x = math.clamp(lpos.x, -size2.x, size2.x);
	local y = math.clamp(lpos.y, -size2.y, size2.y);
	local z = -modelSize.y/2

	local g = self.GridUnit
	if (g > 0) then
		x = math.sign(x)*((math.abs(x) - math.abs(x) % g) + (size2.x % g))
		y = math.sign(y)*((math.abs(y) - math.abs(y) % g) + (size2.y % g))
	end
	
	local touching = model:GetTouchingParts()

	-- create and return the CFrame
	return cf * CFrame.new(x, y, z) * CFrame.Angles(-math.pi/2, rotation, 0)
end
1 Like

Your CalcPlacementCFrame function is essentially constraining the model only along X and Y relative to the canvas surface and then placing it at a fixed z = -modelSize.y/2. Thats why it works fine for horizontal placement but dosent support stacking along Z

  1. Treat the canvas as a 3D grid not just a 2D one
  2. Track which “cells” along X, Y, Z are already occupied
  3. Snap the model’s position along Z based on the highest occupied cell under the target X,Y
function Placement:CalcPlacementCFrame(model, position, rotation)
    local cf, size = self:CalcCanvas()

    -- rotate the size so that we can properly constrain to the surface
    local modelSize = CFrame.fromEulerAnglesYXZ(0, rotation, 0) * model.Size
    modelSize = Vector3.new(math.abs(modelSize.x), math.abs(modelSize.y), math.abs(modelSize.z))

    -- get the position relative to the surfaces CFrame thingi
    local lpos = cf:pointToObjectSpace(position)
    local size2 = (size - Vector2.new(modelSize.x, modelSize.z))/2

    -- constrain X and Y within the canvas
    local x = math.clamp(lpos.x, -size2.x, size2.x)
    local y = math.clamp(lpos.y, -size2.y, size2.y)

    local g = self.GridUnit
    if g > 0 then
        x = math.sign(x) * ((math.abs(x) - math.abs(x) % g) + (size2.x % g))
        y = math.sign(y) * ((math.abs(y) - math.abs(y) % g) + (size2.y % g))
    end

    -- find the highest Z at this X,Y to stack on top
    local highestZ = 0
    for _, part in ipairs(self.Canvas:GetChildren()) do
        if part:IsA("BasePart") then
            local partPos = cf:pointToObjectSpace(part.Position)
            local dx = math.abs(partPos.x - x)
            local dy = math.abs(partPos.y - y)
            if dx < g/2 and dy < g/2 then
                local topZ = part.Position.Z + part.Size.Z/2
                if topZ > highestZ then
                    highestZ = topZ
                end
            end
        end
    end

    -- place the model on top of the highest occupied cell
    local z = highestZ + modelSize.y/2

    -- create and return the CFrame thingi
    return cf * CFrame.new(x, y, z) * CFrame.Angles(-math.pi/2, rotation, 0)
end

If im not stupid and this works this now supports vertical stacking on the Z axis while keeping X,Y snapping!!! Hope this helped you!!!