///// <summary> ///// Builds this quaternion from an axis-angle pair ///// </summary> ///// <param name="vec">Rotation vector</param> ///// <param name="angle">Rotation angle (in radians)</param> //public Quaternion( Vector3 vec, float angle ) //{ // // TODO: AP: ... //} /// <summary> /// Builds this quaternion from the rotation part of a transform matrix /// </summary> public Quaternion( Matrix44 matrix ) { // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes // article "Quaternion Calculus and Fast Animation". float trace = matrix[ 0, 0 ] + matrix[ 1, 1 ] + matrix[ 2, 2 ]; float root; if ( trace > 0.0f ) { // |w| > 1/2, may as well choose w > 1/2 root = Functions.Sqrt( trace + 1.0f ); // 2w m_W = 0.5f * root; root = 0.5f / root; // 1/(4w) m_X = ( matrix[ 2, 1 ] - matrix[ 1, 2 ] ) * root; m_Y = ( matrix[ 0, 2 ] - matrix[ 2, 0 ] ) * root; m_Z = ( matrix[ 1, 0 ] - matrix[ 0, 1 ] ) * root; } else { // |w| <= 1/2 int i = 0; if ( matrix[ 1, 1 ] > matrix[ 0, 0 ] ) { i = 1; } if ( matrix[ 2, 2 ] > matrix[ i, i ] ) { i = 2; } int j = ( i + 1 ) % 3; int k = ( j + 1 ) % 3; root = Functions.Sqrt( matrix[ i, i ] - matrix[ j, j ] - matrix[ k, k ] + 1.0f ); Point3 quat = new Point3( ); quat[ i ] = 0.5f * root; root = 0.5f / root; m_W = ( matrix[ k, j ] - matrix[ j, k ] ) * root; quat[ j ] = ( matrix[ j, i ] + matrix[ i, j ] ) * root; quat[ k ] = ( matrix[ k, i ] + matrix[ i, k ] ) * root; m_X = quat.X; m_Y = quat.Y; m_Z = quat.Z; } }
/// <summary> /// Sets up the renderer /// </summary> /// <param name="mainRenderingThread">Main rendering thread</param> /// <param name="mainRenderingThreadMarshaller">Marshaller for the main rendering thread</param> /// <remarks> /// OpenGL works within a single thread, and any resources (textures, display lists,...) must /// be created and destroyed on the same thread. /// </remarks> public OpenGlRenderer( Thread mainRenderingThread, DelegateMarshaller mainRenderingThreadMarshaller ) : base(mainRenderingThread, mainRenderingThreadMarshaller) { for ( int stackIndex = 0; stackIndex < m_LocalToWorldModifierStack.Length; ++stackIndex ) { m_LocalToWorldModifierStack[ stackIndex ] = InvariantMatrix44.Identity; } for ( int stackIndex = 0; stackIndex < m_WorldToViewModifierStack.Length; ++stackIndex ) { m_WorldToViewModifierStack[ stackIndex ] = InvariantMatrix44.Identity; } for ( int stackIndex = 0; stackIndex < m_LocalToWorldStack.Length; ++stackIndex ) { m_LocalToWorldStack[ stackIndex ] = new Matrix44( ); } for ( int stackIndex = 0; stackIndex < m_WorldToViewStack.Length; ++stackIndex ) { m_WorldToViewStack[ stackIndex ] = new Matrix44( ); } }
/// <summary> /// Renders the entity /// </summary> /// <param name="context">Rendering context</param> public override void Render( IRenderContext context ) { // Get the interpolated position of the entity float t = ( float )( context.RenderTime - m_Position.LastStepTime ) / m_Position.LastStepInterval; Point3 curPos = m_Position.UpdateCurrent( t ); // TODO: Get the interpolated rotation of the entity // Push the entity transform Matrix44 mat = new Matrix44( curPos, Left, Up, Ahead ); Rb.Rendering.Graphics.Renderer.PushTransform( TransformType.LocalToWorld, mat ); base.Render( context ); // Pop the entity transform Rb.Rendering.Graphics.Renderer.PopTransform( TransformType.LocalToWorld ); }
// TODO: AP: Add rotation code /// <summary> /// Translation. Stores the result of this * T, where T is the translation matrix for (x,y,z) /// </summary> public void Translate( float x, float y, float z ) { // TODO: This is pretty lazy :) Matrix44 lhs = new Matrix44( this ); Matrix44 rhs = new Matrix44( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1 ); StoreMultiply( lhs, rhs ); }
/// <summary> /// Stores the result of multiplying this * rhs in this matrix /// </summary> public void StoreMultiply( Matrix44 rhs ) { // TODO: CHEATER! Matrix44 tmp = new Matrix44( this ); StoreMultiply( tmp, rhs ); }
/// <summary> /// Stores the inverse of mat (mat.mat'=I) in this matrix /// </summary> /// <remarks> /// The generic inverse of a matrix A is found by dividing the adjoint of A by the determinant of A. The adjoint B of a matrix A is /// defined by B=bij, where bij is the determinant of A with row i and column j removed (the co-factors of A). /// I was too lazy to fully expand the calculations this implies for a 4x4 matrix, so I grabbed and adapted the following code from /// http://www.geometrictools.com /// </remarks> public void StoreInverse( Matrix44 mat ) { MakeInverse( this, mat ); }
/// <summary> /// Returns true if the two matrices are equal, within a given tolerance per element /// </summary> public bool IsCloseTo( Matrix44 mat, float tol ) { for ( int index = 0; index < 16; ++index ) { if ( Math.Abs( this[ index ] - mat[ index ] ) > tol ) { return false; } } return true; }
/// <summary> /// Makes a matrix from a quaternion /// </summary> public static new Matrix44 MakeTranslationMatrix( Point3 pt ) { Matrix44 result = new Matrix44( ); MakeTranslationMatrix( result, pt.X, pt.Y, pt.Z ); return result; }
/// <summary> /// Stores the reflection of src in result /// </summary> public static new Matrix44 MakeReflectionMatrix( Point3 pointOnPlane, Vector3 planeNormal ) { Matrix44 result = new Matrix44( ); MakeReflectionMatrix( ( InvariantMatrix44 )result, pointOnPlane, planeNormal ); return result; }
/// <summary> /// Reads frame information /// </summary> private static void ReadFrames( BinaryReader reader, long offset, int numFrames, ModelMesh mesh, Matrix44 transform ) { reader.BaseStream.Seek( offset, SeekOrigin.Begin ); ModelMesh.FrameInfo[] frames = new ModelMesh.FrameInfo[ numFrames ]; float scaleLength = ( transform * new Vector3( 1, 1, 1 ) ).Length; for ( int frameCount = 0; frameCount < numFrames; ++frameCount ) { frames[ frameCount] = new ModelMesh.FrameInfo( ); ModelMesh.FrameInfo curFrame = frames[ frameCount ]; curFrame.MinBounds = transform * ReadPoint( reader ); curFrame.MaxBounds = transform * ReadPoint( reader ); curFrame.Origin = transform * ReadPoint( reader ); curFrame.Radius = reader.ReadSingle( ) * scaleLength; curFrame.Name = ReadString( reader, FrameNameLength ); } mesh.FrameInfoList = frames; }
private static void LoadObjectModel( Model model, ISource source, Matrix44 transform ) { // Load the skin file for the current part IDictionary< string, ITexture2d > textureTable = LoadSkin( source, DefaultSkinFile( source, ModelPart.Weapon ) ); // Load the MD3 associated with the current part ModelMesh partMesh = LoadMd3( source, model, ModelPart.Weapon, transform, source, textureTable ); model.SetPartMesh( ModelPart.Weapon, partMesh ); model.SetRootMesh( partMesh ); }
private static void LoadNestedModel( Model model, ISource source, Matrix44 transform ) { // Run through all the parts for ( int partIndex = 0; partIndex < ( int )ModelPart.NumParts; ++partIndex ) { ModelPart curPart = ( ModelPart )partIndex; if ( curPart == ModelPart.Weapon ) { // The weapon does not have a related mesh, so just ignore... continue; } // Load the skin file for the current part IDictionary< string, ITexture2d > surfaceTextureTable = LoadSkin( source, DefaultSkinFile( source, curPart ) ); // Load the MD3 associated with the current part ModelMesh partMesh = LoadMd3( source, model, curPart, transform, MeshFile( source, curPart ), surfaceTextureTable ); model.SetPartMesh( curPart, partMesh ); } model.SetRootMesh( model.GetPartMesh( ModelPart.Lower ) ); NestMesh( model, ModelPart.Lower, ModelPart.Upper, "tag_torso" ); NestMesh( model, ModelPart.Upper, ModelPart.Head, "tag_head" ); NestMesh( model, ModelPart.Upper, ModelPart.Weapon, "tag_weapon" ); }
/// <summary> /// Loads an MD3 mesh resource from a stream /// </summary> private static ModelMesh LoadMd3( ISource source, Model model, ModelPart part, Matrix44 transform, ISource md3Source, IDictionary<string, ITexture2d> surfaceTextureTable ) { using ( Stream inputStream = OpenStream( md3Source ) ) { BinaryReader reader = new BinaryReader( inputStream ); // http://icculus.org/homepages/phaethon/q3a/formats/md3format.html // Make sure of the MD3 identity byte[] ident = reader.ReadBytes( 4 ); if ( ( ident[ 0 ] != 'I' ) || ( ident[ 1 ] != 'D' ) || ( ident[ 2 ] != 'P' ) || ( ident[ 3 ] != '3' ) ) { throw new ApplicationException( "Failed to load MD3 resource - stream did not start with 'IDP3' MD3 identifier" ); } // Read in header //int version = reader.ReadInt32( ); //string name = ReadString( reader, MaxPathLength ); //int flags = reader.ReadInt32( ); int numFrames = reader.ReadInt32( ); int numTags = reader.ReadInt32( ); int numSurfaces = reader.ReadInt32( ); //int numSkins = reader.ReadInt32( ); int framesOffset = reader.ReadInt32( ); int tagsOffset = reader.ReadInt32( ); int surfacesOffset = reader.ReadInt32( ); //int eofOffset = reader.ReadInt32( ); // TODO: Can load directly into mesh frame, tag and surface structures - don't do this intermediate step ModelMesh mesh = new ModelMesh( model, part ); ReadFrames( reader, framesOffset, numFrames, mesh, transform ); ReadTags( reader, tagsOffset, numTags, numFrames, mesh, transform ); ReadSurfaces( source, reader, surfacesOffset, numSurfaces, numFrames, mesh, surfaceTextureTable, transform ); // TODO: REMOVE. test frames string md3Name = md3Source.ToString( ); if ( md3Name.IndexOf( "Upper" ) != -1 ) { mesh.DefaultFrame = 151; } else if ( md3Name.IndexOf( "Head" ) != -1 ) { mesh.DefaultFrame = 0; } else { mesh.DefaultFrame = 0; } return mesh; } }
/// <summary> /// Creates a quaternion from a matrix /// </summary> public static Quaternion FromMatrix( Matrix44 matrix ) { return new Quaternion( matrix ); }
/// <summary> /// Renders reflections on the first <see cref="ReflectionPlane"/> found in the scene /// </summary> private void RenderPlanarReflections( ReflectionsRenderContext reflectionsContext, ICamera3 camera ) { ReflectionPlane plane = ReflectionPlane; if ( plane == null ) { return; } // Matrix44 reflectionMatrix = Matrix44.MakeReflectionMatrix( new Point3( 0, -10, 0 ), new Vector3( 0, 1, 0 ) ); Matrix44 reflectionMatrix = plane.CreateReflectionMatrix( ); Matrix44 reflectedCameraMatrix = new Matrix44( camera.InverseFrame * reflectionMatrix ); // reflectedCameraMatrix.YAxis = -reflectedCameraMatrix.YAxis; // Restore handedness Graphics.Renderer.PushTransform( TransformType.WorldToView ); Graphics.Renderer.SetTransform( TransformType.WorldToView, reflectedCameraMatrix ); Graphics.Renderer.PushTransform( TransformType.ViewToScreen ); IProjectionCamera projCamera = ( IProjectionCamera )camera; Graphics.Renderer.SetPerspectiveProjectionTransform( projCamera.PerspectiveFovDegrees, 1, projCamera.PerspectiveZNear, projCamera.PerspectiveZFar ); m_ReflectionMatrixDataSource.Value = Graphics.Renderer.GetTransform( TransformType.ViewToScreen ) * reflectedCameraMatrix; // TODO: AP: Reflect camera position System.Drawing.Rectangle viewport = Graphics.Renderer.Viewport; Graphics.Renderer.SetViewport( 0, 0, m_Reflections.Width, m_Reflections.Height ); reflectionsContext.RenderingReflections = true; m_Reflections.Begin( ); Graphics.Renderer.ClearDepth( 1.0f ); Graphics.Renderer.ClearColour( Color.SteelBlue ); RenderSceneObjects( reflectionsContext ); m_Reflections.End( ); Graphics.Renderer.SetViewport( viewport.X, viewport.Y, viewport.Width, viewport.Height ); Graphics.Renderer.PopTransform( TransformType.WorldToView ); Graphics.Renderer.PopTransform( TransformType.ViewToScreen ); }
/// <summary> /// Makes a matrix from a quaternion /// </summary> public static new Matrix44 MakeQuaternionMatrix( Quaternion quaternion ) { Matrix44 result = new Matrix44( ); MakeQuaternionMatrix( ( InvariantMatrix44 )result, quaternion ); return result; }
/// <summary> /// Makes a matrix from a quaternion /// </summary> public static void MakeQuaternionMatrix( Matrix44 matrix, Quaternion quaternion ) { MakeQuaternionMatrix( ( InvariantMatrix44 )matrix, quaternion ); }
/// <summary> /// Reads surface information /// </summary> private static void ReadSurfaces( ISource source, BinaryReader reader, long offset, int numSurfaces, int numFrames, ModelMesh mesh, IDictionary<string, ITexture2d> surfaceTextureTable, Matrix44 transform) { // Move to the start of the first surface reader.BaseStream.Seek( offset, SeekOrigin.Begin ); // Allocate mesh surface array mesh.Surfaces = new ModelMesh.Surface[ numSurfaces ]; for ( int surfaceCount = 0; surfaceCount < numSurfaces; ++surfaceCount ) { // Create a new surface ModelMesh.Surface curSurface = new ModelMesh.Surface( ); //int ident = reader.ReadInt32( ); string name = ReadString ( reader, MaxPathLength ); //int flags = reader.ReadInt32( ); //int surfaceNumFrames = reader.ReadInt32( ); //int numShaders = reader.ReadInt32( ); int numVertices = reader.ReadInt32( ); int numTriangles = reader.ReadInt32( ); int trianglesOffset = reader.ReadInt32( ); //int shadersOffset = reader.ReadInt32( ); int texturesOffset = reader.ReadInt32( ); int verticesOffset = reader.ReadInt32( ); int endOffset = reader.ReadInt32( ); // Assign surface texture curSurface.Texture = GetTexture( source, surfaceTextureTable, name ); // Assign surface shaders // ReadShaders( reader, offset + shadersOffset, numShaders ); // Read in surface index group and texture UVs curSurface.Group = ReadTriangles( reader, offset + trianglesOffset, numTriangles ); curSurface.TextureUVs = ReadTextureUVs( reader, offset + texturesOffset, numVertices ); // Read in surface vertices // curSurface.NumVertices = numVertices; ReadVertices( reader, offset + verticesOffset, numVertices, numFrames, curSurface, transform ); // Assign the new surface to the mesh mesh.Surfaces[ surfaceCount ] = curSurface; // Move the stream to the next surface reader.BaseStream.Seek( offset + endOffset, SeekOrigin.Begin ); offset += endOffset; } }
/// <summary> /// Stores the reflection of src in result /// </summary> public static void MakeReflectionMatrix( Matrix44 result, Point3 pointOnPlane, Vector3 planeNormal ) { MakeReflectionMatrix( result, pointOnPlane, planeNormal ); }
/// <summary> /// Reads tag information /// </summary> private static void ReadTags( BinaryReader reader, long offset, int numTags, int numFrames, ModelMesh mesh, Matrix44 transform ) { reader.BaseStream.Seek( offset, SeekOrigin.Begin ); mesh.TagNames = new string[ numTags ]; for ( int frameIndex = 0; frameIndex < numFrames; ++frameIndex ) { ModelMesh.Tag[] tags = new ModelMesh.Tag[ numTags ]; for ( int tagCount = 0; tagCount < numTags; ++tagCount ) { string tagName = ReadString( reader, MaxPathLength ); if ( frameIndex == 0 ) { mesh.TagNames[ tagCount ] = tagName; } Point3 origin = transform * ReadPoint( reader ); Vector3 xAxis = ReadVector( reader ); Vector3 yAxis = ReadVector( reader ); Vector3 zAxis = ReadVector( reader ); ModelMesh.Tag curTag = new ModelMesh.Tag( ); curTag.Transform = new Matrix44( origin, xAxis, yAxis, zAxis ); curTag.Name = tagName; tags[ tagCount ] = curTag; } mesh.FrameInfoList[ frameIndex ].Tags = tags; } }
/// <summary> /// Creates an inverted copy of this matrix /// </summary> public new Matrix44 Invert( ) { Matrix44 result = new Matrix44( ); MakeInverse( result, this ); return result; }
/// <summary> /// Reads surface vertices (positions and normals) /// </summary> private static void ReadVertices( BinaryReader reader, long offset, int numVertices, int numFrames, ModelMesh.Surface surface, Matrix44 transform ) { reader.BaseStream.Seek( offset, SeekOrigin.Begin ); // Allocate surface frames surface.SurfaceFrames = new ModelMesh.SurfaceFrame[ numFrames ]; // Allocate temporary arrays for storing position and normal data float[] positions = new float[ numVertices * 3 ]; float[] normals = new float[ numVertices * 3 ]; // Run through all frames for ( int frameIndex = 0; frameIndex < numFrames; ++frameIndex ) { int positionIndex = 0; int normalIndex = 0; // Allocate a surface frame ModelMesh.SurfaceFrame frame = new ModelMesh.SurfaceFrame( ); // Run through all vertices for ( int vertexCount = 0; vertexCount < numVertices; ++vertexCount ) { // NOTE: Re-order coordinates. I use +ve Y as up, +ve X as right, +ve Z as into, MD3 default is +ve X as into, +ve Y as right, +ve Z as up Point3 pt = new Point3( ); pt.Z = reader.ReadInt16( ) * XyzScale; pt.X = reader.ReadInt16( ) * XyzScale; pt.Y = reader.ReadInt16( ) * XyzScale; pt = transform * pt; positions[ positionIndex + 0 ] = pt.X; positions[ positionIndex + 1 ] = pt.Y; positions[ positionIndex + 2 ] = pt.Z; positionIndex += 3; float s = reader.ReadByte( ) * ByteToAngle; float t = reader.ReadByte( ) * ByteToAngle; normals[ normalIndex + 2 ] = ( Functions.Cos( s ) * Functions.Sin( t ) ); normals[ normalIndex + 0 ] = ( Functions.Sin( s ) * Functions.Sin( t ) ); normals[ normalIndex + 1 ] = ( Functions.Cos( t ) ); normalIndex += 3; } // Convert position and normal data into vertex buffer objects frame.VertexBuffers = new OpenGlVertexArray[ 2 ]; frame.VertexBuffers[ 0 ] = new OpenGlVertexArray( numVertices, 0, Gl.GL_VERTEX_ARRAY, 0, 3, Gl.GL_STATIC_DRAW_ARB, positions ); frame.VertexBuffers[ 1 ] = new OpenGlVertexArray( numVertices, 0, Gl.GL_NORMAL_ARRAY, 0, 3, Gl.GL_STATIC_DRAW_ARB, normals ); // Assign the frame to the surface surface.SurfaceFrames[ frameIndex ] = frame; } }
/// <summary> /// Scales this matrix /// </summary> public void Scale( float x, float y, float z ) { // TODO: CHEAT! AGAIN! Matrix44 mat = new Matrix44( this ); Matrix44 scaleMat = new Matrix44( x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 ); StoreMultiply( mat, scaleMat ); }
/// <summary> /// Loads... stuff /// </summary> public override object Load( ISource source, LoadParameters parameters ) { parameters.CanCache = true; Vector3 scale = new Vector3 ( DynamicProperties.GetProperty( parameters.Properties, "scaleX", 1.0f ), DynamicProperties.GetProperty( parameters.Properties, "scaleY", 1.0f ), DynamicProperties.GetProperty( parameters.Properties, "scaleZ", 1.0f ) ); Matrix44 transform = new Matrix44( ); transform.Scale( scale.X, scale.Y, scale.Z ); // create the model Model model = new Model( ); // oh dear another md3 hack - directories mean articulated models, with skins and animations. Files // mean single objects if ( source is IFolder ) { // Load animations model.Animations = LoadAnimations( AnimationFile( source ) ); LoadNestedModel( model, source, transform ); } else { LoadObjectModel( model, source, transform ); } return model; }
/// <summary> /// Stores the inverse transpose of mat in this matrix. This is used to create a matrix that can be used to transform vertex normals /// </summary> public void StoreInverseTranspose( Matrix44 mat ) { }
/// <summary> /// Gets the current matrix from the specified transform stack /// </summary> public abstract void GetTransform( TransformType type, Matrix44 matrix );
/// <summary> /// Stores the transpose of mat in this matrix /// </summary> /// <param name="mat"></param> public void StoreTranspose( Matrix44 mat ) { for ( int row = 0; row < 4; ++row ) { for ( int col = 0; col < 4; ++col ) { this[ row, col ] = mat[ col, row ]; } } }
/// <summary> /// Multiplication operator /// </summary> public static Matrix44 operator *( Matrix44 lhs, Matrix44 rhs ) { Matrix44 mat = new Matrix44( ); mat.StoreMultiply( lhs, rhs ); return mat; }
/// <summary> /// Creates a transposed copy of this matrix /// </summary> public new Matrix44 Transpose( ) { Matrix44 result = new Matrix44( ); MakeTransposeMatrix( result, this ); return result; }
/// <summary> /// Makes the specified matrix an identity matrix /// </summary> public static void MakeIdentityMatrix( Matrix44 matrix ) { matrix.Set( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); }