上下求索
探索DirectX9.0)

DirectX9 3D 快速上手 8

By sssa2000

5/4/2005

 

上一次中途结束了本来应该讲到的控制Mesh的细节程度的方法的,这一次补上。

我们这里使用的是简单的方法,并没有涉及到场景剔出等等复杂的方法,我这里主要还是用DX9提供给我们的类库,progressive meshe

progressive meshes主要的优点就是允许我们控制顶点和面的数目,这样我们就可以灵活的控制mesh细节的显示程度。

Mesh一样,progressive meshe也都是继承自BaseMesh这个类。比较重要的属性主要有2NumberFacesNumberVertices。从字面我们就可以看出两个属性的含义。

有了前面的基础在使用progressive mesh上也变得十分的简单,首先我们需要声明一个progressive mesh的变量,然后我们看看progressive mesh的构造函数:

public ProgressiveMesh(
    Mesh mesh,
    GraphicsStream adjacency,
    GraphicsStream vertexWeights, //
顶点的权值,越高越不容易被剔出,如果设为null就全部被设为1
    int minValue, //
设定被简化的最小值
    MeshFlags options
);

以下就是核心的代码:

private void LoadMesh(string file)
{
    ExtendedMaterial[] mtrl;
    GraphicsStream adj;
 
    
// Load our mesh
    using(Mesh mesh = Mesh.FromFile(file, MeshFlags.Managed, device,
                
out adj, out mtrl))
    
{
 
        
// If we have any materials, store them
        if ((mtrl != null&& (mtrl.Length > 0))
        
{
            meshMaterials 
= new Material[mtrl.Length];
            meshTextures 
= new Texture[mtrl.Length];
 
            
// Store each material and texture
            for (int i = 0; i < mtrl.Length; i++)
            
{
                meshMaterials[i] 
= mtrl[i].Material3D;
                
if ((mtrl[i].TextureFilename != null&&
                    (mtrl[i].TextureFilename 
!= string.Empty))
                
{
                    
// We have a texture, try to load it
                    meshTextures[i] = TextureLoader.FromFile(device,
                        
@"..\..\" + mtrl[i].TextureFilename);
                }

            }

        }

 
        
// Clean our main mesh
        using(Mesh tempMesh = Mesh.Clean(mesh, adj, adj))
        
{
            
// Create our progressive mesh
            progressiveMesh = new ProgressiveMesh(tempMesh, adj,
                
null1, MeshFlags.SimplifyVertex);
 
            
// Set the initial mesh to the max
            progressiveMesh.NumberFaces = progressiveMesh.MaxFaces;
            progressiveMesh.NumberVertices 
= progressiveMesh.MaxVertices;
        }

    }

}

其实这里大部分代码和前面的关于Mesh那一节的代码差不多,关于progressiveMesh,微软的SDK也有一个例子,不过那个例子太过于庞大,大部分都是在处理UI方面,真正涉及到progressiveMesh的部分还是很少的。

以下是代码的事例图和完整代码:





这个例子里可以切换线框渲染模式和实体作色模式。

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

using Microsoft.DirectX;

using Microsoft.DirectX.Direct3D;

 

namespace progressiveMesh

{

     /// <summary>

     /// Summary description for Form1.

     /// </summary>

     public class Form1 : System.Windows.Forms.Form

     {

        private Device device = null;

        private ProgressiveMesh progressiveMesh = null;

        private Material[] meshMaterials;

        private Texture[] meshTextures;

 

        private Microsoft.DirectX.Direct3D.Font font = null;

        private float cameraPos = 8.0f;

        private const int MoveAmount = 2;

 

        /// <summary>

         /// Required designer variable.

         /// </summary>

         private System.ComponentModel.Container components = null;

        private float angle = 0.0f;

 

         public Form1()

         {

              //

              // Required for Windows Form Designer support

              //

              InitializeComponent();

 

            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);

         }

 

        /// <summary>

        /// We will initialize our graphics device here

        /// </summary>

        public void InitializeGraphics()

        {

            // Set our presentation parameters

            PresentParameters presentParams = new PresentParameters();

 

            presentParams.Windowed = true;

            presentParams.SwapEffect = SwapEffect.Discard;

            presentParams.AutoDepthStencilFormat = DepthFormat.D16;

            presentParams.EnableAutoDepthStencil = true;

 

            // Create our device

            device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);

 

            // Load our mesh

            LoadMesh(@"..\..\SprintRacer.x");

 

            // Create our font

            font = new Microsoft.DirectX.Direct3D.Font(device, new System.Drawing.Font

                ("Arial", 14.0f, FontStyle.Bold | FontStyle.Italic));

        }

 

        private void LoadMesh(string file)

        {

            ExtendedMaterial[] mtrl;

            GraphicsStream adj;

 

            // Load our mesh

            using(Mesh mesh = Mesh.FromFile(file, MeshFlags.Managed, device,

                        out adj, out mtrl))

            {

 

                // If we have any materials, store them

                if ((mtrl != null) && (mtrl.Length > 0))

                {

                    meshMaterials = new Material[mtrl.Length];

                    meshTextures = new Texture[mtrl.Length];

 

                    // Store each material and texture

                    for (int i = 0; i < mtrl.Length; i++)

                    {

                        meshMaterials[i] = mtrl[i].Material3D;

                        if ((mtrl[i].TextureFilename != null) &&

                            (mtrl[i].TextureFilename != string.Empty))

                        {

                            // We have a texture, try to load it

                            meshTextures[i] = TextureLoader.FromFile(device,

                                @"..\..\" + mtrl[i].TextureFilename);

                        }

                    }

                }

 

                // Clean our main mesh

                using(Mesh tempMesh = Mesh.Clean(CleanType.Simplification ,mesh, adj, adj))

                {

                    // Create our progressive mesh

                    progressiveMesh = new ProgressiveMesh(tempMesh, adj,

                        null, 1, MeshFlags.SimplifyVertex);

 

                    // Set the initial mesh to the max

                    progressiveMesh.NumberFaces = progressiveMesh.MaxFaces;

                    progressiveMesh.NumberVertices = progressiveMesh.MaxVertices;

                }

            }

        }

 

        private void SetupCamera()

        {

            device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, this.Width / this.Height, 1.0f, 10000.0f);

            device.Transform.View = Matrix.LookAtLH(new Vector3(0,0, cameraPos),

                new Vector3(), new Vector3(0,1,0));

 

            device.RenderState.Ambient = Color.Blue;

            device.Lights[0].Type = LightType.Directional;

            device.Lights[0].Diffuse = Color.White;

            device.Lights[0].Direction = new Vector3(0, -1, -1);

            device.Lights[0].Update();

            device.Lights[0].Enabled = true;

 

        }

 

        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)

        {

            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Blue , 1.0f, 0);

 

            SetupCamera();

 

            device.BeginScene();

 

            // Draw our Mesh

            DrawMesh(angle / (float)Math.PI, angle / (float)Math.PI * 2.0f, angle / (float)Math.PI / 4.0f, 0.0f, 0.0f, 0.0f);

 

            font.DrawText(null, string.Format("Number vertices in mesh: {0}",

                ((BaseMesh)progressiveMesh).NumberVertices), new Rectangle(10, 10, 0, 0),

                DrawTextFormat.NoClip, Color.BlanchedAlmond);

 

            font.DrawText(null, string.Format("Number faces in mesh: {0}",

                ((BaseMesh)progressiveMesh).NumberFaces), new Rectangle(10, 30, 0, 0),

                DrawTextFormat.NoClip, Color.BlanchedAlmond);

 

            device.EndScene();

 

            device.Present();

 

            this.Invalidate();

        }

 

        private void DrawMesh(float yaw, float pitch, float roll, float x, float y, float z)

        {

            angle += 0.11f;

 

            device.Transform.World = Matrix.RotationYawPitchRoll(yaw, pitch, roll) * Matrix.Translation(x, y, z);

            for (int i = 0; i < meshMaterials.Length; i++)

            {

                device.Material = meshMaterials[i];

                device.SetTexture(0, meshTextures[i]);

                progressiveMesh.DrawSubset(i);

            }

        }

        protected override void OnKeyPress(KeyPressEventArgs e)

        {

            if (e.KeyChar == '+')

            {

                cameraPos += (MoveAmount * 2);

                progressiveMesh.NumberVertices =

                    ((BaseMesh)progressiveMesh).NumberVertices - MoveAmount;

 

                progressiveMesh.NumberFaces =

                    ((BaseMesh)progressiveMesh).NumberFaces - MoveAmount;

            }

            if (e.KeyChar == '-')

            {

                cameraPos -= (MoveAmount * 2);

                progressiveMesh.NumberVertices =

                    ((BaseMesh)progressiveMesh).NumberVertices + MoveAmount;

 

                progressiveMesh.NumberFaces =

                    ((BaseMesh)progressiveMesh).NumberFaces + MoveAmount;

            }

            if (e.KeyChar == 'w')

                device.RenderState.FillMode = FillMode.WireFrame;

 

            if (e.KeyChar == 's')

                device.RenderState.FillMode = FillMode.Solid;

        }

 

        /// <summary>

         /// Clean up any resources being used.

         /// </summary>

         protected override void Dispose( bool disposing )

         {

              if( disposing )

              {

                   if (components != null)

                   {

                       components.Dispose();

                   }

              }

              base.Dispose( disposing );

         }

 

         #region Windows Form Designer generated code

         /// <summary>

         /// Required method for Designer support - do not modify

         /// the contents of this method with the code editor.

         /// </summary>

         private void InitializeComponent()

         {

              this.components = new System.ComponentModel.Container();

              this.Size = new Size(800,600);

              this.Text = "Form1";

         }

         #endregion

 

         /// <summary>

         /// The main entry point for the application.

         /// </summary>

        static void Main()

        {

            using (Form1 frm = new Form1())

            {

                // Show our form and initialize our graphics engine

                frm.Show();

                frm.InitializeGraphics();

                Application.Run(frm);

            }

        }

     }

}

 

这里我们已经实现了能自由控制显示的细节问题,通过progressiveMesh 我们可以很方便的完成这一点,接下来,顺着这个话题我们来讨论一下如何增加细节的显示程度,也就是说我们将要把读入的Mesh的细节程度增多。这里我们部探讨原理,因为我数学也是很差,我们可以借助Patch Meshes 来实现。

Patch Meshes通过增加读入的Mesh的顶点来对细节程度进行增加。

public PatchMesh(ID3DXPatchMesh); 这就是PathMesh的构造函数,只要传入一个读入的Mesh就可以了。接下来我们将用一个例子展示怎么增加显示的细节程度



private void CreatePatchMesh(string file, float tessLevel)
{
    
if (tessLevel < 1.0f//如果小于默认的值,不再增加,直接返回
        return;
 
    
if (mesh != null)
        mesh.Dispose();
 
    
using (Mesh tempmesh = LoadMesh(file))
    
{
        
using (PatchMesh patch = PatchMesh.CreateNPatchMesh(tempmesh))
        
{
            
// Calculate the new number of faces/vertices
            int numberFaces = (int)(tempmesh.NumberFaces
                
* Math.Pow(tessLevel, 3));
            
int numberVerts = (int)(tempmesh.NumberVertices
                
* Math.Pow(tessLevel, 3));
 
            mesh 
= new Mesh(numberFaces, numberVerts, MeshFlags.Managed
                
| MeshFlags.Use32Bit, tempmesh.VertexFormat, device);
 
            
// Tessellate the patched mesh
            patch.Tessellate(tessLevel, mesh); //在tessLevel的基础上把mesh分成小方格
        }

    }

}

 
private Mesh LoadMesh(string file)
{
……………………….略
if ((mesh.VertexFormat & VertexFormats.Normal) != VertexFormats.Normal)
//如果没有法线信息,就要计算法线,发现信息会在上面的Tessellate方法中被用到。
    {
        
// We must have normals for our patch meshes
        Mesh tempMesh = mesh.Clone(mesh.Options.Value,
            mesh.VertexFormat 
| VertexFormats.Normal, device);
 
        tempMesh.ComputeNormals();
 
        mesh.Dispose();
        mesh 
= tempMesh;
    }

    
return mesh;
}

 

以下就是运行的图例



可以明显地看出,我们的程序还是有明显的效果的,不过对于增加的细节,程序将变得十分的缓慢。

以下是全部的代码:

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

using Microsoft.DirectX;

using Microsoft.DirectX.Direct3D;

 

namespace PathMesh

{

     /// <summary>

     /// Summary description for Form1.

     /// </summary>

     public class Form1 : System.Windows.Forms.Form

     {

        private Device device = null;

        private Mesh mesh = null;

        private Material[] meshMaterials;

        private Texture[] meshTextures;

 

        private float tessLevel = 1.0f;

        private const float tessIncrement = 1.0f;

        private string filename = @"..\..\sphere.x";

 

        private Microsoft.DirectX.Direct3D.Font font = null;

 

        /// <summary>

         /// Required designer variable.

         /// </summary>

         private System.ComponentModel.Container components = null;

        private float angle = 0.0f;

 

         public Form1()

         {

              //

              // Required for Windows Form Designer support

              //

              InitializeComponent();

 

            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);

         }

 

        /// <summary>

        /// We will initialize our graphics device here

        /// </summary>

        public void InitializeGraphics()

        {

            // Set our presentation parameters

            PresentParameters presentParams = new PresentParameters();

 

            presentParams.Windowed = true;

            presentParams.SwapEffect = SwapEffect.Discard;

            presentParams.AutoDepthStencilFormat = DepthFormat.D16;

            presentParams.EnableAutoDepthStencil = true;

 

            // Create our device

            device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);

 

            // Create our patch mesh

            CreatePatchMesh(filename, tessLevel);

 

            // Create our font

            font = new Microsoft.DirectX.Direct3D.Font(device, new System.Drawing.Font

                ("Arial", 14.0f, FontStyle.Bold | FontStyle.Italic));

 

            // Default to wireframe mode first

            device.RenderState.FillMode = FillMode.WireFrame;

        }

 

        /// <summary>

        /// Creates a temporary mesh object, and a patch mesh from it

        /// </summary>

        /// <param name="file">The original mesh to use</param>

        /// <param name="tessLevel">The tesselation level</param>

        private void CreatePatchMesh(string file, float tessLevel)

        {

            if (tessLevel < 1.0f) // Nothing to do

                return;

 

            if (mesh != null)

                mesh.Dispose();

 

            using (Mesh tempmesh = LoadMesh(file))

            {

                using (PatchMesh patch = PatchMesh.CreateNPatchMesh(tempmesh))

                {

                    // Calculate the new number of faces/vertices

                    int numberFaces = (int)(tempmesh.NumberFaces

                        * Math.Pow(tessLevel, 3));

                    int numberVerts = (int)(tempmesh.NumberVertices

                        * Math.Pow(tessLevel, 3));

 

                    mesh = new Mesh(numberFaces, numberVerts, MeshFlags.Managed

                        | MeshFlags.Use32Bit, tempmesh.VertexFormat, device);

 

                    // Tessellate the patched mesh

                    patch.Tessellate(tessLevel, mesh);

                }

            }

        }

 

        /// <summary>

        /// Load a mesh from a file and return it

        /// </summary>

        /// <param name="file">The file to load</param>

        /// <returns>The created mesh</returns>

        private Mesh LoadMesh(string file)

        {

            ExtendedMaterial[] mtrl;

 

            // Load our mesh

            Mesh mesh = Mesh.FromFile(file, MeshFlags.Managed, device,

                        out mtrl);

 

            // If we have any materials, store them

            if ((mtrl != null) && (mtrl.Length > 0))

            {

                meshMaterials = new Material[mtrl.Length];

                meshTextures = new Texture[mtrl.Length];

 

                // Store each material and texture

                for (int i = 0; i < mtrl.Length; i++)

                {

                    meshMaterials[i] = mtrl[i].Material3D;

                    if ((mtrl[i].TextureFilename != null) &&

                        (mtrl[i].TextureFilename != string.Empty))

                    {

                        // We have a texture, try to load it

                        meshTextures[i] = TextureLoader.FromFile(device,

                            @"..\..\" + mtrl[i].TextureFilename);

                    }

                }

            }

 

            if ((mesh.VertexFormat & VertexFormats.Normal) != VertexFormats.Normal)

            {

                // We must have normals for our patch meshes

                Mesh tempMesh = mesh.Clone(mesh.Options.Value,

                    mesh.VertexFormat | VertexFormats.Normal, device);

 

                tempMesh.ComputeNormals();

 

                mesh.Dispose();

                mesh = tempMesh;

            }

            return mesh;

        }

 

        private void SetupCamera()

        {

            device.Transform.Projection = Matrix.PerspectiveFovLH(

                (float)Math.PI / 4, this.Width / this.Height, 1.0f, 100.0f);

 

            device.Transform.View = Matrix.LookAtLH(new Vector3(0,0, 5.0f),

                new Vector3(), new Vector3(0,1,0));

 

            device.Lights[0].Type = LightType.Directional;

            device.Lights[0].Diffuse = Color.DarkKhaki;

            device.Lights[0].Direction = new Vector3(0, 0, -1);

            device.Lights[0].Update();

            device.Lights[0].Enabled = true;

 

        }

 

        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)

        {

            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.CornflowerBlue, 1.0f, 0);

 

            SetupCamera();

 

            device.BeginScene();

 

            // Draw our Mesh

            DrawMesh(angle / (float)Math.PI, angle / (float)Math.PI * 2.0f, angle / (float)Math.PI / 4.0f, 0.0f, 0.0f, 0.0f);

 

            font.DrawText(null, string.Format

                ("Number Vertices: {0}\r\nNumber Faces: {1}",

                mesh.NumberVertices, mesh.NumberFaces),

                new Rectangle(10,10,0,0),

                DrawTextFormat.NoClip, Color.Black);

 

            device.EndScene();

 

            device.Present();

 

            this.Invalidate();

        }

 

        private void DrawMesh(float yaw, float pitch, float roll, float x, float y, float z)

        {

            angle += 0.01f;

 

            device.Transform.World = Matrix.RotationYawPitchRoll(yaw, pitch, roll) * Matrix.Translation(x, y, z);

            for (int i = 0; i < meshMaterials.Length; i++)

            {

                device.Material = meshMaterials[i];

                device.SetTexture(0, meshTextures[i]);

                mesh.DrawSubset(i);

            }

        }

        protected override void OnKeyPress(KeyPressEventArgs e)

        {

            if (e.KeyChar == '+')

            {

                tessLevel += tessIncrement;

                CreatePatchMesh(filename, tessLevel);

            }

            if (e.KeyChar == '-')

            {

                tessLevel -= tessIncrement;

                CreatePatchMesh(filename, tessLevel);

            }

            if (e.KeyChar == 'c')

            {

                filename = @"..\..\cube.x";

                tessLevel = 1.0f;

                CreatePatchMesh(filename, tessLevel);

            }

            if (e.KeyChar == 'o')

            {

                filename = @"..\..\sphere.x";

                tessLevel = 1.0f;

                CreatePatchMesh(filename, tessLevel);

            }

            if (e.KeyChar == 't')

            {

                filename = @"..\..\tiger.x";

                tessLevel = 1.0f;

                CreatePatchMesh(filename, tessLevel);

            }

            if (e.KeyChar == 'w')

                device.RenderState.FillMode = FillMode.WireFrame;

 

            if (e.KeyChar == 's')

                device.RenderState.FillMode = FillMode.Solid;

 

        }

 

        /// <summary>

         /// Clean up any resources being used.

         /// </summary>

         protected override void Dispose( bool disposing )

         {

              if( disposing )

              {

                   if (components != null)

                   {

                       components.Dispose();

                   }

              }

              base.Dispose( disposing );

         }

 

         #region Windows Form Designer generated code

         /// <summary>

         /// Required method for Designer support - do not modify

         /// the contents of this method with the code editor.

         /// </summary>

         private void InitializeComponent()

         {

              this.components = new System.ComponentModel.Container();

              this.Size = new Size(800,600);

              this.Text = "Form1";

         }

         #endregion

 

         /// <summary>

         /// The main entry point for the application.

         /// </summary>

        static void Main()

        {

            using (Form1 frm = new Form1())

            {

                // Show our form and initialize our graphics engine

                frm.Show();

                frm.InitializeGraphics();

                Application.Run(frm);

            }

        }

     }

}

 

 

By sssa2000

5/4/2005

posted on 2005-05-04 03:34  大河马和小魔鱼  阅读(2497)  评论(2编辑  收藏  举报