CFrame.new(Vector3 p, Vector3 right, Vector3 up[, Vector3 back])

Like the title says, I’m asking for a new CFrame constructor.

CFrame.new(Vector3 p, Vector3 right, Vector3 up[, Vector3 back])

This would create a rotated CFrame from a position and the first two column vectors of the rotation matrix. The third column vector would be optional–if omitted, it would be inferred from the first two by normalizing their cross product.

###Why we need it

Here’s some code I wrote a few minutes ago to answer a question in development discussion. It uses the common pattern of taking some column vectors, crossing a bunch of things, and then packing the individual components into the CFrame matrix.

Take note of the last line–specifically, how it indexes 12 userdata fields, and how the syntax looks like roadkill.

local p2c = p2.CFrame
local vz = -p2c.lookVector
local vy = p1.CFrame.rightVector:Cross(vz).unit
local vx = vy:Cross(vz).unit
p2.CFrame = CFrame.new(p2c.x, p2c.y, p2c.z, vx.x, vy.x, vz.x, vx.y, vy.y, vz.y, vx.z, vy.z, vz.z)

That’s pretty garbage. Let’s refactor with the constructor I’m proposing.

local p1c = p1.CFrame
local p2c = p2.CFrame
p2.CFrame = CFrame.new(
	p2c.p, 
	p1c.rightVector, 
	p2c.lookVector:Cross(p1c.rightVector).unit
)

The second way saves time and mitigates a potential source of bugs.

28 Likes

Yes please.

2 Likes

Yes. Yes.

Please coordinate this feature in, it would make life easier :slight_smile:

1 Like

I would be hesitant to add just one of these, because they constitute a general class comprising six specific functions. Why is the one with a rightVector and upVector special?

What happens if right and up aren’t orthogonal? Should cf.rightVector point toward right, or should cf.upVector point toward up? The other vector would be the closest possible vector pointing in the given direction while still being orthogonal. There are two courses of action, here, and there really is no right answer.

Then there’s the variants using up and back, back and right.

What I’m trying to get at is that programmers will have the happiest time if they write these themselves. There’s a 1/6 chance that the implementation this gives is the best one for the job, so they’ll have to write the functions for the other 5/6 times regardless.


If the biggest concern is the 12-field constructor, maybe a constructor which takes a position, rightVector, upVector, and backVector would be a good solution.

Most power and ease of use with three basis vectors but the third being optional.

I think that’s the way to go. updated the OP

Since cframe matrices are stored in with 12 values, if you’re not going to do it, the CFrame.new function will do the same for you, so it’s technically unnecessary given that you know math. Even tho one could argue that lerp and other functions are useless too but it feels like this has less usage cases than the other ones.

Why is look optional? It makes a lot more sense to just add an optional up argument to CFrame.new(p, at)…

4 Likes

Not saying I agree with making it optional, but the reasoning is most likely: you can define the CFrame with position + 2 vectors, and it doesn’t make sense to make the order of right/up/look different because you want it ordered as x/y/z like all other API.

It doesn’t matter if it’s explicitly consistent if it makes it less useful…

I’m asking for a better way to construct rotation matrices. I’m not looking for a more flexible way to look at a point. The look vector is optional because users will want an orthogonal matrix 99% of the time.

But you’ll also want to use the look vector and the up vector 99% of the time. I really don’t see your point, it’s easy enough to do CFrame.new(pos, pos + look, up).

That’s effectively the same thing as what I’m requesting but with additional complexity and without the flexibility of being able to specify the third column vector.

If CFrames are forced to be orthogonal and non-scaling, you gain absolutely nothing by being able to set the third vector, but you will produce confusion because it will be nonfunctional.

They’re not:

> print(CFrame.new(1,2,3,4,5,6,7,8,9,10,11,12))
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
> local cf = CFrame.new(1,2,3,4,5,6,7,8,9,10,11,12) workspace.Baseplate.CFrame = cf print(workspace.Baseplate.CFrame)
1, 2, 3, 0.311399579, 0.858116508, -0.895533562, 0.544949293, 0.190692365, -0.398014903, 0.778498948, -0.476730913, 0.199007452

If you want to be able to construct an actual matrix that you can do scaling and shearing transformations with, you should be requesting CFrame.new(p, right, up, back).

Well, do you mean literally “new” here as function name? Because that would actually be confusing, because you’d have CFrame.new(pos1, pos2) and then CFrame.new(pos, right, up, back). In other words the second parameter would be interpreted differently depending on what is given.

If you mean something else than “new”, then you’ve basically got this feature request

That would actually be better. CFrame.fromMatrix()?

1 Like

I actually made a typo in the OP by specifying look as the fourth argument. Should be back.

1 Like