Mark Sibly 8 年 前
コミット
d429ebd15b

+ 9 - 250
modules/mojo3d/graphics/entity.monkey2

@@ -129,7 +129,7 @@ Class Entity
 		Return _children.ToArray()
 	End
 
-	#rem monkeydoc entity visibility flag.
+	#rem monkeydoc Visibility flag.
 	#end
 	Property Visible:Bool()
 		
@@ -140,7 +140,7 @@ Class Entity
 		If _visible Show() Else Hide()
 	End
 
-	#rem monkeydoc entity animator.
+	#rem monkeydoc Entity animator.
 	#end	
 	Property Animator:Animator()
 		
@@ -155,7 +155,7 @@ Class Entity
 
 	#rem monkeydoc Local transformation matrix.
 	
-	The local matrix combines the local position, rotation and scale of the entity into a single affine 4x4 matrix.
+	The local matrix combines the local position, orientation and scale of the entity into a single affine 4x4 matrix.
 	
 	#end
 	Property Matrix:AffineMat4f()
@@ -181,10 +181,10 @@ Class Entity
 		Invalidate()
 	End
 	
-	#rem monkeydoc Local rotation basis matrix.
-	
-	A basis matrix is a 3x3 matrix representation of a rotation.
+	#rem monkeydoc Local basis matrix.
 	
+	A basis matrix is a 3x3 matrix representation of an orientation.
+
 	A basis matrix is orthogonal (ie: the i,j,k members are perpendicular to each other) and normalized (ie: the i,j,k members all have unit length).
 	
 	#end
@@ -199,19 +199,6 @@ Class Entity
 		Invalidate()
 	End
 
-	#rem monkeydoc Local rotation in euler angles.
-	#end
-	Property Rotation:Vec3f()
-		
-		Return _r.GetRotation()
-	
-	Setter( rotation:Vec3f )
-		
-		_r=Mat3f.Rotation( rotation )
-		
-		Invalidate()
-	End
-	
 	#rem monkeydoc Local scale.
 	#end	
 	Property Scale:Vec3f()
@@ -225,50 +212,11 @@ Class Entity
 		Invalidate()
 	End
 	
-	#rem monkeydoc X coordinate of local position.
-	#end
-	Property X:Float()
-		
-		Return _t.x
-		
-	Setter( x:Float )
-		
-		_t.x=x
-		
-		Invalidate()
-	End
-	
-	#rem monkeydoc Y coordinate of local position.
-	#end
-	Property Y:Float()
-	
-		Return _t.y
-	
-	Setter( y:Float )
-		
-		_t.y=y
-		
-		Invalidate()
-	End
-
-	#rem monkeydoc Z coordinate of local position.
-	#end
-	Property Z:Float()
-	
-		Return _t.z
-	
-	Setter( z:Float )
-		
-		_t.z=z
-		
-		Invalidate()
-	End
-	
 	'***** World space properties *****
 	
 	#rem monkeydoc World transformation matrix.
 	
-	The world matrix combines the world position, rotation and scale of the entity into a single affine 4x4 matrix.
+	The world matrix combines the world position, basis matrix and scale of the entity into a single affine 4x4 matrix.
 	
 	#end
 	Property WorldMatrix:AffineMat4f()
@@ -306,9 +254,9 @@ Class Entity
 		Invalidate()
 	End
 	
-	#rem monkeydoc World basis rotation matrix.
+	#rem monkeydoc World basis matrix.
 
-	A basis matrix is a 3x3 matrix representation of a rotation.
+	A basis matrix is a 3x3 matrix representation of an orientation.
 	
 	A basis matrix is orthogonal (ie: the i,j,k members are perpendicular to each other) and normalized (ie: the i,j,k members all have unit length).
 	
@@ -324,17 +272,6 @@ Class Entity
 		Invalidate()
 	End
 	
-	#rem monkeydoc World rotation in euler angles.
-	#end
-	Property WorldRotation:Vec3f()
-		
-		Return WorldBasis.GetRotation()
-	
-	Setter( rotation:Vec3f )
-		
-		WorldBasis=Mat3f.Rotation( rotation )
-	End
-	
 	#rem monkeydoc World scale.
 	#end	
 	Property WorldScale:Vec3f()
@@ -348,42 +285,6 @@ Class Entity
 		Invalidate()
 	End
 	
-	#rem monkeydoc X coordinate of world position.
-	#end
-	Property WorldX:Float()
-		
-		Return WorldPosition.x
-		
-	Setter( x:Float )
-
-		Local v:=WorldPosition		
-		WorldPosition=New Vec3f( x,v.y,v.z )
-	End
-	
-	#rem monkeydoc Y coordinate of world position.
-	#end
-	Property WorldY:Float()
-	
-		Return WorldPosition.y
-	
-	Setter( y:Float )
-		
-		Local v:=WorldPosition		
-		WorldPosition=New Vec3f( v.x,y,v.z )
-	End
-
-	#rem monkeydoc Z coordinate of world position.
-	#end
-	Property WorldZ:Float()
-	
-		Return _t.z
-	
-	Setter( z:Float )
-		
-		Local v:=WorldPosition		
-		WorldPosition=New Vec3f( v.x,v.y,z )
-	End
-	
 	'***** Methods ******
 
 	#rem monkeydoc Hides the entity and all of its children
@@ -440,148 +341,6 @@ Class Entity
 		Destroyed()
 	End
 	
-	#rem monkeydoc Sets entity position in local or world space.
-	#end
-	Method SetPosition( position:Vec3f,worldSpace:Bool=False )
-		
-		If worldSpace WorldPosition=position Else Position=position
-	End
-	
-	Method SetPosition( x:Float,y:Float,z:Float,worldSpace:Bool=False )
-		
-		SetPosition( x,y,z,worldSpace )
-	End
-	
-	#rem monkeydoc Gets entity position in local or world space.
-	#end
-	Method GetPostition:Vec3f( worldSpace:Bool=False )
-		
-		Return worldSpace ? WorldPosition Else Position
-	End
-	
-	#rem monkeydoc Sets entity rotation in euler angles in local or world space.
-	#end
-	Method SetRotation( rotation:Vec3f,worldSpace:Bool=False )
-		
-		If worldSpace WorldRotation=rotation Else Rotation=rotation
-	End
-	
-	Method SetRotation( rx:Float,ry:Float,rz:Float,worldSpace:Bool=False )
-		
-		SetRotation( New Vec3f( rx,ry,rz ),worldSpace )
-	End
-	
-	#rem monkeydoc Gets entity rotation in euler angles in local or world space.
-	#end
-	Method GetRotation:Vec3f( worldSpace:Bool=False )
-		
-		Return worldSpace ? WorldRotation Else Rotation
-	End
-	
-	#rem monkeydoc Sets entity scale in local or world space.
-	#end
-	Method SetScale( scale:Vec3f,worldSpace:Bool=False )
-		
-		If worldSpace WorldScale=scale Else Scale=scale
-	End
-	
-	Method SetScale( sx:Float,sy:Float,sz:Float,worldSpace:Bool=False )
-		
-		SetScale( New Vec3f( sx,sy,sz ),worldSpace )
-	End
-
-	#rem monkeydoc Gets entity scale in local or world space.
-	#end
-	Method GetScale:Vec3f( worldSpace:Bool=False )
-		
-		Return worldSpace ? WorldScale Else Scale
-	End
-	
-	#rem monkeydoc Moves the entity.
-	
-	Moves the entity relative to its current orientation.
-	
-	#end	
-	Method Move( tv:Vec3f )
-		
-		Position+=Basis * tv
-	End
-	
-	Method Move( tx:Float,ty:Float,tz:Float )
-		
-		Move( New Vec3f( tx,ty,tz ) )
-	End
-	
-	#rem monkeydoc Moves the entity on the X axis.
-	
-	Moves the entity relative to its current orientation.
-	
-	#end	
-	Method MoveX( tx:Float )
-		
-		Position+=Basis.i * tx
-	End
-	
-	#rem monkeydoc Moves the entity on the Y axis.
-	
-	Moves the entity relative to its current orientation.
-	
-	#end	
-	Method MoveY( ty:Float )
-
-		Position+=Basis.j * ty
-	End
-	
-	#rem monkeydoc Moves the entity on the Z axis.
-	
-	Moves the entity relative to its current orientation.
-	
-	#end	
-	Method MoveZ( tz:Float )
-
-		Position+=Basis.k * tz
-	End
-
-	#rem monkeydoc Rotates the entity.
-	
-	Rotates the entity.
-	
-	If `postRotate` is true, the rotation is applied after the entity's world rotation.
-		
-	If `postRotate` is false, the rotation is applied before the entity's local rotation.
-		
-	#end
-	Method Rotate( rv:Vec3f,postRotate:Bool=False )
-		
-		If postRotate WorldBasis=Mat3f.Rotation( rv )*WorldBasis Else Basis*=Mat3f.Rotation( rv )
-	End
-	
-	Method Rotate( rx:Float,ry:Float,rz:Float,postRotate:Bool=False )
-		
-		Rotate( New Vec3f( rx,ry,rz ),postRotate )
-	End
-	
-	#rem monkeydoc Rotates the entity around the X axis.
-	#end
-	Method RotateX( rx:Float,postRotate:Bool=False )
-		
-		If postRotate WorldBasis=Mat3f.Pitch( rx )*WorldBasis Else Basis*=Mat3f.Pitch( rx )
-	End
-
-	#rem monkeydoc Rotates the entity around the Y axis.
-	#end
-	Method RotateY( ry:Float,postRotate:Bool=False )
-		
-		If postRotate WorldBasis=Mat3f.Yaw( ry )*WorldBasis Else Basis*=Mat3f.Yaw( ry )
-	End
-
-	#rem monkeydoc Rotates the entity around the Z axis.
-	#end
-	Method RotateZ( rz:Float,postRotate:Bool=False )
-		
-		If postRotate WorldBasis=Mat3f.Roll( rz )*WorldBasis Else Basis*=Mat3f.Roll( rz )
-	End
-	
 Protected
 
 	#rem monkeydoc @hidden

+ 445 - 0
modules/mojo3d/graphics/entityexts.monkey2

@@ -0,0 +1,445 @@
+
+Namespace mojo3d.graphics
+
+Private
+
+Const DegreesToRadians:=Pi/180.0
+
+Const RadiansToDegrees:=180.0/Pi
+
+Public
+
+#rem monkeydoc Entity extension methods.
+#end
+Class Entity Extension
+
+	#rem monkeydoc Local rotation in degrees.
+	#end
+	Property Rotation:Vec3f()
+		
+		Return Basis.GetRotation() * RadiansToDegrees
+	
+	Setter( rotation:Vec3f )
+		
+		Basis=Mat3f.Rotation( rotation * DegreesToRadians )
+	End
+	
+	#rem monkeydoc World rotation in degrees.
+	#end
+	Property WorldRotation:Vec3f()
+		
+		Return WorldBasis.GetRotation() * RadiansToDegrees
+	
+	Setter( rotation:Vec3f )
+		
+		WorldBasis=Mat3f.Rotation( rotation * DegreesToRadians )
+	End
+	
+	#rem monkeydoc X coordinate of local position.
+	#end
+	Property X:Float()
+		
+		Return Position.x
+		
+	Setter( x:Float )
+		
+		Local v:=Position
+		Position=New Vec3f( x,v.y,v.z )
+	End
+	
+	#rem monkeydoc Y coordinate of local position.
+	#end
+	Property Y:Float()
+	
+		Return Position.y
+	
+	Setter( y:Float )
+		
+		Local v:=Position
+		Position=New Vec3f( v.x,y,v.z )
+	End
+
+	#rem monkeydoc Z coordinate of local position.
+	#end
+	Property Z:Float()
+	
+		Return Position.z
+	
+	Setter( z:Float )
+		
+		Local v:=Position
+		Position=New Vec3f( v.x,v.y,z )
+	End
+	
+	#rem monkeydoc X coordinate of world position.
+	#end
+	Property WorldX:Float()
+		
+		Return WorldPosition.x
+		
+	Setter( x:Float )
+
+		Local v:=WorldPosition		
+		WorldPosition=New Vec3f( x,v.y,v.z )
+	End
+	
+	#rem monkeydoc Y coordinate of world position.
+	#end
+	Property WorldY:Float()
+	
+		Return WorldPosition.y
+	
+	Setter( y:Float )
+		
+		Local v:=WorldPosition		
+		WorldPosition=New Vec3f( v.x,y,v.z )
+	End
+
+	#rem monkeydoc Z coordinate of world position.
+	#end
+	Property WorldZ:Float()
+	
+		Return WorldPosition.z
+	
+	Setter( z:Float )
+		
+		Local v:=WorldPosition		
+		WorldPosition=New Vec3f( v.x,v.y,z )
+	End
+	
+	#Rem monkeydoc Rotation around the X axis in degrees.
+	#End
+	Property Rx:Float()
+		
+		Return Rotation.x
+		
+	Setter( rx:Float )
+		
+		Local r:=Rotation
+		Rotation=New Vec3f( rx,r.y,r.z )
+	End
+
+	#Rem monkeydoc Rotation around the Y axis in degrees.
+	#End
+	Property Ry:Float()
+		
+		Return Rotation.y
+		
+	Setter( ry:Float )
+		
+		Local r:=Rotation
+		Rotation=New Vec3f( r.x,ry,r.z )
+	End
+
+	#Rem monkeydoc Rotation around the X axis in degrees.
+	#End
+	Property Rz:Float()
+		
+		Return Rotation.z
+		
+	Setter( rz:Float )
+		
+		Local r:=Rotation
+		Rotation=New Vec3f( r.x,r.y,rz )
+	End
+
+	#Rem monkeydoc Rotation around the X axis in degrees.
+	#End
+	Property WorldRx:Float()
+		
+		Return WorldRotation.x
+		
+	Setter( rx:Float )
+		
+		Local r:=WorldRotation
+		WorldRotation=New Vec3f( rx,r.y,r.z )
+	End
+
+	#Rem monkeydoc Rotation around the Y axis in degrees.
+	#End
+	Property WorldRy:Float()
+		
+		Return WorldRotation.y
+		
+	Setter( ry:Float )
+		
+		Local r:=WorldRotation
+		WorldRotation=New Vec3f( r.x,ry,r.z )
+	End
+
+	#Rem monkeydoc Rotation around the X axis in degrees.
+	#End
+	Property WorldRz:Float()
+		
+		Return WorldRotation.z
+		
+	Setter( rz:Float )
+		
+		Local r:=WorldRotation
+		WorldRotation=New Vec3f( r.x,r.y,rz )
+	End
+	
+	#rem monkeydoc Scale on the x axis.
+	#end
+	Property Sx:Float()
+		
+		Return Scale.x
+	
+	Setter( sx:Float )
+		
+		Local s:=Scale
+		Scale=New Vec3f( sx,s.y,s.z )
+	End
+	
+	#rem monkeydoc Scale on the y axis.
+	#end
+	Property Sy:Float()
+		
+		Return Scale.y
+	
+	Setter( sy:Float )
+		
+		Local s:=Scale
+		Scale=New Vec3f( s.x,sy,s.z )
+	End
+	
+	#rem monkeydoc Scale on the z axis.
+	#end
+	Property Sz:Float()
+		
+		Return Scale.z
+	
+	Setter( sz:Float )
+		
+		Local s:=Scale
+		
+		Scale=New Vec3f( s.x,s.y,sz )
+	End
+	
+	#rem monkeydoc Scale on the x axis.
+	#end
+	Property WorldSx:Float()
+		
+		Return WorldScale.x
+	
+	Setter( sx:Float )
+		
+		Local s:=WorldScale
+		
+		WorldScale=New Vec3f( sx,s.y,s.z )
+	End
+	
+	#rem monkeydoc Scale on the y axis.
+	#end
+	Property WorldSy:Float()
+		
+		Return WorldScale.y
+	
+	Setter( sy:Float )
+		
+		Local s:=WorldScale
+		
+		WorldScale=New Vec3f( s.x,sy,s.z )
+	End
+	
+	#rem monkeydoc Scale on the z axis.
+	#end
+	Property WorldSz:Float()
+		
+		Return WorldScale.z
+	
+	Setter( sz:Float )
+		
+		Local s:=WorldScale
+		
+		WorldScale=New Vec3f( s.x,s.y,sz )
+	End
+	
+	#rem monkeydoc Sets entity basis matrix in local or world space.
+	#end
+	Method SetBasis( basis:Mat3f,worldSpace:Bool=False )
+		
+		If worldSpace WorldBasis=basis Else Basis=basis
+	End
+	
+	#rem monkeydoc Gets entity basis matrix in local or world space.
+	#end
+	method GetBasis:Mat3f( worldSpace:Bool=False )
+		
+		Return worldSpace ? WorldBasis Else Basis
+	
+	End
+
+	#rem monkeydoc Sets entity position in local or world space.
+	#end
+	Method SetPosition( position:Vec3f,worldSpace:Bool=False )
+		
+		If worldSpace WorldPosition=position Else Position=position
+	End
+	
+	Method SetPosition( x:Float,y:Float,z:Float,worldSpace:Bool=False )
+		
+		SetPosition( New Vec3f( x,y,z ),worldSpace )
+	End
+	
+	#rem monkeydoc Gets entity position in local or world space.
+	#end
+	Method GetPostition:Vec3f( worldSpace:Bool=False )
+		
+		Return worldSpace ? WorldPosition Else Position
+	End
+	
+	#rem monkeydoc Sets entity rotation in euler angles in local or world space.
+	#end
+	Method SetRotation( rotation:Vec3f,worldSpace:Bool=False )
+		
+		Local basis:=Mat3f.Rotation( rotation * DegreesToRadians )
+		
+		If worldSpace WorldBasis=basis Else Basis=basis
+	End
+	
+	Method SetRotation( rx:Float,ry:Float,rz:Float,worldSpace:Bool=False )
+		
+		SetRotation( New Vec3f( rx,ry,rz ),worldSpace )
+	End
+	
+	#rem monkeydoc Gets entity rotation in euler angles in local or world space.
+	#end
+	Method GetRotation:Vec3f( worldSpace:Bool=False )
+		
+		Local basis:=worldSpace ? WorldBasis Else Basis
+		
+		Return basis.GetRotation() * RadiansToDegrees
+	End
+	
+	#rem monkeydoc Sets entity scale in local or world space.
+	#end
+	Method SetScale( scale:Vec3f,worldSpace:Bool=False )
+		
+		If worldSpace WorldScale=scale Else Scale=scale
+	End
+	
+	Method SetScale( sx:Float,sy:Float,sz:Float,worldSpace:Bool=False )
+		
+		SetScale( New Vec3f( sx,sy,sz ),worldSpace )
+	End
+
+	#rem monkeydoc Gets entity scale in local or world space.
+	#end
+	Method GetScale:Vec3f( worldSpace:Bool=False )
+		
+		Return worldSpace ? WorldScale Else Scale
+	End
+	
+	#rem monkeydoc Moves the entity.
+	
+	Moves the entity relative to its current orientation.
+	
+	#end	
+	Method Move( tv:Vec3f,worldSpace:Bool=False )
+		
+		If worldSpace WorldPosition+=tv Else Position+=Basis * tv
+	End
+	
+	Method Move( tx:Float,ty:Float,tz:Float )
+		
+		Move( New Vec3f( tx,ty,tz ) )
+	End
+	
+	#rem monkeydoc Moves the entity on the X axis.
+	
+	Moves the entity relative to its current orientation.
+	
+	#end	
+	Method MoveX( tx:Float,worldSpace:Bool=False )
+		
+		If worldSpace WorldX+=tx Else Position+=Basis.i * tx
+	End
+	
+	#rem monkeydoc Moves the entity on the Y axis.
+	
+	Moves the entity relative to its current orientation.
+	
+	#end	
+	Method MoveY( ty:Float,worldSpace:Bool=False )
+
+		If worldSpace WorldY+=ty Else Position+=Basis.j * ty
+	End
+	
+	#rem monkeydoc Moves the entity on the Z axis.
+	
+	Moves the entity relative to its current orientation.
+	
+	#end	
+	Method MoveZ( tz:Float,worldSpace:Bool=False )
+
+		If worldSpace WorldZ+=tz Else Position+=Basis.k * tz
+	End
+	
+	#rem monkeydoc Rotates the entity.
+	
+	Rotates the entity.
+	
+	If `postRotate` is true, the rotation is applied after the entity's world rotation.
+		
+	If `postRotate` is false, the rotation is applied before the entity's local rotation.
+		
+	#end
+	Method Rotate( rv:Vec3f,postRotate:Bool=False )
+		
+		Local basis:=Mat3f.Rotation( rv * DegreesToRadians )
+		
+		If postRotate WorldBasis=basis*WorldBasis Else Basis*=basis
+	End
+	
+	Method Rotate( rx:Float,ry:Float,rz:Float,postRotate:Bool=False )
+		
+		Rotate( New Vec3f( rx,ry,rz ),postRotate )
+	End
+	
+	#rem monkeydoc Rotates the entity around the X axis.
+	#end
+	Method RotateX( rx:Float,postRotate:Bool=False )
+		
+		Local basis:=Mat3f.Pitch( rx * DegreesToRadians )
+		
+		If postRotate WorldBasis=basis*WorldBasis Else Basis*=basis
+	End
+
+	#rem monkeydoc Rotates the entity around the Y axis.
+	#end
+	Method RotateY( ry:Float,postRotate:Bool=False )
+
+		Local basis:=Mat3f.Yaw( ry * DegreesToRadians )
+		
+		If postRotate WorldBasis=basis*WorldBasis Else Basis*=basis
+	End
+
+	#rem monkeydoc Rotates the entity around the Z axis.
+	#end
+	Method RotateZ( rz:Float,postRotate:Bool=False )
+
+		Local basis:=Mat3f.Roll( rz * DegreesToRadians )
+		
+		If postRotate WorldBasis=basis*WorldBasis Else Basis*=basis
+	End
+
+	#rem monkeydoc Points the entity at a target.
+	#end
+	Method PointAt( target:Vec3f,up:Vec3f=New Vec3f( 0,1,0 ) )
+		
+		Local k:=(target-WorldPosition).Normalize()
+		
+		Local i:=up.Cross( k ).Normalize()
+		
+		Local j:=k.Cross( i )
+		
+		WorldBasis=New Mat3f( i,j,k )
+	End
+	
+	Method PointAt( target:Entity,up:Vec3f=New Vec3f( 0,1,0 ) )
+		
+		PointAt( target.WorldPosition )
+	End
+
+End

+ 126 - 16
modules/mojo3d/graphics/gltf2loader.monkey2

@@ -6,44 +6,47 @@ Private
 Class Gltf2Loader
 
 	Method New( asset:Gltf2Asset,dir:String )
+		
 		_asset=asset
 		_dir=dir
 	End
 	
 	Method LoadMesh:Mesh()
 		
-		_loadedMesh=New Mesh
-		_loadedMaterials=New Stack<Material>
+		Local mesh:=New Mesh
 		
 		For Local node:=Eachin _asset.scenes[0].nodes
-			AddMeshes( node )
+			
+			LoadMesh( node,mesh,Null )
 		Next
 		
-		_loadedMesh.UpdateTangents()
-		
-		Local mesh:=_loadedMesh
+		mesh.UpdateTangents()
 		
 		Return mesh
 	End
 
 	Method LoadModel:Model()
 		
-		_loadedMesh=New Mesh
-		_loadedMaterials=New Stack<Material>
+		Local mesh:=New Mesh
+		
+		Local materials:=New Stack<Material>
 		
 		For Local node:=Eachin _asset.scenes[0].nodes
-			AddMeshes( node )
+			
+			LoadMesh( node,mesh,materials )
 		Next
 		
-		_loadedMesh.UpdateTangents()
+		mesh.UpdateTangents()
 		
 		Local model:=New Model
-		model.Mesh=_loadedMesh
-		model.Materials=_loadedMaterials.ToArray()
+		
+		model.Mesh=mesh
+		
+		model.Materials=materials.ToArray()
 		
 		Return model
 	End
-	
+
 	Private
 	
 	Alias IndexType:UInt
@@ -55,9 +58,6 @@ Class Gltf2Loader
 	Field _textureCache:=New Map<Gltf2Texture,Texture>
 	Field _materialCache:=New Map<Gltf2Material,Material>
 	
-	Field _loadedMesh:Mesh
-	Field _loadedMaterials:Stack<Material>
-
 	Method GetData:UByte Ptr( uri:String )
 		Local data:=_data[uri]
 		If Not data
@@ -144,6 +144,109 @@ Class Gltf2Loader
 		Return node.matrix
 	End
 	
+	Method LoadMesh( node:Gltf2Node,mesh:Mesh,materials:Stack<Material> )
+		
+		If node.mesh
+			
+			Print "mesh="+node.mesh.name
+			
+			Local matrix:=Cast<AffineMat4f>( GetMatrix( node ) )
+
+			Local cofactor:=matrix.m.Cofactor()
+			
+			For Local prim:=Eachin node.mesh.primitives
+				
+				'some sanity checking!
+				'
+				If prim.mode<>4 Continue
+				
+				If Not prim.POSITION Or prim.POSITION.componentType<>5126 Or prim.POSITION.type<>"VEC3" DebugStop()
+				If Not prim.NORMAL Or prim.NORMAL.componentType<>5126 Or prim.NORMAL.type<>"VEC3" DebugStop()
+				If Not prim.TEXCOORD_0 Or prim.TEXCOORD_0.componentType<>5126 Or prim.TEXCOORD_0.type<>"VEC2" DebugStop()
+				If Not prim.indices Or (prim.indices.componentType<>5123 And prim.indices.componentType<>5125) Or prim.indices.type<>"SCALAR" DebugStop()
+				
+				Local pp:=GetData( prim.POSITION )
+				Local pstride:=prim.POSITION.bufferView.byteStride
+				If Not pstride pstride=12
+				
+				Local np:=GetData( prim.NORMAL )
+				Local nstride:=prim.NORMAL.bufferView.byteStride
+				If Not nstride nstride=12
+				
+				Local tp:=GetData( prim.TEXCOORD_0 )
+				Local tstride:=prim.TEXCOORD_0.bufferView.byteStride
+				If Not tstride tstride=8
+					
+				Local vcount:=prim.POSITION.count
+				
+				Local vertices:=New Vertex3f[vcount],dstvp:=vertices.Data
+				
+				For Local i:=0 Until vcount
+					
+					dstvp[i].position=Cast<Vec3f Ptr>( pp )[0]
+					dstvp[i].normal=Cast<Vec3f Ptr>( np )[0]
+					dstvp[i].texCoord0=Cast<Vec2f Ptr>( tp )[0]
+					
+					dstvp[i].position.z=-dstvp[i].position.z
+					dstvp[i].normal.z=-dstvp[i].normal.z
+					
+					dstvp[i].position=matrix * dstvp[i].position
+					dstvp[i].normal=cofactor * dstvp[i].normal
+					
+					pp+=pstride
+					np+=nstride
+					tp+=tstride
+				Next
+				
+				Local icount:=prim.indices.count
+				
+				Local indices:=New IndexType[icount],dstip:=indices.Data
+
+				Local ip:=GetData( prim.indices )
+				Local istride:=prim.indices.bufferView.byteStride
+				
+				Local v0:=mesh.NumVertices
+				
+				If prim.indices.componentType=5123
+					If Not istride istride=2
+					For Local i:=0 Until icount Step 3
+						dstip[i+0]=Cast<UShort Ptr>( ip )[0] + v0
+						dstip[i+2]=Cast<UShort Ptr>( ip )[1] + v0
+						dstip[i+1]=Cast<UShort Ptr>( ip )[2] + v0
+						ip+=istride*3
+					Next
+				Else
+					If Not istride istride=4
+					For Local i:=0 Until icount Step 3
+						dstip[i+0]=Cast<UInt Ptr>( ip )[0] + v0
+						dstip[i+2]=Cast<UInt Ptr>( ip )[1] + v0
+						dstip[i+1]=Cast<UInt Ptr>( ip )[2] + v0
+						ip+=istride*3
+					Next
+				Endif
+				
+				If materials And Not mesh.NumVertices mesh.AddMaterials( 1 )				
+				
+				mesh.AddVertices( vertices )
+				
+				mesh.AddTriangles( indices,mesh.NumMaterials-1 )
+				
+				If materials materials.Push( GetMaterial( prim.material ) )
+					
+				Print "Added "+vcount+" vertices, "+icount+" indices."
+				
+			Next
+			
+		Endif
+		
+		For Local child:=Eachin node.children
+			
+			LoadMesh( child,mesh,materials )
+		Next
+		
+	End
+	
+	#rem
 	Method AddMeshes( node:Gltf2Node )
 		
 		If node.mesh
@@ -236,6 +339,8 @@ Class Gltf2Loader
 		
 	End
 	
+	#end
+	
 End
 
 Public
@@ -272,4 +377,9 @@ Class Gltf2Mojo3dLoader Extends Mojo3dLoader
 		Return mesh
 	End
 
+	Method LoadBonedModel:Model( path:String ) Override
+		
+		Return Null
+	End
+
 End

+ 8 - 0
modules/mojo3d/graphics/loader.monkey2

@@ -8,14 +8,22 @@ Class Mojo3dLoader
 	Const Instances:=New Stack<Mojo3dLoader>
 	
 	Method New()
+		
 		Instances.Push( Self )
 	End
 	
 	Method LoadMesh:Mesh( path:String ) Virtual
+		
 		Return Null
 	End
 	
 	Method LoadModel:Model( path:String ) Virtual
+		
+		Return Null
+	End
+	
+	Method LoadBonedModel:Model( path:String ) Virtual
+		
 		Return Null
 	End
 

+ 4 - 0
modules/mojo3d/graphics/meshprims.monkey2

@@ -52,6 +52,8 @@ End
 
 Public
 
+#rem monkeydoc Extension methods for creating meshes.
+#end
 Class Mesh Extension
 	
 	Function CreateRect:Mesh( rect:Rectf )
@@ -487,6 +489,8 @@ Class Mesh Extension
 	
 End
 
+#rem monkeydoc Extension methods for creating models.
+#end
 Class Model Extension
 	
 	Function CreateBox:Model( box:Boxf,xsegs:Int,ysegs:Int,zsegs:Int,material:Material,parent:Entity=Null )

+ 30 - 8
modules/mojo3d/graphics/model.monkey2

@@ -104,14 +104,42 @@ Class Model Extends Entity
 		For Local loader:=Eachin Mojo3dLoader.Instances
 		
 			Local model:=loader.LoadModel( path )
+			
 			If model Return model
-		
 		Next
 		
 		Return Null
 	
 	End
 
+	#rem monkeydoc Loads a boned model from a file path.
+	
+	On its own, mojo3d can only load gltf2 format mesh and model files.
+	
+	To add more formats, #import the mojo3d-assimp module into your app, eg:
+	
+	```
+	#Import "<mojo3d>"
+	#Import "<mojo3d-assimp>"
+	```
+	
+	This will allow you to load any format supported by the assimp module.
+	
+	However, importing the assimp module into your app will also increase its size.
+	
+	#end
+	Function LoadBoned:Model( path:String )
+	
+		For Local loader:=Eachin Mojo3dLoader.Instances
+		
+			Local model:=loader.LoadBonedModel( path )
+			
+			If model Return model
+		Next
+		
+		Return Null
+	End
+
 	#rem monkeydoc @hidden
 	#end	
 	Method OnRender( rq:RenderQueue )
@@ -121,19 +149,15 @@ Class Model Extends Entity
 		Local instance:=Self
 		
 		If _bones
-		
+
 			instance=Null
 		
 			If _boneMatrices.Length<>_bones.Length _boneMatrices=New Mat4f[ _bones.Length ]
 			
 			For Local i:=0 Until _bones.Length
-				
 				Local bone:=_bones[i]
-				
 				_boneMatrices[i]=New Mat4f( bone.entity.WorldMatrix * bone.offset )
-'				_boneMatrices[i]=New Mat4f( bone.offset * bone.entity.WorldMatrix )
 			Next
-		
 		End
 		
 		Local vbuffer:=_mesh.GetVertexBuffer()
@@ -144,8 +168,6 @@ Class Model Extends Entity
 			
 			Local material:=i<_materials.Length And _materials[i] ? _materials[i] Else _material
 			
-			If Not material DebugStop()
-			
 			Local ibuffer:=ibuffers[i]
 			
 			rq.AddRenderOp( material,vbuffer,ibuffer,instance,_boneMatrices,3,ibuffer.Length/3,0 )

+ 2 - 1
modules/mojo3d/graphics/pbrmaterial.monkey2

@@ -28,7 +28,7 @@ Class PbrMaterial Extends Material
 		EmissiveFactor=Color.Black
 	
 		MetalnessTexture=Texture.ColorTexture( Color.White )
-		MetalnessFactor=1.0
+		MetalnessFactor=0.0
 		
 		RoughnessTexture=Texture.ColorTexture( Color.White )
 		RoughnessFactor=1.0
@@ -39,6 +39,7 @@ Class PbrMaterial Extends Material
 	End
 	
 	Method New( material:PbrMaterial )
+		
 		Super.New( material )
 	End
 	

+ 46 - 47
modules/mojo3d/graphics/renderer.monkey2

@@ -346,60 +346,59 @@ Class Renderer
 		_device.DepthMask=True
 		_device.Clear( Null,1.0 )
 		
-		If light.ShadowsEnabled
+		_device.DepthFunc=DepthFunc.LessEqual
+		_device.BlendMode=BlendMode.Opaque
+		_device.CullMode=CullMode.Back
+		_device.RenderPass=2
+
+		Local invLightMatrix:=light.InverseWorldMatrix
+		Local viewLight:=invLightMatrix * _camera.WorldMatrix
 		
-			_device.DepthFunc=DepthFunc.LessEqual
-			_device.BlendMode=BlendMode.Opaque
-			_device.CullMode=CullMode.Back
-			_device.RenderPass=2
-	
-			Local invLightMatrix:=light.InverseWorldMatrix
-			Local viewLight:=invLightMatrix * _camera.WorldMatrix
+		For Local i:=0 Until _csmSplits.Length-1
 			
-			For Local i:=0 Until _csmSplits.Length-1
-				
-				Local znear:=_csmSplits[i]
-				Local zfar:=_csmSplits[i+1]
-				
-				Local splitProj:=Mat4f.Perspective( _camera.Fov,_camera.Aspect,znear,zfar )
-							
-				Local invSplitProj:=-splitProj
-				
-				Local bounds:=Boxf.EmptyBounds
-				
-				For Local z:=-1 To 1 Step 2
-					For Local y:=-1 To 1 Step 2
-						For Local x:=-1 To 1 Step 2
-							Local c:=New Vec3f( x,y,z )				'clip coords
-							Local v:=invSplitProj * c				'clip->view
-							Local l:=viewLight * v					'view->light
-							bounds|=l
-						Next
+			Local znear:=_csmSplits[i]
+			Local zfar:=_csmSplits[i+1]
+			
+			Local splitProj:=Mat4f.Perspective( _camera.Fov,_camera.Aspect,znear,zfar )
+						
+			Local invSplitProj:=-splitProj
+			
+			Local bounds:=Boxf.EmptyBounds
+			
+			For Local z:=-1 To 1 Step 2
+				For Local y:=-1 To 1 Step 2
+					For Local x:=-1 To 1 Step 2
+						Local c:=New Vec3f( x,y,z )				'clip coords
+						Local v:=invSplitProj * c				'clip->view
+						Local l:=viewLight * v					'view->light
+						bounds|=l
 					Next
 				Next
+			Next
+			
+			bounds.min.z-=100
+			
+			Local lightProj:=Mat4f.Ortho( bounds.min.x,bounds.max.x,bounds.min.y,bounds.max.y,bounds.min.z,bounds.max.z )
+			
+			'set matrices for next pass...
+			_uniforms.SetMat4f( "ShadowMatrix"+i,lightProj * viewLight )
+			
+			Local size:=_csmTexture.Size,hsize:=size/2
+			
+			Select i
+			Case 0 _device.Viewport=New Recti( 0,0,hsize.x,hsize.y )
+			Case 1 _device.Viewport=New Recti( hsize.x,0,size.x,hsize.y )
+			Case 2 _device.Viewport=New Recti( 0,hsize.y,hsize.x,size.y )
+			Case 3 _device.Viewport=New Recti( hsize.x,hsize.y,size.x,size.y )
+			End
+			
+			_device.Scissor=_device.Viewport
 				
-				bounds.min.z-=100
-				
-				Local lightProj:=Mat4f.Ortho( bounds.min.x,bounds.max.x,bounds.min.y,bounds.max.y,bounds.min.z,bounds.max.z )
-				
-				'set matrices for next pass...
-				_uniforms.SetMat4f( "ShadowMatrix"+i,lightProj * viewLight )
-				
-				Local size:=_csmTexture.Size,hsize:=size/2
-				
-				Select i
-				Case 0 _device.Viewport=New Recti( 0,0,hsize.x,hsize.y )
-				Case 1 _device.Viewport=New Recti( hsize.x,0,size.x,hsize.y )
-				Case 2 _device.Viewport=New Recti( 0,hsize.y,hsize.x,size.y )
-				Case 3 _device.Viewport=New Recti( hsize.x,hsize.y,size.x,size.y )
-				End
-				
-				_device.Scissor=_device.Viewport
-					
+			If light.ShadowsEnabled
 				RenderRenderOps( _renderQueue.OpaqueOps,invLightMatrix,lightProj )
-			Next
+			Endif
 			
-		Endif
+		Next
 		
 		_device.RenderTarget=t_rtarget
 		_device.Viewport=t_viewport

+ 3 - 2
modules/mojo3d/graphics/shaders/directional-light.glsl

@@ -50,7 +50,8 @@ float viewDepth( float depth ){
 
 float evalShadow(){
 
-	vec4 vpos=vec4( v_Position + v_Normal * .01,1.0 );
+	vec4 vpos=vec4( v_Position + v_Normal * .05,1.0 );
+//	vec4 vpos=vec4( v_Position,1.0 );
 	vec4 lpos;
 	vec2 off;
 	
@@ -70,7 +71,7 @@ float evalShadow(){
 	
 	vec3 spos=lpos.xyz/lpos.w * vec3( 0.25,0.25,0.5 ) + vec3( 0.25,0.25,0.5 );
 
-	spos.z*=0.99;
+//	spos.z*=0.999;
 	
 	float d=texture2D( r_ShadowTexture,spos.xy+off ).r;
 	

+ 2 - 0
modules/mojo3d/mojo3d.monkey2

@@ -27,6 +27,8 @@ Using gles20..
 #Import "graphics/gltf2"
 #Import "graphics/gltf2loader"
 
+#Import "graphics/entityexts"
+
 #Import "graphics/animation"
 #Import "graphics/animator"
 

+ 2 - 2
modules/mojo3d/tests/cubetest.monkey2

@@ -60,7 +60,7 @@ Class MyWindow Extends Window
 		'create light
 		'
 		'_light=New Light
-		'_light.RotateX( Pi/2 )	'aim directional light 'down' - Pi/2=90 degrees.
+		'_light.RotateX( 90 )	'aim directional light 'down' - Pi/2=90 degrees.
 		
 		'create donut - metallic silver...
 		'		
@@ -78,7 +78,7 @@ Class MyWindow Extends Window
 		
 		If Keyboard.KeyHit( Key.Space ) _donut.Visible=Not _donut.Visible
 		
-		_donut.Rotate( .01,.02,.03 )
+		_donut.Rotate( .1,.2,.3 )
 		
 		util.Fly( _camera,Self )
 		

+ 3 - 2
modules/mojo3d/tests/donut.monkey2

@@ -41,7 +41,8 @@ Class MyWindow Extends Window
 		'create light
 		'
 		_light=New Light
-		_light.RotateX( Pi/2 )	'aim directional light 'down' - Pi/2=90 degrees.
+
+		_light.RotateX( 90 )
 		
 		'create donut - metallic silver...
 		
@@ -58,7 +59,7 @@ Class MyWindow Extends Window
 		
 		If Keyboard.KeyHit( Key.Space ) _donut.Visible=Not _donut.Visible
 		
-		_donut.Rotate( .01,.02,.03 )
+		_donut.Rotate( .1,.2,.3 )
 		
 		util.Fly( _camera,Self )
 		

+ 4 - 3
modules/mojo3d/tests/ducks.monkey2

@@ -63,7 +63,7 @@ Class MyWindow Extends Window
 		'create light
 		'
 		_light=New Light
-		_light.RotateX( Pi/2 )	'aim directional light downwards - Pi/2=90 degrees.
+		_light.RotateX( 90 )	'aim directional light downwards
 		
 		'create ground
 		'
@@ -87,7 +87,7 @@ Class MyWindow Extends Window
 			
 				Local copy:=duck.Copy( root )
 				
-				copy.RotateY( i*TwoPi/360 )
+				copy.RotateY( i )
 				
 				copy.Move( 0,0,6+m*16 )
 				
@@ -120,7 +120,8 @@ Class MyWindow Extends Window
 		'_monochrome.Level=Sin( Now()*3 ) * .5 + .5
 		
 		For Local duck:=Eachin _ducks
-			duck.Rotate( 0,.01,0 )
+			
+			duck.Rotate( 0,1,0 )
 		Next
 		
 		util.Fly( _camera,Self )

+ 1 - 2
modules/mojo3d/tests/makequad.monkey2

@@ -33,7 +33,6 @@ Class MyWindow Extends Window
 		'create light
 		'
 		_light=New Light
-		_light.RotateX( Pi/2 )	'aim directional light downwards - Pi/2=90 degrees.
 		
 		'create quad mesh
 		'
@@ -66,7 +65,7 @@ Class MyWindow Extends Window
 
 		RequestRender()
 		
-		_model.RotateY( .1 )
+		_model.RotateY( 1 )
 		
 		_scene.Render( canvas,_camera )
 

+ 1 - 1
modules/mojo3d/tests/pbrspheres.monkey2

@@ -43,7 +43,7 @@ Class MyWindow Extends Window
 		'create light
 		'
 		_light=New Light
-		_light.RotateX( Pi/2 )	'aim directional light 'down' - Pi/2=90 degrees.
+		_light.RotateX( 60 )	'aim directional light 'downish'.
 		
 		'create ground
 		'

+ 1 - 1
modules/mojo3d/tests/sprites.monkey2

@@ -47,7 +47,7 @@ Class MyWindow Extends Window
 		
 		'create ground
 		'
-		_ground=Model.CreateBox( New Boxf( -50,-1,-50,50,0,50 ),1,1,1,New PbrMaterial( Color.Green,0,.5 ) )
+		_ground=Model.CreateBox( New Boxf( -50,-1,-50,50,0,50 ),1,1,1,New PbrMaterial( Color.Green ) )
 		
 		'create sprites
 		'

+ 10 - 8
modules/mojo3d/tests/util.monkey2

@@ -2,30 +2,32 @@
 Namespace util
 
 Function Fly( entity:Entity,view:View )
+	
+	Const rspeed:=1.0
 
 	If Keyboard.KeyDown( Key.Up )
-		entity.RotateX( .1 )
+		entity.RotateX( rspeed )
 	Else If Keyboard.KeyDown( Key.Down )
-		entity.RotateX( -.1 )
+		entity.RotateX( -rspeed )
 	Endif
 	
 	If Keyboard.KeyDown( Key.Q )
-		entity.RotateZ( .1 )
+		entity.RotateZ( rspeed )
 	Else If Keyboard.KeyDown( Key.W )
-		entity.RotateZ( -.1 )
+		entity.RotateZ( -rspeed )
 	Endif
 	
 	If Keyboard.KeyDown( Key.Left )
-		entity.RotateY( .1,True )
+		entity.RotateY( rspeed,True )
 	Else If Keyboard.KeyDown( Key.Right )
-		entity.RotateY( -.1,True )
+		entity.RotateY( -rspeed,True )
 	Endif
 
 	If Mouse.ButtonDown( MouseButton.Left )
 		If Mouse.X<view.Width/3
-			entity.RotateY( .1,True )
+			entity.RotateY( rspeed,True )
 		Else If Mouse.X>view.Width/3*2
-			entity.RotateY( -.1,True )
+			entity.RotateY( -rspeed,True )
 		Else
 			entity.Move( New Vec3f( 0,0,.1 ) )
 		Endif

+ 1 - 1
modules/mojo3d/tests/water.monkey2

@@ -41,7 +41,7 @@ Class MyWindow Extends Window
 		'create light
 		'
 		_light=New Light
-		_light.RotateX( Pi/2 )	'aim directional light 'down' - Pi/2=90 degrees.
+		_light.RotateX( 90 )	'aim directional light 'down' - Pi/2=90 degrees.
 		
 		'create water material
 		'