/// <summary>
 /// Copy constructor. Creates a deep copy of another GModel.
 /// </summary>
 /// <param name="gm">GModel to clone.</param>
 public GModel(GModel gm)
 {
     model = gm.model;
     skinningData = gm.skinningData;
     Texture = gm.Texture;
     MaterialColors = gm.MaterialColors != null ? (Vector3[])gm.MaterialColors.Clone() : null;
     Emissive = gm.Emissive;
     EmissiveTexture = gm.EmissiveTexture;
     IsWaterfall = gm.IsWaterfall;
 }
        /// <summary>
        /// Draw forward-rendered model.
        /// </summary>
        /// <param name="model">Model to draw</param>
        /// <param name="position">Position</param>
        /// <param name="rotation">Rotation</param>
        /// <param name="scale">Scale (x, y, z)</param>
        /// <param name="enableShading">Enables textures and lighting</param>
        private void DrawForwardShadedModel(
			GModel gmodel,
			ref Vector3 position,
			ref Quaternion rotation,
			ref Vector3 scale,
			bool enableShading)
        {
            Model model = gmodel.model;

            // Calculate transforms
            Matrix world = Matrix.CreateScale(scale)
                            * Matrix.CreateFromQuaternion(rotation)
                            * Matrix.CreateTranslation(position);
            Matrix[] meshTransforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(meshTransforms); // get bone transforms

            // Render the mesh
            foreach (ModelMesh mesh in model.Meshes)
            {
                // Set effect data
                Matrix animatedWorld = meshTransforms[mesh.ParentBone.Index] * world;
                Matrix worldViewProjection = animatedWorld * camera.View * camera.Projection;

                forwardUnshadedEffect.Parameters["xDepthTex"].SetValue(deferredTargets[1].RenderTarget);
                forwardUnshadedEffect.Parameters["xWorld"].SetValue(animatedWorld);
                forwardUnshadedEffect.Parameters["xWorldViewProjection"].SetValue(worldViewProjection);

                // Draw
                forwardUnshadedEffect.CurrentTechnique.Passes[0].Apply(); // single-pass
                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    Device.Indices = part.IndexBuffer;
                    Device.SetVertexBuffer(part.VertexBuffer);
                    Device.DrawIndexedPrimitives(
                        PrimitiveType.TriangleList,
                        part.VertexOffset,
                        0,
                        part.NumVertices,
                        part.StartIndex,
                        part.PrimitiveCount);

                    // draw call count
                    nDrawCalls++;
                }
            }
        }
        /// <summary>
        /// Draws models, shaded or unshaded. Contains the actual drawing code.
        /// </summary>
        /// <param name="model">Model to draw</param>
        /// <param name="position">Position</param>
        /// <param name="rotation">Rotation</param>
        /// <param name="scale">Scale (x, y, z)</param>
        /// <param name="enableShading">Enables textures and lighting</param>
        private void DrawDeferredShadedModel(
			GModel gmodel,
			ref Vector3 position,
			ref Quaternion rotation,
			ref Vector3 scale,
			bool enableShading,
            Matrix[] bones)
        {
            Model model = gmodel.model;

            // Calculate transforms
            Matrix world = Matrix.CreateScale(scale)
                            * Matrix.CreateFromQuaternion(rotation)
                            * Matrix.CreateTranslation(position);
            Matrix[] meshTransforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(meshTransforms); // get bone transforms

            // Set effect
            Effect effect;
            if (model.Tag is SkinningData && bones != null)
            {
                deferredSkinnedEffect.CurrentTechnique = deferredSkinnedEffect.Techniques["DeferredSkinned"];
                effect = deferredSkinnedEffect;
                effect.Parameters["xBones"].SetValue(bones);
            }
            else if (gmodel.Texture != null && renderState == RenderState.Deferred)
            {
                effect = deferredTextureEffect;
                effect.Parameters["xTexture"].SetValue(gmodel.Texture);
            }
            else if (renderState == RenderState.Deferred)
            {
                deferredEffect.CurrentTechnique = deferredEffect.Techniques["Deferred"];
                effect = deferredEffect;
            }
            else // shadow
            {
                deferredEffect.CurrentTechnique = deferredEffect.Techniques["DeferredLightMap"];
                effect = deferredEffect;
            }

            // Emissive
            if (gmodel.EmissiveTexture != null)
            {
                effect.Parameters["xEnableEmissiveTexture"].SetValue(1);
                effect.Parameters["xEmissiveTex"].SetValue(gmodel.EmissiveTexture);
                effect.Parameters["xEmissive"].SetValue(gmodel.Emissive);
            }
            else
            {
                effect.Parameters["xEnableEmissiveTexture"].SetValue(0);
                effect.Parameters["xEmissive"].SetValue(gmodel.Emissive);
            }

            // Render the mesh
            int iMeshColor = 0;
            effect.Parameters["xWorld"].SetValue(world);
            foreach (ModelMesh mesh in model.Meshes)
            {
                if (bones != null)
                    effect.Parameters["xBone"].SetValue(bones[mesh.ParentBone.Index]);
                else
                    effect.Parameters["xBone"].SetValue(meshTransforms[mesh.ParentBone.Index]);
                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    if (gmodel.MaterialColors != null)
                        effect.Parameters["xColor"].SetValue(gmodel.MaterialColors[iMeshColor++]);
                    effect.CurrentTechnique.Passes[0].Apply(); // Deferred and DeferredTextured are both single-pass

                    Device.Indices = part.IndexBuffer;
                    Device.SetVertexBuffer(part.VertexBuffer);
                    Device.DrawIndexedPrimitives(
                        PrimitiveType.TriangleList,
                        part.VertexOffset,
                        0,
                        part.NumVertices,
                        part.StartIndex,
                        part.PrimitiveCount);

                    // draw call count
                    nDrawCalls++;
                }
            }
        }
        /// <summary>
        /// Loads a model from a file and places it in the models dictionary.
        /// </summary>
        /// <param name="fileName">Path to model</param>
        /// <param name="texture">Texture override (null for default)</param>
        /// <param name="emissiveTexture">Emissive texture (or null)</param>
        protected override GModel LoadModel(string fileName, string texture, string emissiveTexture)
        {
            GModel gmodel;

            // Check for pre-existence
            List<GModel> modelList;
            if (models.ContainsKey(fileName))
            {
                modelList = models[fileName];

                // Find GModel if it exists
                foreach (GModel listModel in modelList)
                {
                    if (listModel.TexturePath == texture && listModel.EmissivePath == emissiveTexture)
                    {
                        return listModel;
                    }
                }

                // Didn't find, copy an existing model
                gmodel = new GModel(modelList[0]);
            }
            else
            {
                gmodel = new GModel();
                modelList = new List<GModel>();
                modelList.Add(gmodel);
                models.Add(fileName, modelList);

                // Load model
                gmodel.model = content.Load<Model>(fileName);
                gmodel.Texture = null;

                //Load skinning and animation (null if none)
                gmodel.skinningData = gmodel.model.Tag as SkinningData;
            }

            // Assign texture if one is provided
            if (texture != null)
            {
                gmodel.Texture = content.Load<Texture2D>(texture);
                gmodel.MaterialColors = null;
            }

            // Else, check for materials and textures
            LinkedList<Vector3> colors = new LinkedList<Vector3>();
            foreach (ModelMesh mesh in gmodel.model.Meshes)
            {
                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    BasicEffect be = part.Effect as BasicEffect;
                    if (be != null && be.TextureEnabled)
                    {
                        gmodel.Texture = be.Texture; // only one texture supported
                    }
                    else
                    {
                        colors.AddLast(be != null ? be.DiffuseColor : Vector3.One);
                    }
                }
            }

            gmodel.MaterialColors = gmodel.Texture == null ? colors.ToArray() : null;
            gmodel.EmissiveTexture = null;
            if (emissiveTexture != null)
                gmodel.EmissiveTexture = content.Load<Texture2D>(emissiveTexture);
            gmodel.Emissive = 0;

            // HACK: workaround for the lack of a materials system
            gmodel.IsWaterfall = (fileName == @"MapObjects\RttT\models\waterfall-1");

            return gmodel;
        }
        /// <summary>
        /// Draws a character's shield.
        /// </summary>
        /// <param name="shieldModel">Model</param>
        /// <param name="position">Center position</param>
        /// <param name="scale">Scale</param>
        public override void DrawShield(GModel shieldModel, Vector3 position, float scale)
        {
            // Rasterizer state settings
            if (Device.RasterizerState.FillMode != FillMode.Solid)
            {
                RasterizerState rs = new RasterizerState();
                rs.FillMode = FillMode.Solid;
                rs.CullMode = CullMode.CullCounterClockwiseFace;
                Device.RasterizerState = rs;
            }

            Device.BlendState = BlendState.Additive;

            // Parameters
            Matrix world = Matrix.CreateScale(scale) * Matrix.CreateTranslation(position);
            shieldEffect.Parameters["xWorld"].SetValue(world);
            shieldEffect.Parameters["xShieldTex"].SetValue(shieldModel.Texture);

            // Draw
            shieldEffect.CurrentTechnique.Passes[0].Apply(); // single-pass
            foreach (ModelMeshPart part in shieldModel.model.Meshes[0].MeshParts)
            {
                Device.Indices = part.IndexBuffer;
                Device.SetVertexBuffer(part.VertexBuffer);
                Device.DrawIndexedPrimitives(
                    PrimitiveType.TriangleList,
                    part.VertexOffset,
                    0,
                    part.NumVertices,
                    part.StartIndex,
                    part.PrimitiveCount);

                // draw call count
                nDrawCalls++;
            }
        }
        /// <summary>
        /// Draws a model with deferred shading.
        /// </summary>
        /// <param name="gmodel">Model info</param>
        /// <param name="position">Position</param>
        /// <param name="rotation">Rotation</param>
        /// <param name="scale">Scale</param>
        /// <param name="bones">Animation data.</param>
        public override void DrawModel(GModel gmodel, Vector3 position, Quaternion rotation, Vector3 scale, Matrix[] bones)
        {
            // Rasterizer state settings
            if (renderState == RenderState.Deferred && Device.RasterizerState.FillMode != FillMode.Solid)
            {
                RasterizerState rs = new RasterizerState();
                rs.FillMode = FillMode.Solid;
                rs.CullMode = CullMode.CullCounterClockwiseFace;
                Device.RasterizerState = rs;
            }

            DrawDeferredShadedModel(gmodel, ref position, ref rotation, ref scale, true, bones);
        }
 /// <summary>
 /// Draws a model with deferred shading.
 /// </summary>
 /// <param name="gmodel">Model info</param>
 /// <param name="position">Position</param>
 /// <param name="rotation">Rotation</param>
 /// <param name="scale">Scale</param>
 /// <param name="bones">Animation data.</param>
 public override void DrawModel(GModel gmodel, Vector3 position, Quaternion rotation, float scale, Matrix[] bones)
 {
     DrawModel(gmodel, position, rotation, new Vector3(scale), bones);
 }
 /// <summary>
 /// Draws a model with deferred shading.
 /// </summary>
 /// <param name="model">Model</param>
 /// <param name="position">Position</param>
 /// <param name="rotation">Model rotation</param>
 public override void DrawModel(GModel gmodel, Vector3 position, Quaternion rotation, float scale)
 {
     DrawModel(gmodel, position, rotation, new Vector3(scale), null);
 }
        /// <summary>
        /// Uses geometry instancing to draw several models. Models must have one
        /// ModelMesh with a single ModelMeshPart.
        /// </summary>
        /// <param name="model">Model</param>
        /// <param name="transforms">Transform data</param>
        /// <param name="nPlatforms">Number of transforms</param>
        public override void DrawInstancedPlatforms(GModel gmodel, Matrix[] transforms, int nPlatforms)
        {
            // The single mesh part being drawn.
            ModelMeshPart meshPart = gmodel.model.Meshes[0].MeshParts[0];

            if (instanceBuffer == null || instanceBuffer.VertexCount < nPlatforms)
            {
                if (instanceBuffer != null)
                    instanceBuffer.Dispose();

                instanceBuffer = new DynamicVertexBuffer(Device, instanceDeclaration,
                                                         nPlatforms, BufferUsage.WriteOnly);
            }

            // Draw model
            Device.RasterizerState = RasterizerState.CullCounterClockwise;

            // Upload instance transform data
            Device.SetVertexBuffers();
            instanceBuffer.SetData(transforms, 0, nPlatforms, SetDataOptions.Discard);
            Device.SetVertexBuffers(
                new VertexBufferBinding(meshPart.VertexBuffer),
                new VertexBufferBinding(instanceBuffer, 0, 1)
            );
            Device.Indices = meshPart.IndexBuffer;

            if (gmodel.Texture != null && renderState == RenderState.Deferred)
            {
                instanceEffect.CurrentTechnique = instanceEffect.Techniques["DeferredInstancedTextured"];
                instanceEffect.Parameters["xTexture"].SetValue(gmodel.Texture);
            }
            else if (renderState == RenderState.Deferred)
            {
                instanceEffect.CurrentTechnique = instanceEffect.Techniques["DeferredInstanced"];
                instanceEffect.Parameters["xColor"].SetValue(gmodel.MaterialColors[0]);
            }
            else
            {
                instanceEffect.CurrentTechnique = instanceEffect.Techniques["DeferredInstancedLight"];
            }

            // Emissive
            if (gmodel.EmissiveTexture != null)
            {
                instanceEffect.Parameters["xEnableEmissiveTexture"].SetValue(1);
                instanceEffect.Parameters["xEmissiveTex"].SetValue(gmodel.EmissiveTexture);
                instanceEffect.Parameters["xEmissive"].SetValue(gmodel.Emissive);
            }
            else
            {
                instanceEffect.Parameters["xEnableEmissiveTexture"].SetValue(0);
                instanceEffect.Parameters["xEmissive"].SetValue(gmodel.Emissive);
            }

            instanceEffect.Parameters["xTextureOffset"].SetValue(
                gmodel.IsWaterfall ? waterfallTimer / waterfallPeriod : 0);

            instanceEffect.CurrentTechnique.Passes[0].Apply();

            Device.DrawInstancedPrimitives(
                PrimitiveType.TriangleList, 0, 0,
                meshPart.NumVertices, meshPart.StartIndex,
                meshPart.PrimitiveCount, nPlatforms);
        }
 /// <summary>
 /// Draws a character's shield.
 /// </summary>
 /// <param name="shieldModel">Model</param>
 /// <param name="position">Center position</param>
 /// <param name="scale">Scale</param>
 public abstract void DrawShield(GModel shieldModel, Vector3 position, float scale);
 /// <summary>
 /// Draws a model with a basic shader.
 /// </summary>
 /// <param name="model">Model</param>
 /// <param name="position">Position</param>
 /// <param name="rotation">Model rotation</param>
 /// <param name="scale">Model scaling</param>
 public abstract void DrawModel(GModel gmodel, Vector3 position, Quaternion rotation, Vector3 scale, Matrix[] bones);
 /// <summary>
 /// Draws a model with a basic shader.
 /// </summary>
 /// <param name="model">Model</param>
 /// <param name="position">Position</param>
 /// <param name="rotation">Model rotation</param>
 public abstract void DrawModel(GModel gmodel, Vector3 position, Quaternion rotation, float scale);
 /// <summary>
 /// Uses geometry instancing to draw several models. Models must have one
 /// ModelMesh with a single ModelMeshPart.
 /// </summary>
 /// <param name="model">Model</param>
 /// <param name="transforms">Transform data</param>
 /// <param name="nPlatforms">Number of transforms</param>
 public abstract void DrawInstancedPlatforms(GModel model, Matrix[] transforms, int nPlatforms);