I made a mirror tool but had to do some fixes to make it work with rotation. After those changes my previous mirroring logic completely broke.
Old mirror logic:
local targetCFrame = info.CFrame
if mirror and minBound and maxBound and face then
local pivot = info.CFrame
local pos = pivot.Position
local mirroredPos
if face == Enum.NormalId.Right then
mirroredPos = Vector3.new(maxBound.X + (maxBound.X - pos.X), pos.Y, pos.Z)
elseif face == Enum.NormalId.Left then
mirroredPos = Vector3.new(minBound.X - (pos.X - minBound.X), pos.Y, pos.Z)
elseif face == Enum.NormalId.Top then
mirroredPos = Vector3.new(pos.X, maxBound.Y + (maxBound.Y - pos.Y), pos.Z)
elseif face == Enum.NormalId.Bottom then
mirroredPos = Vector3.new(pos.X, minBound.Y - (pos.Y - minBound.Y), pos.Z)
elseif face == Enum.NormalId.Front then
mirroredPos = Vector3.new(pos.X, pos.Y, minBound.Z - (pos.Z - minBound.Z))
elseif face == Enum.NormalId.Back then
mirroredPos = Vector3.new(pos.X, pos.Y, maxBound.Z + (maxBound.Z - pos.Z))
end
if mirroredPos then
targetCFrame = CFrame.new(mirroredPos) * (pivot - pivot.Position)
end
end
(I made sure that none of the stuff in the if statement are nil)

Client logic (No problems, no changes needed that I know of.)
local normal = Vector3.FromNormalId(currentFace)
local worldNormal = boundingPart.CFrame:VectorToWorldSpace(normal)
local localNormal = boundingPart.CFrame:VectorToObjectSpace(worldNormal)
local size = boundingPart.Size
local moveAmount
if math.abs(localNormal.X) > 0.9 then
moveAmount = size.X
elseif math.abs(localNormal.Y) > 0.9 then
moveAmount = size.Y
elseif math.abs(localNormal.Z) > 0.9 then
moveAmount = size.Z
else
moveAmount = size.X
end
boundingPart.CFrame = boundingPart.CFrame + worldNormal * moveAmount
local positions = {}
for model, offset in pairs(modelOffsets) do
if typeof(offset) == "CFrame" and boundingPart and boundingPart.CFrame then
table.insert(positions, {Model = model, CFrame = boundingPart.CFrame * offset})
else
if model and model:IsA("Model") then
local success, modelCFrame = pcall(function()
return model:GetBoundingBox()
end)
if success and modelCFrame and originalBoundingCFrame then
local fallbackOffset = originalBoundingCFrame:ToObjectSpace(modelCFrame)
if typeof(fallbackOffset) == "CFrame" and boundingPart and boundingPart.CFrame then
table.insert(positions, {Model = model, CFrame = boundingPart.CFrame * fallbackOffset})
end
end
end
end
end
local result
pcall(function()
result = ValidateMirror:InvokeServer(positions, currentFace)
end)
The mirror logic for the server loops through all the blocks in the positions table.
for _, info in ipairs(positions) do
local clone = info.Model:Clone()
clone.Parent = userFolder
for _, part in ipairs(clone:GetDescendants()) do
if part:IsA("BasePart") then
anchoredStates[part] = part.Anchored
part.Anchored = true
end
end
Weld.DeleteAllConnectedWelds(clone, userFolder)
Weld.PruneWeldCache()
local targetCFrame = info.CFrame
--Mirror logic
clone:PivotTo(targetCFrame)
table.insert(NewBlocks, clone)
RunService.Heartbeat:Wait()
end
Any help on how I can mirror these models is appreciated! ![]()