2
\$\begingroup\$

I'm having a small issue with my design for hierarchical 3D objects. I'll try to sum things up concisely. Every object has a vector3 for its position, scale, and rotation, as well as a std::vector of children objects. An object's render function takes an optional parameter for a parent's transformation matrix, which defaults to the identity matrix. In this render function, the object's transformation matrix is computed with this form:

transformation = parentTransformation * translationMatrix * rotationMatrix * scaleMatrix;

After the matrix is computed, all of the childrens' render functions are called with the computed matrix being passed, then the actual render for THIS object happens.

My problem seems to be with the scaling bit. For this case, I have a wooden manikin built out of several spheres, all translated and scaled to give shape. For the legs and arms, I scaled the sphere pieces obviously not uniformly. To avoid the children objects of those body parts from also being scaled like that, I scale them by the inverse of that scale factor after scaling the particular body part. However, when I rotate a joint which is a child of one of these oddly scaled pieces, you can see how the scaling is really only in the childrens' "model space" and as a result, they get strangely contorted. Is there a misunderstanding on my part about how objects should inherit their parents' transformations?

Here are some images to demonstrate my results. The second image is after rotating the knee joint 80 degrees.

Before rotation of the knee

After rotating the knee joint 80 degrees

\$\endgroup\$
3
\$\begingroup\$

So firstly, I'd like to apologize if my question was poorly worded or if it was just confusing in general. I think I've figured out the proper solution now. It took a while because I had passed it off earlier after trying it and not immediately seeing desired results (I needed to basically remake my wooden manikin object from scratch).

Basically, I found that objects really shouldn't be inheriting their parents' scales to begin with, just for safety. I changed my design so that when a transformation matrix was to be recomputed, a scaleless transformation was computed and passed down to the children, but then the real transformation including scale was also computed to be used for rendering. What this ultimately means is that I had to also change my scale methods to take a "recursive" boolean as a parameter, where if true, the scale factor would be applied to all children objects recursively. I like having that functionality - the ability to scale an entire complex object, or just a piece of it - however I'm not sure if this is how it "should" be done, or if this is the most efficient way. If anybody has any feedback or advice for me on this topic, please share, but for now what I've learned is

scale should not be inherited from parent objects by means of inherited transformation matrices.

My results - bending the knee by 80 degrees:

enter image description here

\$\endgroup\$
0
\$\begingroup\$

In my own project I suddenly came to the same conclusion - the parent scale shouldn't be passed down to the children's transformation matrix. And I thought it was weird too. I figured, if you want to stay consistent throughout your code, either you pass through all of it, or you pass through nothing. However, seeing how you came to the same conclusion is kind of reassuring, so I suppose I'm sticking with it.

I came to this conclusion while trying to roll my own GUI implementation. I had pixel art images of my GUI elements, and when I changed my monitor's resolution, the images rescaled automatically and quite frankly looked butt ugly. So I had to find a way to keep my images the same size (or rescaled in powers of 2 to keep the pixel art consistent). Dropping the parent scale from the hierarchy and keeping the scale of my objects absolute instead of relative seemed to be the obvious solution.

Now at some point I needed relative scale anyways, but this was easily fixed. Whenever one of my objects resizes, it calls a scaleTo(width, height) event. Now I can easily override this event and adjust the scale (and position) appropriately, i.e.

@Override
public void scaleTo(float w, float h)
{
    super.scaleTo(w, h);

    background.scaleTo(w - 210, h - 230);
    background.moveTo(85, -95);

    lblTitle.setFontSize(48);
    lblTitle.moveTo(220 - w / 2, h / 2 - 160);
    lblTitle.scaleTo(400, 50);
}

To keep my code consistent, I've made it a habit to always put my move and scale code inside this event.

One thing that might be worth noting: for every one of your objects, you're creating both a full transformation matrix and a scaleless matrix in the event of children. For performance sake, it may be a good idea to cache the result of these calculations, and not recalculate them every time you need them. Simply add an 'isChanged' flag to your transformations, which is set to true whenever the object is moved/scaled/rotated, and makes sure the matrix recalculation is delayed up until the moment you actually need it.

So yeah, this is how I'm doing it at the moment. I'd love to hear thoughts of anyone else who has worked on a similar problem. Also, Elephly, since this was posted several months ago, if by any chance you've further improved on the system since then, I'd love to hear that too.

\$\endgroup\$
  • \$\begingroup\$ Note, if you want maximum flexibility, you could in fact expand your idea and have an absolute/relative toggle switch for position, scale, and rotation. Here's someone that has done exactly that. \$\endgroup\$ – Zeno Apr 23 '16 at 15:08

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.