//////////////////////////////////////////////////////////////////////////////////// #region 根据顶点集及其面索引创建Mesh,并优化之 public void CreateExtrusionMesh(Device device) { int totalf = idxbuf.Length / 3; //总三角面数 int totalv = vexbuf.Length; //总顶点数 //创建网格对象 mesh = new Mesh(totalf, totalv, MeshFlags.Dynamic, CustomVertex.PositionNormalColored.Format, device); //设置mesh mesh.SetVertexBufferData(vexbuf, LockFlags.None); mesh.SetIndexBufferData(idxbuf, LockFlags.None); DEBUG_PrintMeshInfo("[Mesh] 没有优化"); //优化顶点数量,删除多余顶点,只有几乎绝对重复的点才删除 WeldEpsilons epsilon = new WeldEpsilons(); epsilon.Diffuse = 0.01F; epsilon.Position = 0.01F; epsilon.Normal = 0.01F; mesh.WeldVertices(WeldEpsilonsFlags.WeldPartialMatches, epsilon, null, null); DEBUG_PrintMeshInfo("[Mesh] 顶点优化"); //优化mesh,并由OptimizeInPlace自动计算SubSet个数 int[] adjacency = new int[mesh.NumberFaces * 3]; mesh.GenerateAdjacency(0.01F, adjacency); mesh.OptimizeInPlace(MeshFlags.OptimizeVertexCache | MeshFlags.OptimizeCompact, adjacency); //将mesh变成是writeonly的,加快效率! Mesh m = mesh.Clone(MeshFlags.WriteOnly, CustomVertex.PositionNormalColored.Format, device); mesh.Dispose(); mesh = m; }
/// <summary> /// Vytvoreni progressive meshe /// </summary> /// <param name="rootFrame">rootframe animace</param> /// <returns>vztvoreni progressive mesh</returns> private ProgressiveMesh GetMesh(AnimationRootFrame rootFrame) { ProgressiveMesh pm = null; Frame rf = rootFrame.FrameHierarchy; List <MeshContainer> meshes = new List <MeshContainer>(); getAnimationMesh(rf, meshes); anim.AnimationMeshContainer container = meshes[0] as anim.AnimationMeshContainer; int use32Bit = (int)(container.MeshData.Mesh.Options.Value & MeshFlags.Use32Bit); GraphicsStream adjacency = container.GetAdjacencyStream();//container.adjency; using (Mesh currentmesh = Mesh.Clean(CleanType.Simplification, container.MeshData.Mesh, adjacency, adjacency)) { WeldEpsilons epsilons = new WeldEpsilons(); currentmesh.WeldVertices(0, epsilons, adjacency, adjacency); currentmesh.Validate(adjacency); Mesh newmesh = currentmesh.Optimize(MeshFlags.OptimizeStripeReorder | MeshFlags.OptimizeAttributeSort, adjacency); using (newmesh = currentmesh.Clone(MeshFlags.Managed | (MeshFlags)use32Bit, GeneralObject.GeneralVertex.vertexElements, container.MeshData.Mesh.Device)) { newmesh.ComputeNormals(); pm = new ProgressiveMesh(newmesh, adjacency, null, 1, MeshFlags.SimplifyFace); } } return(pm); }
//////////////////////////////////////////////////////////////////////////////////// public void CreateWallMesh(Device device) { if (vexbuf == null) { Debug.WriteLine("[WallMesh] 空数据"); //此时根本无需存在任何垂直面需要绘制 mesh = null; return; } int totalf = vexbuf.Length / 4 * 2; //三角面总数 int totalv = vexbuf.Length; //顶点总数 //创建网格对象 mesh = new Mesh(totalf, totalv, MeshFlags.Dynamic, CustomVertex.PositionNormalColored.Format, device); //设置mesh mesh.SetVertexBufferData(vexbuf, LockFlags.None); mesh.SetIndexBufferData(idxbuf, LockFlags.None); DEBUG_PrintMeshInfo("[WallMesh] 没有优化"); //优化顶点数量,删除多余顶点,只有几乎绝对重复的顶点才进行融接 WeldEpsilons epsilon = new WeldEpsilons(); epsilon.Diffuse = 0.01F; epsilon.Position = 0.01F; epsilon.Normal = 0.01F; mesh.WeldVertices(WeldEpsilonsFlags.WeldPartialMatches, epsilon, null, null); DEBUG_PrintMeshInfo("[WallMesh] 顶点优化"); //针对cache命中率进行优化排序 int[] adjacency = new int[mesh.NumberFaces * 3]; mesh.GenerateAdjacency(0.01F, adjacency); mesh.OptimizeInPlace(MeshFlags.OptimizeVertexCache | MeshFlags.OptimizeCompact, adjacency); //将mesh变成是writeonly的,加快效率! Mesh m = mesh.Clone(MeshFlags.WriteOnly, CustomVertex.PositionNormalColored.Format, device); mesh.Dispose(); mesh = m; }
} // initGfx() // ---] // [--- private void gerarModeloSimplificado() { // Primeiro o objeto original é clonado mesh_simplificado = objeto3D.Clone(objeto3D.Options.Value, objeto3D.VertexFormat | VertexFormats.Normal, device); // Este conjunto de flags, WeldEpsilonsFlags, define opções de // soldagem. WeldPartialMatches solda os vértices que estão // dentro dos valores de tolerância definidos pela estrutura // WeldEpsilons; WeldAll solda os vértices se houver sobreposição // indicada pela informação de adjacências. WeldEpsilonsFlags wy_flags; wy_flags = WeldEpsilonsFlags.WeldPartialMatches | WeldEpsilonsFlags.WeldAll; // Produzimos uma estrutura WeldEpsilons que já é configurada // com valores de tolerâncias aceitáveis para os diversos // atributos do objeto 3d para o processo de soldagem. WeldEpsilons wy_tolerancias = new WeldEpsilons(); mesh_simplificado.WeldVertices(wy_flags, wy_tolerancias, null, null); } // gerarModeloSimplificado()
/// <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; } }
/// <summary> /// The device exists, but may have just been Reset(). Resources in /// Pool.Default and any other device state that persists during /// rendering should be set here. Render states, matrices, textures, /// etc., that don't change during rendering can be set once here to /// avoid redundant state setting during Render() or FrameMove(). /// </summary> protected override void RestoreDeviceObjects(System.Object sender, System.EventArgs e) { MyVertex[] v; Mesh pWallMeshTemp = null; // Create a square grid numberVertsX*numberVertsZ for rendering the wall pWallMeshTemp = new Mesh(numTriangles, numTriangles * 3, 0, MyVertex.Format, device); // Fill in the grid vertex data v = (MyVertex[])pWallMeshTemp.LockVertexBuffer(typeof(MyVertex), 0, numTriangles * 3); float dX = 1.0f / (numberVertsX - 1); float dZ = 1.0f / (numberVertsZ - 1); uint k = 0; for (uint z = 0; z < (numberVertsZ - 1); z++) { for (uint x = 0; x < (numberVertsX - 1); x++) { v[k].p = new Vector3(10 * x * dX, 0.0f, 10 * z * dZ); v[k].n = new Vector3(0.0f, 1.0f, 0.0f); k++; v[k].p = new Vector3(10 * x * dX, 0.0f, 10 * (z + 1) * dZ); v[k].n = new Vector3(0.0f, 1.0f, 0.0f); k++; v[k].p = new Vector3(10 * (x + 1) * dX, 0.0f, 10 * (z + 1) * dZ); v[k].n = new Vector3(0.0f, 1.0f, 0.0f); k++; v[k].p = new Vector3(10 * x * dX, 0.0f, 10 * z * dZ); v[k].n = new Vector3(0.0f, 1.0f, 0.0f); k++; v[k].p = new Vector3(10 * (x + 1) * dX, 0.0f, 10 * (z + 1) * dZ); v[k].n = new Vector3(0.0f, 1.0f, 0.0f); k++; v[k].p = new Vector3(10 * (x + 1) * dX, 0.0f, 10 * z * dZ); v[k].n = new Vector3(0.0f, 1.0f, 0.0f); k++; } } pWallMeshTemp.UnlockVertexBuffer(); // Fill in index data ushort[] pIndex; pIndex = (ushort[])pWallMeshTemp.LockIndexBuffer(typeof(ushort), 0, numTriangles * 3); for (ushort iIndex = 0; iIndex < numTriangles * 3; iIndex++) { pIndex[iIndex] = iIndex; } pWallMeshTemp.UnlockIndexBuffer(); // Eliminate redundant vertices int[] pdwAdjacency = new int[3 * numTriangles]; WeldEpsilons we = new WeldEpsilons(); pWallMeshTemp.GenerateAdjacency(0.01f, pdwAdjacency); pWallMeshTemp.WeldVertices(WeldEpsilonsFlags.WeldAll, we, pdwAdjacency); // Optimize the mesh wallMesh = pWallMeshTemp.Optimize(MeshFlags.OptimizeCompact | MeshFlags.OptimizeVertexCache | MeshFlags.VbDynamic | MeshFlags.VbWriteOnly, pdwAdjacency); pWallMeshTemp = null; pdwAdjacency = null; // Create sphere and cone meshes to represent the lights sphereMesh = Mesh.Sphere(device, 0.25f, 20, 20); coneMesh = Mesh.Cylinder(device, 0.0f, 0.25f, 0.5f, 20, 20); // Set up a material Microsoft.DirectX.Direct3D.Material mtrl = GraphicsUtility.InitMaterial(System.Drawing.Color.White); device.Material = mtrl; // Set miscellaneous render states device.RenderState.DitherEnable = false; device.RenderState.SpecularEnable = false; // Set the world matrix Matrix matIdentity = Matrix.Identity; device.SetTransform(TransformType.World, matIdentity); // Set the view matrix. Matrix matView; Vector3 vFromPt = new Vector3(-10, 10, -10); Vector3 vLookatPt = new Vector3(0.0f, 0.0f, 0.0f); Vector3 vUpVec = new Vector3(0.0f, 1.0f, 0.0f); matView = Matrix.LookAtLH(vFromPt, vLookatPt, vUpVec); device.SetTransform(TransformType.View, matView); // Set the projection matrix Matrix matProj; float fAspect = ((float)device.PresentationParameters.BackBufferWidth) / device.PresentationParameters.BackBufferHeight; matProj = Matrix.PerspectiveFovLH((float)Math.PI / 4, fAspect, 1.0f, 100.0f); device.SetTransform(TransformType.Projection, matProj); // Turn on lighting. device.RenderState.Lighting = true; // Enable ambient lighting to a dim, grey light, so objects that // are not lit by the other lights are not completely black device.RenderState.Ambient = System.Drawing.Color.Gray; // Set light #0 to be a simple, faint grey directional light so // the walls and floor are slightly different shades of grey device.Lights[0].Type = LightType.Directional; device.Lights[0].Direction = new Vector3(0.3f, -0.5f, 0.2f); device.Lights[0].Diffuse = System.Drawing.Color.FromArgb(64, 64, 64); device.Lights[0].Commit(); // Set light #1 to be a simple, bright directional light to use // on the mesh representing light #2 device.Lights[1].Type = LightType.Directional; device.Lights[1].Direction = new Vector3(0.5f, -0.5f, 0.5f); device.Lights[1].Diffuse = System.Drawing.Color.White; device.Lights[1].Commit(); // Light #2 will be the light used to light the floor and walls. It will // be set up in FrameMove() since it changes every frame. }
/// <summary> /// Generate normals and tangents if not present and convert into TangentVertex format for shaders. /// </summary> /// <param name="device">The <see cref="Device"/> containing the mesh</param> /// <param name="mesh">The mesh to be manipulated</param> /// <param name="weldVertexes">Weld vertexes before generating tangents. /// Useful for organic objects, stones, trees, etc. (anything with a lot of round surfaces). /// If a lot of single faces are not connected on the texture (e.g. rockets, buildings, etc.) do not use.</param> public static void GenerateNormalsAndTangents(Device device, ref Mesh mesh, bool weldVertexes) { #region Sanity checks if (device == null) { throw new ArgumentNullException(nameof(device)); } if (mesh == null) { throw new ArgumentNullException(nameof(mesh)); } #endregion bool hadNormals, hadTangents; if (!ExpandDeclaration(device, ref mesh, out hadNormals, out hadTangents)) { return; } var decl = mesh.GetDeclaration(); #region Check existing info bool gotMilkErmTexCoords = false; bool gotValidNormals = true; bool gotValidTangents = true; var vertexes = BufferHelper.ReadVertexBuffer <PositionNormalBinormalTangentTextured>(mesh); // Check all vertexes for (int num = 0; num < vertexes.Length; num++) { // We need at least 1 texture coordinate different from (0, 0) if (vertexes[num].Tu != 0.0f || vertexes[num].Tv != 0.0f) { gotMilkErmTexCoords = true; } // All normals and tangents must be valid, otherwise generate them below if (vertexes[num].Normal == default(Vector3)) { gotValidNormals = false; } if (vertexes[num].Tangent == default(Vector3)) { gotValidTangents = false; } // If we found valid texture coordinates and no normals or tangents, // there isn't anything left to check here if (gotMilkErmTexCoords && !gotValidNormals && !gotValidTangents) { break; } } // If declaration had normals, but we found no valid normals, // set hadNormals to false and generate valid normals (see below) if (!gotValidNormals) { hadNormals = false; } // Same check for tangents if (!gotValidTangents) { hadTangents = false; } // Generate dummy texture coordinates if (!gotMilkErmTexCoords) { for (int num = 0; num < vertexes.Length; num++) { vertexes[num].Tu = -0.75f + vertexes[num].Position.X / 2.0f; vertexes[num].Tv = +0.75f - vertexes[num].Position.Y / 2.0f; } } BufferHelper.WriteVertexBuffer(mesh, vertexes); #endregion if (!hadNormals) { using (new TimedLogEvent("Computed normals")) mesh.ComputeNormals(); } if (weldVertexes) { // Reduce amount of vertexes var weldEpsilons = new WeldEpsilons { Position = 0.0001f, Normal = 0.0001f }; mesh.WeldVertices(WeldFlags.WeldPartialMatches, weldEpsilons); if (!hadTangents) { #region Compute tangents using (new TimedLogEvent("Computed tangents")) { // If the vertexes for a smoothend point exist several times the // DirectX ComputeTangent method is not able to treat them all the // same way. // To circumvent this, we collapse all vertexes in a cloned mesh // even if the texture coordinates don't fit. Then we copy the // generated tangents back to the original mesh vertexes (duplicating // the tangents for vertexes at the same point with the same normals // if required). This happens usually with models exported from 3DSMax. // Clone mesh just for tangent generation Mesh dummyTangentGenerationMesh = mesh.Clone(device, mesh.CreationOptions, decl); // Reuse weldEpsilons, just change the TextureCoordinates, which we don't care about anymore weldEpsilons.TextureCoordinate1 = 1; weldEpsilons.TextureCoordinate2 = 1; weldEpsilons.TextureCoordinate3 = 1; weldEpsilons.TextureCoordinate4 = 1; weldEpsilons.TextureCoordinate5 = 1; weldEpsilons.TextureCoordinate6 = 1; weldEpsilons.TextureCoordinate7 = 1; weldEpsilons.TextureCoordinate8 = 1; // Rest of the weldEpsilons values can stay 0, we don't use them dummyTangentGenerationMesh.WeldVertices(WeldFlags.WeldPartialMatches, weldEpsilons); // Compute tangents if (!CompareDecl(PositionNormalMultiTextured.GetVertexElements(), decl)) { dummyTangentGenerationMesh.ComputeTangent(0, 0, 0, false); } var tangentVertexes = BufferHelper.ReadVertexBuffer <PositionNormalBinormalTangentTextured>(dummyTangentGenerationMesh); dummyTangentGenerationMesh.Dispose(); // Copy generated tangents back vertexes = BufferHelper.ReadVertexBuffer <PositionNormalBinormalTangentTextured>(mesh); for (int num = 0; num < vertexes.Length; num++) { // Search for tangent vertex with the exact same position and normal. for (int tangentVertexNum = 0; tangentVertexNum < tangentVertexes.Length; tangentVertexNum++) { if (vertexes[num].Position == tangentVertexes[tangentVertexNum].Position && vertexes[num].Normal == tangentVertexes[tangentVertexNum].Normal) { // Copy the tangent over vertexes[num].Tangent = tangentVertexes[tangentVertexNum].Tangent; // No more checks required, proceed with next vertex break; } } } BufferHelper.WriteVertexBuffer(mesh, vertexes); } #endregion } } else { if (!hadTangents && CompareDecl(PositionNormalMultiTextured.GetVertexElements(), decl)) { using (new TimedLogEvent("Computed tangents")) mesh.ComputeTangent(0, 0, D3DX.Default, false); } } Optimize(mesh); }
public Model(string name, string meshFile, Vector3 offset, Attitude adjust) : base(name) { Mesh pTempMesh = null; WeldEpsilons Epsilons = new WeldEpsilons(); Vector3 objectCenter; // Center of bounding sphere of object m_vOffset = offset; m_AttitudeOffset = adjust; m_vPosition.X = 100.0f; m_vPosition.Z = 100.0f; ExtendedMaterial[] materials = null; try { // Load the m_mesh from the specified file m_mesh = Mesh.FromFile(meshFile, MeshFlags.SystemMemory, CGameEngine.Device3D, out m_adj, out materials); // Lock the vertex buffer to generate a simple bounding sphere VertexBuffer vb = m_mesh.VertexBuffer; GraphicsStream vertexData = vb.Lock(0, 0, LockFlags.NoSystemLock); m_fRadius = Geometry.ComputeBoundingSphere(vertexData, m_mesh.NumberVertices, m_mesh.VertexFormat, out objectCenter); Geometry.ComputeBoundingBox(vertexData, m_mesh.NumberVertices, m_mesh.VertexFormat, out m_NegativeExtents, out m_PositiveExtents); vb.Unlock(); vb.Dispose(); m_vOffset.Y = -m_NegativeExtents.Y; m_Corners[0].X = m_NegativeExtents.X; m_Corners[0].Y = m_NegativeExtents.Y + m_vOffset.Y; m_Corners[0].Z = m_NegativeExtents.Z; m_Corners[1].X = m_PositiveExtents.X; m_Corners[1].Y = m_NegativeExtents.Y + m_vOffset.Y; m_Corners[1].Z = m_NegativeExtents.Z; m_Corners[2].X = m_NegativeExtents.X; m_Corners[2].Y = m_PositiveExtents.Y + m_vOffset.Y; m_Corners[2].Z = m_NegativeExtents.Z; m_Corners[3].X = m_PositiveExtents.X; m_Corners[3].Y = m_PositiveExtents.Y + m_vOffset.Y; m_Corners[3].Z = m_NegativeExtents.Z; m_Corners[4].X = m_NegativeExtents.X; m_Corners[4].Y = m_NegativeExtents.Y + m_vOffset.Y; m_Corners[4].Z = m_PositiveExtents.Z; m_Corners[5].X = m_PositiveExtents.X; m_Corners[5].Y = m_NegativeExtents.Y + m_vOffset.Y; m_Corners[5].Z = m_PositiveExtents.Z; m_Corners[6].X = m_PositiveExtents.X; m_Corners[6].Y = m_PositiveExtents.Y + m_vOffset.Y; m_Corners[6].Z = m_PositiveExtents.Z; m_Corners[7].X = m_PositiveExtents.X; m_Corners[7].Y = m_PositiveExtents.Y + m_vOffset.Y; m_Corners[7].Z = m_PositiveExtents.Z; // Console.AddLine("Max extents " + m_PositiveExtents); // Console.AddLine("Min extents " + m_NegativeExtents); // perform simple cleansing operations on m_mesh pTempMesh = Mesh.Clean(m_mesh, m_adj, m_adj); m_mesh.Dispose(); m_mesh = 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 m_mesh using all epsilons of 0.0f. A small epsilon like 1e-6 works well too m_mesh.WeldVertices(0, Epsilons, m_adj, m_adj); // verify validity of m_mesh for simplification m_mesh.Validate(m_adj); CreateLod(); } catch (DirectXException d3de) { Console.AddLine("Unable to load mesh " + meshFile); Console.AddLine(d3de.ErrorString); } catch (Exception e) { Console.AddLine("Unable to load mesh " + meshFile); Console.AddLine(e.Message); } if (m_meshTextures == null && materials != null) { // We need to extract the material properties and texture names m_meshTextures = new Texture[materials.Length]; m_meshMaterials = new Material[materials.Length]; for (int i = 0; i < materials.Length; i++) { m_meshMaterials[i] = materials[i].Material3D; // Set the ambient color for the material (D3DX does not do this) m_meshMaterials[i].Ambient = m_meshMaterials[i].Diffuse; // Create the texture try { if (materials[i].TextureFilename != null) { m_meshTextures[i] = TextureLoader.FromFile(CGameEngine.Device3D, @"..\..\Resources\" + materials[i].TextureFilename); } } catch (DirectXException d3de) { Console.AddLine("Unable to load texture " + materials[i].TextureFilename); Console.AddLine(d3de.ErrorString); } catch (Exception e) { Console.AddLine("Unable to load texture " + materials[i].TextureFilename); Console.AddLine(e.Message); } } } }
public MeshObject(string fileName, Vector3 offset, Attitude adjust) : base() { _levelsOfDetail = 1; _maxLevelOfDetailRange = 1.0f; _positiveExtents = new Vector3(-1.0f, -1.0f, -1.0f); _negativeExtents = new Vector3(1.0f, 1.0f, 1.0f); _corners = new Vector3[8]; Mesh tempMesh = null; WeldEpsilons epsilons = new WeldEpsilons(); Vector3 center; _offset = offset; _attitudeOffset = adjust; Position = new Vector3(100.0f, 100.0f, 0.0f); MaterialList materials = null; _mesh = new Mesh(GameEngine.Device, fileName, MeshFlags.SystemMemory, _adjacency, materials, new EffectInstanceList()); VertexBuffer vertexBuffer = _mesh.VertexBuffer; GraphicsBuffer vertexData = vertexBuffer.Lock(0, 0, LockFlags.NoSystemLock); BoundingRadius = Geometry.ComputeBoundingSphere(vertexData, _mesh.NumberVertices, _mesh.VertexFormat).Radius; BoundingBox box = Geometry.ComputeBoundingBox(vertexData, _mesh.NumberVertices, _mesh.VertexFormat); vertexBuffer.Unlock(); vertexBuffer.Dispose(); //calculate the corners of the bounding box based on the extents obtained //from the mesh. _offset.Y = -_negativeExtents.Y; _corners[0].X = _negativeExtents.X; _corners[0].Y = _negativeExtents.Y + _offset.Y; _corners[0].Z = _negativeExtents.Z; _corners[1].X = _positiveExtents.X; _corners[1].Y = _negativeExtents.Y + _offset.Y; _corners[1].Z = _negativeExtents.Z; _corners[2].X = _negativeExtents.X; _corners[2].Y = _positiveExtents.Y + _offset.Y; _corners[2].Z = _negativeExtents.Z; _corners[3].X = _positiveExtents.X; _corners[3].Y = _positiveExtents.Y + _offset.Y; _corners[3].Z = _negativeExtents.Z; _corners[4].X = _negativeExtents.X; _corners[4].Y = _negativeExtents.Y; _corners[4].Z = _positiveExtents.Z; _corners[5].X = _positiveExtents.X; _corners[5].Y = _negativeExtents.Y + _offset.Y; _corners[5].Z = _positiveExtents.Z; _corners[6].X = _positiveExtents.X; _corners[6].Y = _positiveExtents.Y + _offset.Y; _corners[6].Z = _positiveExtents.Z; //TODO: IS THIS CORRECT? _corners[7].X = _positiveExtents.X; _corners[7].Y = _positiveExtents.Y + _offset.Y; _corners[7].Z = _positiveExtents.Z; tempMesh = Mesh.Clean(CleanType.BackFacing, _mesh, _adjacency, _adjacency); _mesh.Dispose(); _mesh = tempMesh; _mesh.WeldVertices(0, epsilons, _adjacency); _mesh.Validate(_adjacency); //CreateLevelOfDetail(); if (_meshTextures == null && materials != null) { _meshTextures = new Texture[materials.Count]; _meshMaterials = new Material[materials.Count]; for (int i = 0; i < materials.Count; i++) { _meshMaterials[i] = materials[i].Material; _meshMaterials[i].AmbientColor = _meshMaterials[i].DiffuseColor; if (materials[i].TextureFileName != null) { _meshTextures[i] = new Texture(GameEngine.Device, materials[i].TextureFileName); } } } }
/// <summary> /// Nacteni meshe a textur a vytvoreni progressive meshe /// </summary> /// <param name="loaded">Entita do ktere chceme pridat mesh a texturz</param> /// <param name="data">pole obsahujuci nazev meshe a textur</param> /// <param name="name">nazev objektu</param> /// <returns>Entita s meshem a texturami</returns> private Entity LoadMeshFromFile(Entity loaded, string[] data, string name) { string[] textureUrl; string objectUrl = data[0]; int p = FindMesh(objectUrl); if (p != -1) { loaded.AddParametr(new Parametr("Objekt[]", data[1], meshInf[p].textures)); loaded.AddParametr(new Parametr("Microsoft.DirectX.Direct3D.ProgressiveMesh", name, meshInf[p].pMesh)); loaded.AddParametr(new Parametr("Objekt[]", "SpecialTextureUrl", meshInf[p].texturesUrl)); return(loaded); } if (data.Length < 2) { throw new Exception("Chyba pri nacitani nespravny pocet parametru"); } Texture[] textures; ProgressiveMesh currentLoadedMesh; Matrix localTransformation = Matrix.Identity; GraphicsStream adjency = null; ExtendedMaterial[] mat = null; using (Mesh mesh = Mesh.FromFile(objectUrl, MeshFlags.Managed, dev, out adjency, out mat)) { #region Texture Loading textures = new Texture[mat.Length]; textureUrl = new string[mat.Length]; string pathname = Path.GetDirectoryName(objectUrl) + @"\"; for (int i = 0; i < mat.Length; i++) { if (mat[i].TextureFilename == null) { continue; } string abspath = (pathname + mat[i].TextureFilename); textureUrl[i] = abspath; string path = mat[i].TextureFilename; bool createtexture = true; for (int t = 0; t < i; t++) { if (mat[t].TextureFilename == mat[i].TextureFilename) { textures[i] = textures[t]; createtexture = false; break; } } if (createtexture) { if (File.Exists(path)) { textures[i] = TextureLoader.FromFile(dev, path, 0, 0, 0, Usage.None, Format.R8G8B8, Pool.Managed, Filter.Linear, Filter.Linear, 0); } else if (File.Exists(abspath)) { textures[i] = TextureLoader.FromFile(dev, abspath, 0, 0, 0, Usage.None, Format.R8G8B8, Pool.Managed, Filter.Linear, Filter.Linear, 0); } } } loaded.AddParametr(new Parametr("Objekt[]", "SpecialTextureUrl", textureUrl)); #endregion #region Mesh clean, optimize and compute normals/tangents int use32Bit = (int)(mesh.Options.Value & MeshFlags.Use32Bit); using (Mesh currentmesh = Mesh.Clean(CleanType.Simplification, mesh, adjency, adjency)) { WeldEpsilons epsilons = new WeldEpsilons(); currentmesh.WeldVertices(0, epsilons, adjency, adjency); currentmesh.Validate(adjency); Mesh newmesh = currentmesh.Optimize(MeshFlags.OptimizeStripeReorder | MeshFlags.OptimizeAttributeSort, adjency); using (newmesh = currentmesh.Clone(MeshFlags.Managed | (MeshFlags)use32Bit, GeneralObject.GeneralVertex.vertexElements, dev)) { newmesh.ComputeNormals(); ProgressiveMesh pm = new ProgressiveMesh(newmesh, adjency, null, 1, MeshFlags.SimplifyFace); currentLoadedMesh = pm; currentLoadedMesh.NumberFaces = currentLoadedMesh.MaxFaces; } } #endregion } meshInf.Add(new MeshInformation(currentLoadedMesh, objectUrl, textures, textureUrl)); loaded.AddParametr(new Parametr("Objekt[]", data[1], textures)); loaded.AddParametr(new Parametr("Microsoft.DirectX.Direct3D.ProgressiveMesh", name, currentLoadedMesh)); return(loaded); }