OpenGL building hierarchy transform system

Viewed 103

I'm trying to build transform system, imitating Unity's. See my code:

void BaseObject::RecalculateTransform()
{
    // For top object
    if (parent.expired())
    {
        worldTransform = localTransform = glm::mat4(1);
        return;
    }

    // bunch of shit
    localTransform = glm::translate(glm::mat4(1), localPosition) * glm::scale(glm::mat4(1), localScale) * glm::mat4(localRotation);

    worldTransform = parent.lock()->GetWorldTransform() * localTransform;
}

This is how I'm calculating the local transform and world transform. I've tried pretty a lot of combinations of the order of transformation matrices, but none of them worked correctly. I tried to reference Unity3d, but couldn't get something useful.

localPosition and localScale is glm::vec3 type, and localRotation is glm::quat type.

As it maybe helps, I picked a screenshot from above code:

auto top = app->GetHierarchy()->GetTopObject();

auto childDepth1 = top->Instantiate().lock();
childDepth1->SetLocalPosition(glm::vec3(0, 1, 0));

// spiral one
auto mesh1 = childDepth1->AddComponent<RandomMesh>().lock();
mesh1->SetVerticeData("l 30");

auto childDepth2 = childDepth1->Instantiate().lock();
childDepth2->SetLocalPosition(glm::vec3(0, 1, 0));

// spherical one
auto mesh2 = childDepth2->AddComponent<RandomMesh>().lock();
mesh2->SetVerticeData("s 100 t");

Screenshot:

enter image description here

As you can see, the spiral one has the sphere as it's child. spiral one's localPosition is (0, 1, 0), an so is the worldPosition. But it's rendered on -y location. (I have confirmed that +y location is above the middle line in other way.)

Moreover, the sphere's localPosition is also (0, 1, 0), which makes it's worldPosition (0, 2, 0). Keeping direction that +y location is beneath the middle line, it must be rendered beneath the spiral one, but it's rendered over the sphere. With debugging with printing, it says that the sphere's worldPosition is (0, 0, 0), not (0, 2, 0).

After some experiments, I figured out that the transform matrix is applied plus and minus, depending the odd-evenness of the child depth.

What I want is just Unity-like transform system. How can I fix this?

1 Answers

It was due to my stupid mistake. When I was initializing localRotation quaternion value, I was doing

localRotation = glm::quat(0, 0, 0, 1);

I don't know exactly why, but with debugger it negated the position value every time when the rotation matrix from this has multiplied.

The correct initialization is

localRotation = glm::identity<glm::quat>();

which will not apply rotation at all, as the name suggests.

Here is my solution:

void BaseObject::RecalculateTransform()
{
    isTransformChanged = false;

    // For top object
    if (parent.expired())
    {
        worldTransform = localTransform = glm::mat4(1);
        return;
    }

    const auto& scaled = glm::scale(glm::mat4(1), localScale);
    const auto& rotated = glm::mat4(localRotation) * scaled;
    const auto& translated = glm::translate(glm::mat4(1), localPosition) * rotated;

    localTransform = translated;

    worldTransform = parent.lock()->GetWorldTransform() * localTransform;
}

All is fine. It's already midnight in Korea. I can finally go bed. For someone who faces the same problem with me, please do not be stupid :(

Related