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 CustomVertex;
// エイリアス
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;
MouseState mStateOld = new MouseState();
List<VertexPositionNormalColor> vertexList = new List<VertexPositionNormalColor>();
List<short> indexList = new List<short>();
VertexBuffer vertexBuffer;
IndexBuffer indexBuffer;
int[] adjacency; // 重複頂点
int originalVertices;
int[] faceIndexes; // マテリアル
List<Color> materialList = new List<Color>();
// 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");
effect = new BasicEffect(GraphicsDevice);
effect.EnableDefaultLighting();
effect.VertexColorEnabled = true;
effect.Projection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(45), GraphicsDevice.Viewport.AspectRatio, 1, 100);
CreateMesh();
base.LoadContent();
}
void CreateMesh()
{
Color c = new Color(0, 0.5f, 0, 1.0f);
//CreateBox(-0.5f, 0.5f, -0.5f, 0.5f, -0.5f, 0.5f);
CreateBox(-2.5f, -1.5f, -0.5f, 0.5f, -0.5f, 0.5f, new Color(0, 0, 0, 1.0f)); // エンジン
CreateBox(-1.5f, 1.5f, -0.5f, 0.5f, -0.5f, 0.5f, c); // 胴体
CreateBox(-1.0f, 1.0f, -0.4f, -0.2f, -5.0f, 5.0f, c); // 主翼
CreateBox(3.5f, 4.5f, 0.0f, 0.1f, -2.0f, 2.0f, c); // 水平尾翼
CreateBox(4.0f, 5.0f, 0.0f, 1.2f, -0.05f, 0.05f, c); // 垂直尾翼
CreateBox(-0.5f, 1.5f, 0.5f, 1.0f, -0.3f, 0.3f, new Color(0, 0.5f, 0, 0.5f)); // 風防
// 胴体後部
Vector3[] pos =
{
new Vector3(5.2f, 0.0f, 0.0f),
new Vector3(1.5f, 0.5f, 0.5f), new Vector3(1.5f, -0.5f, 0.5f),
new Vector3(1.5f, -0.5f, -0.5f), new Vector3(1.5f, 0.5f, -0.5f),
};
CreateTriangle(pos[0], pos[1], pos[2], c);
CreateTriangle(pos[0], pos[2], pos[3], c);
CreateTriangle(pos[0], pos[3], pos[4], c);
CreateTriangle(pos[0], pos[4], pos[1], c);
// 頂点
vertexBuffer = new VertexBuffer(GraphicsDevice,
typeof(VertexPositionNormalColor), 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);
// マテリアル
GenerateMaterial();
}
void CreateBox(float x1, float x2, float y1, float y2, float z1, float z2, Color c)
{
// 頂点リスト
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], c); // +X面
CreateRectangle(box[4], box[5], box[6], box[7], c); // -X面
CreateRectangle(box[0], box[2], box[7], box[6], c); // +Y面
CreateRectangle(box[4], box[3], box[5], box[1], c); // -Y面
CreateRectangle(box[0], box[7], box[1], box[5], c); // +Z面
CreateRectangle(box[4], box[6], box[3], box[2], c); // -Z面
}
void CreateRectangle(Vector3 v1, Vector3 v2, Vector3 v3, Vector3 v4, Color c)
{
int numVertices = vertexList.Count;
Vector3 norm = Vector3.Cross(v2 - v1, v3 - v1);
norm.Normalize();
vertexList.Add(new VertexPositionNormalColor(v1, norm, c));
vertexList.Add(new VertexPositionNormalColor(v2, norm, c));
vertexList.Add(new VertexPositionNormalColor(v3, norm, c));
vertexList.Add(new VertexPositionNormalColor(v4, norm, c));
for (int i = 0; i < 6; i++)
{
indexList.Add((short)(numVertices + (i < 3 ? i : 6 - i)));
}
}
void CreateTriangle(Vector3 v1, Vector3 v2, Vector3 v3, Color c)
{
int numVertices = vertexList.Count;
Vector3 norm = Vector3.Cross(v2 - v1, v3 - v1);
norm.Normalize();
vertexList.Add(new VertexPositionNormalColor(v1, norm, c));
vertexList.Add(new VertexPositionNormalColor(v2, norm, c));
vertexList.Add(new VertexPositionNormalColor(v3, norm, c));
for (int i = 0; i < 3; i++)
{
indexList.Add((short)(numVertices + 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++;
}
}
void GenerateMaterial()
{
faceIndexes = new int[indexList.Count / 3];
for (int i = 0; i < faceIndexes.Length; i++)
{
Color c = vertexList[indexList[i * 3]].Color;
int j;
for (j = 0; j < materialList.Count; j++)
{
if (materialList[j] == c) break;
}
faceIndexes[i] = j;
if (j == materialList.Count) materialList.Add(c);
}
}
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);
}
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};", materialList.Count));
w.WriteLine(string.Format(" {0};", faceIndexes.Length));
foreach (int i in faceIndexes)
{
w.WriteLine(string.Format(" {0},", i));
}
foreach (Color c in materialList)
{
w.WriteLine(" Material {");
Vector4 v = c.ToVector4();
w.WriteLine(string.Format(" {0:f6};{1:f6};{2:f6};{3:f6};;",
v.X, v.Y, v.Z, v.W));
w.WriteLine(string.Format(" {0:f6};", 0.0f));
w.WriteLine(string.Format(" {0:f6};{1:f6};{2:f6};;", 0.0f, 0.0f, 0.0f));
w.WriteLine(string.Format(" {0:f6};{1:f6};{2:f6};;", 0.0f, 0.0f, 0.0f));
w.WriteLine(" }"); // Material
}
w.WriteLine(" }"); // MeshMaterialList
w.WriteLine("}"); // Mesh
}
MessageBox.Show("保存しました。", "XnaCreateMesh",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception e)
{
MessageBox.Show(e.Message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
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);
}
}
}