開発環境 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
添付ファイル