Rocket Exhaust

Blitz3D Forums/Blitz3D Programming/Rocket Exhaust

Axel Wheeler(Posted 2013) [#1]
Hi, there.

I've seen people dance around this topic, but they always seem to go in a different direction.

What I want is to show a single quad showing a rocket's exhaust. It has to stay put, and stay aligned to the rocket, except for the roll angle, which has to face the camera. That's the hard part.

I have the texture on the quad, parented to the ship.

I tried various permutations of:

PointEntity(exhaust,cam) 
RotateEntity(exhaust,0,0,EntityRoll(exhaust))


in the hope that it would keep the roll angle facing the camera, but it didn't.

Also:

EntityParent(cam,exhaust)
roll#=(various trig stuff based on the cam's x and y position relative to the exhaust)
EntityParent(cam,0)
RotateEntity(exhaust,0,0,-roll#)


Not only does this not work, it causes the cam to go out to lunch (the sides of the display actually close in in a warp drive kind of way) ... even though nothing happens while it is parented to the exhaust except gathering data in the roll# variable; even if I comment that out it happens:

EntityParent(cam,exhaust)
EntityParent(cam,0)


still screws up the cam?

Anyway what I'm really looking for is a PointEntityRollOnly() that only rotates the entity on that axis to look at the destination. Does this make sense?

Any help would be much appreciated


Floyd(Posted 2013) [#2]
This will be pure fantasy because I have written no code, but I think the right idea is in here somewhere.

The quad Z-axis is the direction of motion. Some other axis, let's say the quad Y-axis, is perpendicular to the "front" of the quad, i.e. the textured side. We want to roll the quad around the Z-axis to make the Y-axis point as close to the camera as possible.

We want to figure out how much to turn so the camera will be in the Y-Z plane of the quad. I think it goes like this:
TFormPoint 0,0,0, camera, quad
angle# = ATan2( TFormedX(), TFormedY() )
Notice x,y are reversed from normal ATan2 usage because I want the angle relative to the Y-axis rather than the usual X-axis.

That should give the angle to use for roll. I'm not sure if you want -angle or +angle. I have forgotten the details about which turns are positive or negative for the various axes.


Axel Wheeler(Posted 2013) [#3]
Thanks for the prompt reply.

I just tried that and so far no luck, although it produces a random rotation that looks kinda cool - almost realistic. I'll keep playing with it and let you know if some variation of this works.

Thanks.


Kryzon(Posted 2013) [#4]
EDIT: nevermind.

Last edited 2013


Axel Wheeler(Posted 2013) [#5]
Ok, so with the exhaust parented to the rocket.

TFormPoint (0,0,0, cam,myDemoShip\ship\exhaustSprite)
angle# = ATan2( TFormedY(), TFormedX() )
RotateEntity(myDemoShip\ship\exhaustSprite,EntityPitch(myDemoShip\ship\exhaustSprite),EntityYaw(myDemoShip\ship\exhaustSprite),angle)


This produces a flame which points in a random direction

If I make the RotateEntity refer to global space...

RotateEntity(myDemoShip\ship\exhaustSprite,EntityPitch(myDemoShip\ship\exhaustSprite,1),EntityYaw(myDemoShip\ship\exhaustSprite,1),angle,1)


... the same thing happens.


If I unparent the exhaust first...

EntityParent(myDemoShip\ship\exhaustSprite,0)
TFormPoint (0,0,0, cam,myDemoShip\ship\exhaustSprite)
angle# = ATan2( TFormedY(), TFormedX() )
RotateEntity(myDemoShip\ship\exhaustSprite,EntityPitch(myDemoShip\ship\exhaustSprite,1),EntityYaw(myDemoShip\ship\exhaustSprite,1),angle,1)
EntityParent(myDemoShip\ship\exhaustSprite,myDemoShip\ship\e)


... the same thing happens, regardless of whether I use global or relative versions of RotateEntity.

I also tried switching cam and the exhaust in the TFormPoint command, but no dice.

Any ideas?


Kryzon(Posted 2013) [#6]
Hi. Just tested Floyd's snippet, it's working:



So the problem is most likely adapting it to your scene (getting the right axes, value signs etc.).


Axel Wheeler(Posted 2013) [#7]
Edit: I just read the comment in the code. So ignore the next paragraph

Hmm. Maybe my hardware is behaving differently. When your yellow ship is point toward me but beneath me (i.e. toward my feet) the exhaust square goes edge on. It should be pretty much flat horizontal at that point.

Nice demo, btw!

I was looking at possibly using a sprite with SpriteViewMode 4 (the billboard that supposedly faces the camera, for trees and such.) I'll try it and post results if I can get it to work.

Last edited 2013


Axel Wheeler(Posted 2013) [#8]
SpriteViewMode 4 seems to produce a sprite that frequently faces away from the camera. Does it have to be vertical in global space in order to work? I was hoping it could be parented to a pivot and attached to the ship, so it would "think" it's vertical, regardless of which way the ship is going. Wishful thinking, I guess.

I think Floyd has the right idea, I just have to tweak it a tad.


Axel Wheeler(Posted 2013) [#9]
Ok, Floyd's method does work. Just by changing sphere to camera in the TFormPoint command, it suddenly works a treat.

So, then I do have a bug in my code somewhere. I'll go find it.

Thanks, Floyd, Kryzon, and other folks!


Axel Wheeler(Posted 2013) [#10]
Got it!

I was running TFormPoint(Cam,Flame) rather than (Cam,Ship), so of course it was doing a little feedback dance no matter what else I did.

Thank you guys so much!!!


Kryzon(Posted 2013) [#11]
Hi, good job on getting it working.

I should've added more info: when you TForm to the rocket's "point of view" you are already finding a roll value that's relative to it.
So any transformation commands (TurnEntity, RotateEntity etc.) you use in the quad with values derived from that TForm don't need the "Global" flag, since the quad is parented to the rocket and by default will transform relatively to it, as is needed in this case.

But seriously, what Floyd thought there was brilliant... I hadn't realized you could exchange X and Y at ATan2 to change the axis (thinking about it now, makes total sense - the division is finding the tangent of the other angle).
And he used 0,0,0, with source entity at TForm, which I now realize is much faster than writing the entity's global coordinates like one would intuitively do:
TFormPoint 0,0,0,Source,Destination

TFormPoint EntityX(Source,True),EntityY(Source,True),EntityZ(Source,True),0,Destination
Both lines produce the exact same results, but guess which is quicker to write.

Last edited 2013


Axel Wheeler(Posted 2013) [#12]
Cool.

This led to the next problem, which was the fact that the quad had a hard line at the base (the rocket end). I had planned to deal with this with a quad with a circle texture of the same color/alpha fade as the main flame, but this produces wacky results because the half of the circle behind the main flame kept shimmering through.

Then I thought: Cone! If I just used a cone with an alpha gradient texture, it would seem to wrap around close to the pointy end, which would look fake. So, I tried the same flame-shaped texture I used on the quad and it looks great - as long as that side always faces the viewer. So I'm using Floyd's method with the cone and it looks pretty good.

Here are some screenies:

http://tinypic.com/view.php?pic=wmlf9j&s=6
http://tinypic.com/view.php?pic=5wasqq&s=6
http://tinypic.com/view.php?pic=23iuu6d&s=6
http://tinypic.com/view.php?pic=2s9vudh&s=6
http://tinypic.com/view.php?pic=33y18r7&s=6

Keep in mind there are no actual engines yet; just the exhaust!


Kryzon(Posted 2013) [#13]
Hello. Pardon the resurrection.
There's an alternative way to calculate the orientation that the rocket trail sprite should have in order to face the camera in the most frontal way.



This alignment technique is used in games like Homeworld 2 (above) for the rocket thrusts of the ships and the energy projectiles.

It can be reproduced with the following:

- The trail sprite needs to be double-sided.
- You need to know the 'src' and 'dst' points, which are respectively the start and end locations of the trail sprite in world space.
- Consider the vector 'A' as the vector from point 'src' to 'dst.'
- Consider the vector 'B' as the vector from point 'src' to the location of the camera.
- Consider the vector 'C' as the cross-product of vectors 'A' and 'B.'
- Align the Y axis of the trail sprite with vector 'C' using AlignToVector().

Pseudo-code:
;Double-sided rendering for the trail.

EntityFX( trail.mesh, 16 )


;A: From the start point of the trail to its end point.

A = trail.dst - trail.src


;B: From the start point of the trail to the position of the camera.

B = camera.position - trail.src


;C: Cross-product of A and B.

C = CrossProduct( A, B )


;Align the Y axis of the trail sprite with C.

AlignToVector( trail.mesh, C.x, C.y, C.z, 2 )
This might be faster than using the "TFormPoint + Atan2 + Rotation" as discussed previously.


Depending on the language or engine that you're using, it might be faster to replace the last step ("use AlignToVector") with the following:

- Normalize the vector 'C' and multiply it by half the width that the trail should have.
- Caculate the position of the four vertices of the trail based on the start and end points of the trail and the previous step.
;Normalize the vector C and multiply it by half the desired width of the trail.

offset = Normalize( C ) * ( trail.width / 2 )


;Calculate the vertex positions of the trail based on its start and end points and the offset.

V1 = trail.src + offset
V2 = trail.dst + offset
V3 = trail.dst - offset
V4 = trail.src - offset



RemiD(Posted 2013) [#14]
Interesting, thanks for the example Kryzon ! :)