How do I take an existing CFrame and flip one of its axes?

So, I’m constructing a functional mirror in my game. I’ve almost got it working. One problem:

The limbs appear in the correct position, however, the parts in the reflection model retain the same CFrame as the parts in the player model.
How would I fix this? I am not very good at working with CFrame.

Here is my current code.

local mirror = workspace.MirrorA
local reflection = workspace.PlayerReflectionA
--Mirror reflection
while true do
	wait()
	local mirrorCFrame = mirror.CFrame
	for _, reflPart in pairs(reflection:GetChildren()) do
		local charPart = char:FindFirstChild(reflPart.Name)
		if charPart then
			local charPartMirrorDifferential = Vector3.new(-(charPart.CFrame.X - mirrorCFrame.X), charPart.CFrame.Y - mirrorCFrame.Y, charPart.CFrame.Z - mirrorCFrame.Z)
			local reflCFrame = mirrorCFrame + charPartMirrorDifferential
			local charCFrame = charPart.CFrame
			reflPart.CFrame = CFrame.new(reflCFrame.X, reflCFrame.Y, reflCFrame.Z) * CFrame.Angles(charPart.CFrame:toEulerAnglesXYZ())	
		end
	end
end
7 Likes

I don’t know if this will work or not but you can try i guess

(plr cframe thing) + CFrame.new(plrcframething/2, 0, 0)

changing some stuff would probably make it look better, if that didnt work try changing it to -plrcframething/2

not 100% sure this will work but its worth a shot i guess.

This would change position. I am looking to change orientation.

1 Like

Oh. sorry about that but i think Vector3.new would work fine with this, i don’t think it will work with CFrame, not 100% sure about it

#1
Don’t ever use wait(), it’s notorious for being unreliable

#2: a while loop with wait() isnt the smoothest way to do this, use Heartbeat or RenderStepped because they fire every frame. RenderStepped is for things that need immediate feedback like camera manipulation, but I think Heartbeat would work fine with this

#3: A mirror is basically a 180 degree rotation on the Y axis (if I remember my axes correctly), so try this new code:

local mirror = workspace.MirrorA
local reflection = workspace.PlayerReflectionA
local RunService = game:GetService(“RunService”)

--Mirror reflection
RunService.Heartbeat:Connect(function(dt)
	local mirrorCFrame = mirror.CFrame
	for _, reflPart in pairs(reflection:GetChildren()) do
		local charPart = char:FindFirstChild(reflPart.Name)
		if charPart then
			local charPartMirrorDifferential = Vector3.new(-(charPart.CFrame.X - mirrorCFrame.X), charPart.CFrame.Y - mirrorCFrame.Y, charPart.CFrame.Z - mirrorCFrame.Z)
			local reflCFrame = mirrorCFrame + charPartMirrorDifferential
			local charCFrame = charPart.CFrame
			reflPart.CFrame = CFrame.new(reflCFrame.X, reflCFrame.Y, reflCFrame.Z) * CFrame.Angles(0, math.rad(180), 0)
		end
	end
end)

Not sure if this is completely right because I dont know exactly how your code works

Edit: Actually, mirrors aren’t always 180 degree turns. They work as angle reflections. So you’d need to find the angle of the character to the mirror and then the reflection’s rotation would be the inverse of that

Do you know how I would go about doing that? I think just knowing how to get the inverse of an existing CFrame might be all I need to get my code finalized.

1 Like

In short: That’s not fully possible on Roblox.

You want the vertices that were the furthest away from the mirror before the reflection to be the furthest away from the mirror after the reflection:

image

This would require manipulating the meshes beyond what is possible with a rotation matrix (which is what CFrames essentially are). To go from blue, to orange, to green, for example, you have to go clockwise in one case and counter-clockwise in the other.

In other words, you essentially have to flip the mesh inside-out to get a proper reflection.

Now, this isn’t impossible. You can generate a malformed CFrame that essentially flips the mesh in such a way, but it causes some strange issues with the rendering. This is because the normals of the mesh are also all inverted, making the “inside” of the mesh the “outside” of the mesh, and vice versa:

True reflection

function reflectCFrame(cframe, mirror)
	--Get the CFrame relative to the mirror
	local relCF = mirror.CFrame:toObjectSpace(cframe)
	
	--Get the original CFrame values
	local x, y, z,
		a, b, c,
		d, e, f,
		g, h, i = relCF:components()
	
	--Reflecting along Z direction - negate Z axis on 
	--all vectors
	local newCF = CFrame.new(
		 x, y, -z,
		 a, b, c,
		 d, e, f,
		 -g, -h, -i
	)
	
	--Convert back to world space
	return mirror.CFrame:toWorldSpace(newCF)
end

Another approach would be to calculate the angle relative to the mirror and negate that angle. The specifics will still need some experimentation, but the main concept stands:

Imitated reflection

function reflectCFrameSortOf(cframe, mirror)
	--Get the CFrame relative to the mirror
	local relCF = mirror.CFrame:toObjectSpace(cframe)
	
	--Get the original CFrame values
	local x, y, z,
		a, b, c,
		d, e, f,
		g, h, i = relCF:components()
	
	--Calculate horizontal angle
	local angY = math.atan2(-c, -a)
	
	--Reflecting along Z direction - negate position
	--and relevant axes
	local newCF = CFrame.new(x, y, -z) * CFrame.Angles(0, -angY, 0)
	
	--Convert back to world space
	return mirror.CFrame:toWorldSpace(newCF)
end

Note that some of the parts aren’t being reflected properly - this currently only works with parts that only have horizontal rotation. It also reverses the rotation on some of the parts - note, for example, the hinges that are pointing outwards in the reflection - those are facing inwards in the original. It’s actually impossible to accurately reflect objects that aren’t symmetrical about their X axis using this method.

The clear advantage of this approach, of course, is that the parts are all right-side-out and don’t display any strange rendering bugs.

2 Likes

You could multiply the CFrame by CFrame.Angles() if you want to rotate it around one of the unit axes, or CFrame.fromAxisAngle() if you want to rotate it around a specific vector.

I wouldn’t know about reflecting things, though. I don’t think there’s a CFrame method for it so you might have to write your own implementation.

I applied the “imitated reflection” method mentioned in this post and these are the results I got.
mirror1
It’s close to being what I need, but there are… problems. Like for example, the head is on backwards.

After making a modification to the code provided, I got something closer to the goal.
local newCF = CFrame.new(-x, y, z) * CFrame.Angles(0, -angY, 0) * CFrame.Angles(math.rad(180), 0, math.rad(180))

mirror2
Very close! I noticed that it wasn’t exact, and I thought this had to do with the rendering problem you mentioned, but upon closer inspection, I found the actual problem is that all of the parts on the character reflection are completely straight instead of the limbs bending.

Is this the side effect you meant when you said the method only works with parts that have horizontal rotation?

I did it guys! I got it working!

mirror works

This was accomplished by using a modified version of the “true reflection” code provided by @ImSimplyAnna.

Doing some fiddling, I was able to take the inverted glitchy look of the true reflection and essentially “uninvert” it, providing the intended result seen in the picture above.

Here’s the code for anyone who’d like to use it in their own projects!

function reflectCFrame(cframe, mirror)
	--Get the CFrame relative to the mirror
	local relCF = mirror.CFrame:toObjectSpace(cframe)
	
	--Get the original CFrame values
	local x, y, z,
		a, b, c,
		d, e, f,
		g, h, i = relCF:components()
	
	--Reflecting along Z direction - negate Z axis on 
	--all vectors
	local newCF = CFrame.new(
		 x, y, -z,
		 a, b, c,
		 d, e, f,
		 -g, -h, -i
	)
	
	--Convert back to world space
	local finalCFrame = mirror.CFrame:toWorldSpace(newCF)
	local x, y, z, r00, r01, r02, r10, r11, r12, r20, r21, r22 = finalCFrame:components()
	finalCFrame = CFrame.new(x, y, z, -r00, r01, r02, -r10, r11, r12, -r20, r21, r22)
	return finalCFrame
end
5 Likes

You can simplify your CFrame reflection code like this.

local function GetReflectedCFrame(CF, MirrorCFrame)

    local X, Y, Z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = MirrorCFrame:ToObjectSpace(CF):GetComponents()
    -- Reflect along X/Y plane (Z axis).
    local Reflection = CFrame.new(
	    X, Y, -Z,
    	-R00, R01, R02,
    	-R10, R11, R12,
    	R20, -R21, -R22
    )
    local ReflectedCFrame = MirrorCFrame:ToWorldSpace(Reflection)
    return ReflectedCFrame

end
6 Likes

I’ve been trying to figure out how to use the function you provided but I don’t know how to make it work. Could you please explain how I would reflect the player or regular parts using the function?

Pass in the CFrame of the part you want to mirror and the function will return the CFrame for the mirrored version of that part.

The “mirror” arg would just be whatever part you want to serve as the mirror, and will be used as the reference point.

1 Like

Got it, thank you. I thought the function duplicated the part right off the bat for some reason, but now I get how it works