There are cases where you'd only want to simplify a mesh (for example, imagine a model for a rocket you've fired). However, if you are only simplifying, it means the object's detail never needs to be increased, which isn't as common as the cases where you may need to progressively lower or raise the level of detail. I'm sure you can see where the name of the progressive mesh came from. To show the behavior of the progressive meshes, we will write a similar application to the one we wrote for the simplification mesh a moment ago. However, instead of just simplifying, we will also allow you to raise the level of detail by bringing the model closer to the camera as well. Once again we will start with the example to load a mesh from a file. The ProgressiveMesh class derives from the BaseMesh class just like the Mesh class does. You can use the progressive mesh class to draw your objects, unlike the SimplificationMesh object. Knowing this, we can replace the declaration for your mesh object with this one: private ProgressiveMesh progressiveMesh = null; You will also need to replace each instance of the old mesh variable with the new progressiveMesh variable. With the new variable name and type, we obviously need to update the LoadMesh method, since our code won't even compile right now. We will use a similar method; we will just generate our progressive mesh at the end. Use the code shown in Listing 9.3. Listing 9.3 Loading a Progressive Meshprivate 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, null, 1, MeshFlags.SimplifyVertex); // Set the initial mesh to the max progressiveMesh.NumberFaces = progressiveMesh.MaxFaces; progressiveMesh.NumberVertices = progressiveMesh.MaxVertices; } } } Notice that we use two temporary mesh objects to generate our progressive mesh. We use the cleaned mesh to do the actual generation of our progressive mesh. The fourth parameter is the important one for the progressive mesh constructor: It is the minimum number of vertices or faces we want in the generated mesh, depending on the MeshFlags option you pass in (either SimplifyFace or SimplifyVertex). Naturally, this is just an approximation, and even if it's not possible to simplify the mesh to that degree, this method should still succeed. You'll also notice that immediately we set the number of vertices and facesto the maximum values. Generating the mesh leaves it simplified. Setting the number of faces and/or vertices on a progressive mesh will update the current level of detail used to render the mesh. Since we want to initially start out at full detail, we naturally update the number of faces and vertices to their maximums. To get the application to compile, you'll need to change the DrawSubset call in your DrawMesh method as follows: progressiveMesh.DrawSubset(i); Running the application now, you should notice that it behaves exactly as the original sample did. Our model spins around and is fully detailed. Now, we need to handle our keypresses and camera movement. We will use code similar to what we used in our simplification mesh example. You will need the declarations for the camera position variable and the move amount constant: private float cameraPos = 580.0f; private const int MoveAmount = 100; You'll once again need to modify your view transform to allow the camera position to be updated. Replace the view transform line with the following: device.Transform.View = Matrix.LookAtLH(new Vector3(0,0, cameraPos), new Vector3(), new Vector3(0,1,0)); Finally, you'll need to handle the keystrokes and react accordingly. Use the override in Listing 9.4: Listing 9.4 KeyPress Event Handlerprotected 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; } Once again, we allow you to switch between wire-frame mode and solid mode by pressing the "w" or "s" key. We also keep the "+" key as our "decrease level of detail" key. When the "+" key is pressed, we move our camera farther away, and update the number of vertices and faces. You'll notice that when we are attempting to actually get the number of faces or vertices, we must first cast our progressive mesh to a BaseMesh object. Since the set property exists on the progressive mesh object, while the get property exists on the base object, this cast is necessary. We also added the capability to increase our level of detail now as well. Naturally, we used the " " key for this operation. It essentially does the exact opposite of what the "+" keys does. It moves the camera closer, and increases the number of vertices and faces in the mesh.
It would also be nice to add some rendered text to the application to show the number of vertices and meshes currently being rendered. Rather than repeat the code here, you can look earlier in this chapter for the declaration and initialization of a font variable. Once those are in your application, you can use the following in your rendering method: 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); |