3D Spaceship with turret

Blitz3D Forums/Blitz3D Programming/3D Spaceship with turret

Volturna(Posted 2015) [#1]
Hi everyone,

I've search forum files for something like this ang got something but i'm failing to understand it.

i've a Spaceship in a 3d world with full movement and rotation. As a child of that ship i have a TurretBase that can only rotate his Y axis(yaw). As a child of that TurretBase i have a TurretGun that can only rotate in his X axis (pitch).

How can i point that gun to a target entity using DeltaYaw and DeltaPitch values?

I getting stuck with that relationship between global and local angles once my Spaceship rotates in the 3D space.

Need some help. Thanks in advance.


Floyd(Posted 2015) [#2]
I'm not visualizing any difficulties here. Wouldn't this work?

1. Use DeltaYaw with TurretBase and Target.
2. Turn TurretBase on Y-axis.
3. Use DeltaPitch with TurretGun and Target.
4. Turn TurretGun on X-axis.



Volturna(Posted 2015) [#3]
I have that but it only works when the Spaceship is aligned with axis X and Y, by other words, when Spaceship has no rotation.


Kryzon(Posted 2015) [#4]
Does it need to be DeltaYaw and Pitch?
If you use AlignToVector, which takes in vectors and handles the rotation, it might be slightly faster as it may use more optimised code.

http://www.blitzbasic.com/b3ddocs/command.php?name=AlignToVector&ref=3d_cat

vectorX = targetX - turretGunX ;Destination minus source.
vectorY = targetY - turretGunY
vectorZ = targetZ - turretGunZ

AlignToVector( turretGun, vectorX, vectorY, vectorZ, 3 ) ;With the 'Z' axis used, or whatever axis your turretGun is pointing in, in the model file \ when it's created.


Volturna(Posted 2015) [#5]
My first attempt was with aligntovector and it does the job but... Always a 'but' :D

aligntovector has a rate field set to 1 as default and it represents an instant align to target that I don't want. So if I set a value to that rate between 0 and 1 I will get a smooth align but... Another 'but'... That align is not constant and I need a fixed value (previously set by the turret data)...

...so I created a pivot (1 for base and other for gun with same relationship parent-child) that aligns instantly to target and then I rotate TurretBase and TurretGun at constant angles.

Recently I found that delta commands and thought it was a better way to achieve same result but got that problem with the align... I have a similar system for missiles so it is a good option to save all those pivots


Kryzon(Posted 2015) [#6]
I see, you want a fixed rate turning speed. I don't think you can do that with vectors, you really need DeltaYaw and DeltaPitch then.

What happens when you use the Delta commands and when turning use the 'global' parameter set to 'True'? I think that should work.

deltaX = DeltaPitch( ... )
deltaY = DeltaYaw( ... )

TurnEntity( turretGun, deltaX, deltaY, 0, True ) ;True to treat the 'delta' values as a global rotation rather than local.


angros47(Posted 2015) [#7]
The rate of aligntovector is a percentage of the gap: let's say that, to align the turret, you need to turn it by 10 degrees: a rate of .5 will turn it by 50% of it, so 5 degrees; at the next step, it will turn by half of remaining gap, so 2.5... then 1.25, then 0.625, and it will never be aligned (do you know Zeno's paradox , right?)

The fix is simple: if you want to take 10 steps to align, for the first step use a rate of 1/10 (1/10 of 10 degrees is 1 degree, still 9 to go); for the second step, use a rate of 1/9 (1/9 of 9 is again 1 degree... 8 to go), then 1/8, 1/7... until you have only one degree to align, and you use 1/1, so you complete the alignment. In other words, use as a rate the value 1/steps remaining.


Volturna(Posted 2015) [#8]
Kryzon:
TurretBase can only rotate his local Yaxis andTurretGun can only rotate his local Xaxis so i'm using DeltaYaw for Base and DeltaPitch for Gun, can't use both 'pitch' and 'yaw' at the same entity :(

Using global TurnEntity at TurretBase will make it rotate around all his local axis. same for TurretGun.

angros47:
I see the point and it seems to work although having a Delta command working with local angles would be so nice.


Volturna(Posted 2015) [#9]
well... this does the job:



since there is no Roll changes at all we can remove some lines:



To avoid instant point at target, just need to set a max and min value for DX# and DY#


Volturna(Posted 2015) [#10]
This is the final state (i hope)...


Function PointTurretTo#(BaseEnt,GunEnt,TargetEnt,rate#=1,maxPitch#=90,minPitch#=-90)
	
	;BaseEnt	- entity used as base for turret
	;GunEnt		- entity used as gun for turret
	;rate		- max ammount of degrees to increment
	;maxPitch	- max Pitch gun can handle
	;minPitch	- min Pitch gun can handle
	
	Local aX#,aY#		;used to store entity pitch and yaw values
	Local DX#,DY#		;used as delta pitch and yaw variables, also used as pitch and yaw rotation
	Local vX#,vY#	;used to store delta pitch and yaw variables
	
	;Rotate Turret Base------------------------------------------------------------------
	
	
	aX=EntityPitch#(BaseEnt)			;store actual Base entity pitch
	aY=EntityYaw#(BaseEnt)				;store actual Base entity yaw
	
	PointEntity BaseEnt,TargetEnt		;points base entity to target entity	(final angle)	
	
	
	DY=EntityYaw#(BaseEnt)-aY			;store angle between initial angle and final angle (it is a deltaYaw)
	
	If DY <= -180 Then DY = DY + 360	;fixes angles limits
	If DY >  +180 Then DY = DY - 360
	
	vX=DY								;stores deltaYaw for future use [optional]
	
	If DY>rate Then DY=rate				;sets a limit for turret rotation rate
	If DY<-rate Then DY=-rate
	
	DY=DY+aY							;transforms deltaYaw in a final angle
	
	RotateEntity BaseEnt,aX,DY,0		;sets Base entity to initial state with Yaw variation
	
	
	;Rotate Turret Gun------------------------------------------------------------------
	aX=EntityPitch#(GunEnt)				;store actual Gun entity pitch
	aY=EntityYaw#(GunEnt)				;store actual Gun entity yaw
	
	PointEntity GunEnt,TargetEnt		;points Gun entity to target entity	(final angle)	
	
	
	DX=EntityPitch#(GunEnt)-aX			;store angle between initial angle and final angle (it is a deltaPitch)
	
	vX=DX								;stores deltaPitch for future use [optional]
	
	If Abs(DX)>=Abs(vX)/2.				;only aligns gun at the near end of base align [optional]
		
		If DX>rate*.5 Then DX=rate*.5	;sets a limit for turret rotation rate note:i set the Gun align to be 50% of the Base align rate [optional]
		If DX<-rate*.5 Then DX=-rate*.5
		
		DX=DX+aX						;transforms deltaPitch in a final angle
		
		If DX>maxPitch Then DX=maxPitch		;sets a limit for Gun max angle
		If DX<minPitch Then DX=minPitch		;sets a limit for Gun min angle
		
		RotateEntity GunEnt,DX,aY,0		;sets Gun entity to initial state with Yaw variation
		
	Else
		
		RotateEntity GunEnt,aX,aY,0		;sets Gun entity to initial state without Yaw variation
		
	EndIf
	
	
	
	Return vX+vY						;returns total amount left to complete the alignment (from 360 to 0) [optional]
	
End Function



...feel free to use or update


Kryzon(Posted 2015) [#11]
That looks somewhat convoluted.
From previous experience (thanks to user Bobysait) we noticed that PointEntity is about 4x slower than AlignToVector, and both do similar tasks. So I'd at least replace it with AlignToVector.

But still, I think it might be faster to do this with TFormPoint. Essentially, what you're trying to do is turn the base so its YZ plane contains the target, and turn the gun so its XZ plane contains the target as well.

The TFormPoint will treat each object as the origin, with the position of the target transformed to this coordinate space.
Then you can find the angles using ATan2.

Local targetX# = EntityX( target, True )
Local targetY# = EntityY( target, True )
Local targetZ# = EntityZ( target, True )

Local turretBaseX# = EntityX( turretBase, True )
Local turretBaseY# = EntityY( turretBase, True )
Local turretBaseZ# = EntityZ( turretBase, True )

;Get the vector from the turret base to the target, in the turret base space.

TFormPoint( targetX - turretBaseX, targetY - turretBaseY, targetZ - turretBaseZ, 0, turretBase ) 

;Use the Y and Z components to find out the delta pitch angle.

Local dtPitch# = -Atan2( TFormedY(), TFormedZ() ) 

;Use the X and Z components to find out the delta yaw angle.

Local dtYaw# = -ATan2( TFormedX(), TFormedZ() )

;Then limit the delta angles in whatever way you want.

;Local finalPitch# = ...
;Local finalYaw# = ...

;Finally, apply the angles to the turret base and the gun.

TurnEntity( turretBase,	0, finalYaw, 0 ) ;Global flag turned off.
RotateEntity( turretGun, finalPitch, 0, 0 ) ;Rotate instead of Turn.

;I haven't tested if all of the above needs to be calculated for the turret gun as well.
;I don't think it does.
EDIT: Changed from TFormNormal to TFormPoint, it wasn't necessary.


Volturna(Posted 2015) [#12]
As far i can say it works just fine!! Changed TFormPoint to TFormNormal again. With TFormPoint after ship goes away from position x=0 y=0 z=0 the alignment wont work.

I really appreciate your help!


Bobysait(Posted 2015) [#13]
@Kryzon:
EDIT: Changed from TFormNormal to TFormPoint, it wasn't necessary.

Maybe you're looking for TFormVector ? (it should fit better the needs : as you don't need the start position, and it's both faster than TFormPoint AND TformNormal (as far as it's a not normalized TFormNormal))

BTW, it actually works pretty well using vectorpitch/vectoryaw
(ZQSD/WASD to move the camera)




Kryzon(Posted 2015) [#14]
On an unrelated note, TFormVector and TFormPoint seem confusing to me, since a 3D point is a vector.

From a quick test, TFormVector seems to ignore the translation of the objects involved. TFormPoint takes the translation into account.

For example, move a pivot by ( +5, 0, 0 ).
If you do TFormPoint( +1, 0, 0, pivot, 0 ), the result is ( +6, 0, 0 ).
If you do TFormVector( +1, 0, 0, pivot, 0 ), the result is ( +1, 0, 0 ).

Both functions take the rotation and scale of the source and destination objects into consideration, but only TFormPoint also uses their position.


Bobysait(Posted 2015) [#15]
Actually, the 3 functions do what they say :

TFormPoint transforms a point (coordinates) seen from the source entity (or world coordinates) then transforms the result relative to the destination entity.

TFormVector transform a vector (a direction + length)
So the result is like a rotation+scale

TFormNormal is just the same as TFormVector but ends with a normalization of the result vector so its length is 1.0

For commun use, TFormPoint is required to get "real vertex coordinates"
for exemple > TFormPoint(VertexX(surf, v), VertexY(surf, v), VertexZ(surf, v), mesh, camera)
will give the vertex coordinates in camera coordinate system (for exemple again, checking TFormedZ()>0 tell you if the vertex is front or back from the camera)

TFormVector/TFormNormal can be used to retreive a rotation matrix
TFormVector(1,0,0, entity, dest) -> vector Xx,Xy,Xz
TFormVector(0,1,0, entity, dest) -> vector Yx,Yy,Yz
TFormVector(0,0,1, entity, dest) -> vector Zx,Zy,Zz

for a particle system, its more usefull than TFormPoint

get vector transformation from the camera for each corner using a transform from camera to mesh like
TFormVector (-1,+1,0, camera, Mesh): VULx# = tformedX(): VULy# = tformedY(): VULy# = tformedZ()
TFormVector (+1,+1,0, camera, Mesh): VURx# = tformedX(): VURy# = tformedY(): VURy# = tformedZ()
TFormVector (+1,-1,0, camera, Mesh): VBRx# = tformedX(): VBRy# = tformedY(): VBRy# = tformedZ()
TFormVector (-1,-1,0, camera, Mesh): VBLx# = tformedX(): VBLy# = tformedY(): VBLy# = tformedZ()

you can now compute easily each particle vertices coordinates.
For each particle
addvertex ( Surf, particle\X + particle\sx * VULx, particle\Y + particle\sy * VULy, particle\z + VULz ,0.0,0.0 );
addvertex ( Surf, particle\X + particle\sx * VURx, particle\Y + particle\sy * VURy, particle\z + VURz ,1.0,0.0 );
addvertex ( Surf, particle\X + particle\sx * VBRx, particle\Y + particle\sy * VBRy, particle\z + VBRz ,1.0,1.0 );
addvertex ( Surf, particle\X + particle\sx * VBLx, particle\Y + particle\sy * VBLy, particle\z + VBLz ,0.0,1.0 );

It's fast and more efficient than most particle engines that transform points for each particles ;)

(and this is the quick and dirty demo you didn't ask)



Rick Nasher(Posted 2015) [#16]
Wauw Bobysait, this is a really nice demo and informative too. Can't hit a thing for the target object is moving way to fast, but a very, very pretty demo. Thanks for sharing man.


Bobysait(Posted 2015) [#17]
Yep, it's not designed to allow the turret to shoot the target accurately, it's just a "point at target", so it will actually probably never reached it as long as the turret shoots at the current position of the target which is already too late since the bullet can't teleport itself :)

But, I could implement it using an old code which aims the target relative to its orientation and speed ...

ps : I didn't sort the particles, so, as it uses alpha (vertex alpha and texture alpha) the Z-sorting should be mandatory.
I'll post an update in the afternoon at least whith z-sorting (using a mix of QSort and bubble sort).


Rick Nasher(Posted 2015) [#18]
Cool!


Floyd(Posted 2015) [#19]
On an unrelated note, TFormVector and TFormPoint seem confusing to me, since a 3D point is a vector.

The idea is that a point represents a position in space. A vector represents direction and length. A normal is a vector which is perpendicular to something, another vector or vectors or perhaps a surface.

In traditional mathematics you will see a point depicted as a dot, a vector as an arrow. The direction and length of the arrow are the direction and magnitude of the vector. It often has some physical interpretation such as velocity, which has direction and speed. There are many other uses, such as orientation and strength of a gravitational or electromagnetic field.

It gets somewhat confusing in programming languages. In 3D space a point or a vector are both represented by an ordered triple of numbers. You can't tell if (1,4,2) is a point or a vector unless I tell you how to interpret it. If it is a vector then it means "one step right, four steps up and two forward". Starting from point (x,y,z) and applying this vector will give a new point (x+1,y+4,z+2).

A point is sometimes interpreted as a position vector. In this case we assume the vector (1,4,2) begins at initial point (0,0,0) and ends at point (1,4,2).

Incidentally, TFormNormal is often misunderstood. The idea is that TFormVector and TFormNormal, when applied to two perpendicular vectors, will preserve perpendicularity. This is trivial if there is no scaling involved, just rigid motions i.e. rotation and movements.

But scaling changes angles. Imagine a triangle in 2D space. Put a vertex at (-1,0) and another at (1,0). Put the third vertex at (0,1). This has 45 degree angles at the left and right vertices and a right angle at the top.

Now scale this by multiplying the y-coordinate of all three vertices. The left left and right vertices don't change because y is zero. But the top can become any (0,y). For very large y the triangle is tall and thin. The base angles are almost 90 degrees and the top is close the zero.

I posted about the TFormNormal issue eight years ago, and gave some code which was old even then. You can tell it's old because my example uses 640x480 full screen graphics. Feel free to change that.

Here is the post: http://www.blitzbasic.com/Community/post.php?topic=72322&post=808271


Bobysait(Posted 2015) [#20]
here it is, Z-sorted particles
I put some comments to make the code more understandable

10 turrets aiming at a single "bird" which they still never will touch (for some reasons previously explained)

on top, you'll find a TFormPoint function, it is not used at all in the demo, it's just an extra to show the maths behind the scene of this function.
Just notice : TFormVector is almost the same function, it just don't use the position of both entities involved.

And TFormNormal removes the scaling (as mentionned by Floyd)




Rick Nasher(Posted 2015) [#21]
Amazing stuff, love the clouds, and that all in code! Great stuff.


Matty(Posted 2015) [#22]
If you want to hit a target which is moving and which takes velocity but not acceleration into account I did a code archives entry earlier in the year......

http://www.blitzbasic.com/codearcs/codearcs.php?code=3184

Explanation and example (javascript but blitz is much the same)
http://mattiesgames.com/webfolder/index.html


Bobysait(Posted 2015) [#23]
@Matty: thanks but I already have a function for that (did it myself long time ago)


A bit messy, but now, the turret shoot (and kill) the birds.
nothing to do but watch.

(ZQSD to move, Right Mouse Down to rotate the camera)
F1 - show debug lines (current aiming)




RemiD(Posted 2015) [#24]
furious bobysait ^^ (tu comptes créer le nouveau "gamemaker" avec des lances missiles et des missiles autoguidés en une soirée ? :P)


Bobysait(Posted 2015) [#25]
Ha ha, non, c'est tout pour cette démo :)
Je retourne à mes travaux, c'était juste ma "pause dèj'" on va dire...
De temps en temps consacrer une petite soirée pour pondre un truc totalement inutile, ça fait du bien ^_^


Kryzon(Posted 2015) [#26]
Thank you for the explanation Floyd.


Rick Nasher(Posted 2015) [#27]
Well, Bobysait, I'm thankful that you did, for it's really a site for sore eyes. It's like watching an Alien Blitz taking place. All that's left to add is an air raid alarm, sound effects and it's a complete demo scene.
I changed the colors a bit for bird/missiles so it's easier to see what's what, like this:
Function createbird.TBird(v# = -1)
	Local b.TBird = New TBird;
	b\ent				=	CreateSphere		( 4										 );
							ScaleMesh			( b\ent			, .2,.3,1				 );
							PositionEntity		( b\ent			, Rnd(-100,100),0,Rnd(-100,100) );
							EntityColor 		b\ent,25,25,55 : EntityFX b\ent,1 ;<<<<

Function launchMissile(turret.TTurret)
	Local m.Missile = New Missile
	m\m = CreateCylinder(6,1);
	EntityColor m\m,255,255,55 : EntityFX m\m,1 ;<<<<



I'm jealous you can pull this out of your sleeve just like that.
BTW: Dunno why, but the cannons are somehow vaguely making me think of Dr. Who's Daleks ("Exterminate-Exterminate!") lol