Question about free 3d rotation

Started by Pappons, September 15, 2021, 20:17:55

Previous topic - Next topic

Pappons

Hi guys,

I'm having a spot of trouble with free rotation in 3D. I am trying to recreate controls similar to those of that old Freelancer game where you direct your ship with the mouse, and it sort of works, but not fully.

My code:

'
'
'
Strict

Framework Openb3dmax.B3dglgraphics

'- init -----------------------------------------------------------------------------------
Graphics3D DesktopWidth(),DesktopHeight(), 32, 60

ClearTextureFilters

Local cam:TCamera=CreateCamera()
PositionEntity cam, 0, 3, 0
CameraRange cam, .1, 750  ' set the camera range To something reasonable

'- light ---------------------------------------------------------------------------------
Local light:TLight=CreateLight(2,cam)
LightColor light,100,100,100
PositionEntity light,0,0,0

AmbientLight 255,255,255

'- sky box ---------------------------------------------------------------------------------
Local sky:TMesh=CreateSphere(24)
ScaleEntity sky,500,500,500
FlipMesh sky
EntityFX sky,1
Local sky_tex:TTexture=LoadTexture("data\tilingspacebackground.jpg")
ScaleTexture sky_tex, 0.5, 0.5
EntityTexture sky,sky_tex

'- player ship ---------------------------------------------------------------------------------
Local ship:TEntity = LoadMesh("data\InterstellarRunner.b3d")
ScaleEntity ship, 0.15, 0.15, 0.15
PositionEntity ship, 5, 0.1, 4

Local shiptex:TTexture=LoadTexture("data\InterstellarRunner.png")
EntityTexture ship, shiptex
EntityShininess(ship,0.1)

'- player ship ---------------------------------------------------------------------------------
Local cx:Float = DesktopWidth() / 2
Local cy:Float = DesktopHeight() / 2

While Not KeyDown(KEY_ESCAPE)

If KeyHit(KEY_ENTER) Then DebugStop

MoveEntity ship,0,0,0.10 ' move straight forward

Local dx:Float = ( MouseX() - cx ) / (cx ) ' get the mouse position
Local dy:Float = ( MouseY() - cy ) / (cy ) ' relative to the center of the screen

If MouseDown(1)
TurnEntity ship,(dy), -(dx),0 ' turn the ship in direction of the mouse
EndIf

PointEntity( cam, ship ) ' point camera at ship

If EntityDistance( cam, ship ) > 10
MoveEntity cam, 0, 0, 0.1 ' move towards ship if at a certain distance
EndIf

UpdateWorld
RenderWorld

Flip
GCCollect

Wend


I have a feeling the solution has to do with quaternions, but my experience with it is limited and any tinkering I've done has been fruitless so far.

Would anyone be able to nudge me in the right direction here?

Thanks in advance!

markcwm

Hi,

I tried your code but I don't know what type of controls are in Freelancer even after watching a video.

I assume your main problem is that the ship ends up dis-orientated, upside down, etc. so I tried to implement a simple self-correcting system where it stabilizes itself automatically but I couldn't really manage it, it was trickier than I thought it would be. It shouldn't be a problem with quaternions they should work fine.

One thing that might help is to use RotateEntity instead of TurnEntity because then it's easier to know where you are and can start re-balancing the ship. Another thing that might help is to limit the value of dx and dy to a maximum so you don't end up over-turning the ship.

STEVIE G

I'm assuming minib3d works like b3d in terms of rotations. Turnentity should use quarternions. The problem is turning on 2 axis at the same time.  You could try turnentity separately on each axis or do this ...

Make your ship entity a pivot. Load and parent your ship model to this pivot. Turnentity the pivot only  on the yaw axis. Turnentity the ship model only on the local pitch axis. 

Pappons

#3
Thanks for the replies, guys. I really appreciate it!

Turning on each axis didn't seem to change much. I'm noticing a certain behavior where I can pretty much keep turning to either side but if I keep guiding the ship either up or down, it causes it to "flip" and turn in the opposite direction once it passes 180 degrees.

Here's a video that shows what I am aiming to do: https://www.youtube.com/watch?v=NODc1lXn0ak&t=27s - Watching the video again, makes it seem like it's barely rotating compared to the camera.

That leads me to wonder; could it be that the camera chasing the ship could be confusing the view and the ship indeed is moving as it should?

angros47

Quote from: STEVIE G on September 17, 2021, 22:08:38
I'm assuming minib3d works like b3d in terms of rotations. Turnentity should use quarternions.

OpenB3D uses quaternions (like b3d). Minib3d doesn't (unless you are using the version patched by Warner or the one patched by EdzUp)

markcwm

Stevie G has the right idea, you need to use pivots to control the rotations. The controls are very similar to airplane controls, so I ported the xfighter sample from Blitz3D and added your mouse controls. It's not that far away from the Freelancer controls but needs work, mainly it will jump when you click somewhere else, so you would need to store the last mouse values and then move from there, there's mouselook code that should help you fix it.
Code (blitzmax) Select
' plane controls from Blitz3D/samples/mak/xfighter

Strict

Framework Openb3dmax.B3dglgraphics

'- init -----------------------------------------------------------------------------------
Graphics3D DesktopWidth(),DesktopHeight(), 32, 60

ClearTextureFilters

Global use_fog%=False

Type Player
Field entity:TEntity,camera:TCamera
Field ctrl_mode%,cam_mode%,ignition%
Field pitch#,yaw#,pitch_speed#,yaw_speed#,roll#,thrust#
Field cx#,cy#,dx#,dy#
End Type

'- player ship ---------------------------------------------------------------------------------
Local plane:TPivot=LoadAirPlane("../openb3dmax.docs/media/rallycar1.3ds")

'Local plane:TPivot=LoadAirPlane("data\InterstellarRunner.b3d")
'Local planetex:TTexture=LoadTexture("data\InterstellarRunner.png")
'EntityTexture plane, planetex
'EntityShininess(plane,0.1)

Local player1:Player=CreatePlayer( plane,0,0,GraphicsWidth(),GraphicsHeight(),2 )

player1.cx = DesktopWidth() / 2
player1.cy = DesktopHeight() / 2

'- light ---------------------------------------------------------------------------------
Local light:TLight=CreateLight(2,player1.camera)
LightColor light,100,100,100
PositionEntity light,0,0,0

AmbientLight 255,255,255

'- sky box ---------------------------------------------------------------------------------
Local sky:TMesh=CreateSphere(24)
ScaleEntity sky,3000,3000,3000
FlipMesh sky
EntityFX sky,1
Local sky_tex:TTexture=LoadTexture("../openb3dmax.docs/media/sky.bmp")
'Local sky_tex:TTexture=LoadTexture("data\tilingspacebackground.jpg")
ScaleTexture sky_tex, 0.5, 0.5
EntityTexture sky,sky_tex

While Not KeyDown(KEY_ESCAPE)

UpdatePlayer(player1)
UpdateWorld
RenderWorld

Text 0,20,"dx="+player1.dx+" dy="+player1.dy

Flip
GCCollect

Wend

Function LoadAirPlane:TPivot( file$ )
UseMeshTransform 1 ' mesh transforms, default is False
Local pivot:TPivot=CreatePivot()
Local plane:TMesh
If file$="" Then plane=CreateCube() Else plane=LoadMesh( file$,pivot )
'ScaleEntity plane,.08,.08,.08
ScaleMesh plane,.05,.05,.05 'make it more spherical!
plane.RotateMesh(90,180,0)
'RotateEntity plane,0,180,0 'And align To z axis
EntityRadius pivot,1
EntityType pivot,1
HideEntity pivot
Return pivot
End Function

Function CreatePlayer:Player( plane:TPivot,vp_x%,vp_y%,vp_w%,vp_h%,ctrl_mode% )
Local p:Player=New Player
p.ctrl_mode=ctrl_mode
p.cam_mode=1
Local x#=ctrl_mode*10
Local z#=ctrl_mode*10-2500

p.entity=TPivot( CopyEntity( plane ) )
PositionEntity p.entity,x,50,z
RotateEntity p.entity,0,180,0
ResetEntity p.entity

p.camera=CreateCamera( p.entity )
PositionEntity p.camera,0,3,-10
CameraViewport p.camera,vp_x,vp_y,vp_w,vp_h
CameraClsColor p.camera,0,192,255
CameraFogColor p.camera,0,192,255
CameraFogRange p.camera,1000,3000
CameraRange p.camera,1,6000
'CameraRange cam, .1, 750  ' set the camera range to something reasonable
' CameraZoom p.camera,1.5
If use_fog Then CameraFogMode p.camera,1
Return p
End Function

Function UpdatePlayer( p:Player )

Local x_dir%,y_dir%,z_dir%

Select p.ctrl_mode
Case 1
If KeyDown(KEY_LEFT) x_dir=-1
If KeyDown(KEY_RIGHT) x_dir=1

If KeyDown(KEY_UP) y_dir=-1
If KeyDown(KEY_DOWN) y_dir=1

If KeyDown(KEY_A) z_dir=1
If KeyDown(KEY_Z) z_dir=-1

If KeyHit(KEY_F1) p.cam_mode=1
If KeyHit(KEY_F2) p.cam_mode=2
If KeyHit(KEY_F3) p.cam_mode=3
If KeyHit(KEY_F4) p.cam_mode=4

If x_dir<0
p.yaw_speed=p.yaw_speed + (4-p.yaw_speed)*.04
Else If x_dir>0
p.yaw_speed=p.yaw_speed + (-4-p.yaw_speed)*.04
Else
p.yaw_speed=p.yaw_speed + (-p.yaw_speed)*.02
EndIf

If y_dir<0
p.pitch_speed=p.pitch_speed + (2-p.pitch_speed)*.2
Else If y_dir>0
p.pitch_speed=p.pitch_speed + (-2-p.pitch_speed)*.2
Else
p.pitch_speed=p.pitch_speed + (-p.pitch_speed)*.1
EndIf

Case 2
p.dx = ( MouseX() - p.cx ) / p.cx ' get the mouse position
p.dy = ( MouseY() - p.cy ) / p.cy ' relative to the center of the screen

If MouseDown(1)
x_dir=1
y_dir=1
z_dir=1
EndIf
If MouseDown(2)
z_dir=-1
EndIf

If x_dir>0
p.yaw_speed=p.yaw_speed + (-(p.dx*4)-p.yaw_speed)*.8
Else If p.dx=0
p.yaw_speed=p.yaw_speed + (p.dx-p.yaw_speed)*.4
EndIf
If y_dir>0
p.pitch_speed=p.pitch_speed + ((p.dy*4)-p.pitch_speed)*1.5
Else If p.dy=0
p.pitch_speed=p.pitch_speed + (p.dy-p.pitch_speed)*.75
EndIf


If KeyHit(KEY_F5) p.cam_mode=1
If KeyHit(KEY_F6) p.cam_mode=2
If KeyHit(KEY_F7) p.cam_mode=3
If KeyHit(KEY_F8) p.cam_mode=4

End Select

p.yaw=p.yaw+p.yaw_speed
If p.yaw<-180 Then p.yaw=p.yaw+360
If p.yaw>=180 Then p.yaw=p.yaw-360

p.pitch=p.pitch+p.pitch_speed
If p.pitch<-45 Then p.pitch=-45
If p.pitch>=45 Then p.pitch=45

p.roll=p.yaw_speed*30
RotateEntity p.entity,p.pitch,p.yaw,p.roll

'see If y/p/r funcs are working...
Local t_p#=EntityPitch( p.entity )
Local t_y#=EntityYaw( p.entity )
Local t_r#=EntityRoll( p.entity )
RotateEntity p.entity,t_p,t_y,t_r

If p.ignition
If z_dir>0 'faster?
p.thrust=p.thrust + (1.5-p.thrust)*.04 '1.5
Else If z_dir<0 'slower?
p.thrust=p.thrust + (-p.thrust)*.04
EndIf
MoveEntity p.entity,0,0,p.thrust
Else If z_dir>0
p.ignition=True
EndIf

If p.camera
Select p.cam_mode
Case 1
EntityParent p.camera,p.entity
RotateEntity p.camera,0,p.yaw,0,True
PositionEntity p.camera,EntityX(p.entity),EntityY(p.entity),EntityZ(p.entity),True
MoveEntity p.camera,0,1,-5
PointEntity p.camera,p.entity,p.roll/2
Case 2
EntityParent p.camera,Null
PositionEntity p.camera,EntityX(p.entity),EntityY(p.entity),EntityZ(p.entity)
TranslateEntity p.camera,0,1,-5
PointEntity p.camera,p.entity,0
Case 3
EntityParent p.camera,p.entity
PositionEntity p.camera,0,.25,0
RotateEntity p.camera,0,0,0
Case 4
EntityParent p.camera,Null
PointEntity p.camera,p.entity,0
End Select
EndIf

End Function