using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace HwiTexture
{
class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch sprite;
SpriteFont font;
Effect effect;
Texture2D texture;
// fps
DateTime prevTime;
int draw = 0;
int fps = 0;
const int count = 20000;
VertexBuffer instanceBuffer;
VertexBuffer geometryBuffer;
IndexBuffer indexBuffer;
VertexDeclaration instanceVertexDeclaration;
VertexBufferBinding[] bindings;
Matrix projection;
// カメラ
Vector3 camPos;
int camLat = -30;
int camLong = 225;
struct InstanceInfo
{
public Matrix World;
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>("default_256"); // 既存の項目:default_256.png
GenerateGeometryBuffers(1);
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, 2000);
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);
vertices[4 + n].Position = new Vector3(u, -size, -v);
vertices[8 + n].Position = new Vector3(-size, -v, u);
vertices[12 + n].Position = new Vector3(u, -v, size);
vertices[16 + n].Position = new Vector3(size, -v, -u);
vertices[20 + n].Position = new Vector3(-u, -v, -size);
}
for (int n = 0; n < 24; n++)
{
vertices[n].TextureCoordinate = new Vector2(n & 1, (n >> 1) & 1);
}
geometryBuffer = new VertexBuffer(GraphicsDevice,
typeof(VertexPositionTexture), 24, BufferUsage.WriteOnly);
geometryBuffer.SetData(vertices);
int[] indices = new int[36];
for (int n = 0; n < 6; n++)
{
int n6 = n * 6;
int n4 = n * 4;
indices[n6 + 0] = n4 + 0; indices[n6 + 1] = n4 + 1; indices[n6 + 2] = n4 + 2;
indices[n6 + 3] = n4 + 1; indices[n6 + 4] = n4 + 3; indices[n6 + 5] = n4 + 2;
}
indexBuffer = new IndexBuffer(GraphicsDevice, typeof(int), 36, BufferUsage.WriteOnly);
indexBuffer.SetData(indices);
}
void GenerateInstanceVertexDeclaration()
{
VertexElement[] elements = new VertexElement[5];
for (int n = 0; n < 4; n++)
{
elements[n] = new VertexElement(sizeof(float) * 4 * n,
VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, n + 1);
}
elements[4] = new VertexElement(sizeof(float) * 16,
VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 5);
instanceVertexDeclaration = new VertexDeclaration(elements);
}
void GenerateInstanceInformation()
{
InstanceInfo[] instances = new InstanceInfo[count];
Random rnd = new Random();
for (int n = 0; n < count; n++)
{
instances[n].World =
Matrix.CreateScale((float)rnd.NextDouble() + 0.5f) *
Matrix.CreateFromYawPitchRoll(
(float)rnd.NextDouble() * MathHelper.TwoPi,
(float)rnd.NextDouble() * MathHelper.TwoPi,
(float)rnd.NextDouble() * MathHelper.TwoPi) *
Matrix.CreateTranslation(new Vector3(-rnd.Next(300), -rnd.Next(250), -rnd.Next(400)));
instances[n].AtlasCoordinate = new Vector2(rnd.Next(0, 2), rnd.Next(0, 2));
}
instanceBuffer = new VertexBuffer(GraphicsDevice,
instanceVertexDeclaration, count, 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--;
if (state[Keys.Right] == KeyState.Down) camLong++;
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.Black);
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.CurrentTechnique.Passes[0].Apply();
GraphicsDevice.SetVertexBuffers(bindings);
GraphicsDevice.DrawInstancedPrimitives(PrimitiveType.TriangleList, 0, 0, 24, 0, 12, count);
// フレームレート
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.White);
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.White);
sprite.End();
base.Draw(gameTime);
}
}
}