private void CreateLod() { ProgressiveMesh pPMesh = null; int cVerticesMin = 0; int cVerticesMax = 0; int cVerticesPerMesh = 0; pPMesh = new ProgressiveMesh(m_mesh, m_adj, null, 1, MeshFlags.SimplifyVertex); cVerticesMin = pPMesh.MinVertices; cVerticesMax = pPMesh.MaxVertices; if (m_pMeshes != null) { for (int iPMesh = 0; iPMesh < m_pMeshes.Length; iPMesh++) { m_pMeshes[iPMesh].Dispose(); } } cVerticesPerMesh = (cVerticesMax - cVerticesMin) / m_nNumLOD; m_pMeshes = new ProgressiveMesh[m_nNumLOD]; // clone all the separate m_pMeshes for (int iPMesh = 0; iPMesh < m_pMeshes.Length; iPMesh++) { m_pMeshes[m_pMeshes.Length - 1 - iPMesh] = pPMesh.Clone(MeshFlags.Managed | MeshFlags.VbShare, pPMesh.VertexFormat, CGameEngine.Device3D); // trim to appropriate space if (m_nNumLOD > 1) { m_pMeshes[m_pMeshes.Length - 1 - iPMesh].TrimByVertices(cVerticesMin + cVerticesPerMesh * iPMesh, cVerticesMin + cVerticesPerMesh * (iPMesh + 1)); } m_pMeshes[m_pMeshes.Length - 1 - iPMesh].OptimizeBaseLevelOfDetail(MeshFlags.OptimizeVertexCache); } m_currentPmesh = 0; m_pMeshes[m_currentPmesh].NumberVertices = cVerticesMax; pPMesh.Dispose(); }
/// <summary> /// This event will be fired immediately after the Direct3D device has been /// created, which will happen during application initialization and windowed/full screen /// toggles. This is the best location to create Pool.Managed resources since these /// resources need to be reloaded whenever the device is destroyed. Resources created /// here should be released in the Disposing event. /// </summary> private void OnCreateDevice(object sender, DeviceEventArgs e) { // Initialize the stats font statsFont = ResourceCache.GetGlobalInstance().CreateFont(e.Device, 15, 0, FontWeight.Bold, 1, false, CharacterSet.Default, Precision.Default, FontQuality.Default, PitchAndFamily.FamilyDoNotCare | PitchAndFamily.DefaultPitch , "Arial"); // Define DEBUG_VS and/or DEBUG_PS to debug vertex and/or pixel shaders with the // shader debugger. Debugging vertex shaders requires either REF or software vertex // processing, and debugging pixel shaders requires REF. The // ShaderFlags.Force*SoftwareNoOptimizations flag improves the debug experience in the // shader debugger. It enables source level debugging, prevents instruction // reordering, prevents dead code elimination, and forces the compiler to compile // against the next higher available software target, which ensures that the // unoptimized shaders do not exceed the shader model limitations. Setting these // flags will cause slower rendering since the shaders will be unoptimized and // forced into software. See the DirectX documentation for more information about // using the shader debugger. ShaderFlags shaderFlags = ShaderFlags.None; #if (DEBUG_VS) shaderFlags |= ShaderFlags.ForceVertexShaderSoftwareNoOptimizations; #endif #if (DEBUG_PS) shaderFlags |= ShaderFlags.ForcePixelShaderSoftwareNoOptimizations; #endif // Read the D3DX effect file string path = Utility.FindMediaFile("ProgressiveMesh.fx"); effect = ResourceCache.GetGlobalInstance().CreateEffectFromFile(e.Device, path, null, null, shaderFlags, null); // Set the technique now, it will never be updated effect.Technique = "RenderScene"; // Load the mesh GraphicsStream adjacencyBuffer = null; ExtendedMaterial[] materials = null; // Find the mesh path = Utility.FindMediaFile("dwarf\\dwarf.x"); // Change the current directory to the mesh's directory so we can // find the textures. string currentFolder = System.IO.Directory.GetCurrentDirectory(); System.IO.FileInfo info = new System.IO.FileInfo(path); System.IO.Directory.SetCurrentDirectory(info.Directory.FullName); using (Mesh originalMesh = Mesh.FromFile(path, MeshFlags.Managed, e.Device, out adjacencyBuffer, out materials)) { int use32Bit = (int)(originalMesh.Options.Value & MeshFlags.Use32Bit); // Perform simple cleansing operations on mesh using (Mesh mesh = Mesh.Clean(CleanType.Simplification, originalMesh, adjacencyBuffer, adjacencyBuffer)) { // Perform a weld to try and remove excess vertices. // Weld the mesh using all epsilons of 0.0f. A small epsilon like 1e-6 works well too WeldEpsilons epsilons = new WeldEpsilons(); mesh.WeldVertices(0, epsilons, adjacencyBuffer, adjacencyBuffer); // Verify validity of mesh for simplification mesh.Validate(adjacencyBuffer); // Allocate a material/texture arrays meshMaterials = new Material[materials.Length]; meshTextures = new Texture[materials.Length]; // Copy the materials and load the textures for (int i = 0; i < meshMaterials.Length; i++) { meshMaterials[i] = materials[i].Material3D; meshMaterials[i].AmbientColor = meshMaterials[i].DiffuseColor; if ((materials[i].TextureFilename != null) && (materials[i].TextureFilename.Length > 0)) { // Create the texture meshTextures[i] = ResourceCache.GetGlobalInstance().CreateTextureFromFile(e.Device, materials[i].TextureFilename); } } // Find the mesh's center, then generate a centering matrix using (VertexBuffer vb = mesh.VertexBuffer) { using (GraphicsStream stm = vb.Lock(0, 0, LockFlags.NoSystemLock)) { try { objectRadius = Geometry.ComputeBoundingSphere(stm, mesh.NumberVertices, mesh.VertexFormat, out objectCenter); worldCenter = Matrix.Translation(-objectCenter); float scaleFactor = 2.0f / objectRadius; worldCenter *= Matrix.Scaling(scaleFactor, scaleFactor, scaleFactor); } finally { vb.Unlock(); } } } // If the mesh is missing normals, generate them. Mesh currentMesh = mesh; if ((mesh.VertexFormat & VertexFormats.Normal) == 0) { currentMesh = mesh.Clone(MeshFlags.Managed | (MeshFlags)use32Bit, mesh.VertexFormat | VertexFormats.Normal, e.Device); // Compute normals now currentMesh.ComputeNormals(); } using (currentMesh) { // Generate progressive meshes using (ProgressiveMesh pMesh = new ProgressiveMesh(currentMesh, adjacencyBuffer, null, 1, MeshFlags.SimplifyVertex)) { int minVerts = pMesh.MinVertices; int maxVerts = pMesh.MaxVertices; int vertsPerMesh = (maxVerts - minVerts + 10) / 10; // How many meshes should be in the array int numMeshes = Math.Max(1, (int)Math.Ceiling((maxVerts - minVerts + 1) / (float)vertsPerMesh)); meshes = new ProgressiveMesh[numMeshes]; // Clone full sized pmesh fullMesh = pMesh.Clone(MeshFlags.Managed | MeshFlags.VbShare, pMesh.VertexFormat, e.Device); // Clone all the separate pmeshes for (int iMesh = 0; iMesh < numMeshes; iMesh++) { meshes[iMesh] = pMesh.Clone(MeshFlags.Managed | MeshFlags.VbShare, pMesh.VertexFormat, e.Device); // Trim to appropriate space meshes[iMesh].TrimByVertices(minVerts + vertsPerMesh * iMesh, minVerts + vertsPerMesh * (iMesh + 1)); meshes[iMesh].OptimizeBaseLevelOfDetail(MeshFlags.OptimizeVertexCache); } // Set the current to be max vertices currentMeshIndex = numMeshes - 1; meshes[currentMeshIndex].NumberVertices = maxVerts; fullMesh.NumberVertices = maxVerts; // Set up the slider to reflect the vertices range the mesh has sampleUi.GetSlider(Detail).SetRange(meshes[0].MinVertices, meshes[meshes.Length - 1].MaxVertices); sampleUi.GetSlider(Detail).Value = (meshes[currentMeshIndex] as BaseMesh).NumberVertices; } } } } // Restore the original folder System.IO.Directory.SetCurrentDirectory(currentFolder); // Setup the camera's view parameters camera.SetViewParameters(new Vector3(0.0f, 0.0f, -5.0f), Vector3.Empty); }
/// <summary> /// The device has been created. Resources that are not lost on /// Reset() can be created here -- resources in Pool.Managed, /// Pool.Scratch, or Pool.SystemMemory. Image surfaces created via /// CreateImageSurface are never lost and can be created here. Vertex /// shaders and pixel shaders can also be created here as they are not /// lost on Reset(). /// </summary> protected override void InitializeDeviceObjects() { // Initialize the font's internal textures font.InitializeDeviceObjects(device); string path = DXUtil.FindMediaFile(initialDirectory, meshFilename); Mesh pMesh = null; Mesh pTempMesh = null; GraphicsStream adj = null; ExtendedMaterial[] mtrl = null; MeshFlags i32BitFlag; WeldEpsilons Epsilons = new WeldEpsilons(); ProgressiveMesh pPMesh = null; int cVerticesMin = 0; int cVerticesMax = 0; int cVerticesPerMesh = 0; try { // Load the mesh from the specified file pMesh = Mesh.FromFile(path, MeshFlags.Managed, device, out adj, out mtrl); i32BitFlag = pMesh.Options.Use32Bit ? MeshFlags.Use32Bit : 0; // perform simple cleansing operations on mesh pTempMesh = Mesh.Clean(pMesh, adj, adj); pMesh.Dispose(); pMesh = pTempMesh; // Perform a weld to try and remove excess vertices like the model bigship1.x in the DX9.0 SDK (current model is fixed) // Weld the mesh using all epsilons of 0.0f. A small epsilon like 1e-6 works well too pMesh.WeldVertices(0, Epsilons, adj, adj); // verify validity of mesh for simplification pMesh.Validate(adj); meshMaterials = new Direct3D.Material[mtrl.Length]; meshTextures = new Texture[mtrl.Length]; for (int i = 0; i < mtrl.Length; i++) { meshMaterials[i] = mtrl[i].Material3D; meshMaterials[i].Ambient = meshMaterials[i].Diffuse; if ((mtrl[i].TextureFilename != null) && (mtrl[i].TextureFilename != "")) { path = DXUtil.FindMediaFile(initialDirectory, mtrl[i].TextureFilename); // Find the path to the texture and create that texture try { meshTextures[i] = TextureLoader.FromFile(device, path); } catch { meshTextures[i] = null; } } } // Lock the vertex buffer to generate a simple bounding sphere VertexBuffer vb = pMesh.VertexBuffer; GraphicsStream vertexData = vb.Lock(0, 0, LockFlags.NoSystemLock); objectRadius = Geometry.ComputeBoundingSphere(vertexData, pMesh.NumberVertices, pMesh.VertexFormat, out objectCenter); vb.Unlock(); vb.Dispose(); if (meshMaterials.Length == 0) { throw new Exception(); } if ((pMesh.VertexFormat & VertexFormats.Normal) == 0) { pTempMesh = pMesh.Clone(i32BitFlag | MeshFlags.Managed, pMesh.VertexFormat | VertexFormats.Normal, device); pTempMesh.ComputeNormals(); pMesh.Dispose(); pMesh = pTempMesh; } pPMesh = new ProgressiveMesh(pMesh, adj, null, 1, MeshFlags.SimplifyVertex); cVerticesMin = pPMesh.MinVertices; cVerticesMax = pPMesh.MaxVertices; cVerticesPerMesh = (cVerticesMax - cVerticesMin) / 10; pmeshes = new ProgressiveMesh[(int)Math.Max(1, Math.Ceiling((cVerticesMax - cVerticesMin) / (float)cVerticesPerMesh))]; // clone full size pmesh fullPmesh = pPMesh.Clone(MeshFlags.Managed | MeshFlags.VbShare, pPMesh.VertexFormat, device); // clone all the separate pmeshes for (int iPMesh = 0; iPMesh < pmeshes.Length; iPMesh++) { pmeshes[iPMesh] = pPMesh.Clone(MeshFlags.Managed | MeshFlags.VbShare, pPMesh.VertexFormat, device); // trim to appropriate space pmeshes[iPMesh].TrimByVertices(cVerticesMin + cVerticesPerMesh * iPMesh, cVerticesMin + cVerticesPerMesh * (iPMesh + 1)); pmeshes[iPMesh].OptimizeBaseLevelOfDetail(MeshFlags.OptimizeVertexCache); } currentPmesh = pmeshes.Length - 1; pmeshes[currentPmesh].NumberVertices = cVerticesMax; fullPmesh.NumberVertices = cVerticesMax; pPMesh.Dispose(); } catch { // hide error so that device changes will not cause exit, shows blank screen instead return; } }