開発環境 Microsoft Visual C# 2010 Express (SP1)
実行環境 Microsoft Windows XP Home Edition (SP3)
プロジェクトの種類 Windows Game (4.0)
プロジェクト名 XnaCreateMesh

CustomVertex.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
 
namespace CustomVertex
{
    struct VertexPositionNormalColor : IVertexType
    {
        public Vector3 Position;
        public Vector3 Normal;
        public Color Color;
 
        public VertexPositionNormalColor(Vector3 position, Vector3 normal, Color color)
        {
            Position = position;
            Normal = normal;
            Color = color;
        }
 
        static readonly VertexDeclaration VertexDeclaration = new VertexDeclaration(
            new VertexElement(0,
                VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
            new VertexElement(sizeof(float) * 3,
                VertexElementFormat.Vector3, VertexElementUsage.Normal, 0),
            new VertexElement(sizeof(float) * 6,
                VertexElementFormat.Color, VertexElementUsage.Color, 0));
 
        VertexDeclaration IVertexType.VertexDeclaration { get { return VertexDeclaration; } }
    }
}
 

Game1.cs
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);
        }
    }
}
 

template.txt
xof 0303txt 0032
template Vector {
 <3d82ab5e-62da-11cf-ab39-0020af71e433>
 FLOAT x;
 FLOAT y;
 FLOAT z;
}
 
template MeshFace {
 <3d82ab5f-62da-11cf-ab39-0020af71e433>
 DWORD nFaceVertexIndices;
 array DWORD faceVertexIndices[nFaceVertexIndices];
}
 
template Mesh {
 <3d82ab44-62da-11cf-ab39-0020af71e433>
 DWORD nVertices;
 array Vector vertices[nVertices];
 DWORD nFaces;
 array MeshFace faces[nFaces];
 [...]
}
 
template MeshNormals {
 <f6f23f43-7686-11cf-8f52-0040333594a3>
 DWORD nNormals;
 array Vector normals[nNormals];
 DWORD nFaceNormals;
 array MeshFace faceNormals[nFaceNormals];
}
 
template VertexDuplicationIndices {
 <b8d65549-d7c9-4995-89cf-53a9a8b031e3>
 DWORD nIndices;
 DWORD nOriginalVertices;
 array DWORD indices[nIndices];
}
 
template Material {
 <3D82AB4D-62DA-11cf-AB39-0020AF71E433>
 ColorRGBA faceColor;
 FLOAT power;
 ColorRGB specularColor;
 ColorRGB emissiveColor;
 [...]
}
 
template MeshMaterialList {
 <F6F23F42-7686-11cf-8F52-0040333594A3>
 DWORD nMaterials;
 DWORD nFaceIndexes;
 array DWORD faceIndexes[nFaceIndexes];
 [Material]
}
 
////////////////////////////////////////////////////////////////////////////////
 
最終更新:2012年12月26日 21:40