開発環境 |
Microsoft Visual C# 2010 Express (SP1) |
実行環境 |
Microsoft Windows XP Home Edition (SP3) |
プロジェクトの種類 |
Windows Game (4.0) |
プロジェクト名 |
XnaSnow |
Dice6.png
Game1.cs
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace XnaSnow
{
class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch sprite;
SpriteFont font;
Effect effect;
Texture2D texture;
// fps
DateTime prevTime;
int draw = 0;
int fps = 0;
const int blockMax = 30000;
VertexBuffer instanceBuffer;
VertexBuffer geometryBuffer;
IndexBuffer indexBuffer;
VertexDeclaration instanceVertexDeclaration;
VertexBufferBinding[] bindings;
Matrix projection;
// カメラ
Vector3 camPos = new Vector3(-50, -96, -50);
int camLat = 0;
int camLong = 0;
struct InstanceInfo
{
public Vector4 InitPos;
public Vector4 Motion;
public Vector4 Rotation;
public Vector2 AtlasCoordinate;
}
public Game1()
{
graphics = new GraphicsDeviceManager(this);
graphics.PreferredBackBufferWidth = 1280;
graphics.PreferredBackBufferHeight = 720;
Content.RootDirectory = "Content";
IsMouseVisible = true;
}
protected override void Initialize()
{
sprite = new SpriteBatch(GraphicsDevice);
prevTime = DateTime.Now;
base.Initialize();
}
protected override void LoadContent()
{
// ソリューション エクスプローラーの(Content)に追加しておく
font = Content.Load<SpriteFont>("SpriteFont1"); // 新しい項目:Sprite Font
effect = Content.Load<Effect>("HardwareInstancing"); // 新しい項目:Effect File (.fx)
texture = Content.Load<Texture2D>("Dice6"); // 既存の項目:Dice6.png
GenerateGeometryBuffers(0.5f);
GenerateInstanceVertexDeclaration();
GenerateInstanceInformation();
bindings = new VertexBufferBinding[2];
bindings[0] = new VertexBufferBinding(geometryBuffer, 0);
bindings[1] = new VertexBufferBinding(instanceBuffer, 0, 1);
projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45),
GraphicsDevice.Viewport.AspectRatio, 0.1f, 200);
effect.CurrentTechnique = effect.Techniques["Instancing"];
effect.Parameters["cubeTexture"].SetValue(texture);
base.LoadContent();
}
void GenerateGeometryBuffers(float size)
{
VertexPositionTexture[] vertices = new VertexPositionTexture[24];
for (int n = 0; n < 4; n++)
{
float u = size * ((n & 1) * 2 - 1);
float v = size * ((n & 2) - 1);
vertices[0 + n].Position = new Vector3(u, size, v); // 1 Up
vertices[4 + n].Position = new Vector3(-u, -v, -size); // 2 West
vertices[8 + n].Position = new Vector3(-size, -v, u); // 3 South
vertices[12 + n].Position = new Vector3(size, -v, -u); // 4 North
vertices[16 + n].Position = new Vector3(u, -v, size); // 5 East
vertices[20 + n].Position = new Vector3(u, -size, -v); // 6 Down
}
for (int n = 0; n < 24; n++)
{
vertices[n].TextureCoordinate = new Vector2(
(n / 4 % 4) + (n & 1), (n / 16) + ((n >> 1) & 1));
}
geometryBuffer = new VertexBuffer(GraphicsDevice,
typeof(VertexPositionTexture), 24, BufferUsage.WriteOnly);
geometryBuffer.SetData(vertices);
int[] squareVert = { 0, 1, 2, 1, 3, 2 };
int[] indices = new int[36];
for (int n = 0; n < 36; n++)
{
indices[n] = (n / 6) * 4 + squareVert[n % 6];
}
indexBuffer = new IndexBuffer(GraphicsDevice, typeof(int), 36, BufferUsage.WriteOnly);
indexBuffer.SetData(indices);
}
void GenerateInstanceVertexDeclaration()
{
int f4Num = 3;
VertexElement[] elements = new VertexElement[f4Num + 1];
int n;
for (n = 0; n < f4Num; n++)
{
elements[n] = new VertexElement(sizeof(float) * 4 * n,
VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, n + 1);
}
elements[n] = new VertexElement(sizeof(float) * 4 * n,
VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, n + 1);
instanceVertexDeclaration = new VertexDeclaration(elements);
}
void GenerateInstanceInformation()
{
InstanceInfo[] instances = new InstanceInfo[blockMax];
// 地面
int n = 0;
for (int x = -100; x < 0; x++)
{
for (int z = -100; z < 0; z++)
{
instances[n].InitPos = new Vector4(x, -97, z, 1);
instances[n].Motion = new Vector4();
instances[n].Rotation = new Vector4();
instances[n].AtlasCoordinate = new Vector2();
n++;
}
}
Random rnd = new Random();
for (; n < blockMax; n++)
{
instances[n].InitPos = new Vector4(
rnd.Next(-100, 0), rnd.Next(-100, 0), rnd.Next(-100, 0), 1);
instances[n].Motion = new Vector4(
(float)rnd.NextDouble() - 0.5f,
(float)rnd.NextDouble() - 1.5f,
(float)rnd.NextDouble() - 0.5f, 1);
instances[n].Rotation = new Vector4(
(float)(rnd.NextDouble() - 0.5f) * MathHelper.TwoPi,
(float)(rnd.NextDouble() - 0.5f) * MathHelper.TwoPi,
(float)(rnd.NextDouble() - 0.5f) * MathHelper.TwoPi, 1);
instances[n].AtlasCoordinate = new Vector2();
}
instanceBuffer = new VertexBuffer(GraphicsDevice,
instanceVertexDeclaration, blockMax, BufferUsage.WriteOnly);
instanceBuffer.SetData(instances);
}
protected override void Update(GameTime gameTime)
{
KeyboardState state = Keyboard.GetState();
if (state[Keys.Escape] == KeyState.Down) Exit();
if (state[Keys.W] == KeyState.Down) Move(0, 0);
if (state[Keys.S] == KeyState.Down) Move(180, 0);
if (state[Keys.A] == KeyState.Down) Move(0, -90);
if (state[Keys.D] == KeyState.Down) Move(0, 90);
if (state[Keys.Up] == KeyState.Down) camLat++;
if (state[Keys.Down] == KeyState.Down) camLat--;
if (state[Keys.Left] == KeyState.Down) camLong = (camLong + 359) % 360;
if (state[Keys.Right] == KeyState.Down) camLong = (camLong + 1) % 360;
if (state[Keys.PageUp] == KeyState.Down) Move(90, 0);
if (state[Keys.PageDown] == KeyState.Down) Move(-90, 0);
base.Update(gameTime);
}
void Move(int degLat, int degLong)
{
float rad = MathHelper.ToRadians(camLat + degLat);
if (degLong == 0)
{
camPos.Y += (float)Math.Sin(rad) * 0.2f;
}
float r = (float)Math.Cos(rad) * 0.2f;
rad = MathHelper.ToRadians(camLong + degLong);
camPos.X += (float)Math.Cos(rad) * r;
camPos.Z += (float)Math.Sin(rad) * r;
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
float rad = MathHelper.ToRadians(camLat);
float y = (float)Math.Sin(rad) + camPos.Y;
float r = (float)Math.Cos(rad);
rad = MathHelper.ToRadians(camLong);
float x = camPos.X + (float)Math.Cos(rad) * r;
float z = camPos.Z + (float)Math.Sin(rad) * r;
Matrix view = Matrix.CreateLookAt(camPos, new Vector3(x, y, z), Vector3.Up);
GraphicsDevice.Indices = indexBuffer;
effect.Parameters["WVP"].SetValue(view * projection);
effect.Parameters["totalTime"].SetValue((float)gameTime.TotalGameTime.TotalSeconds);
effect.CurrentTechnique.Passes[0].Apply();
GraphicsDevice.SetVertexBuffers(bindings);
GraphicsDevice.DrawInstancedPrimitives(PrimitiveType.TriangleList, 0, 0, 24, 0, 12, blockMax);
// フレームレート
draw++;
DateTime now = DateTime.Now;
TimeSpan t = now - prevTime;
if (t.TotalMilliseconds >= 1000)
{
fps = draw;
draw = 0;
prevTime = now;
}
sprite.Begin();
string text = "fps=" + fps + " lat=" + camLat + " long=" + camLong;
sprite.DrawString(font, text, new Vector2(0, 0), Color.Lime);
text = string.Format("x={0:f1} y={1:f1} z={2:f1}", camPos.X, camPos.Y, camPos.Z);
sprite.DrawString(font, text, new Vector2(0, 20), Color.Lime);
sprite.End();
base.Draw(gameTime);
}
}
}
HardwareInstancing.fx
float4x4 WVP;
texture cubeTexture;
float totalTime;
sampler TextureSampler = sampler_state
{
texture = <cubeTexture>;
minfilter = linear;
magfilter = linear;
};
struct InstancingVSInOut
{
float4 Position : POSITION;
float2 TexCoord : TEXCOORD;
};
InstancingVSInOut InstancingVS(
InstancingVSInOut input,
float4 initPos : TEXCOORD1,
float4 motion : TEXCOORD2,
float4 rotation : TEXCOORD3,
float2 atlasCoord : TEXCOORD4)
{
InstancingVSInOut output;
float4 pos = input.Position; // 複製元の座標
float rad = rotation.x * totalTime;
float s = sin(rad);
float c = cos(rad);
float4x4 Rx = {
{ 1, 0, 0, 0},
{ 0, c,-s, 0},
{ 0, s, c, 0},
{ 0, 0, 0, 1}};
rad = rotation.y * totalTime;
s = sin(rad);
c = cos(rad);
float4x4 Ry = {
{ c, 0, s, 0},
{ 0, 1, 0, 0},
{-s, 0, c, 0},
{ 0, 0, 0, 1}};
rad = rotation.z * totalTime;
s = sin(rad);
c = cos(rad);
float4x4 Rz = {
{ c,-s, 0, 0},
{ s, c, 0, 0},
{ 0, 0, 1, 0},
{ 0, 0, 0, 1}};
pos = mul(pos, mul(Rz, mul(Rx, Ry)));
pos.xyz += initPos.xyz;
pos.xyz += motion.xyz * totalTime;
pos.y %= 100.0f;
if (pos.y < -98.0f || -2.0f < pos.y) atlasCoord.x = 2.0f;
pos = mul(pos, WVP); // スクリーン座標に変換
output.Position = pos;
output.TexCoord = float2(
atlasCoord.x + (input.TexCoord.x / 4.0f),
atlasCoord.y + (input.TexCoord.y / 4.0f));
return output;
}
float4 InstancingPS(float2 texCoord : TEXCOORD) : COLOR
{
if (2.0f <= texCoord.x) discard;
return tex2D(TextureSampler, texCoord);
}
technique Instancing
{
pass Pass0
{
VertexShader = compile vs_3_0 InstancingVS();
PixelShader = compile ps_3_0 InstancingPS();
}
}
最終更新:2012年12月05日 21:35