Grid Snapping Issue [Bounty]

Ignoring the fact that you’re making a Plane Crazy clone, this can be solved using an iterated occlusion check, which can come in two different flavors:

  1. You scan the placement volume for colliding voxels, and you move the placement based on which quadrant of the volume the occupied voxel resides. This can be done by summing up the signs of the occupied voxel’s displacement relative to the center of the placement. Essentially, you want to nudge the placement away from the area with the most collisions, ensuring that the placement finds a suitable empty spot.
    Pseudocode:
foreach iteration:
  collision: {Vector3} = checkForCollisions(center)
  sumOfSigns: Vector3 = Vector3.zero
  for voxels in collision:
      disp = center - voxels
      sumOfSigns += disp:Sign()
  end
  center += sumOfSigns:Sign()
  1. You know what the surface normal of the adjacent block is, you can use that as the direction vector to slowly inch the placement away, repeat it until the placement no longer clips.

I have an old project that does both of those things, in the same order, which I have a video demo of:

Code sample:

@native local function rectifyPlacement(vcr: pPlot.VoxelCastResult): Vector3?
	local io: pPlot.PlayerInterface = plot.IO
	local pType: pReg.GenericEntry? = io.GhostType
	
	if pType and io.GhostRotation then
		
		--scalable parts are always 1x1x1, so they dont need to be rectified
		if pType.Part then 
			return vcr.Voxel + vcr.Normal
		end
		
		--c0 and c1 are the corners of the bounds of the part.
		local c0: Vector3 = pType.C0::Vector3
		local c1: Vector3 = pType.C1::Vector3
		local result: Vector3 = vcr.Voxel --voxel position of the center of the placement; initialized to be where the mouse is aiming at.
		
		--rotate the bounds based on the orientation of the placement preview
		local rot: Vector3 = pFuncs.AngleToRA(io.GhostRotation)
		local b0: Vector3 = pFuncs.RotVecRA(c0, rot)
		local b1: Vector3 = pFuncs.RotVecRA(c1, rot)
		local partSize: Vector3 = c1 - c0
		
		local maxTries: number = math.max(partSize.X, partSize.Y, partSize.Z) + 10
		local c0, c1: Vector3
		local querySuccess: boolean

		local timeOut: number = 0
		while true do --this is where the occlusion check happens
			timeOut += 1
			if timeOut > maxTries then break end

			c0 = result + b0
			c1 = result + b1
			local obstructions: Tensor.TensorHash<true> --this is just a hashmap in the form {[Vector3]: true}
			querySuccess, obstructions = plot:GetCollisions(c0, c1) --finds all colliding voxels within the c0 and c1 bounds

			if querySuccess then break end --no more collisions detected; we can stop now
			
			--otherwise, if there are collisions, move the placement AWAY from occupied voxels
			local moveTowards: Vector3 = c0:Lerp(c1, .5)
			local totalMove: Vector3 = Vector3.zero
			for obstacle: Vector3 in obstructions do
				totalMove += (moveTowards - obstacle):Sign()
			end
			result += totalMove:Sign()
		end
		
		--if the occlusion check above fails, here's another one
		if not querySuccess then
			result = vcr.Voxel
			c0 = result + b0
			c1 = result + b1

			timeOut = 0
			while true do
				timeOut += 1
				if timeOut > maxTries then break end

				c0 = result + b0
				c1 = result + b1
				querySuccess = plot:CanPlace(c0, c1) --:CanPlace() is a wrapper for :GetCollisions()

				if querySuccess then break end

				result += vcr.Normal --move away from the face that the mouse is aiming at
			end
		end

		if not querySuccess then
			--print('smart placement failed')
			result = vcr.Voxel
			c0 = result + b0
			c1 = result + b1
		end
		
		return result
	end
	return
end