0

Okay, I'm trying to write something resembling a 3D game engine. That means I have to deal with matrix transformations and similar stuff.

For any of you who don't know, you need to use three basic matrices to define where a vertex will be positioned on the screen: projection matrix, view matrix and entity matrix.

View matrix moves the camera, while the entity matrix moves an entity in the world. We don't care about projection matrix here. Since the camera doesn't exist, it's simulated by moving the whole scene in inverse way (negate translation vectors and inverse rotation quaternions).

Given their similarity, I wanted to write a base class to act as an interface and write two implementations that would deal with each of the matrices, entity one using the values provided, and view one inverting them appropriately. That way the client can move the camera more intuitively leaving the inversion conundrum to be dealt internally. It also allows me to write methods that operate on either of matrix types.

But there are two things that oppose each other, making achieving this a bit difficulty (or at least messy).

Entity matrix has three components: position, rotation and scale. The view matrix only has the first two - it makes no sense to scale the camera. I could code the first two components into the base class and have the scale be specific for the entity matrix.

NOTE: When I say matrix type, or refer to a matrix, I actually mean a custom type that holds the components I can create the matrix out of, not a matrix object.

But I want to write another class that would hold a single method that would take in a matrix and transform it based on some rules (I'd have an abstract interface and client could implement any number of methods which somehow transform the object - move/rotate object based on player input, move it along a specific curve, rotate around a point, etc). You can see that this methods could be executed on either entities or camera(s). Making the base class makes it possible - I can pass the base matrix type to the method and have it return the transformed matrix, regardless of it's concrete type.

However, if I put scale component in entity matrix, I won't be able to use it in that method since it will take the base type that will not have it defined.

If I put scale in the base type, it makes the whole thing a bit dirty, since I can now scale the view matrix. In this particular case, scaling the view matrix changes nothing and nothing bad happens due to the way I construct the actual matrix from the data, it just makes me feel bad and dirty.

But imagine this same situation in different environment. Doing this there might cause your computer to spit fire and dragons to come out of your display and eat your face off.

My question isn't about my specific case (where I can ignore it, but feel dirty and bad), but a generic one (with dragons and fire). How can I achieve what I want without having a dragon chew my face off?

1 Answer 1

6

You're using the wrong tool here, as is often the case when people try to use inheritance-based polymorphism on basic types.

For example, a view matrix is first and foremost a matrix. The fact that you use it for a camera is about the meaning of its value, not about what you can do with it. To give it a separate type from a regular matrix is simply not a reasonable design. It limits your ability to do things that you might ultimately want to do. The same goes for the perspective matrix.

So there is no need for them to have a distinct type from a "matrix" type. What makes them distinct is their values, not the type itself.

That is, consider if you had a speed type. You wouldn't make separate "car speed" and "truck speed" types; cars and trucks would simply have a speed. A speed is a "car speed" because the speed came from a car.

The same goes here. A "view matrix" is a matrix that gets used for the camera part of the transformation.

Your definition of "entity matrix" as having "three components: position, rotation and scale" (RPS) represents something that is actually distinct from a traditional matrix. What you have isn't a matrix at all; it is a specific kind of transformation. That's separate from the concept of a "matrix".

You can use the transformation to generate a matrix, but the transformation is not a matrix. You cannot multiply an RPS transformation by an arbitrary 4x4 matrix and still have it be an RPS transformation. Well, you could, but doing so would require performing singular-value-decomposition on the resulting matrix in order to get your RPS values back. And that can fail if the arbitrary matrix included sheering or projection properties.

Since an RPS transformation is not a matrix, it should not inherit from a matrix type. It should be its own type with its own operations. You convert it into a matrix as needed, but it is not the same thing as a matrix.

Now, I have had need of a special type that represents an RPS transformation, but can also be combined with a matrix. Basically, what I did was store a variant<RPS, matrix>. So the object could either be an RPS transformation or a standard 4x4 matrix.

If it was in decomposed RPS form, then you could affect the position, orientation, or scale of the decomposed form with decomposed operations. But if you tried to combine it with a 4x4 matrix, then it switched to the matrix form. Once that happened, you couldn't affect the RPS components directly; you had to use matrix operations on it.

But note that this type was not a "matrix". It was a composable transformation, a distinct type that had some inter-operability with matrices. But you could not reasonably replace matrix with composable transform or vice-versa. So there was no need to use inheritance.

5
  • I was perhaps a bit unclear. I do have a custom type that holds that three components, and matrix is constructed by the objects that use it. The custom type has never heard of any matrix type. The thing is that I don't want to have the scale component in the base class, while being forced to have it at the same time. Nov 5, 2016 at 17:58
  • @Karlovsky120: If you're not using inheritance, where does this "base class" that the "scale component" come from? Nov 5, 2016 at 18:08
  • I am using inheritance, I have a base type that holds the three components and has pure virtual functions that manage them, and two implementations that override the functions in different manners. Problem is that camera implementation also has to deal with scale component, but it shouldn't be. If I move out the scale component to entity implementation, I won't be able to use the base class as input to transformation methods (and thus be able to use the same method to operate on both camera and entity) or if I do, won't be able to manipulate the scale component of the entity. Nov 5, 2016 at 18:12
  • @Karlovsky120: ... why? What are you using polymorphism to accomplish? What are these "two implementations", and why do they differ? And why do you need to be able to treat them as though they were the same type? Also, why is having a scale in a camera transformation wrong? Nov 5, 2016 at 18:18
  • To translate entity forward, I add (0.0, 0.0, 1.0) vector to it's position. To translate camera forward, I add (0.0, 0.0, -1.0) vector to it's position because I'm actually moving the world back. I can negate the translation value in camera implementation and have the user use (0.0, 0.0, 1.0) to translate camera forward, which is more intuitive. Scaling doesn't work with the camera. It's shouldn't be there. I can code around it and just make sure that scale is (1.0, 1.0, 1.0) regardless of input, but the interface would imply that the camera can be scaled. Nov 5, 2016 at 18:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.