using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms; // .NET参照
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
// エイリアス
using Keys = Microsoft.Xna.Framework.Input.Keys;
using ButtonState = Microsoft.Xna.Framework.Input.ButtonState;
namespace XnaCreateMesh
{
class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch sprite;
SpriteFont font;
BasicEffect effect;
Texture2D texture;
MouseState mStateOld = new MouseState();
List<VertexPositionNormalTexture> vertexList = new List<VertexPositionNormalTexture>();
List<short> indexList = new List<short>();
VertexBuffer vertexBuffer;
IndexBuffer indexBuffer;
int[] adjacency; // 重複頂点
int originalVertices;
// fps
int fpsSec;
int fpsDraw = 0;
int fpsCount = 0;
// カメラ
float camLat = 30;
float camLon = -150;
float camDist = 10;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
graphics.PreferredBackBufferWidth = 1280;
graphics.PreferredBackBufferHeight = 720;
Content.RootDirectory = "Content";
IsMouseVisible = true;
}
protected override void LoadContent()
{
sprite = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>("SpriteFont1");
texture = Content.Load<Texture2D>("Dice6");
effect = new BasicEffect(GraphicsDevice);
effect.EnableDefaultLighting();
//effect.VertexColorEnabled = true;
effect.TextureEnabled = true;
effect.Texture = texture;
effect.Projection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(45), GraphicsDevice.Viewport.AspectRatio, 1, 100);
CreateMesh();
base.LoadContent();
}
void CreateMesh()
{
CreateBox(-0.5f, 0.5f, -0.5f, 0.5f, -0.5f, 0.5f);
// 頂点
vertexBuffer = new VertexBuffer(GraphicsDevice,
typeof(VertexPositionNormalTexture), vertexList.Count, BufferUsage.WriteOnly);
vertexBuffer.SetData(vertexList.ToArray());
// 索引
indexBuffer = new IndexBuffer(GraphicsDevice,
typeof(short), indexList.Count, BufferUsage.WriteOnly);
indexBuffer.SetData(indexList.ToArray());
// 重複頂点
GenerateAdjacency(0.01f);
}
void CreateBox(float x1, float x2, float y1, float y2, float z1, float z2)
{
// 頂点リスト
Vector3[] box =
{
new Vector3(x2, y2, z2), new Vector3(x2, y1, z2),
new Vector3(x2, y2, z1), new Vector3(x2, y1, z1),
new Vector3(x1, y1, z1), new Vector3(x1, y1, z2),
new Vector3(x1, y2, z1), new Vector3(x1, y2, z2),
};
CreateRectangle(box[0], box[1], box[2], box[3], 3, 0); // +X面 4 North
CreateRectangle(box[4], box[5], box[6], box[7], 2, 0); // -X面 3 South
CreateRectangle(box[0], box[2], box[7], box[6], 0, 0); // +Y面 1 Up
CreateRectangle(box[4], box[3], box[5], box[1], 1, 1); // -Y面 6 Down
CreateRectangle(box[0], box[7], box[1], box[5], 0, 1); // +Z面 5 East
CreateRectangle(box[4], box[6], box[3], box[2], 1, 0); // -Z面 2 West
}
void CreateRectangle(Vector3 v1, Vector3 v2, Vector3 v3, Vector3 v4, int u, int v)
{
int numVertices = vertexList.Count;
Vector3 norm = Vector3.Cross(v2 - v1, v3 - v1);
norm.Normalize();
vertexList.Add(new VertexPositionNormalTexture(
v1, norm, new Vector2((u + 0) / 4.0f, (v + 0) / 4.0f)));
vertexList.Add(new VertexPositionNormalTexture(
v2, norm, new Vector2((u + 1) / 4.0f, (v + 0) / 4.0f)));
vertexList.Add(new VertexPositionNormalTexture(
v3, norm, new Vector2((u + 0) / 4.0f, (v + 1) / 4.0f)));
vertexList.Add(new VertexPositionNormalTexture(
v4, norm, new Vector2((u + 1) / 4.0f, (v + 1) / 4.0f)));
for (int i = 0; i < 6; i++)
{
indexList.Add((short)(numVertices + (i < 3 ? i : 6 - i)));
}
}
void GenerateAdjacency(float epsilon)
{
adjacency = new int[vertexList.Count];
originalVertices = 0;
for (int i = 0; i < vertexList.Count; i++)
{
int j;
for (j = 0; j < i; j++)
{
if ((vertexList[i].Position - vertexList[j].Position).Length() < epsilon) break;
}
adjacency[i] = j;
if (i == j) originalVertices++;
}
}
protected override void Update(GameTime gameTime)
{
float deltaLat = 0;
float deltaLon = 0;
float deltaDist = 0;
// キーボード
KeyboardState kState = Keyboard.GetState();
if (kState.IsKeyDown(Keys.Escape)) Exit();
if (kState.IsKeyDown(Keys.Up)) deltaLat = 1;
if (kState.IsKeyDown(Keys.Down)) deltaLat = -1;
if (kState.IsKeyDown(Keys.Left)) deltaLon = 1;
if (kState.IsKeyDown(Keys.Right)) deltaLon = -1;
if (kState.IsKeyDown(Keys.PageUp)) deltaDist = -0.2f;
if (kState.IsKeyDown(Keys.PageDown)) deltaDist = 0.2f;
if (kState.IsKeyDown(Keys.S) && kState.IsKeyDown(Keys.LeftControl)) SaveMesh();
// マウス
MouseState mState = Mouse.GetState();
if (mState.LeftButton == ButtonState.Pressed)
{
deltaLat = (mState.Y - mStateOld.Y) * 0.5f;
deltaLon = (mState.X - mStateOld.X) * 0.5f;
}
if (mState.ScrollWheelValue != mStateOld.ScrollWheelValue)
{
deltaDist = (mState.ScrollWheelValue - mStateOld.ScrollWheelValue) / -120;
}
mStateOld = mState;
// カメラ移動
if (deltaLat != 0)
{
camLat = MathHelper.Clamp(camLat + deltaLat, -89.9f, 89.9f);
}
if (deltaLon != 0)
{
camLon += deltaLon;
if (camLon <= -180) camLon += 360;
if (180 < camLon) camLon -= 360;
}
if (deltaDist != 0)
{
camDist = MathHelper.Clamp(camDist + deltaDist, 1, 1000);
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.RasterizerState = RasterizerState.CullClockwise;
// カメラ位置
float rad = MathHelper.ToRadians(camLat);
float y = (float)Math.Sin(rad) * camDist;
float r = (float)Math.Cos(rad) * camDist;
rad = MathHelper.ToRadians(camLon);
float x = (float)Math.Cos(rad) * r;
float z = (float)Math.Sin(rad) * r;
effect.View = Matrix.CreateLookAt(new Vector3(x, y, z), Vector3.Zero, Vector3.Up);
GraphicsDevice.SetVertexBuffer(vertexBuffer);
GraphicsDevice.Indices = indexBuffer;
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList,
0, 0, vertexBuffer.VertexCount, 0, indexBuffer.IndexCount / 3);
}
// fps
fpsDraw++;
if (gameTime.TotalGameTime.Seconds != fpsSec)
{
fpsCount = fpsDraw;
fpsDraw = 0;
fpsSec = gameTime.TotalGameTime.Seconds;
}
sprite.Begin();
string text = String.Format(
"fps={0} lat={1:f0} lon={2:f0} dist={3:f1}", fpsCount, camLat, camLon, camDist);
sprite.DrawString(font, text, new Vector2(0, 0), Color.White);
sprite.End();
base.Draw(gameTime);
}
void SaveMesh()
{
try
{
string workDir = "C:\\tmp\\";
string workFile = "mesh.x";
File.Copy(workDir + "template.txt", workDir + workFile, true);
using (StreamWriter w = new StreamWriter(workDir + workFile, true))
{
w.WriteLine();
w.WriteLine("Mesh {");
w.WriteLine(string.Format(" {0};", vertexList.Count));
foreach (var vertex in vertexList)
{
w.WriteLine(string.Format(" {0:f6};{1:f6};{2:f6};,",
vertex.Position.X, vertex.Position.Y, vertex.Position.Z));
}
w.WriteLine(string.Format(" {0};", indexList.Count / 3));
for (int i = 0; i < indexList.Count; i += 3)
{
w.WriteLine(string.Format(" 3;{0},{1},{2};,",
indexList[i], indexList[i + 1], indexList[i + 2]));
}
w.WriteLine();
w.WriteLine(" MeshNormals {");
w.WriteLine(string.Format(" {0};", vertexList.Count));
foreach (var vertex in vertexList)
{
w.WriteLine(string.Format(" {0:f6};{1:f6};{2:f6};,",
vertex.Normal.X, vertex.Normal.Y, vertex.Normal.Z));
}
w.WriteLine(string.Format(" {0};", indexList.Count / 3));
for (int i = 0; i < indexList.Count; i += 3)
{
w.WriteLine(string.Format(" 3;{0},{1},{2};,",
indexList[i], indexList[i + 1], indexList[i + 2]));
}
w.WriteLine(" } // MeshNormals");
w.WriteLine();
w.WriteLine(" VertexDuplicationIndices {");
w.WriteLine(string.Format(" {0};", adjacency.Length));
w.WriteLine(string.Format(" {0};", originalVertices));
foreach (int i in adjacency)
{
w.WriteLine(string.Format(" {0},", i));
}
w.WriteLine(" } // VertexDuplicationIndices");
w.WriteLine();
w.WriteLine(" MeshMaterialList {");
w.WriteLine(string.Format(" {0};", 1));
w.WriteLine(string.Format(" {0};", 1));
w.WriteLine(string.Format(" {0},", 0));
w.WriteLine(" Material {");
w.WriteLine(string.Format(" {0:f6};{1:f6};{2:f6};{3:f6};;", 1, 1, 1, 1));
w.WriteLine(string.Format(" {0:f6};", 50));
w.WriteLine(string.Format(" {0:f6};{1:f6};{2:f6};;", 1, 1, 1));
w.WriteLine(string.Format(" {0:f6};{1:f6};{2:f6};;", 0, 0, 0));
w.WriteLine(" TextureFilename {");
w.WriteLine(" \"{0}\";", "Dice6.png");
w.WriteLine(" } // TextureFilename");
w.WriteLine(" } // Material");
w.WriteLine(" } // MeshMaterialList");
w.WriteLine();
w.WriteLine(" MeshTextureCoords {");
w.WriteLine(string.Format(" {0};", vertexList.Count));
foreach (var vertex in vertexList)
{
w.WriteLine(string.Format(" {0:f6};{1:f6};,",
vertex.TextureCoordinate.X, vertex.TextureCoordinate.Y));
}
w.WriteLine(" } // MeshTextureCoords");
w.WriteLine("} // Mesh");
}
MessageBox.Show("保存しました。", "XnaCreateMesh",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception e)
{
MessageBox.Show(e.Message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}