Keeping two triangles with different scale attached during rotation

I'm making a simple Lunar Lander clone. The ship is a triangle, and I'm trying to draw the exhaust fumes of the ship.

I'm wondering how to draw the exhaust so it's always relative to the ship. I'm currently trying to offset it's x and y position on the screen by using the following code:

if (m_state == State::Thrust) {
// Calculate position for small triangle
float flameSize = SHIP_SIZE / 2;
glm::vec2 flamePosition = shipPosition;

float dx = 0;
float dy = SHIP_SIZE;

flamePosition.x = cos * dx - sin * dy + m_position.x;
flamePosition.y = cos * dy + sin * dx + m_position.y;

m_pRenderer->Render(flamePosition.x, flamePosition.y, flameSize, flameSize, 180.0f + m_rotation, glm::vec3(1.0f, 0.0f, 0.0f));
}


However I'm not getting the desired result:

The small exhaust triangle is always off by some offset. It's worth nothing that when I don't scale the exhaust triangle, it rotates correctly, so I think that could have something to do with the issue but I can't see how that would effect the positions I'm calculating. For reference, here is the code I use to render:

m_pShader->use();
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(x, y, 0.0f));

model = glm::translate(model, glm::vec3(0.5f * height, 0.5f * width, 0.0f));
model = glm::rotate(model, glm::radians(rotation), glm::vec3(0.0f, 0.0f, 1.0f));
model = glm::translate(model, glm::vec3(-0.5f * height, -0.5f * width, 0.0f));

model = glm::scale(model, glm::vec3(width, height, 1.0f));

glBindVertexArray(m_vao);

glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);


I'm wondering what is the correct way to keep smaller triangle attached to the bottom of the bigger one?

Thanks.

If I understand you correctly you have a problem with drawing models relatively to each other (you write about fumes, but it's exhaust pipe that is problematic, isn't it?).

It's usually done by creating a hierarchy of objects via their transforms.

What you conceptually want to do is:

1. render a ship relatively to the world and
2. render the exhaust relatively to the ship.

That's commonly done by 'parenting' children objects to parent objects. To do that you have to create a transformation matrix for the exhaust relative to the ship (this will be constant in your case). Then you create a transformation matrix of a ship relatively to the world (dynamic, based on it's position and rotation). Now when drawing objects you multiply transformation matrices in a proper order. Remember that translation, rotation and scale must be applied in that specific order (left to right), and so do have to be the chained parents

Assuming your scales of objects are 1.0 you could draw things like that ( where VP is camera's view-projection matrix and TRS is Translation, Rotation and Scale respectively):

ship: $$VP \;\cdot\; T_{ship}R_{ship}S_{ship} \;\cdot\; $$ exhaust: $$VP \;\cdot\; T_{ship}R_{ship}S_{ship} \;\cdot\; T_{exhaust}R_{exhaust}S_{exhaust} \;\cdot\; $$

Remember, that when parent is scaled, children objects can behave weirdly. The weirdest things are rotations, because they in reality get stretched into ellipses. You could skip scales in the matrix multiplication chain for children objects. You will have to properly adjust the translation taking parent scale into account though. The exhaust could then become

exhaust: $$VP \;\cdot\; T_{ship}R_{ship} \;\cdot\; T'_{exhaust}R_{exhaust}S_{exhaust} \;\cdot\; $$

where $$T'_{exhaust}$$ would take height of parent/2 * parent's scale into account.

Here you have a (unnecessarily long) tutorial about transformations: https://learnopengl.com/Getting-started/Transformations

If you later want to draw fumes later you can think about them as 'cloud objects' spawned at the position of the exhaust, but not parented to the ship.

• Thank you so much for the detailed answer. After working through it, I managed to make the exhaust position relatives to the ship using the ships model matrix which put it in the correct position. I think in future for this case I should really just define the vertices for the two shapes together, that way I don't have to calculate anything, but this was a good learning experience!
– Jhax
Sep 14 '21 at 20:07