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

Dice6.png


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 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);
            }
        }
    }
}
 

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