private void ShowMeshTextures() { // Clear texture flow panel TexturesPanel.Controls.Clear(); // Get the mesh data DFMesh mesh = arch3dFile.GetMesh(MeshIndexTrackBar.Value); // Loop through all submeshes foreach (DFMesh.DFSubMesh sm in mesh.SubMeshes) { // Load texture file string FileName = TextureFile.IndexToFileName(sm.TextureArchive); MyImageFileReader.LoadFile(FileName); // Get texture file DFImageFile textureFile = MyImageFileReader.ImageFile; // Get managed bitmap Bitmap bm = textureFile.GetManagedBitmap(sm.TextureRecord, 0, true, false); // Create a new picture box PictureBox pb = new PictureBox(); pb.Width = bm.Width; pb.Height = bm.Height; pb.Image = bm; // Add picture box to flow panel TexturesPanel.Controls.Add(pb); } }
/// <summary> /// Loads model data for conversion. /// </summary> /// <param name="id">Model ID.</param> /// <param name="scale">Amount to scale model.</param> private void LoadModelData(uint id, float scale) { // Open ARCH3D.BSA Arch3dFile arch3dFile = new Arch3dFile( Path.Combine(arena2Path, "ARCH3D.BSA"), FileUsage.UseDisk, true); // Get DFMesh int record = arch3dFile.GetRecordIndex(id); DFMesh dfMesh = arch3dFile.GetMesh(record); // Scale DFMesh if (scale != 1.0f) { foreach (var mesh in dfMesh.SubMeshes) { foreach (var plane in mesh.Planes) { for (int point = 0; point < plane.Points.Length; point++) { plane.Points[point].X *= scale; plane.Points[point].Y *= scale; plane.Points[point].Z *= scale; } } } } // Load model data model = new ModelData(); model.DFMesh = dfMesh; LoadVertices(ref model); LoadIndices(ref model); }
private void AddVisualScene(ref _daeElement root, ref DFMesh dfMesh) { string geomId = "model" + dfMesh.ObjectId.ToString(); string sceneName = geomId + "-scene"; _daeElement visualSceneLib = root.add("library_visual_scenes"); _daeElement visualScene = visualSceneLib.add("visual_scene"); visualScene.setAttribute("id", sceneName); // Add <node> _daeElement node = visualScene.add("node"); node.setAttribute("id", geomId + "-node"); // Transform <node> based on up vector switch (this.upVector) { case UpVectors.X_UP: node.add("rotate").setCharData("0 0 1 -90"); break; case UpVectors.Y_UP: // No change required. This is how the geometry is in DFMesh. break; case UpVectors.Z_UP: node.add("rotate").setCharData("1 0 0 90"); break; } // Instantiate the <geometry> _daeElement instanceGeom = node.add("instance_geometry"); instanceGeom.setAttribute("url", MakeUriRef(geomId)); // Bind material parameters _daeElement techniqueCommon = instanceGeom.add("bind_material technique_common"); foreach (var subMesh in dfMesh.SubMeshes) { string materialName = MakeMaterialName(subMesh.TextureArchive, subMesh.TextureRecord); _daeElement instanceMaterial = techniqueCommon.add("instance_material"); instanceMaterial.setAttribute("symbol", materialName + "-material"); instanceMaterial.setAttribute("target", MakeUriRef(materialName + "-material")); _daeElement bindVertexInput = instanceMaterial.add("bind_vertex_input"); bindVertexInput.setAttribute("semantic", "uv0"); bindVertexInput.setAttribute("input_semantic", "TEXCOORD"); bindVertexInput.setAttribute("input_set", "0"); } // Add a <scene> root.add("scene instance_visual_scene").setAttribute("url", MakeUriRef(sceneName)); }
/// <summary> /// Loads model data from DFMesh. /// </summary> /// <param name="id">Key of source mesh.</param> /// <param name="modelData">ModelData out.</param> /// <returns>True if successful.</returns> private bool LoadModelData(uint id, out ModelData modelData) { // Return from cache if present if (cacheModelData && modelDataDict.ContainsKey(id)) { modelData = modelDataDict[id]; return(true); } // New model object modelData = new ModelData(); // Find mesh index int index = arch3dFile.GetRecordIndex(id); if (index == -1) { return(false); } // Get DFMesh DFMesh dfMesh = arch3dFile.GetMesh(index); if (dfMesh.TotalVertices == 0) { return(false); } // Load mesh data modelData.DFMesh = dfMesh; LoadVertices(ref modelData); LoadIndices(ref modelData); AddModelTangents(ref modelData); CreateModelBuffers(graphicsDevice, ref modelData); // Load materials for (int i = 0; i < modelData.SubMeshes.Length; i++) { // Create material BaseMaterialEffect material = CreateModelMaterial( modelData.DFMesh.SubMeshes[i].TextureArchive, modelData.DFMesh.SubMeshes[i].TextureRecord); // Save key in submesh modelData.SubMeshes[i].MaterialKey = material.ID; } // Add to cache if (cacheModelData) { modelDataDict.Add(id, modelData); } return(true); }
/// <summary> /// Adds materials. /// </summary> /// <param name="root">Root element.</param> /// <param name="dfMesh">DFMesh.</param> /// <returns>True if successful.</returns> private bool AddMaterials(ref _daeElement root, ref DFMesh dfMesh) { // Add <library_materials> _daeElement materialLib = root.add("library_materials"); // Add one material per submesh foreach (var subMesh in dfMesh.SubMeshes) { // Add <material> string materialName = MakeMaterialName(subMesh.TextureArchive, subMesh.TextureRecord); _daeElement material = materialLib.add("material"); material.setAttribute("id", materialName + "-material"); material.add("instance_effect").setAttribute("url", MakeUriRef(materialName + "-effect")); } return(true); }
/// <summary> /// Loads model data. /// </summary> /// <param name="id">Key of source mesh.</param> /// <param name="modelData">ModelData out.</param> /// <param name="scale">Scale of model.</param> /// <returns>True if successful.</returns> public bool GetModelData(uint id, out ModelData modelData) { // New model object modelData = new ModelData(); // Ready check if (!IsReady) { return(false); } // Return from cache if present if (modelDict.ContainsKey((int)id)) { modelData = modelDict[(int)id]; return(true); } // Find mesh index int index = arch3dFile.GetRecordIndex(id); if (index == -1) { return(false); } // Get DFMesh DFMesh dfMesh = arch3dFile.GetMesh(index); if (dfMesh.TotalVertices == 0) { return(false); } // Load mesh data modelData.DFMesh = dfMesh; LoadVertices(ref modelData, GlobalScale); LoadIndices(ref modelData); // Add to cache modelDict.Add((int)id, modelData); return(true); }
/// <summary> /// Loads model data from DFMesh. /// </summary> /// <param name="id">Key of source mesh.</param> /// <param name="model">ModelData out.</param> /// <returns>True if successful.</returns> private bool LoadModelData(uint id, out ModelData model) { // Return from cache if present if (cacheModelData && modelDataDict.ContainsKey(id)) { model = modelDataDict[id]; return(true); } // New model object model = new ModelData(); // Find mesh index int index = arch3dFile.GetRecordIndex(id); if (index == -1) { return(false); } // Get DFMesh DFMesh dfMesh = arch3dFile.GetMesh(index); if (dfMesh.TotalVertices == 0) { return(false); } // Load mesh data model.DFMesh = dfMesh; LoadVertices(ref model); LoadIndices(ref model); AddModelTangents(ref model); CreateModelBuffers(ref model); // Add to cache if (cacheModelData) { modelDataDict.Add(id, model); } return(true); }
/// <summary> /// Exports texture images and add references to the dae file. /// </summary> /// <param name="root">Root element.</param> /// <param name="dfMesh">DFMesh.</param> /// <returns>True if successful.</returns> private bool AddImages(ref _daeElement root, ref DFMesh dfMesh) { // Create <library_images> _daeElement imageLib = root.add("library_images"); // Export all textures. foreach (var subMesh in dfMesh.SubMeshes) { // Construct string string materialName = MakeMaterialName(subMesh.TextureArchive, subMesh.TextureRecord); string inputFilename = TextureFile.IndexToFileName(subMesh.TextureArchive); string outputFilename = materialName + "." + imageFormat.ToString().ToLower(); // Export texture string outputPath = Path.Combine(modelOutputPath, imageOutputRelativePath); string outputFilePath = Path.Combine(outputPath, outputFilename); if (!File.Exists(outputFilePath)) { // Load texture file textureFile.Load( Path.Combine(arena2Path, inputFilename), FileUsage.UseDisk, true); // Export image Bitmap bm = textureFile.GetManagedBitmap( subMesh.TextureRecord, 0, false, true); bm.Save(outputFilePath, imageFormat); } // Add <image> string relativeOutputPath = Path.Combine(imageOutputRelativePath, outputFilename); relativeOutputPath = relativeOutputPath.Replace("\\", "/"); _daeElement image = imageLib.add("image"); image.setAttribute("id", materialName + "-image"); image.add("init_from").setCharData(relativeOutputPath); } return(true); }
/// <summary> /// Adds visual effects for materials. /// </summary> /// <param name="root">Root element.</param> /// <param name="dfMesh">DFMesh.</param> /// <returns>True if successful.</returns> private bool AddEffects(ref _daeElement root, ref DFMesh dfMesh) { // Add <library_effects> _daeElement effectLib = root.add("library_effects"); // Add one effect per submesh foreach (var subMesh in dfMesh.SubMeshes) { // Compose strings string materialName = MakeMaterialName(subMesh.TextureArchive, subMesh.TextureRecord); string effectName = materialName + "-effect"; // Add <effect> and profile_COMMON _daeElement effect = effectLib.add("effect"); effect.setAttribute("id", effectName); _daeElement profile = effect.add("profile_COMMON"); // Add <surface> _daeElement newparam = profile.add("newparam"); newparam.setAttribute("sid", "surface"); _daeElement surface = newparam.add("surface"); surface.setAttribute("type", "2D"); surface.add("init_from").setCharData(materialName + "-image"); // Add <sampler2D> newparam = profile.add("newparam"); newparam.setAttribute("sid", "sampler"); _daeElement sampler = newparam.add("sampler2D"); sampler.add("source").setCharData("surface"); sampler.add("minfilter").setCharData("LINEAR_MIPMAP_LINEAR"); sampler.add("magfilter").setCharData("LINEAR"); // Add <technique> _daeElement technique = profile.add("technique"); technique.setAttribute("sid", "common"); _daeElement texture = technique.add("phong diffuse texture"); texture.setAttribute("texture", "sampler"); texture.setAttribute("texcoord", "uv0"); } return(true); }
static void Main(string[] args) { // Specify Arena2 path of local Daggerfall installation string MyArena2Path = "C:\\dosgames\\DAGGER\\ARENA2"; // Path to BSA file string FilePath = Path.Combine(MyArena2Path, "ARCH3D.BSA"); // Open file Arch3dFile arch3dFile = new Arch3dFile( FilePath, FileUsage.UseDisk, true); // Output some information about the file Console.WriteLine("{0} has {1} records", Path.GetFileName(FilePath), arch3dFile.Count); // Get a mesh record int record = 5557; DFMesh mesh = arch3dFile.GetMesh(record); // Output some information about this record Console.WriteLine("Record {0} has {1} total vertices and ObjectID {2}", record, mesh.TotalVertices, mesh.ObjectId); // Output the texture archives used for this mesh Console.WriteLine("The following textures are used:"); foreach (DFMesh.DFSubMesh sm in mesh.SubMeshes) { Console.WriteLine("TEXTURE.{0:000} (Record {1})", sm.TextureArchive, sm.TextureRecord); } }
/// <summary> /// Adds geometry elements. /// </summary> /// <param name="root">Root element.</param> /// <param name="dfMesh">DFMesh.</param> private void AddGeometry(ref _daeElement root, ref DFMesh dfMesh) { _daeElement geomLib = root.add("library_geometries"); _daeElement geom = geomLib.add("geometry"); string geomId = "model" + dfMesh.ObjectId.ToString(); geom.setAttribute("id", geomId); _daeElement mesh = geom.add("mesh"); // Get source vertex data List <float> posArray = new List <float>(dfMesh.TotalVertices * 3); List <float> normalArray = new List <float>(dfMesh.TotalVertices * 3); List <float> uvArray = new List <float>(dfMesh.TotalVertices * 2); foreach (var subMesh in dfMesh.SubMeshes) { // Get texture dimensions for this submesh string archivePath = Path.Combine(arena2Path, TextureFile.IndexToFileName(subMesh.TextureArchive)); System.Drawing.Size sz = TextureFile.QuickSize(archivePath, subMesh.TextureRecord); // Collect vertex information for every plane in this submesh foreach (var plane in subMesh.Planes) { foreach (var point in plane.Points) { // Add position data posArray.Add(point.X); posArray.Add(-point.Y); posArray.Add(-point.Z); // Add normal data normalArray.Add(point.NX); normalArray.Add(-point.NY); normalArray.Add(-point.NZ); // Add UV data uvArray.Add(point.U / (float)sz.Width); uvArray.Add(-(point.V / (float)sz.Height)); } } } // Add positions, normals, and texture coordinates AddSource(ref mesh, geomId + "-positions", "X Y Z", ref posArray); AddSource(ref mesh, geomId + "-normals", "X Y Z", ref normalArray); AddSource(ref mesh, geomId + "-uv", "S T", ref uvArray); // Add <vertices> element _daeElement vertices = mesh.add("vertices"); vertices.setAttribute("id", geomId + "-vertices"); _daeElement verticesInput = vertices.add("input"); verticesInput.setAttribute("semantic", "POSITION"); verticesInput.setAttribute("source", MakeUriRef(geomId + "-positions")); // Add triangle indices for each submesh uint vertexCount = 0; foreach (var subMesh in dfMesh.SubMeshes) { // Loop through all planes in this submesh List <uint> indexArray = new List <uint>(subMesh.TotalTriangles * (3 * 3)); foreach (var plane in subMesh.Planes) { // Every DFPlane is a triangle fan radiating from point 0 uint sharedPoint = vertexCount++; // Index remaining points. There are (plane.Points.Length - 2) triangles in every plane for (int tri = 0; tri < plane.Points.Length - 2; tri++) { // Position, Normal, UV index for shared point indexArray.Add(sharedPoint); indexArray.Add(sharedPoint); indexArray.Add(sharedPoint); // Position, Normal, UV index for vertexCount indexArray.Add(vertexCount); indexArray.Add(vertexCount); indexArray.Add(vertexCount); // Position, Normal, UV index for vertexCount + 1 indexArray.Add(vertexCount + 1); indexArray.Add(vertexCount + 1); indexArray.Add(vertexCount + 1); // Increment vertexCount to next point in fan vertexCount++; } // Increment vertexCount to start of next fan in vertex buffer vertexCount++; } // Add <triangle> string materialName = MakeMaterialName(subMesh.TextureArchive, subMesh.TextureRecord); _daeElement triangles = mesh.add("triangles"); triangles.setAttribute("count", (indexArray.Count / (3 * 3)).ToString()); triangles.setAttribute("material", materialName + "-material"); AddInput(ref triangles, "VERTEX", geomId + "-vertices", 0); AddInput(ref triangles, "NORMAL", geomId + "-normals", 1); AddInput(ref triangles, "TEXCOORD", geomId + "-uv", 2); // Add <p> _daeElement p = triangles.add("p"); p.setCharData(UIntArrayToString(ref indexArray)); } }
/// <summary> /// Exports a DFMesh object to Collada format. /// </summary> /// <param name="id">ID of DFMesh to export.</param> /// <param name="scale">Scale model by value.</param> /// <returns>True if successful.</returns> public bool DFMeshToCollada(uint id, float scale) { // Get source mesh int index = arch3dFile.GetRecordIndex(id); DFMesh dfMesh = arch3dFile.GetMesh(index); if (dfMesh.SubMeshes == null) { string error = string.Format("Model '{0}' not found", id); Console.WriteLine(error); throw new Exception(error); } // Test model output path if (!Directory.Exists(modelOutputPath)) { string error = string.Format("Model output path '{0}' does not exist.", modelOutputPath); Console.WriteLine(error); throw new Exception(error); } // Test or create image output path string fullImageOutputPath = Path.Combine(modelOutputPath, imageOutputRelativePath); if (!Directory.Exists(fullImageOutputPath)) { Directory.CreateDirectory(fullImageOutputPath); } // Scale model if (scale != 1.0f) { foreach (var mesh in dfMesh.SubMeshes) { foreach (var plane in mesh.Planes) { for (int point = 0; point < plane.Points.Length; point++) { plane.Points[point].X *= scale; plane.Points[point].Y *= scale; plane.Points[point].Z *= scale; } } } } // Create dae root string filePath = Path.Combine(modelOutputPath, dfMesh.ObjectId.ToString() + ".dae"); _daeElement root = dae.add(filePath); // Build dae file AddAsset(ref root); AddGeometry(ref root, ref dfMesh); AddImages(ref root, ref dfMesh); AddEffects(ref root, ref dfMesh); AddMaterials(ref root, ref dfMesh); AddVisualScene(ref root, ref dfMesh); // Write dae file dae.writeAll(); return(true); }