blitz-research/monkey2

Need a way to rotate meshes

Opened this issue · 6 comments

This is a very messy WIP test using two Google Poly models, an island and a plane.

When I load the plane, it's rotated 180 degrees on the y-axis, so faces the camera, so I can only fly with my plane pointing in reverse, as this loaded orientation is what the CylinderCollider's Axis.Z aligns with.

Blitz3D had a RotateMesh command to take care of this, but there doesn't seem to be any way to do the same thing in mojo3d.

This is the crap demo, controls are weird (not taking into account proper plane movement) and plane flies backwards!

island.zip

Unimportant source update, but much better control-wise, at least for arcade purposes! Just that little thing of... flying backwards... need media from zip above.

#Import "<std>"
#Import "<mojo>"
#Import "<mojo3d>"
#Import "<mojo3d-loaders>"

#Import "assets/"

Using std..
Using mojo..
Using mojo3d..

Class MyWindow Extends Window
	
	Field scene:Scene
	Field camera:Camera
	Field light:Light

	Field ground:GroundBehaviour
	Field plane:PlaneBehaviour
	
	Method New( title:String="Place app",width:Int=640,height:Int=480,flags:WindowFlags=WindowFlags.Resizable )
		
		Super.New( title,width,height,flags )
	End
	
	Method OnCreateWindow() Override
		
		'create (current) scene
		scene=New Scene
		scene.ClearColor = New Color( 0.2, 0.6, 1.0 )
		scene.AmbientLight = scene.ClearColor * 0.25
		scene.FogColor = scene.ClearColor
		scene.FogNear = 1.0
		scene.FogFar = 5120.0
		
		'create camera
		camera=New Camera()
'		camera.AddComponent<FlyBehaviour>()
	'	camera.Move( 0,0,-512 )
		camera.Far = 10240.0
		
		'create light
		light=New Light
		light.CastsShadow=True
		light.Rotate( 45, 45, 0 )
		
		'create ground
		Local ground_size:Float = 4096 * 0.5
		
		Local ground_box:Boxf = New Boxf( -ground_size,-ground_size * 0.5,-ground_size,ground_size,0,ground_size )
		'Local groundMaterial:=New PbrMaterial( Color.Lime )
		Local ground_model:Model=Model.Load ("asset::model_gltf_6G3x4Sgg6iX_7QCCWe9sgpb\model.gltf")'CreateBox( groundBox,1,1,1,groundMaterial )
		ground_model.Mesh.FitVertices (ground_box, False)
		
'		ground_model.Scale = New Vec3f (100, 100, 100)
'		ground_model.Move (0, 100, 0)

		ground_model.CastsShadow=True

		ground = New GroundBehaviour (ground_model)

		Local ground_collider:MeshCollider	= ground.Entity.AddComponent <MeshCollider> ()

		ground_collider.Mesh = Cast <Model> (ground.Entity).Mesh
		
		Local ground_body:RigidBody		= ground.Entity.AddComponent <RigidBody> ()

			ground_body.Mass				= 0.0
	
		Local plane_size:Float = 16.83 * 0.5
		Local plane_box:Boxf = New Boxf (-plane_size, -plane_size, -plane_size, plane_size, plane_size, plane_size)
		
		Local plane_model:Model = Model.Load ("asset::1397 Jet_gltf_3B3Pa6BHXn1_fKZwaiJPXpf\1397 Jet.gltf")
		
		plane_model.Mesh.FitVertices (plane_box)

		camera.Parent = plane_model
		camera.Move (0, 0, -20)
		Local plane_coll_radius:Float = plane_size * 0.5
		Local plane_coll_length:Float = plane_size * 1.5
		
		Local plane_vis:Model = Model.CreateCylinder (plane_coll_radius, plane_coll_length, Axis.Z, 16, New PbrMaterial (Color.White), plane_model)
		plane_vis.Alpha = 0.25
		plane_vis.Visible = False
		
		plane_model.Move (0.0, 5.0, 0.0)
'		plane_model.Rotate (0.0, 180.0, 0.0)

		plane = New PlaneBehaviour (plane_model)

'		Local plane_collider:SphereCollider	= plane.Entity.AddComponent <SphereCollider> ()
		Local plane_collider:CylinderCollider	= plane.Entity.AddComponent <CylinderCollider> ()

'		apply to collider??? plane_model.Basis.Rotate (0.0, 180.0, 0.0)

		plane_collider.Radius = plane_coll_radius
		plane_collider.Axis = Axis.Z
		plane_collider.Length = plane_coll_length
		
		Local plane_body:RigidBody		= plane.Entity.AddComponent <RigidBody> ()

			plane_body.Mass				= 1.0
			plane_body.Restitution		= 0.5
			plane_body.AngularDamping	= 0.5
			plane_body.LinearDamping	= 0.5
			
	'		plane_body.ApplyTorqueImpulse (New Vec3f (0.0, 0.0, -15.0))
		
			plane_body.ApplyImpulse (plane.Entity.Basis * New Vec3f (0.0, 0.0, 33.0))
		
	End
	
	Method OnRender( canvas:Canvas ) Override
	
		'camera.PointAt (plane.Entity)
		
'		camera.FOV = 200.0 - Clamp (Float (camera.Position.Distance (plane.Entity.Position)), 22.5, 150.0)
		
		If Keyboard.KeyHit (Key.Escape) Then App.Terminate ()
		
		Local speed:Float = 1.0
		
		Local roll_rate:Float = 25.0
		Local pitch_rate:Float = 33.0
		
		' -Scene.GetCurrent ().World.Gravity.Y
		plane.Entity.GetComponent <RigidBody> ().ApplyForce (plane.Entity.Basis * New Vec3f (0.0, -Scene.GetCurrent ().World.Gravity.Y, 75.0))
		
		If Keyboard.KeyDown (Key.LeftShift)
			speed = speed * 2.0
		Endif
		
		If Keyboard.KeyDown (Key.Left)
			plane.Entity.GetComponent <RigidBody> ().ApplyTorque (plane.Entity.Basis * New Vec3f (0.0, 0.0, roll_rate))
		Endif

		If Keyboard.KeyDown (Key.Right)
			plane.Entity.GetComponent <RigidBody> ().ApplyTorque (plane.Entity.Basis * New Vec3f (0.0, 0.0, -roll_rate))
		Endif

		If Keyboard.KeyDown (Key.Up)
			plane.Entity.GetComponent <RigidBody> ().ApplyTorque (plane.Entity.Basis * New Vec3f (pitch_rate, 0.0, 0.0))
			plane.Entity.GetComponent <RigidBody> ().ApplyForce (plane.Entity.Basis * New Vec3f (0.0, Scene.GetCurrent ().World.Gravity.Y * 0.5, Scene.GetCurrent ().World.Gravity.Y * 0.5))
		Endif

		If Keyboard.KeyDown (Key.Down)
			plane.Entity.GetComponent <RigidBody> ().ApplyTorque (plane.Entity.Basis * New Vec3f (-pitch_rate, 0.0, 0.0))
			plane.Entity.GetComponent <RigidBody> ().ApplyForce (plane.Entity.Basis * New Vec3f (0.0, -Scene.GetCurrent ().World.Gravity.Y * 0.5, Scene.GetCurrent ().World.Gravity.Y * 0.5))
		Endif
		
		RequestRender()
		scene.Update()
		camera.Render( canvas )
		canvas.DrawText( "FPS="+App.FPS,0,0 )
		canvas.DrawText( "FOV="+camera.FOV,0,20 )
		
	End
	
End

Function Main()

	New AppInstance
	
	New MyWindow
	
	App.Run()
End

Class PlaneBehaviour Extends Behaviour
	
	Method New (entity:Entity)
		
		Super.New (entity)
		AddInstance ()

	End
	
	Method OnStart () Override

		'Local spacer:Float = 50.0
		
		'Entity.Move (Rnd (-spacer, spacer), Rnd (-spacer, spacer), Rnd (-spacer, spacer))
		
		Local model:Model = Cast <Model> (Entity)
		
		For Local mat:Material = Eachin model.Materials
			mat.CullMode = CullMode.Back	
		Next

	End
	
	Method OnUpdate (elapsed:Float) Override
	
		'Local body:RigidBody = Cast <Model> (Entity).GetComponent <RigidBody> ()
		
		'body.ApplyForce (Scene.GetCurrent ().World.Gravity * New Vec3f (1.0, -1.0, 1.0))	
		
	End
	
End

Class GroundBehaviour Extends Behaviour
	
	Method New (entity:Entity)
		
		Super.New (entity)
		AddInstance ()

	End
	
	Method OnStart () Override

		Local model:Model = Cast <Model> (Entity)
		
		For Local mat:Material = Eachin model.Materials
			mat.CullMode = CullMode.Back	
		Next

	End
	
	Method OnUpdate (elapsed:Float) Override
		
	End
	
End

Function Degrees:Float (radians:Float)
	Return radians * RAD_DIVIDER
End

Function Radians:Float (degrees:Float)
	Return degrees * DEG_DIVIDER
End

Const RAD_DIVIDER:Float = 180.0 / Pi
Const DEG_DIVIDER:Float = Pi / 180.0

This works for me:

Local matrix := AffineMat4f.Rotation( 0, Radians(180), 0 )
plane_model.Mesh.TransformVertices( matrix )

On a side note, ground model shadows are totally killing the performance on my little laptop, like around 20 fps. Turning them off with ground_model.CastsShadow=False jumps to 60fps, and I still get the plane's shadow on the ground. I wonder if it's related to the model size.

Oh, thanks, DW, will give that a go later. I'd ended up setting model alpha to 0 and loading (and rotating) a parented copy to work around it!

Interesting about the shadows, a lot of self-shadowing going on, I suppose...

It's only going to be a very simple 'how-to' physics demo anyway.

Just stuck it in -- that's worked a treat, thanks very much!

Current version much more controllable and with my rocket game camera hacked-in, looks pretty nice -- will just tidy this up (a lot) and tweak a bit more.


#Import "<std>"
#Import "<mojo>"
#Import "<mojo3d>"
#Import "<mojo3d-loaders>"

#Import "assets/"

Using std..
Using mojo..
Using mojo3d..

Class MyWindow Extends Window
	
	Const WINDOW_WIDTH:Int = 1920
	Const WINDOW_HEIGHT:Int = 1080
	Const WINDOW_FLAGS:WindowFlags = WindowFlags.Fullscreen
	
'	Const WINDOW_WIDTH:Int = 640
'	Const WINDOW_HEIGHT:Int = 480
'	Const WINDOW_FLAGS:WindowFlags = WindowFlags.Resizable
	
	Field scene:Scene
	'Field camera:Camera
	Field camera:GameCamera
	
	Field light:Light

	Field ground:GroundBehaviour
	Field plane:PlaneBehaviour
	
	Method New( title:String="Place app",width:Int=WINDOW_WIDTH,height:Int=WINDOW_HEIGHT,flags:WindowFlags=WINDOW_FLAGS )
		
		Super.New( title,width,height,flags )
	End
	
	Method OnCreateWindow() Override
		
		'create (current) scene
		scene=New Scene
		scene.ClearColor = New Color( 0.2, 0.6, 1.0 )
		scene.AmbientLight = scene.ClearColor * 0.25
		scene.FogColor = scene.ClearColor
		scene.FogNear = 128.0
		scene.FogFar = 2048.0
		
'		'create camera
'		camera=New Camera()
''		camera.AddComponent<FlyBehaviour>()
'	'	camera.Move( 0,0,-512 )
'		camera.Far = 10240.0
'		camera.Viewport = App.ActiveWindow.Rect
'
		camera=New GameCamera(App.ActiveWindow.Rect, Null, 10240)
		
'		camera.AddComponent<FlyBehaviour>()
	'	camera.Move( 0,0,-512 )
		'camera.Far = 10240.0
		'camera.Viewport = App.ActiveWindow.Rect
		
		'create light
		light=New Light
		light.CastsShadow=True
		light.Rotate( 45, 45, 0 )
		
		'create ground
		Local ground_size:Float = 4096 * 0.5
		
		Local ground_box:Boxf = New Boxf( -ground_size,-ground_size * 0.5,-ground_size,ground_size,0,ground_size )
		'Local groundMaterial:=New PbrMaterial( Color.Lime )
		Local ground_model:Model=Model.Load ("asset::model_gltf_6G3x4Sgg6iX_7QCCWe9sgpb\model.gltf")'CreateBox( groundBox,1,1,1,groundMaterial )
		ground_model.Mesh.FitVertices (ground_box, False)
		
'		ground_model.Scale = New Vec3f (100, 100, 100)
'		ground_model.Move (0, 100, 0)

		ground_model.CastsShadow=True

		ground = New GroundBehaviour (ground_model)

		Local ground_collider:MeshCollider	= ground.Entity.AddComponent <MeshCollider> ()

		ground_collider.Mesh = Cast <Model> (ground.Entity).Mesh
		
		Local ground_body:RigidBody		= ground.Entity.AddComponent <RigidBody> ()

			ground_body.Mass				= 0.0
	
		Local plane_size:Float = 16.83 * 0.5
		Local plane_box:Boxf = New Boxf (-plane_size, -plane_size, -plane_size, plane_size, plane_size, plane_size)
		
		Local plane_model:Model = Model.Load ("asset::1397 Jet_gltf_3B3Pa6BHXn1_fKZwaiJPXpf\1397 Jet.gltf")
		
		plane_model.Mesh.FitVertices (plane_box)

		Local matrix := AffineMat4f.Rotation( 0, Radians(180), 0 )
		plane_model.Mesh.TransformVertices( matrix )

		' -------------------------------------------------
		' HACK! To work around lack of Mesh.Rotate!
		' -------------------------------------------------
		
'		Local plane_dummy:Model = plane_model.Copy ()
'		plane_dummy.Parent = plane_model
'		plane_dummy.Rotate (0, 180, 0)
'		
'		plane_model.Alpha = 0.0
'		
		' -------------------------------------------------
		
'		camera.Camera3D.Parent = plane_model
'		camera.Move (0, 0, -20)
		Local plane_coll_radius:Float = plane_size * 0.5
		Local plane_coll_length:Float = plane_size * 1.5
		
		Local plane_vis:Model = Model.CreateCylinder (plane_coll_radius, plane_coll_length, Axis.Z, 16, New PbrMaterial (Color.White), plane_model)
		plane_vis.Alpha = 0.25
		plane_vis.Visible = False
		
		plane_model.Move (0.0, 5.0, 0.0)
'		plane_model.Rotate (0.0, 180.0, 0.0)

		plane = New PlaneBehaviour (plane_model)

'		Local plane_collider:SphereCollider	= plane.Entity.AddComponent <SphereCollider> ()
		Local plane_collider:CylinderCollider	= plane.Entity.AddComponent <CylinderCollider> ()

'		apply to collider??? plane_model.Basis.Rotate (0.0, 180.0, 0.0)

		plane_collider.Radius = plane_coll_radius
		plane_collider.Axis = Axis.Z
		plane_collider.Length = plane_coll_length
		
		Local plane_body:RigidBody		= plane.Entity.AddComponent <RigidBody> ()

			plane_body.Mass				= 1.0
			plane_body.Restitution		= 0.5
			plane_body.AngularDamping	= 0.9
			plane_body.LinearDamping	= 0.5
			plane_body.Friction			= 0.0
			
	'		plane_body.ApplyTorqueImpulse (New Vec3f (0.0, 0.0, -15.0))
		
			plane_body.ApplyImpulse (plane.Entity.Basis * New Vec3f (0.0, 0.0, 33.0))
		
		Mouse.PointerVisible = False
		
	End
	
	Method OnRender( canvas:Canvas ) Override
	
		'camera.PointAt (plane.Entity)
		
'		camera.FOV = 200.0 - Clamp (Float (camera.Position.Distance (plane.Entity.Position)), 22.5, 150.0)
		
		If Keyboard.KeyHit (Key.Escape) Then App.Terminate ()
		
		Local speed:Float = 1.0
		
		Local roll_rate:Float = 50.0
		Local pitch_rate:Float = 30.0
		
		Local plane_body:RigidBody = plane.Entity.GetComponent <RigidBody> ()
		
		' -Scene.GetCurrent ().World.Gravity.Y
		plane_body.ApplyForce (plane.Entity.Basis * New Vec3f (0.0, -Scene.GetCurrent ().World.Gravity.Y, 75.0))
		
		If Keyboard.KeyDown (Key.LeftShift)
			speed = speed * 2.0
		Endif
		
		If Keyboard.KeyDown (Key.Left)
			plane_body.ApplyTorque (plane.Entity.Basis * New Vec3f (0.0, 0.0, roll_rate))
		Endif

		If Keyboard.KeyDown (Key.Right)
			plane_body.ApplyTorque (plane.Entity.Basis * New Vec3f (0.0, 0.0, -roll_rate))
		Endif

		If Keyboard.KeyDown (Key.Up)
			plane_body.ApplyTorque (plane.Entity.Basis * New Vec3f (pitch_rate, 0.0, 0.0))
			plane_body.ApplyForce (plane.Entity.Basis * New Vec3f (0.0, Scene.GetCurrent ().World.Gravity.Y * 0.5, Scene.GetCurrent ().World.Gravity.Y * 0.5))
		Endif

		If Keyboard.KeyDown (Key.Down)
			plane_body.ApplyTorque (plane.Entity.Basis * New Vec3f (-pitch_rate, 0.0, 0.0))
			plane_body.ApplyForce (plane.Entity.Basis * New Vec3f (0.0, -Scene.GetCurrent ().World.Gravity.Y * 0.5, Scene.GetCurrent ().World.Gravity.Y * 0.5))
		Endif
		
		RequestRender()
		scene.Update()
		
		camera.Update (plane)'Render( canvas )
		camera.Camera3D.Render (canvas)
		
		canvas.DrawText( "FPS="+App.FPS,0,0 )
		canvas.DrawText( "FOV="+camera.Camera3D.FOV,0,20 )
		
	End
	
End

Function Main()

	New AppInstance
	
	New MyWindow
	
	App.Run()
End

Class PlaneBehaviour Extends Behaviour
	
	Method New (entity:Entity)
		
		Super.New (entity)
		AddInstance ()

	End
	
	Method OnStart () Override

		'Local spacer:Float = 50.0
		
		'Entity.Move (Rnd (-spacer, spacer), Rnd (-spacer, spacer), Rnd (-spacer, spacer))
		
		Local model:Model = Cast <Model> (Entity)
		
		For Local mat:Material = Eachin model.Materials
			mat.CullMode = CullMode.Back	
		Next

	End
	
	Method OnUpdate (elapsed:Float) Override
	
		'Local body:RigidBody = Cast <Model> (Entity).GetComponent <RigidBody> ()
		
		'body.ApplyForce (Scene.GetCurrent ().World.Gravity * New Vec3f (1.0, -1.0, 1.0))	
		
	End
	
End

Class GroundBehaviour Extends Behaviour
	
	Method New (entity:Entity)
		
		Super.New (entity)
		AddInstance ()

	End
	
	Method OnStart () Override

		Local model:Model = Cast <Model> (Entity)
		
		For Local mat:Material = Eachin model.Materials
			mat.CullMode = CullMode.Back	
		Next

	End
	
	Method OnUpdate (elapsed:Float) Override
		
	End
	
End

Function Degrees:Float (radians:Float)
	Return radians * RAD_DIVIDER
End

Function Radians:Float (degrees:Float)
	Return degrees * DEG_DIVIDER
End

Const RAD_DIVIDER:Float = 180.0 / Pi
Const DEG_DIVIDER:Float = Pi / 180.0


' -----------------------------------------------------------------------------
' What is it?
' -----------------------------------------------------------------------------

Class GameCamera

	Public
	
		Property CameraDistance:Float ()
			Return camera_distance
			Setter (distance:Float)
				camera_distance = distance
		End
		
		Property Camera3D:Camera ()
			Return real_camera
			Setter (new_cam:Camera)
				real_camera = new_cam
		End
		
		Method New (viewport:Recti, current_camera:GameCamera, range_far:Float)
			
			If current_camera Then current_camera.Destroy ()
			
			camera_pivot = New Pivot
			
			Camera3D			= New Camera (camera_pivot)
			Camera3D.Near		= 0.01
			Camera3D.Far		= range_far * Sqrt (3.0) ' Terrain cube diagonal
			Camera3D.FOV		= 90.0 ' Mojo3d default
			
			Camera3D.Viewport = viewport
	
			' Chase target -- position camera tries to move towards...
			
			chase_target 					= Model.CreateSphere (1, 32, 32, New PbrMaterial (Color.Red))
			chase_target.Alpha				= 0.5
			chase_target.Material.CullMode	= CullMode.None
			chase_target.Visible			= False
			
			up = New Vec3f (0.0, up_y_default, 0.0)
			
			Reset ()
			
		End
		
		Method Destroy ()
		
			camera_pivot?.Destroy ()
			real_camera?.Destroy ()
			chase_target?.Destroy ()
			
		End
		
		Method Reset ()
	
			Camera3D.FOV = 90.0
	
'			chase_target.Position = New Vec3f (Game.CurrentLevel.SpawnX, Game.CurrentLevel.SpawnY, Game.CurrentLevel.SpawnZ)
'			camera_pivot.Position = New Vec3f (Game.CurrentLevel.SpawnX, Game.CurrentLevel.SpawnY, Game.CurrentLevel.SpawnZ - 10.0)
	
			lastvel = New Vec3f (0, 0, 15)
			prevvel = lastvel
	
		End
		
		Method Update (target:PlaneBehaviour)
	
			' Camera positioning...
			
			prevvel = lastvel
			
			Local plane_body:RigidBody = target.Entity.GetComponent <RigidBody> ()
			Local plane_model:Model = Cast <Model> (target.Entity)
			
'			If plane_body.LinearVelocity.XZ.Length > 5.0
				lastvel = lastvel.Blend (plane_body.LinearVelocity, 0.045)
'			Endif
			
			chase_target.Position = (plane_model.Position + up) - (lastvel * CameraDistance)

			camera_pivot.Move ((chase_target.Position - camera_pivot.Position) * 0.95, True)
			camera_pivot.PointAt (plane_model)

			Local cam_dist:Float = plane_model.Position.Distance (Camera3D.Position)
			
			Local closeup:Float = 10.5
			Local closer:Float = 0.1
			
			If cam_dist < closeup
				Camera3D.FOV = Blend (Camera3D.FOV, TransformRange (cam_dist, 1.0, closeup, 130.0, 90.0), closer)
				up.Y = Blend (up.Y, 3.0, 0.01)
			Else
				Camera3D.FOV = Blend (Camera3D.FOV, 90.0, 0.075)
				up.Y = Blend (up.Y, up_y_default, 0.01)
			Endif

		End
		
		'Method ShortestVec:Vec3f (v1:Vec3f, v2:Vec3f)
		'	If v1.Length < v2.Length Then Return v1 Else Return v2
		'End
		
		'Method LongestVec:Vec3f (v1:Vec3f, v2:Vec3f)
		'	If v1.Length > v2.Length Then Return v1 Else Return v2
		'End
		
		Method Move (tv:Vec3f, localSpace:Bool = False)
			camera_pivot.Move (tv, localSpace)
		End
		
		Method Move (tx:Float, ty:Float, tz:Float)
			camera_pivot.Move (tx, ty, tz)
		End
		
		Method PointAt (target:Entity)
			camera_pivot.PointAt (target)
		End
		
		Method Position (v3:Vec3f)
			camera_pivot.Position = v3
		End

	Private
	
		Field camera_pivot:Pivot
		Field real_camera:Camera
		
		Field chase_target:Model
	
		Field lastvel:Vec3f
		Field prevvel:Vec3f
	
		Field up:Vec3f
		Field up_y_default:Float = 2.5
		
		Field camera_distance:Float = 0.4
		
End

Function TransformRange:Float (input_value:Float, from_min:Float, from_max:Float, to_min:Float, to_max:Float)

	' Algorithm via jerryjvl at https://stackoverflow.com/questions/929103/convert-a-number-range-to-another-range-maintaining-ratio
	
	Local from_delta:Float	= from_max	- from_min	' Input range,	eg. 0.0 - 1.0
	Local to_delta:Float	= to_max	- to_min	' Output range,	eg. 5.0 - 10.0
	
	Assert (from_delta <> 0.0, "TransformRange: Invalid input range!")
	
	Return (((input_value - from_min) * to_delta) / from_delta) + to_min
	
End

Function Blend:Float (in:Float, target:Float, delta:Float = 0.1)
	If Abs (target - in) < Abs (delta) Then Return target
	Return in + ((target - in) * delta)
End

Maybe closed this issue.