#region File Description
//-----------------------------------------------------------------------------
// GameModel.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using RobotGameData.Render;
using RobotGameData.Resource;
using RobotGameData.Collision;
#endregion
namespace RobotGameData.GameObject
{
///
/// It contains and processes the XNA’s “Model” variable.
///
public class GameModel : GameSceneNode
{
#region Fields
ModelData modelData = null;
Vector3 veclocity = Vector3.Zero;
Matrix rotateMatrix = Matrix.Identity;
Matrix[] boneTransforms = null;
ModelBone rootBone = null;
RenderLighting[] lighting = null;
RenderMaterial material = null;
bool activeLighting = false;
bool activeFog = false;
bool enableCulling = false;
CollideElement modelCollide = null;
BoundingSphere cullingSphere = new BoundingSphere();
Vector3 cullingSphereLocalCenter = Vector3.Zero;
bool alphaTestEnable = false;
bool alphaBlendEnable = false;
CompareFunction alphaFunction = CompareFunction.Always;
Blend sourceBlend = Blend.One;
Blend destinationBlend = Blend.Zero;
BlendFunction blendFunction = BlendFunction.Add;
int referenceAlpha = 0;
bool depthBufferEnable = true;
bool depthBufferWriteEnable = true;
CompareFunction depthBufferFunction = CompareFunction.LessEqual;
CullMode cullMode = CullMode.CullCounterClockwiseFace;
#endregion
#region Events
public class RenderingCustomEffectEventArgs : EventArgs
{
private RenderTracer renderTracer;
public RenderTracer RenderTracer
{
get { return renderTracer; }
}
private ModelMesh mesh;
public ModelMesh Mesh
{
get { return mesh; }
}
private Effect effect;
public Effect Effect
{
get { return effect; }
}
private Matrix world;
public Matrix World
{
get { return world; }
}
public RenderingCustomEffectEventArgs(RenderTracer renderTracer,
ModelMesh mesh, Effect effect, Matrix world)
: base()
{
this.renderTracer = renderTracer;
this.mesh = mesh;
this.effect = effect;
this.world = world;
}
}
public event EventHandler RenderingCustomEffect;
#endregion
#region Properties
public ModelData ModelData
{
get { return modelData; }
protected set { modelData = value; }
}
public Matrix[] BoneTransforms
{
get { return boneTransforms; }
protected set { boneTransforms = value; }
}
public ModelBone RootBone
{
get { return rootBone; }
protected set { rootBone = value; }
}
public CollideElement Collide
{
get { return modelCollide; }
protected set { modelCollide = value; }
}
public bool EnableCulling
{
get { return enableCulling; }
set { enableCulling = value; }
}
public bool ActiveLighting
{
get { return activeLighting; }
set { activeLighting = value; }
}
public bool ActiveFog
{
get { return activeFog; }
set { activeFog = value; }
}
public Vector3 Velocity
{
get { return this.veclocity; }
protected set { this.veclocity = value; }
}
public RenderLighting[] Lighting
{
get { return lighting; }
protected set { lighting = value; }
}
public RenderMaterial Material
{
get { return material; }
set { material = value; }
}
public bool AlphaTestEnable
{
get { return alphaTestEnable; }
set { alphaTestEnable = value; }
}
public bool AlphaBlendEnable
{
get { return alphaBlendEnable; }
set { alphaBlendEnable = value; }
}
public int ReferenceAlpha
{
get { return referenceAlpha; }
set { referenceAlpha = value; }
}
public CompareFunction AlphaFunction
{
get { return alphaFunction; }
set { alphaFunction = value; }
}
public bool DepthBufferEnable
{
get { return depthBufferEnable; }
set { depthBufferEnable = value; }
}
public bool DepthBufferWriteEnable
{
get { return depthBufferWriteEnable; }
set { depthBufferWriteEnable = value; }
}
public CompareFunction DepthBufferFunction
{
get { return depthBufferFunction; }
set { depthBufferFunction = value; }
}
public Blend SourceBlend
{
get { return sourceBlend; }
set { sourceBlend = value; }
}
public Blend DestinationBlend
{
get { return destinationBlend; }
set { destinationBlend = value; }
}
public BlendFunction BlendFunction
{
get { return blendFunction; }
set { blendFunction = value; }
}
public CullMode CullMode
{
get { return cullMode; }
set { cullMode = value; }
}
#endregion
///
/// Constructor.
///
/// model resource
public GameModel(GameResourceModel resource)
: base()
{
if (resource == null)
throw new ArgumentNullException("resource");
BindModel(resource.ModelData);
}
public GameModel(string fileName)
: base()
{
LoadModel(fileName);
}
protected override void OnUpdate(GameTime gameTime)
{
// model moves to the position
if (this.veclocity != Vector3.Zero)
{
Vector3 velocityPerFrame =
CalculateVelocityPerFrame(gameTime, this.veclocity);
AddPosition(velocityPerFrame);
}
// Updates collision mesh
if (modelCollide != null)
{
modelCollide.Transform(TransformedMatrix);
}
// If Animated bones
if (this is GameAnimateModel)
{
// If this is root bone,
// the world transformed matrix weight with only root bone
this.ModelData.model.Root.Transform *= this.TransformedMatrix;
}
// If no animated bones (static bones)
else
{ // Set the world matrix as the root transform of the model.
this.ModelData.model.Root.Transform = this.TransformedMatrix;
}
// Look up combined bone matrices for the entire the model.
this.ModelData.model.CopyAbsoluteBoneTransformsTo(this.boneTransforms);
}
protected override void OnDraw(RenderTracer renderTracer)
{
RenderState renderState = renderTracer.Device.RenderState;
// Transform culling sphere
if (enableCulling )
{
cullingSphere.Center = Vector3.Transform(cullingSphereLocalCenter,
RootAxis * Collide.TransformMatrix);
// Check if the the model is contained inside or intersecting
// with the frustum.
ContainmentType cullingResult =
renderTracer.Frustum.Contains(cullingSphere);
// Draw a the model If inside in the frustum
if (cullingResult == ContainmentType.Disjoint)
return;
}
renderState.AlphaTestEnable = alphaTestEnable;
renderState.AlphaBlendEnable = alphaBlendEnable;
renderState.AlphaFunction = alphaFunction;
renderState.SourceBlend = sourceBlend;
renderState.DestinationBlend = destinationBlend;
renderState.BlendFunction = blendFunction;
renderState.ReferenceAlpha = referenceAlpha;
renderState.DepthBufferEnable = depthBufferEnable;
renderState.DepthBufferWriteEnable = depthBufferWriteEnable;
renderState.DepthBufferFunction = depthBufferFunction;
renderState.CullMode = cullMode;
// Draw the model.
for( int i = 0; i < ModelData.model.Meshes.Count; i++)
{
ModelMesh mesh = ModelData.model.Meshes[i];
for (int j = 0; j < mesh.Effects.Count; j++)
{
// call a entried custom effect processing
if (RenderingCustomEffect != null)
{
// Shader custom processing
RenderingCustomEffect(this,
new RenderingCustomEffectEventArgs(renderTracer, mesh,
mesh.Effects[j], BoneTransforms[mesh.ParentBone.Index]));
}
else if (mesh.Effects[j] is BasicEffect)
{
BasicEffect effect = (BasicEffect)mesh.Effects[j];
// Apply fog
if (renderTracer.Fog != null && ActiveFog )
{
RenderFog fog = renderTracer.Fog;
effect.FogEnabled = fog.enabled;
if (effect.FogEnabled )
{
effect.FogStart = fog.start;
effect.FogEnd = fog.end;
effect.FogColor = fog.color.ToVector3();
}
}
else
{
effect.FogEnabled = false;
}
if (ActiveLighting )
{
// Apply lighting
if (renderTracer.Lighting != null)
{
RenderLighting lighting = renderTracer.Lighting;
effect.LightingEnabled = lighting.enabled;
if (effect.LightingEnabled )
{
effect.AmbientLightColor =
lighting.ambientColor.ToVector3();
effect.DirectionalLight0.Enabled = true;
effect.DirectionalLight0.Direction =
lighting.direction;
effect.DirectionalLight0.DiffuseColor =
lighting.diffuseColor.ToVector3();
effect.DirectionalLight0.SpecularColor =
lighting.specularColor.ToVector3();
}
}
if (Lighting != null)
{
effect.LightingEnabled = true;
for (int cnt = 0; cnt < Lighting.Length; cnt++)
{
RenderLighting lighting = Lighting[cnt];
BasicDirectionalLight basicLight = null;
if (cnt == 0)
basicLight = effect.DirectionalLight1;
else if (cnt == 1)
basicLight = effect.DirectionalLight2;
else
continue;
if (lighting.enabled )
{
basicLight.Enabled = true;
basicLight.Direction =
lighting.direction;
basicLight.DiffuseColor =
lighting.diffuseColor.ToVector3();
basicLight.SpecularColor =
lighting.specularColor.ToVector3();
}
else
{
basicLight.Enabled = false;
}
}
}
if (renderTracer.Lighting == null && Lighting == null)
{
effect.LightingEnabled = false;
}
// Apply material
if (Material != null)
{
effect.Alpha = Material.alpha;
effect.DiffuseColor =
Material.diffuseColor.ToVector3();
effect.SpecularColor =
Material.specularColor.ToVector3();
effect.SpecularPower =
Material.specularPower;
effect.EmissiveColor =
Material.emissiveColor.ToVector3();
effect.VertexColorEnabled =
Material.vertexColorEnabled;
effect.PreferPerPixelLighting =
Material.preferPerPixelLighting;
}
}
else
{
effect.LightingEnabled = false;
}
// Apply transform
effect.World = BoneTransforms[mesh.ParentBone.Index];
effect.View = renderTracer.View;
effect.Projection = renderTracer.Projection;
}
}
mesh.Draw();
}
}
protected override void OnReset()
{
this.veclocity = Vector3.Zero;
// Reset the bone's transform by source transform
this.ModelData.model.CopyBoneTransformsFrom(this.ModelData.boneTransforms);
// Set the world matrix as the root transform of the model.
ModelData.model.Root.Transform = Matrix.Identity;
// Look up combined bone matrices for the entire the model.
ModelData.model.CopyAbsoluteBoneTransformsTo(this.boneTransforms);
base.OnReset();
}
public void LoadModel(string modelFileName)
{
// First, Find the model resource from ResourceManager by key
GameResourceModel resource =
FrameworkCore.ResourceManager.GetModel(modelFileName);
if (resource == null)
{
// Load the model.
FrameworkCore.ResourceManager.LoadContent(modelFileName,
modelFileName);
resource = FrameworkCore.ResourceManager.GetModel(modelFileName);
}
// Load and find resource failed.
if (resource == null)
{
throw new ArgumentException("Cannot load the model : " +
modelFileName);
}
BindModel(resource.ModelData);
}
public virtual void BindModel(ModelData modelData)
{
this.ModelData = modelData;
this.rootBone = modelData.model.Root;
// Set to bone transform matrix
this.boneTransforms = new Matrix[this.ModelData.model.Bones.Count];
this.ModelData.model.CopyAbsoluteBoneTransformsTo(this.boneTransforms);
// Compute the bounding sphere of the ModelData.
cullingSphere = new BoundingSphere();
for (int i = 0; i < this.ModelData.model.Meshes.Count; i++)
{
ModelMesh mesh = this.ModelData.model.Meshes[i];
cullingSphere = BoundingSphere.CreateMerged(cullingSphere,
mesh.BoundingSphere);
}
cullingSphereLocalCenter = cullingSphere.Center;
}
public void Move(Vector3 velocity)
{
this.veclocity = velocity;
}
public void MoveStop()
{
this.veclocity = Vector3.Zero;
}
public Vector3 CalculateVelocity(Vector3 velocity)
{
Vector3 v = Vector3.Zero;
if (velocity.Z != 0.0f)
{
v += Direction * velocity.Z;
}
if (velocity.X != 0.0f)
{
v += Right * velocity.X;
}
if (velocity.Y != 0.0f)
{
v += Up * velocity.Y;
}
return v;
}
public Vector3 CalculateVelocityPerFrame(GameTime gameTime, Vector3 velocity)
{
return CalculateVelocity(velocity) *
(float)gameTime.ElapsedGameTime.TotalSeconds;
}
public void Rotate(Vector2 rotationAmount)
{
// Scale rotation amount to radians per second
rotationAmount *= (float)FrameworkCore.ElapsedDeltaTime.TotalSeconds;
// Create rotation matrix from rotation amount
rotateMatrix = Matrix.CreateFromAxisAngle(
Right,
MathHelper.ToRadians(rotationAmount.Y)) *
Matrix.CreateRotationY(
MathHelper.ToRadians(rotationAmount.X));
// Rotate orientation vectors
Direction = Vector3.TransformNormal(Direction, rotateMatrix);
Up = Vector3.TransformNormal(Up, rotateMatrix);
// Re-normalize orientation vectors
Direction.Normalize();
Up.Normalize();
// Re-calculate Right
Right = Vector3.Cross(Direction, Up);
// The same instability may cause the 3 orientation vectors may
// also diverge. Either the Up or Direction vector needs to be
// re-computed with a cross product to ensure orthagonality
Up = Vector3.Cross(Right, Direction);
}
public void DumpTrace()
{
System.Diagnostics.Debug.WriteLine(WorldTransform.ToString());
}
public void SetRootAxis(Matrix rotation)
{
RootAxis = rotation;
}
public void SetPosition(Vector3 position)
{
Position = position;
}
public void SetPosition(float x, float y, float z)
{
Position = new Vector3(x, y, z);
}
public void AddPosition(Vector3 position)
{
Position += position;
}
public void AddPosition(float x, float y, float z)
{
Position += new Vector3(x, y, z);
}
public Matrix SetRotationX(float angle)
{
Vector3 position = Position;
WorldTransform = Matrix.CreateRotationX(MathHelper.ToRadians(angle));
Position = position;
return WorldTransform;
}
public Matrix SetRotationY(float angle)
{
Vector3 position = Position;
WorldTransform = Matrix.CreateRotationY(MathHelper.ToRadians(angle));
Position = position;
return WorldTransform;
}
public Matrix SetRotationZ(float angle)
{
Vector3 position = Position;
WorldTransform = Matrix.CreateRotationZ(MathHelper.ToRadians(angle));
Position = position;
return WorldTransform;
}
public Matrix AddRotationX(float angle)
{
WorldTransform += Matrix.CreateRotationX(MathHelper.ToRadians(angle));
return WorldTransform;
}
public Matrix AddRotationY(float angle)
{
WorldTransform += Matrix.CreateRotationY(MathHelper.ToRadians(angle));
return WorldTransform;
}
public Matrix AddRotationZ(float angle)
{
WorldTransform += Matrix.CreateRotationZ(MathHelper.ToRadians(angle));
return WorldTransform;
}
public Matrix SetRotationAxis(Vector3 axis, float angle)
{
Vector3 position = Position;
WorldTransform =
Matrix.CreateFromAxisAngle(axis, MathHelper.ToRadians(angle));
Position = position;
return WorldTransform;
}
public Matrix AddRotationAxis(Vector3 axis, float angle)
{
Vector3 position = Position;
WorldTransform +=
Matrix.CreateFromAxisAngle(axis, MathHelper.ToRadians(angle));
Position = position;
return WorldTransform;
}
public void SetCollide(CollideElement collide)
{
modelCollide = collide;
modelCollide.Name = Name + "_Collide";
modelCollide.Owner = (object)this;
}
public Vector3 GetMoveAt(Vector3 velocity)
{
Vector3 v = Vector3.Zero;
if (velocity.Z != 0.0f)
{
v += Direction * velocity.Z;
}
if (velocity.X != 0.0f)
{
v += Right * velocity.X;
}
if (velocity.Y != 0.0f)
{
v += Up * velocity.Y;
}
if (v == Vector3.Zero)
return Vector3.Zero;
else
return Vector3.Normalize(v);
}
}
}