/// <summary> /// Creates a preview of the given <see cref="Unreal.Classes.StaticMesh"/>. /// </summary> /// <param name="Device">The Direct3D device to use for buffer creation.</param> /// <param name="m">The mesh to generate a preview for.</param> /// <param name="texcache">The texture cache for loading textures.</param> public ModelPreview(Device Device, Unreal.Classes.StaticMesh m, PreviewTextureCache texcache) { // STEP 1: MESH List <Triangle> triangles = new List <Triangle>(); List <WorldVertex> vertices = new List <WorldVertex>(); // Gather all the vertex data // Only one LOD? odd but I guess that's just how it rolls. for (int i = 0; i < m.Mesh.Vertices.Points.Count; i++) { // Note the reversal of the Z and Y coordinates. Unreal seems to think that Z should be up. vertices.Add(new Scene3D.WorldVertex(new SharpDX.Vector3(-m.Mesh.Vertices.Points[i].X, m.Mesh.Vertices.Points[i].Z, m.Mesh.Vertices.Points[i].Y), SharpDX.Vector3.Zero, new SharpDX.Vector2(m.Mesh.Edges.UVSet[i].UVs[0].X, m.Mesh.Edges.UVSet[i].UVs[0].Y))); } // Sometimes there might not be an index buffer. // If there is one, use that. // Otherwise, assume that each vertex is uesd exactly once. // Note that this is based on the earlier implementstion which didn't take LODs into consideration, which is odd considering that both the hit testing and the skeletalmesh class do. if (m.Mesh.IdxBuf.Indexes != null && m.Mesh.IdxBuf.Indexes.Count > 0) { // Hey, we have indices all set up for us. How considerate. for (int i = 0; i < m.Mesh.IdxBuf.Indexes.Count; i += 3) { triangles.Add(new Triangle(m.Mesh.IdxBuf.Indexes[i], m.Mesh.IdxBuf.Indexes[i + 1], m.Mesh.IdxBuf.Indexes[i + 2])); } } else { // Gather all the vertex data from the raw triangles, not the Mesh.Vertices.Point list. for (int i = 0; i < m.Mesh.RawTris.RawTriangles.Count; i++) { triangles.Add(new Triangle((uint)m.Mesh.RawTris.RawTriangles[i].v0, (uint)m.Mesh.RawTris.RawTriangles[i].v1, (uint)m.Mesh.RawTris.RawTriangles[i].v2)); } } // STEP 2: MATERIALS Materials = new Dictionary <string, ModelPreviewMaterial>(); foreach (Unreal.Classes.MaterialInstanceConstant mat in m.Mesh.Mat.MatInst) { ModelPreviewMaterial material; // TODO: pick what material class best fits based on what properties the // MaterialInstanceConstant mat has. // For now, just use the default material. material = new TexturedPreviewMaterial(texcache, mat); AddMaterial(material.Properties["Name"], material); } // STEP 3: SECTIONS List <ModelPreviewSection> sections = new List <ModelPreviewSection>(); foreach (Unreal.Classes.StaticMesh.Section section in m.Mesh.Mat.Lods[0].Sections) { sections.Add(new ModelPreviewSection(m.pcc.getObjectName(section.Name), (uint)section.FirstIdx1, (uint)section.NumFaces1)); } LODs = new List <ModelPreviewLOD>(); LODs.Add(new ModelPreviewLOD(new WorldMesh(Device, triangles, vertices), sections)); }
/// <summary> /// Creates a TexturedPreviewMaterial that renders as close to what the given <see cref="MaterialInstanceConstant"/> looks like as possible. /// </summary> /// <param name="texcache">The texture cache to request textures from.</param> /// <param name="mat">The material that this ModelPreviewMaterial will try to look like.</param> public TexturedPreviewMaterial(PreviewTextureCache texcache, MaterialInstanceConstant mat, PackageCache assetCache, List <PreloadedTextureData> preloadedTextures = null) : base(texcache, mat, assetCache, preloadedTextures) { string matPackage = null; if (mat.Export.Parent != null) { matPackage = mat.Export.Parent.FullPath.ToLower(); } foreach (var textureEntry in mat.Textures) { var texObjectName = textureEntry.FullPath.ToLower(); if ((matPackage == null || texObjectName.StartsWith(matPackage)) && texObjectName.Contains("diff")) { // we have found the diffuse texture! DiffuseTextureFullName = textureEntry.FullPath; Debug.WriteLine("Diffuse texture of new material <" + Properties["Name"] + "> is " + DiffuseTextureFullName); return; } } foreach (var textureEntry in mat.Textures) { var texObjectName = textureEntry.ObjectName.Name.ToLower(); if (texObjectName.Contains("diff") || texObjectName.Contains("tex")) { // we have found the diffuse texture! DiffuseTextureFullName = textureEntry.FullPath; Debug.WriteLine("Diffuse texture of new material <" + Properties["Name"] + "> is " + DiffuseTextureFullName); return; } } foreach (var texparam in mat.Textures) { var texObjectName = texparam.ObjectName.Name.ToLower(); if (texObjectName.Contains("detail")) { // I guess a detail texture is good enough if we didn't return for a diffuse texture earlier... DiffuseTextureFullName = texparam.FullPath; Debug.WriteLine("Diffuse (Detail) texture of new material <" + Properties["Name"] + "> is " + DiffuseTextureFullName); return; } } foreach (var texparam in mat.Textures) { var texObjectName = texparam.ObjectName.Name.ToLower(); if (!texObjectName.Contains("norm") && !texObjectName.Contains("opac")) { //Anything is better than nothing I suppose DiffuseTextureFullName = texparam.FullPath; Debug.WriteLine("Using first found texture (last resort) of new material <" + Properties["Name"] + "> as diffuse: " + DiffuseTextureFullName); return; } } }
/// <summary> /// Creates a ModelPreviewMaterial that renders as close to what the given <see cref="Unreal.Classes.MaterialInstanceConstant"/> looks like as possible. /// </summary> /// <param name="texcache">The texture cache to request textures from.</param> /// <param name="mat">The material that this ModelPreviewMaterial will try to look like.</param> public ModelPreviewMaterial(PreviewTextureCache texcache, Unreal.Classes.MaterialInstanceConstant mat) { Properties.Add("Name", mat.pcc.Exports[mat.index].ObjectName); foreach (Unreal.Classes.MaterialInstanceConstant.TextureParam texparam in mat.Textures) { if (texparam.TexIndex != 0) { Textures.Add(texparam.Desc, FindTexture(texcache, mat.pcc.getEntry(texparam.TexIndex).GetFullPath, mat.pcc.FileName)); } } }
/// <summary> /// Creates a preview of a generic untextured mesh /// </summary> /// <param name="device"></param> /// <param name="mesh"></param> public ModelPreview(Device device, WorldMesh mesh, PreviewTextureCache texcache, PackageCache assetCache, PreloadedModelData preloadedData = null) { //Preloaded List <ModelPreviewSection> sections = new List <ModelPreviewSection>(); if (preloadedData != null) { sections = preloadedData.sections; var uniqueMaterials = preloadedData.texturePreviewMaterials.Select(x => x.MaterialExport).Distinct(); foreach (var mat in uniqueMaterials) { var material = new TexturedPreviewMaterial(texcache, new MaterialInstanceConstant(mat), assetCache, preloadedData.texturePreviewMaterials); AddMaterial(mat.ObjectName.Name, material); } } LODs.Add(new ModelPreviewLOD(mesh, sections)); }
/// <summary> /// Creates a preview of the given <see cref="Unreal.Classes.SkeletalMesh"/>. /// </summary> /// <param name="Device">The Direct3D device to use for buffer creation.</param> /// <param name="m">The mesh to generate a preview for.</param> /// <param name="texcache">The texture cache for loading textures.</param> public ModelPreview(Device Device, Unreal.Classes.SkeletalMesh m, PreviewTextureCache texcache) { // STEP 1: MATERIALS Materials = new Dictionary <string, ModelPreviewMaterial>(); foreach (Unreal.Classes.MaterialInstanceConstant mat in m.MatInsts) { ModelPreviewMaterial material; // TODO: pick what material class best fits based on what properties the // MaterialInstanceConstant mat has. // For now, just use the default material. material = new TexturedPreviewMaterial(texcache, mat); AddMaterial(material.Properties["Name"], material); } // STEP 2: LODS LODs = new List <ModelPreviewLOD>(); foreach (Unreal.Classes.SkeletalMesh.LODModelStruct lodmodel in m.LODModels) { // Vertices List <WorldVertex> vertices = new List <WorldVertex>(); foreach (Unreal.Classes.SkeletalMesh.GPUSkinVertexStruct vertex in lodmodel.VertexBufferGPUSkin.Vertices) { // NOTE: note the switched Y and Z coordinates. Unreal seems to think that Z is up. vertices.Add(new WorldVertex(new Vector3(-vertex.Position.X, vertex.Position.Z, vertex.Position.Y), Vector3.Zero, new Vector2(HalfToFloat(vertex.U), HalfToFloat(vertex.V)))); } // Triangles List <Triangle> triangles = new List <Triangle>(); for (int i = 0; i < lodmodel.IndexBuffer.Indexes.Count; i += 3) { triangles.Add(new Triangle(lodmodel.IndexBuffer.Indexes[i], lodmodel.IndexBuffer.Indexes[i + 1], lodmodel.IndexBuffer.Indexes[i + 2])); } WorldMesh mesh = new WorldMesh(Device, triangles, vertices); // Sections List <ModelPreviewSection> sections = new List <ModelPreviewSection>(); foreach (Unreal.Classes.SkeletalMesh.SectionStruct section in lodmodel.Sections) { if (section.MaterialIndex < Materials.Count) { sections.Add(new ModelPreviewSection(Materials.Keys.ElementAt(section.MaterialIndex), (uint)section.BaseIndex, (uint)section.NumTriangles)); } } LODs.Add(new ModelPreviewLOD(mesh, sections)); } }
/// <summary> /// Creates a ModelPreviewMaterial that renders as close to what the given <see cref="MaterialInstanceConstant"/> looks like as possible. /// </summary> /// <param name="texcache">The texture cache to request textures from.</param> /// <param name="mat">The material that this ModelPreviewMaterial will try to look like.</param> protected ModelPreviewMaterial(PreviewTextureCache texcache, MaterialInstanceConstant mat, PackageCache assetCache, List <PreloadedTextureData> preloadedTextures = null) { if (mat == null) { return; } Properties.Add("Name", mat.Export.ObjectName); foreach (var textureEntry in mat.Textures) { if (!Textures.ContainsKey(textureEntry.FullPath) && textureEntry.ClassName == "Texture2D") //Apparently some assets are cubemaps, we don't want these. { if (preloadedTextures != null) { var preloadedInfo = preloadedTextures.FirstOrDefault(x => x.MaterialExport == mat.Export && x.Mip.Export.ObjectName.Name == textureEntry.ObjectName.Name); //i don't like matching on object name but its export vs import here. if (preloadedInfo != null) { Textures.Add(textureEntry.FullPath, texcache.LoadTexture(preloadedInfo.Mip.Export, preloadedInfo.Mip, preloadedInfo.decompressedTextureData)); } else { Debug.WriteLine("Preloading error"); } //if (textureEntry is ExportEntry texPort && preloadedMipInfo.Export != texPort) throw new Exception(); continue; //Don't further parse } if (textureEntry is ImportEntry import) { var extAsset = EntryImporter.ResolveImport(import, null, assetCache); if (extAsset != null) { Textures.Add(textureEntry.FullPath, texcache.LoadTexture(extAsset)); } } else { Textures.Add(textureEntry.FullPath, texcache.LoadTexture(textureEntry as ExportEntry)); } } } }
/// <summary> /// Creates a TexturedPreviewMaterial that renders as close to what the given <see cref="Unreal.Classes.MaterialInstanceConstant"/> looks like as possible. /// </summary> /// <param name="texcache">The texture cache to request textures from.</param> /// <param name="mat">The material that this ModelPreviewMaterial will try to look like.</param> public TexturedPreviewMaterial(PreviewTextureCache texcache, Unreal.Classes.MaterialInstanceConstant mat) : base(texcache, mat) { foreach (Unreal.Classes.MaterialInstanceConstant.TextureParam texparam in mat.Textures) { if (texparam.Desc.ToLower().Contains("diff") || texparam.Desc.ToLower().Contains("tex")) { // we have found the diffuse texture! DiffuseTextureFullName = texparam.Desc; //Console.WriteLine("Diffuse texture of new material <" + Properties["Name"] + "> is " + DiffuseTextureFullName); return; } } foreach (Unreal.Classes.MaterialInstanceConstant.TextureParam texparam in mat.Textures) { if (texparam.Desc.ToLower().Contains("detail")) { // I guess a detail texture is good enough if we didn't return for a diffuse texture earlier... DiffuseTextureFullName = texparam.Desc; //Console.WriteLine("Diffuse texture of new material <" + Properties["Name"] + "> is " + DiffuseTextureFullName); return; } } }
/// <summary> /// Creates a preview of the given <see cref="SkeletalMesh"/>. /// </summary> /// <param name="Device">The Direct3D device to use for buffer creation.</param> /// <param name="m">The mesh to generate a preview for.</param> /// <param name="texcache">The texture cache for loading textures.</param> public ModelPreview(Device Device, SkeletalMesh m, PreviewTextureCache texcache, PackageCache assetCache, PreloadedModelData preloadedData = null) { // STEP 1: MATERIALS if (preloadedData == null) { for (int i = 0; i < m.Materials.Length; i++) { UIndex materialUIndex = m.Materials[i]; MaterialInstanceConstant mat = null; if (materialUIndex.value > 0) { mat = new MaterialInstanceConstant(m.Export.FileRef.GetUExport(materialUIndex.value)); } else if (materialUIndex.value < 0) { // The material instance is an import! ImportEntry matImport = m.Export.FileRef.GetImport(materialUIndex.value); var externalAsset = EntryImporter.ResolveImport(matImport, null, assetCache); if (externalAsset != null) { mat = new MaterialInstanceConstant(externalAsset); } } if (mat != null) { ModelPreviewMaterial material; // TODO: pick what material class best fits based on what properties the // MaterialInstanceConstant mat has. // For now, just use the default material. material = new TexturedPreviewMaterial(texcache, mat, assetCache); AddMaterial(material.Properties["Name"], material); } } } else { //Preloaded //sections = preloadedData.sections; var uniqueMaterials = preloadedData.texturePreviewMaterials.Select(x => x.MaterialExport).Distinct(); foreach (var mat in uniqueMaterials) { var material = new TexturedPreviewMaterial(texcache, new MaterialInstanceConstant(mat), assetCache, preloadedData.texturePreviewMaterials); AddMaterial(mat.ObjectName.Name, material); } } // STEP 2: LODS foreach (var lodmodel in m.LODModels) { // Vertices List <WorldVertex> vertices = new List <WorldVertex>(m.Export.Game == MEGame.ME1 ? lodmodel.ME1VertexBufferGPUSkin.Length : lodmodel.VertexBufferGPUSkin.VertexData.Length); if (m.Export.Game == MEGame.ME1) { foreach (var vertex in lodmodel.ME1VertexBufferGPUSkin) { vertices.Add(new WorldVertex(new Vector3(-vertex.Position.X, vertex.Position.Z, vertex.Position.Y), Vector3.Zero, new Vector2(vertex.UV.X, vertex.UV.Y))); } } else { foreach (var vertex in lodmodel.VertexBufferGPUSkin.VertexData) { vertices.Add(new WorldVertex(new Vector3(-vertex.Position.X, vertex.Position.Z, vertex.Position.Y), Vector3.Zero, new Vector2(vertex.UV.X, vertex.UV.Y))); } } // Triangles List <Triangle> triangles = new List <Triangle>(lodmodel.IndexBuffer.Length / 3); for (int i = 0; i < lodmodel.IndexBuffer.Length; i += 3) { triangles.Add(new Triangle(lodmodel.IndexBuffer[i], lodmodel.IndexBuffer[i + 1], lodmodel.IndexBuffer[i + 2])); } WorldMesh mesh = new WorldMesh(Device, triangles, vertices); // Sections List <ModelPreviewSection> sections = new List <ModelPreviewSection>(); foreach (var section in lodmodel.Sections) { if (section.MaterialIndex < Materials.Count) { sections.Add(new ModelPreviewSection(Materials.Keys.ElementAt(section.MaterialIndex), section.BaseIndex, (uint)section.NumTriangles)); } } LODs.Add(new ModelPreviewLOD(mesh, sections)); } }
/// <summary> /// Creates a preview of the given <see cref="StaticMesh"/>. /// </summary> /// <param name="Device">The Direct3D device to use for buffer creation.</param> /// <param name="m">The mesh to generate a preview for.</param> /// <param name="texcache">The texture cache for loading textures.</param> public ModelPreview(Device Device, StaticMesh m, int selectedLOD, PreviewTextureCache texcache, PackageCache assetCache, PreloadedModelData preloadedData = null) { if (selectedLOD < 0) //PREVIEW BUG WORKAROUND { return; } // STEP 1: MESH var lodModel = m.LODModels[selectedLOD]; List <Triangle> triangles = new List <Triangle>(lodModel.IndexBuffer.Length / 3); List <WorldVertex> vertices = new List <WorldVertex>((int)lodModel.NumVertices); // Gather all the vertex data // Only one LOD? odd but I guess that's just how it rolls. for (int i = 0; i < lodModel.NumVertices; i++) { var v = lodModel.PositionVertexBuffer.VertexData[i]; if (lodModel.VertexBuffer.bUseFullPrecisionUVs) { var uvVector = lodModel.VertexBuffer.VertexData[i].FullPrecisionUVs; //SharpDX takes items differently than unreal. vertices.Add(new Scene3D.WorldVertex(new Vector3(-v.X, v.Z, v.Y), Vector3.Zero, new Vector2(uvVector[0].X, uvVector[0].Y))); } else { var uvVector = lodModel.VertexBuffer.VertexData[i].HalfPrecisionUVs; //SharpDX takes items differently than unreal. vertices.Add(new Scene3D.WorldVertex(new Vector3(-v.X, v.Z, v.Y), Vector3.Zero, new Vector2(uvVector[0].X, uvVector[0].Y))); } } //OLD CODE //for (int i = 0; i < m.L.Vertices.Points.Count; i++) //{ // // Note the reversal of the Z and Y coordinates. Unreal seems to think that Z should be up. // vertices.Add(new Scene3D.WorldVertex(new SharpDX.Vector3(-m.Mesh.Vertices.Points[i].X, m.Mesh.Vertices.Points[i].Z, m.Mesh.Vertices.Points[i].Y), SharpDX.Vector3.Zero, new SharpDX.Vector2(m.Mesh.Edges.UVSet[i].UVs[0].X, m.Mesh.Edges.UVSet[i].UVs[0].Y))); //} // Sometimes there might not be an index buffer. // If there is one, use that. // Otherwise, assume that each vertex is used exactly once. // Note that this is based on the earlier implementation which didn't take LODs into consideration, which is odd considering that both the hit testing and the skeletalmesh class do. if (lodModel.IndexBuffer.Length > 0) { // Hey, we have indices all set up for us. How considerate. for (int i = 0; i < lodModel.IndexBuffer.Length; i += 3) { triangles.Add(new Triangle(lodModel.IndexBuffer[i], lodModel.IndexBuffer[i + 1], lodModel.IndexBuffer[i + 2])); } } else { // Gather all the vertex data from the raw triangles, not the Mesh.Vertices.Point list. if (m.Export.Game <= MEGame.ME2) { var kdop = m.kDOPTreeME1ME2; for (int i = 0; i < kdop.Triangles.Length; i++) { triangles.Add(new Triangle(kdop.Triangles[i].Vertex1, kdop.Triangles[i].Vertex2, kdop.Triangles[i].Vertex3)); } } else { var kdop = m.kDOPTreeME3UDK; for (int i = 0; i < kdop.Triangles.Length; i++) { triangles.Add(new Triangle(kdop.Triangles[i].Vertex1, kdop.Triangles[i].Vertex2, kdop.Triangles[i].Vertex3)); } } } /* * if (m.Mesh.IdxBuf.Indexes != null && m.Mesh.IdxBuf.Indexes.Count > 0) * { * // Hey, we have indices all set up for us. How considerate. * for (int i = 0; i < m.Mesh.IdxBuf.Indexes.Count; i += 3) * { * triangles.Add(new Triangle(m.Mesh.IdxBuf.Indexes[i], m.Mesh.IdxBuf.Indexes[i + 1], m.Mesh.IdxBuf.Indexes[i + 2])); * } * } * else * { * // Gather all the vertex data from the raw triangles, not the Mesh.Vertices.Point list. * for (int i = 0; i < m.Mesh.RawTris.RawTriangles.Count; i++) * { * triangles.Add(new Triangle((uint)m.Mesh.RawTris.RawTriangles[i].v0, (uint)m.Mesh.RawTris.RawTriangles[i].v1, (uint)m.Mesh.RawTris.RawTriangles[i].v2)); * } * }*/ //OLD CODE /* if (m.Mesh.IdxBuf.Indexes != null && m.Mesh.IdxBuf.Indexes.Count > 0) * { * // Hey, we have indices all set up for us. How considerate. * for (int i = 0; i < m.Mesh.IdxBuf.Indexes.Count; i += 3) * { * triangles.Add(new Triangle(m.Mesh.IdxBuf.Indexes[i], m.Mesh.IdxBuf.Indexes[i + 1], m.Mesh.IdxBuf.Indexes[i + 2])); * } * } * else * { * // Gather all the vertex data from the raw triangles, not the Mesh.Vertices.Point list. * for (int i = 0; i < m.Mesh.RawTris.RawTriangles.Count; i++) * { * triangles.Add(new Triangle((uint)m.Mesh.RawTris.RawTriangles[i].v0, (uint)m.Mesh.RawTris.RawTriangles[i].v1, (uint)m.Mesh.RawTris.RawTriangles[i].v2)); * } * }*/ // STEP 2: MATERIALS //foreach (var v in lodModel.Elements) //foreach (Unreal.Classes.MaterialInstanceConstant mat in m.Mesh.Mat.MatInst) //{ // ModelPreviewMaterial material; // // TODO: pick what material class best fits based on what properties the // // MaterialInstanceConstant mat has. // // For now, just use the default material. // material = new TexturedPreviewMaterial(texcache, mat); // AddMaterial(material.Properties["Name"], material); //} // STEP 3: SECTIONS List <ModelPreviewSection> sections = preloadedData != null ? preloadedData.sections : null; List <IMEPackage> assetLookupPackagesToDispose = new List <IMEPackage>(); //This section exists for Meshplorer Winforms. WPF version preloads this in a background thread to improve performance if (sections == null) { sections = new List <ModelPreviewSection>(); foreach (var section in lodModel.Elements) { if (section.Material.value > 0) { ModelPreviewMaterial material; // TODO: pick what material class best fits based on what properties the // MaterialInstanceConstant mat has. // For now, just use the default material. ExportEntry entry = m.Export.FileRef.GetUExport(section.Material.value); material = new TexturedPreviewMaterial(texcache, new MaterialInstanceConstant(entry), assetCache); AddMaterial(material.Properties["Name"], material); } else if (section.Material.value < 0) { var extMaterialExport = FindExternalAsset(m.Export.FileRef.GetImport(section.Material.value), texcache.cache.Select(x => x.TextureExport).ToList(), assetLookupPackagesToDispose); if (extMaterialExport != null) { ModelPreviewMaterial material; // TODO: pick what material class best fits based on what properties the // MaterialInstanceConstant mat has. // For now, just use the default material. material = new TexturedPreviewMaterial(texcache, new MaterialInstanceConstant(extMaterialExport), assetCache); AddMaterial(material.Properties["Name"], material); } else { Debug.WriteLine("Could not find import material from section."); Debug.WriteLine("Import material: " + m.Export.FileRef.GetEntryString(section.Material.value)); } } sections.Add(new ModelPreviewSection(m.Export.FileRef.getObjectName(section.Material.value), section.FirstIndex, section.NumTriangles)); } } else { //Preloaded sections = preloadedData.sections; var uniqueMaterials = preloadedData.texturePreviewMaterials.Select(x => x.MaterialExport).Distinct(); foreach (var mat in uniqueMaterials) { var material = new TexturedPreviewMaterial(texcache, new MaterialInstanceConstant(mat), assetCache, preloadedData.texturePreviewMaterials); AddMaterial(mat.ObjectName.Name, material); } } foreach (var package in assetLookupPackagesToDispose) { package?.Dispose(); //Release } //List<ModelPreviewSection> sections = new List<ModelPreviewSection>(); //foreach (var section in lodModel.Elements) //{ // sections.Add(new ModelPreviewSection(m.Export.FileRef.getObjectName(section.Material.value), section.FirstIndex, section.NumTriangles)); //} LODs.Add(new ModelPreviewLOD(new WorldMesh(Device, triangles, vertices), sections)); }
/// <summary> /// Creates a preview of the given <see cref="Unreal.Classes.SkeletalMesh"/>. /// </summary> /// <param name="Device">The Direct3D device to use for buffer creation.</param> /// <param name="m">The mesh to generate a preview for.</param> /// <param name="texcache">The texture cache for loading textures.</param> public ModelPreview(Device Device, Unreal.Classes.SkeletalMesh m, PreviewTextureCache texcache) { // STEP 1: MATERIALS for (int i = 0; i < m.Materials.Count; i++) { Unreal.Classes.MaterialInstanceConstant mat = m.MatInsts[i]; if (mat == null && m.Materials[i] < 0) { // The material instance is an import! ImportEntry matImport = m.Export.FileRef.GetImport(m.Materials[i]); var externalAsset = FindExternalAsset(matImport, texcache.cache.Select(x => x.TextureExport).ToList()); if (externalAsset != null) { mat = new MaterialInstanceConstant(externalAsset); } } if (mat != null) { ModelPreviewMaterial material; // TODO: pick what material class best fits based on what properties the // MaterialInstanceConstant mat has. // For now, just use the default material. material = new TexturedPreviewMaterial(texcache, mat); AddMaterial(material.Properties["Name"], material); } } // STEP 2: LODS foreach (Unreal.Classes.SkeletalMesh.LODModelStruct lodmodel in m.LODModels) { // Vertices List <WorldVertex> vertices = new List <WorldVertex>(); if (m.Export.Game == MEGame.ME1) { foreach (Unreal.Classes.SkeletalMesh.GPUSkinVertexStruct vertex in lodmodel.VertexBufferGPUSkin.Vertices) { vertices.Add(new WorldVertex(new Vector3(-vertex.Position.X, vertex.Position.Z, vertex.Position.Y), Vector3.Zero, new Vector2(vertex.UFullPrecision, vertex.VFullPrecision))); } } else { foreach (Unreal.Classes.SkeletalMesh.GPUSkinVertexStruct vertex in lodmodel.VertexBufferGPUSkin.Vertices) { // NOTE: note the switched Y and Z coordinates. Unreal seems to think that Z is up. vertices.Add(new WorldVertex(new Vector3(-vertex.Position.X, vertex.Position.Z, vertex.Position.Y), Vector3.Zero, new Vector2(HalfToFloat(vertex.U), HalfToFloat(vertex.V)))); } } // Triangles List <Triangle> triangles = new List <Triangle>(); for (int i = 0; i < lodmodel.IndexBuffer.Indexes.Count; i += 3) { triangles.Add(new Triangle(lodmodel.IndexBuffer.Indexes[i], lodmodel.IndexBuffer.Indexes[i + 1], lodmodel.IndexBuffer.Indexes[i + 2])); } WorldMesh mesh = new WorldMesh(Device, triangles, vertices); // Sections List <ModelPreviewSection> sections = new List <ModelPreviewSection>(); foreach (Unreal.Classes.SkeletalMesh.SectionStruct section in lodmodel.Sections) { if (section.MaterialIndex < Materials.Count) { sections.Add(new ModelPreviewSection(Materials.Keys.ElementAt(section.MaterialIndex), (uint)section.BaseIndex, (uint)section.NumTriangles)); } } LODs.Add(new ModelPreviewLOD(mesh, sections)); } }
public void LoadDirect3D(SharpDX.Direct3D11.Device device) { // Set up description of swap chain /*SwapChainDescription scd = new SwapChainDescription(); * scd.BufferCount = 1; * scd.ModeDescription = new ModeDescription(Width, Height, new Rational(60, 1), Format.B8G8R8A8_UNorm); * scd.Usage = Usage.RenderTargetOutput; * scd.OutputHandle = Handle; * scd.SampleDescription.Count = 1; * scd.SampleDescription.Quality = 0; * scd.IsWindowed = true; * scd.ModeDescription.Width = Width; * scd.ModeDescription.Height = Height;*/ // Create device and swap chain according to the description above //SharpDX.Direct3D11.Device d; //SwapChain sc; //DeviceCreationFlags flags = DeviceCreationFlags.BgraSupport | DeviceCreationFlags.SingleThreaded; #if DEBUG //flags |= DeviceCreationFlags.Debug; #endif //SharpDX.Direct3D11.Device.CreateWithSwapChain(DriverType.Hardware, flags, scd, out d, out sc); //this.SwapChain = sc; // we have to use these temp variables //this.Device = new SharpDX.Direct3D11.Device(DriverType.Hardware, flags); //this.Device = d; // because properties can't be passed as out parameters. =( // Set up the rendering context and buffers and stuff this.Device = device; ImmediateContext = Device.ImmediateContext; //BuildBuffers(); // Build a custom rasterizer state that doesn't cull backfaces RasterizerStateDescription frs = new RasterizerStateDescription(); frs.CullMode = CullMode.None; frs.FillMode = FillMode.Solid; FillRasterizerState = new RasterizerState(Device, frs); ImmediateContext.Rasterizer.State = FillRasterizerState; // Build a custom rasterizer state for wireframe drawing RasterizerStateDescription wrs = new RasterizerStateDescription(); wrs.CullMode = CullMode.None; wrs.FillMode = FillMode.Wireframe; wrs.IsAntialiasedLineEnabled = false; wrs.DepthBias = -10; WireframeRasterizerState = new RasterizerState(Device, wrs); // Set texture sampler state SamplerStateDescription ssd = new SamplerStateDescription(); ssd.AddressU = TextureAddressMode.Wrap; ssd.AddressV = TextureAddressMode.Wrap; ssd.AddressW = TextureAddressMode.Wrap; ssd.Filter = Filter.MinMagMipLinear; ssd.MaximumAnisotropy = 1; SampleState = new SamplerState(Device, ssd); ImmediateContext.PixelShader.SetSampler(0, SampleState); // Load the default texture System.Drawing.Bitmap deftex = new System.Drawing.Bitmap(System.Environment.CurrentDirectory + "\\exec\\Default.bmp"); DefaultTexture = LoadTexture(deftex); deftex.Dispose(); DefaultTextureView = new ShaderResourceView(Device, DefaultTexture); // Load the default position-texture shader DefaultEffect = new Effect <WorldConstants, WorldVertex>(Device, Properties.Resources.StandardShader); TextureCache = new PreviewTextureCache(Device); }
private PreviewTextureCache.PreviewTextureEntry FindTexture(PreviewTextureCache texcache, string FullTextureName, string ImportPCC) { string importfiledir = System.IO.Path.GetDirectoryName(ImportPCC).ToLower(); string importfilename = System.IO.Path.GetFileName(ImportPCC).ToLower(); string pccpath = ""; int id = 0; // First, check the pcc that contains the material using (ME3Package pcc = MEPackageHandler.OpenME3Package(ImportPCC)) { foreach (IExportEntry exp in pcc.Exports) { if (exp.GetFullPath == FullTextureName && exp.ClassName == "Texture2D") { pccpath = ImportPCC; id = exp.Index; break; } } } // Next, split the filename by underscores string[] parts = System.IO.Path.GetFileNameWithoutExtension(importfilename).Split('_'); if (pccpath == "" && (importfilename.StartsWith("bioa") || importfilename.StartsWith("biod"))) { // Maybe go for the one with one less segment? ex. for input BioA_Nor_201CIC.pcc, look in BioA_Nor.pcc if (parts.Length == 3) { string filename = importfiledir + "\\" + parts[0] + "_" + parts[1] + ".pcc"; if (System.IO.File.Exists(filename)) { using (ME3Package pcc = MEPackageHandler.OpenME3Package(filename)) { foreach (IExportEntry exp in pcc.Exports) { if (exp.GetFullPath == FullTextureName && exp.ClassName == "Texture2D") { pccpath = importfiledir + "\\" + parts[0] + "_" + parts[1] + ".pcc"; id = exp.Index; break; } } } } } // Now go for the BioP one. if (pccpath == "" && parts.Length >= 2) { string filename = importfiledir + "\\" + "BioP" + "_" + parts[1] + ".pcc"; if (System.IO.File.Exists(filename)) { using (ME3Package pcc = MEPackageHandler.OpenME3Package(filename)) { foreach (IExportEntry exp in pcc.Exports) { if (exp.GetFullPath == FullTextureName && exp.ClassName == "Texture2D") { pccpath = importfiledir + "\\" + "BioP" + "_" + parts[1] + ".pcc"; id = exp.Index; break; } } } } } } if (id > 0) { return(texcache.LoadTexture(pccpath, id)); } else { Console.WriteLine("[TEXLOAD]: Could not find texture \"" + FullTextureName + "\", imported in \"" + ImportPCC + "\"."); return(null); } }