绘制几何类型
当绘制3D几何对象时,你首先需要使用primitives(图元)定义它的形状。Primitives (图元)是可以被XNA绘制的最基本的对象,最常被使用的图元是三角形。任何形状,包括圆,如果圆的数量足够多的话,都能用来表示三角形。XNA Framework可以将点,线、三角形作为图元绘制。
XNA允许你定义这些图元的所有3D坐标。当你调用DrawUserPrimitives方法时,只要你提供正确的观察矩阵和投影矩阵,XNA会自动将这些3D坐标转换为对应的屏幕坐标。
XNA中有很多现成的结构来储存常见顶点类型,比如:VertexPositionColor, VertexPositionNormalTexture,VertexPositionTexture等结构。你也可以定义新的顶点类型。 目前,简单的VertexPositionColor类型就能满足需要,正如它的名字所示,这种顶点格式包含顶点的位置和颜色信息。位置是包含三个分量的矢量,定义顶点在3D空间中的坐标。XNA有自己的颜色系统,所以不需要使用System.Draw中的Color类。
要将3D坐标转换为屏幕上的像素位置,XNA需要知道相机的位置(存储在观察矩阵中)和关于相机镜头
的某些细节(存储在投影矩阵中)。相机系统
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
namespace xnaDraw_test
{
class QuakeCamera
{
Matrix viewMatrix;
Matrix projectionMatrix;
Viewport viewPort;
float leftrightRot;
float updownRot;
const float rotationSpeed = 0.005f ;
Vector3 cameraPosition;
MouseState originalMouseState;
public QuakeCamera(Viewport viewPort)
: this (viewPort, new Vector3( 0 , 1 , 15 ), 0 , 0 )
{
// calls the constructor below with default startingPos and rotation values
}
public QuakeCamera(Viewport viewPort, Vector3 startingPos, float lrRot, float udRot)
{
this .leftrightRot = lrRot;
this .updownRot = udRot;
this .cameraPosition = startingPos;
this .viewPort = viewPort;
float viewAngle = MathHelper.PiOver4;
float nearPlane = 0.5f ;
float farPlane = 1000.0f ;
projectionMatrix = Matrix.CreatePerspectiveFieldOfView(viewAngle, viewPort.AspectRatio, nearPlane, farPlane);
UpdateViewMatrix();
#if XBOX
#else
Mouse.SetPosition(viewPort.Width / 2 , viewPort.Height / 2 );
originalMouseState = Mouse.GetState();
#endif
}
public void Update(MouseState currentMouseState, KeyboardState keyState, GamePadState gamePadState)
{
if (currentMouseState != originalMouseState)
{
float xDifference = currentMouseState.X - originalMouseState.X;
float yDifference = currentMouseState.Y - originalMouseState.Y;
leftrightRot -= rotationSpeed * xDifference;
updownRot -= rotationSpeed * yDifference;
Mouse.SetPosition(viewPort.Width / 2 , viewPort.Height / 2 );
UpdateViewMatrix();
}
if (keyState.IsKeyDown(Keys.Up) || keyState.IsKeyDown(Keys.W)) // Forward
AddToCameraPosition( new Vector3( 0 , 0 , - 1 ));
if (keyState.IsKeyDown(Keys.Down) || keyState.IsKeyDown(Keys.S)) // Backward
AddToCameraPosition( new Vector3( 0 , 0 , 1 ));
if (keyState.IsKeyDown(Keys.Right) || keyState.IsKeyDown(Keys.D)) // Right
AddToCameraPosition( new Vector3( 1 , 0 , 0 ));
if (keyState.IsKeyDown(Keys.Left) || keyState.IsKeyDown(Keys.A)) // Left
AddToCameraPosition( new Vector3( - 1 , 0 , 0 ));
if (keyState.IsKeyDown(Keys.Q)) // Up
AddToCameraPosition( new Vector3( 0 , 1 , 0 ));
if (keyState.IsKeyDown(Keys.Z)) // Down
AddToCameraPosition( new Vector3( 0 , - 1 , 0 ));
}
private void AddToCameraPosition(Vector3 vectorToAdd)
{
float moveSpeed = 0.5f ;
Matrix cameraRotation = Matrix.CreateRotationX(updownRot) * Matrix.CreateRotationY(leftrightRot);
Vector3 rotatedVector = Vector3.Transform(vectorToAdd, cameraRotation);
cameraPosition += moveSpeed * rotatedVector;
UpdateViewMatrix();
}
private void UpdateViewMatrix()
{
Matrix cameraRotation = Matrix.CreateRotationX(updownRot) * Matrix.CreateRotationY(leftrightRot);
Vector3 cameraOriginalTarget = new Vector3( 0 , 0 , - 1 );
Vector3 cameraOriginalUpVector = new Vector3( 0 , 1 , 0 );
Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation);
Vector3 cameraFinalTarget = cameraPosition + cameraRotatedTarget;
Vector3 cameraRotatedUpVector = Vector3.Transform(cameraOriginalUpVector, cameraRotation);
Vector3 cameraFinalUpVector = cameraPosition + cameraRotatedUpVector;
viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraFinalTarget, cameraRotatedUpVector);
}
public float UpDownRot
{
get { return updownRot; }
set { updownRot = value; }
}
public float LeftRightRot
{
get { return leftrightRot; }
set { leftrightRot = value; }
}
public Matrix ProjectionMatrix
{
get { return projectionMatrix; }
}
public Matrix ViewMatrix
{
get { return viewMatrix; }
}
public Vector3 Position
{
get { return cameraPosition; }
set
{
cameraPosition = value;
UpdateViewMatrix();
}
}
public Vector3 TargetPosition
{
get
{
Matrix cameraRotation = Matrix.CreateRotationX(updownRot) * Matrix.CreateRotationY(leftrightRot);
Vector3 cameraOriginalTarget = new Vector3( 0 , 0 , - 1 );
Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation);
Vector3 cameraFinalTarget = cameraPosition + cameraRotatedTarget;
return cameraFinalTarget;
}
}
public Vector3 Forward
{
get
{
Matrix cameraRotation = Matrix.CreateRotationX(updownRot) * Matrix.CreateRotationY(leftrightRot);
Vector3 cameraForward = new Vector3( 0 , 0 , - 1 );
Vector3 cameraRotatedForward = Vector3.Transform(cameraForward, cameraRotation);
return cameraRotatedForward;
}
}
public Vector3 SideVector
{
get
{
Matrix cameraRotation = Matrix.CreateRotationX(updownRot) * Matrix.CreateRotationY(leftrightRot);
Vector3 cameraOriginalSide = new Vector3( 1 , 0 , 0 );
Vector3 cameraRotatedSide = Vector3.Transform(cameraOriginalSide, cameraRotation);
return cameraRotatedSide;
}
}
public Vector3 UpVector
{
get
{
Matrix cameraRotation = Matrix.CreateRotationX(updownRot) * Matrix.CreateRotationY(leftrightRot);
Vector3 cameraOriginalUp = new Vector3( 0 , 1 , 0 );
Vector3 cameraRotatedUp = Vector3.Transform(cameraOriginalUp, cameraRotation);
return cameraRotatedUp;
}
}
}
}
自定义顶点类型
当XNA中的预定义顶点类型不能满足要求时,可以手动创建结构来保存顶点数据。假设我们希望顶点中包含位置,纹理坐标,法线和切线信息,把这个结构称为VertexPosTexNorTan,它只是一个简单的数据集合:
{
public Vector3 Position;
public Vector3 Normal;
public Vector2 UV;
public Vector3 Tangent;
}
{
new VertexElement( 0 , 0 ,VertexElementFormat.Vector3,VertexElementMethod.Default,VertexElementUsage.Position, 0 ),
new VertexElement( 0 , 12 ,VertexElementFormat.Vector3,VertexElementMethod.Default,VertexElementUsage.Normal, 0 ),
new VertexElement( 0 , 24 ,VertexElementFormat.Vector2,VertexElementMethod.Default,VertexElementUsage.TextureCoordinate, 0 ),
new VertexElement( 0 , 32 ,VertexElementFormat.Vector3,VertexElementMethod.Default,VertexElementUsage.Tangent, 0 ),
};
有了VertexElement数据,创建VertexPosTexNorTan顶点相应的VertexDeclaration就很简单了: