2
\$\begingroup\$

I'm currently writing a game engine from scratch for learning purposes. I just started implementing transformations. I know how they work in general but I'm not sure how to implement them efficiently in hierarchical structure.

Should I keep each transformation separate or keep them bundled in a transformation matrix? I have a feeling a single matrix would be way more efficient than calculating model transformation matrix each frame for each object in component tree. On the other side, I'd like to access position, rotation and scale individually as I plan on using them later for some other optimizations.

I'm aware of glm::decompose but I'm not sure if it's a good idea to call it every frame.

Should I keep both the matrix and the components, decompose the matrix every frame or calculate it every frame?

\$\endgroup\$
3
  • 2
    \$\begingroup\$ You seem to be missing a third option: keep both the matrix and components, and recompute the matrix only when the components change, eg. by means of a dirty flag or similar strategy. \$\endgroup\$
    – DMGregory
    Commented Aug 27, 2019 at 23:21
  • \$\begingroup\$ I though objects containing both might be too big. Probably have to get rid of that phobia in C++ where they literally take up 16 * 4 bytes of memory. After digging around other OSS game engines it seems all of them are doing it. \$\endgroup\$ Commented Aug 31, 2019 at 21:16
  • 1
    \$\begingroup\$ You can store them in separate structures if you need. 12 x 4 bytes for the world matrix in a buffer you use for global/positioning & rendering (reconstructing the 4th row implicitly since it doesn't change for typical object transforms), and 10 x 4 bytes for the local translation/rotation/scale in the local transform component your scripts manipulate. \$\endgroup\$
    – DMGregory
    Commented Aug 31, 2019 at 21:22

1 Answer 1

4
\$\begingroup\$

My general recommendation is to keep both the source components of your transformation for ease of manipulation:

  • local translation vector
  • local orientation quaternion
  • local axis-aligned scale triplet, or uniform scale scalar

And the resulting object-to-world matrix (factoring in the transformations of the parent hierarchy too).

Keeping these separate gives you the best of both worlds:

  • Scripts that need to modify just a rotation or just a scale can act on those properties directly, without decoding and re-encoding them into the matrix on every use.

  • Your rendering (and perhaps physics) systems have access to pre-concatenated object-to-world matrices exactly as they need, and don't need to take responsibility for building them on the fly.

You can even keep these two views of the transformation in separate components/buffers for cache utilization, if your code often works with just one or the other at a time.

You can use a dirty flag to avoid redundant re-computation of the complete matrix for objects that stay stationary for the frame. (Or a dirty list, to avoid iterating your whole collection with random-ish branching along the way) This also saves you re-computing the whole matrix for each intermediate step when an object's local properties are modified by several effects in a single frame - instead you "bake in" the combined effects lazily, the next time the matrix is needed.

A major benefit of separating out the source values is controlling the accumulation of errors. By storing these components separately, they remain pristine - repeated rotation won't "leak" rounding errors into deviations of your scale parameter for example - and you can more easily maintain invariants like orthogonality (something you get by construction when storing the rotation as a quaternion rather than a matrix).

Your hierarchy can be expressed as simply as a parent id / index - keeping the transforms sorted by this index can let you update a whole hierarchy in a single linear pass.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .