Setting part cframe based on subpose cframe

im essentially making a custom animator for a combat security system. this is what i believe one of the last 3 things i need to do in this system. its optimized for 200 player servers.

i have a dictionary of cframes which are the cframes in the poses of a keyframe.
this is pretty much what it looks like, generalized for keyframes which only have all the r6 bodyparts for poses+subposes

{Torso = keyframe.HumanoidRootPart.Torso.CFrame,
Head = keyframe.HumanoidRootPart.Torso.Head.CFrame,
['Left Arm'] = keyframe.HumanoidRootPart.Torso['Left Arm'].CFrame,
['Right Arm'] = keyframe.HumanoidRootPart.Torso['Right Arm'].CFrame,
['Left Leg'] = keyframe.HumanoidRootPart.Torso['Left Leg'].CFrame, 
['Right Leg'] = keyframe.HumanoidRootPart.Torso['Right Leg'].CFrame}

these are changing constantly as the ‘animation’ plays programmatically (or not constantly, but rather they are updated depending on when pretender:Get() is called. pretender:Get() returns this dictionary)

i start the ‘animation’ with pretender:Play(animationName, ...) and i get the limb pose cframes at the current time with pretender:Get()

while (1) do
		pretender:Play('test', .3, .1, 1, 1)
		
		for i = 1, 55 do
			local d = pretender:Get()
			for _, v in ipairs(workspace.Rig:GetChildren()) do
				if (d[v.Name]) then
					if (v.Name == 'Torso') then
                        -- ignore these 2 lines below. 
						print('torsoed')
						v.CFrame = initialLimbCFrames['Torso'] * d[v.Name]
					else
						v.CFrame = initialLimbCFrames[v.Name] * d[v.Name]
					end
				end
			end
			RunService.Heartbeat:Wait()
		end
		
		wait(1)
	end

initialLimbCFrames is the initial cframes of each r6 body part. not the cframes of the poses, but the cframes in world position

as the ‘animation’ plays, it shows this

it should show this

ignore the fact that the latter has a gun. also ignore the fact that it is slower- i made a mistake when programming the system and its not that big of a deal, i can fix it.

my question is, how do i set a parts cframe based on the corresponding poses cframe, as clearly it does not work as intedned. im struggling to find the cframe manipulation that does this
this is a piece of the pretender:Get() function thats involved with creating that dictionary i showed at the start of the post.

local cfScalarArray = {}
	for k, v in next, locomotives do -- let k be the name of the limb and v a dictionary with an array called 'data' filled with arrays that have the pose cframe for the corresponding limb
		if (not cfScalarArray[k]) then
			cfScalarArray[k] = CFrame.identity
		end
		
		for _, datum in ipairs(v.data) do
			local x, y, z, r00, r01, r02, r10, r11, r12, r20, r21, r22 = datum.CFrame:GetComponents()
			
			-- ive tried flipping the lookvector, no luck
            -- by the way these 3 lines of code below does an inverted mesh thingy which could be used for cell shading. its really cool and not easy to explain without an image which would make this post offtopic
			--[[r02 *= -1
			r12 *= -1
			r22 *= -1]]
			
			
			local newCF = CFrame.new(x, y, z, r00, r01, r02, r10, r11, r12, r20, r21, r22)
			--local aY, aX, aZ = newCF:ToEulerAnglesYXZ()
			--newCF *= CFrame.Angles(aZ, aY, aX) -- ive swapped each argument all 6 times, no luck
			
			cfScalarArray[k] *= newCF
		end
	end

--...
return cfScalarArray
2 Likes


2nd video reupload

2 Likes

bump because this needs to be done

1 Like

After some digging, I’ve came to a pretty good speculation.

Keyframes store poses, that store CFrames that manipulate Motor6d’s to animate as you know. But AnimationTracks apparently store offsets of CFrames, not a “Final” CFrames.

This is a default rig, nothing has been moved. The properties show that the left arm’s Motor6d by default has a Orientation of (0, -90, 0)

Going into the animator now, I’ve simply rotated on the X axis only

image

After exporting it and reading the CFrame value, there is no -90 on the y axis.

image

Hopefully, this is correct. If so, hope this helps! :grin:
(I know the arm isnt rotated 70 degrees in the image, I had to redo the photo and figured you get the point.)

2 Likes

ill try to implement my system with this now, thank you
i’ve spent an entire day begging people to help me with this problem, i hope this helps too

1 Like

good news, it doesnt work :cry:
modus operandi:

  • get the cframe of the rightarm subpose (all 12 components)
  • multiply the right arm cframe by that value

result:


(the bottom shown face on the right arm is the front face)

the result should be the right arm position in the video i showed you, at the last keyframe.
i believe im missing a value i need to multiply the cframe by. do i need to take into account the default orientation of the ‘Right Shoulder’ motor6d in the torso? it should already be taken account of.

these are the default motor6d values
image
im pretty sure i dont need to take them into account because the right arms position is already transformed by them.

1 Like

well, sorry about it not working. If you want to try to debug it, set the position of the arm manually and then give it a new weld, so the weld will solve for the correct CFrame value which you can compare to what you are calculating.

2 Likes

So if v is a part and you are trying to find part 1 CFrame then shouldnt it be:

Part0.CFrame*C0*Transform*C1:Inverse()

From rearranging the motor 6d formula

Part0.CFrame*C0*Transform=Part1.CFrame*C1
2 Likes

this seemed like a very correct solution
but it doesnt work sadly

here’s my code for updating the cframes

local pretender = greatPretender.New()
pretender:LoadAnimationAsync('test', 13589597031, Enum.AnimationPriority.Action)
coroutine.wrap(function()
	local initialLimbCFrames = {}
	local rigParts = workspace.Rig:GetChildren()
	
	for _, v in ipairs(rigParts) do
		if (v.ClassName == 'Part') then
			initialLimbCFrames[v.Name] = v.CFrame
		end
	end
	
	-- these are the c0's and c1's of all the limbs.
	local jointLocations = {
		['Left Leg'] = {C0 = CFrame.new(Vector3.new(-1, -1, 0), Vector3.new(0, math.rad(-90), 0)), C1 = CFrame.new(Vector3.new(-0.5, 1, 0), Vector3.new(0, math.rad(-90), 0))},
		['Right Leg'] = {C0 = CFrame.new(Vector3.new(1, -1, 0), Vector3.new(0, math.rad(90), 0)), C1 = CFrame.new(Vector3.new(0.5, 1, 0), Vector3.new(0, math.rad(90), 0))},
		['Left Arm'] = {C0 = CFrame.new(Vector3.new(-1, 0.5, 0), Vector3.new(0, math.rad(-90), 0)), C1 = CFrame.new(Vector3.new(0.5, 0.5, 0), Vector3.new(0, math.rad(-90), 0))},
		['Right Arm'] = {C0 = CFrame.new(Vector3.new(1, 0.5, 0), Vector3.new(0, math.rad(90), 0)), C1 = CFrame.new(Vector3.new(-0.5, 0.5, 0), Vector3.new(0, math.rad(90), 0))},
		['Head'] = {C0 = CFrame.new(Vector3.new(0, 1, 0), Vector3.new(math.rad(-90), math.rad(-180), 0)), C1 = CFrame.new(Vector3.new(0, -0.5, 0), Vector3.new(math.rad(-90), math.rad(-180), 0))},
	}
	
	while (1) do
		local con; con = RunService.Heartbeat:Connect(function()
			local poseCFrames = pretender:Get() -- the pose offset cframes
			for _, limb in ipairs(rigParts) do
				if (poseCFrames[limb.Name]) then
					local C0, C1 = CFrame.identity, CFrame.identity
					if (jointLocations[limb.Name]) then
						C0, C1 = jointLocations[limb.Name].C0, jointLocations[limb.Name].C1
					end
					
					limb.CFrame = initialLimbCFrames[limb.Name] * C0 * poseCFrames[limb.Name] * C1:Inverse()
				end
			end
		end)
		
		pretender:Play('test', .2, .1, 1, 1) -- name; startingTimePosition; fadeTime; weight; speed
		pretender.allAnimationsEnded:Wait()
		con:Disconnect(); con = nil
		
		task.wait(1)
	end
end)()

jointLocations is the c0’s and c1’s of each of these
image

the cframe offsets in the module are correct. here’s a rundown of what my module is doing:
when an animation is loaded, the module fills in all of the keyframes within the animation so that each keyframe has the same number of poses and subposes.
when an animation is played, the module determines which animations to prioritize and calculates the fadetimes, speeds, etc; it determines the last and next keyframe and lerps each pose cframe to where it should be linearly based on the current os.clock(), and adds the newly calculated pose cframes into the table at the very top of the post.
all my calculations are correct with a 95% guarantee, and ive checked them many times.
if you still have any ideas on why this system is not working, that would be very helpful.

edit: cleaned up the code so i dont piss people off with messy code

1 Like

Some more things, turns out that the roblox documentation mentions a property called Transform in Motor6d’s. It recommends manipulating this CFrame instead of C1 and C0 when dealing with custom animations. I Tinkered with it, and Transform basically is the CFrame that describes the offset of an animation track. In other words, This CFrame value manipulates an Offset, with the initial position being C1 and C0 combined (the CFrame of part1 or part0).

As for the 90 degree offsets in motor6d’s I think its a gimbal lock prevention.

One last thing:

This is a constructor for CFrame.LookAt (signalled with 2-3 vector3’s) instead of a Orientation Change which I presume you wanted. You have to use CFrame.Angles to apply an angle change on a CFrame.

Alternatively, you can print and use the C1 and C0 Values of a default rig’s motor6d and use them directly. (0.5, 0.5, 0, -0, -0, -1, 0, 1, 0, 1, 0, 0) is the C1 Value of the Right Shoulder motor6d.

I believe that if you use transform, you wont need to have an initial CFrame in store (unless you create the motor6d’s). But I’d figure I’d point it out just so you can see the mistake.

Motor6D | Documentation - Roblox Creator Hub

Hopefully, this helps! :grin:

1 Like

i found out about m6d.transform yesterday, here’s my understanding of it
anim.txt (1.1 KB)
it interpolates itself to the pose cframe

:sad: all this time i thought it was an alternative to typing out cframe.angles and cframe.new in 1 command. that makes more sense than it being cframe.lookat


your post is confusing. the c0s and c1s in that jointLocations dictionary are the default cframes of the motor6ds that correspond to each limb.
i cant get the transform because the transform simply interpolates to the pose cframe when possible. there’s no motor6d im able to work with to get those transforms either. i’m not using motor6ds at all.

local jointLocations = {
		['Left Leg'] = {
			C0 = CFrame.new(-0.5, -2, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1),
			C1 = CFrame.new(0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1)
		},
		['Right Leg'] = {
			C0 = CFrame.new(0.5, -2, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1),
			C1 = CFrame.new(0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 )
		},
		['Left Arm'] = {
			C0 = CFrame.new(-1.5, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1),
			C1 = CFrame.new(0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 )
		},
		['Right Arm'] = {
			C0 = CFrame.new(1.5, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1),
			C1 = CFrame.new(0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 )
		},
		['Head'] = {
			C0 = CFrame.new(0, 1.5, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1),
			C1 = CFrame.new(0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1)
		},
	}

this is what i had earlier- no luck
i then changed it to

local jointLocations = {
		['Left Leg'] = {
			C0 = CFrame.new(-0.5, -2, 0),
			C1 = CFrame.new(0, 0, 0)
		},
		['Right Leg'] = {
			C0 = CFrame.new(0.5, -2, 0),
			C1 = CFrame.new(0, 0, 0)
		},
		['Left Arm'] = {
			C0 = CFrame.new(-1.5, 0, 0),
			C1 = CFrame.new(0, 0, 0)
		},
		['Right Arm'] = {
			C0 = CFrame.new(1.5, 0, 0),
			C1 = CFrame.new(0, 0, 0)
		},
		['Head'] = {
			C0 = CFrame.new(0, 1.5, 0),
			C1 = CFrame.new(0, 0, 0)
		},
	}

and still no luck. they both produce visually the same wrong output, where all the limbs are rotating incorrectly on the body.

local initialLimbCFrames = {}
	local rigParts = workspace.Rig:GetChildren()
	
	for _, v in ipairs(rigParts) do
		if (v.ClassName == 'Part') then
			initialLimbCFrames[v.Name] = v.CFrame
		end
	end
	
	-- these are the c0's and c1's of all the limbs.
	--[[local jointLocations = {
		['Left Leg'] = {
			C0 = CFrame.new(-0.5, -2, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1),
			C1 = CFrame.new(0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1)
		},
		['Right Leg'] = {
			C0 = CFrame.new(0.5, -2, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1),
			C1 = CFrame.new(0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 )
		},
		['Left Arm'] = {
			C0 = CFrame.new(-1.5, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1),
			C1 = CFrame.new(0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 )
		},
		['Right Arm'] = {
			C0 = CFrame.new(1.5, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1),
			C1 = CFrame.new(0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 )
		},
		['Head'] = {
			C0 = CFrame.new(0, 1.5, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1),
			C1 = CFrame.new(0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1)
		},
	}]]
	
	local jointLocations = {
		['Left Leg'] = {
			C0 = CFrame.new(-0.5, -2, 0),
			C1 = CFrame.new(0, 0, 0)
		},
		['Right Leg'] = {
			C0 = CFrame.new(0.5, -2, 0),
			C1 = CFrame.new(0, 0, 0)
		},
		['Left Arm'] = {
			C0 = CFrame.new(-1.5, 0, 0),
			C1 = CFrame.new(0, 0, 0)
		},
		['Right Arm'] = {
			C0 = CFrame.new(1.5, 0, 0),
			C1 = CFrame.new(0, 0, 0)
		},
		['Head'] = {
			C0 = CFrame.new(0, 1.5, 0),
			C1 = CFrame.new(0, 0, 0)
		},
	}
	
	while (1) do
		local con; con = RunService.Heartbeat:Connect(function()
			local poseCFrames = pretender:Get() -- the pose offset cframes
			for _, limb in ipairs(rigParts) do
				if (poseCFrames[limb.Name]) then
					local C0, C1 = CFrame.identity, CFrame.identity
					if (jointLocations[limb.Name]) then
						C0, C1 = jointLocations[limb.Name].C0, jointLocations[limb.Name].C1
					end
					
					limb.CFrame = initialLimbCFrames['Torso'] * C0 * poseCFrames[limb.Name] * C1:Inverse()
				end
			end
		end)
		
		pretender:Play('test', 0, .1, 1, 1) -- name; startingTimePosition; fadeTime; weight; speed
		pretender.allAnimationsEnded:Wait()
		con:Disconnect(); con = nil
		
		task.wait(1)
	end

it seems like there’s obviously a problem with how limbs are being rotated, but what?
ive already rearranged the offsets angular value all 6 times (xyz, xzy, yxz, yzx, zyx, zxy), i’ve made sure that im not taking into account the original limb cframe or the motor6d cframes in my module, and that it’s only the limb offset cframes being taken account of; i believe im not utilizing the offset cframes correctly. is there something im missing in the code i showed you, or did i misinterpret you or dthecoolests posts?

Just noticed something, there’s a lot of unnecessary information in these posts.

Seems like you are using the initial limb CFrames judging. Rather you should be using what the CFrame of that limb is at the moment starting from the root part down to the end effector joints (ex torso to foot).

Perhaps you should create a fresh baseplate to start a new.

I have an example Motor6D project which calculates the position of the foot where it should be with .Transform animations applied for IK purposes which you can take a look at.

i can’t share the module so im trying to give as much information as possible, as well as lead people into the right direction with their answers

can you elaborate on this? whats an end effector joint, what do you mean at all? are you able to show what you mean visually?

no need. for one, its being made in my game. two, my modules working, it’s the cframing that’s the issue

ill check this out when i can

is anyone else able to help? i will need to do this again in a few weeks, because i remade the game and am gonna remake the combat system.

i found a solution

i’m gonna remake this later on, and make it efficient

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.