private static void ConvertMB() { var model = Resource.Load <Model>(Options.Input); switch (Options.OutputFormat) { case OutputFormat.PB: var modelPack = new ModelPack(); modelPack.Models.Add(model); modelPack.Save(Options.Output); break; case OutputFormat.MB: model.Save(Options.Output); break; case OutputFormat.OBJ: case OutputFormat.DAE: case OutputFormat.FBX: if (Options.OutputFormat == OutputFormat.DAE || Options.OutputFormat == OutputFormat.FBX) { FbxModelExporter.Instance.Export(model, Options.Output, FbxConfig, null); } else { AssimpModelExporter.Instance.Export(model, Options.Output); } break; default: throw new Exception("Unsupported output format"); } }
private static void ConvertPB() { var modelPack = new ModelPack(Options.Input); switch (Options.OutputFormat) { case OutputFormat.PB: modelPack.Save(Options.Output); break; case OutputFormat.MB: for (int i = 0; i < modelPack.Models.Count; i++) { modelPack.Models[i].Save(modelPack.Models.Count == 1 ? Options.Output : $"{Path.GetFileNameWithoutExtension( Options.Output )}_{i}.MB"); } break; case OutputFormat.OBJ: case OutputFormat.DAE: case OutputFormat.FBX: for (int i = 0; i < modelPack.Models.Count; i++) { var modelOutfilePath = modelPack.Models.Count == 1 ? Options.Output : $"{Path.GetFileNameWithoutExtension( Options.Output )}_{i}.{Options.OutputFormat}"; if (Options.OutputFormat == OutputFormat.DAE || Options.OutputFormat == OutputFormat.FBX) { FbxModelExporter.Instance.Export(modelPack.Models[i], modelOutfilePath, FbxConfig, modelPack.TexturePack); } else { AssimpModelExporter.Instance.Export(modelPack.Models[i], modelOutfilePath, modelPack.TexturePack); } if (Options.Assimp.OutputPbMotion) { for (int j = 0; j < modelPack.MotionPacks.Count; j++) { for (int k = 0; k < modelPack.MotionPacks[j].Motions.Count; k++) { var outfilePath = modelPack.MotionPacks.Count == 1 ? $"{Path.GetFileNameWithoutExtension( Options.Output )}_m_{j}.{Options.OutputFormat}" : $"{Path.GetFileNameWithoutExtension( Options.Output )}_mp_{j}_m_{k}.{Options.OutputFormat}"; AssimpMotionExporter.Instance.Export(modelPack.Models[i], modelPack.MotionPacks[j].Motions[k], outfilePath); } } } } break; default: throw new Exception("Unsupported output format"); } }
private static void RemoveChunks() { foreach (var gmd in Directory.GetFiles(Input.retargetFolder, "*.GMD", SearchOption.TopDirectoryOnly)) { var modelPack = ModuleImportUtilities.ImportFile <ModelPack>(gmd); var newModelPack = new ModelPack(); newModelPack.Version = modelPack.Version; newModelPack.Textures = modelPack.Textures; newModelPack.Materials = modelPack.Materials; newModelPack.Model = modelPack.Model; newModelPack.AnimationPack = modelPack.AnimationPack; newModelPack.Save(gmd); } }
private static void OpenAndSaveModelPackBatchTest() { if (!Directory.Exists("unique_models")) { FindUniqueFiles("unique_models", @"D:\Modding\DDS3", ".PB"); } if (!Directory.Exists("unique_lb")) { FindUniqueFiles("unique_lb", @"D:\Modding\DDS3", ".LB"); } if (!Directory.Exists("unique_lb_extracted")) { Directory.CreateDirectory("unique_lb_extracted"); var checksums = new HashSet <string>(); Parallel.ForEach(Directory.EnumerateFiles("unique_lb"), (path) => { var fileName = Path.GetFileNameWithoutExtension(path); var outPath = $"unique_lb_extracted\\"; Directory.CreateDirectory(outPath); using (var lb = new AtlusFileSystemLibrary.FileSystems.LB.LBFileSystem()) { lb.Load(path); foreach (var file in lb.EnumerateFiles()) { var info = lb.GetInfo(file); using (var stream = lb.OpenFile(file)) { var checksum = GetChecksum(stream); stream.Position = 0; var extract = false; lock ( checksums ) { if (!checksums.Contains(checksum)) { checksums.Add(checksum); extract = true; } } if (extract) { var nameParts = Path.GetFileNameWithoutExtension(path).Split(new[] { '_' }); Array.Resize(ref nameParts, nameParts.Length - 1); using (var fileStream = File.Create(Path.Combine(outPath, string.Concat(nameParts) + "_" + file + "_" + checksum + "." + info.Extension) )) { Console.WriteLine($"Extracting: {fileName} #{file} ({info.UserId:D2}, {info.Extension})"); stream.CopyTo(fileStream); } } } } } }); } var uniqueValues = new HashSet <MeshFlags>(); var frequencyMap = new ConcurrentDictionary <MeshFlags, int>(); var paths = Directory.EnumerateFiles("unique_models", "*.PB", SearchOption.AllDirectories).ToList(); var done = 0; //foreach ( var path in paths ) Parallel.ForEach(paths, new ParallelOptions() { MaxDegreeOfParallelism = 16 }, (path) => { Console.WriteLine(Path.GetFileName(path)); var modelPack = new ModelPack(path); new ModelPack(modelPack.Save()); //modelPack.Save( path + ".out" ); //new ModelPack( path + ".out" ); //ExportObj( modelPack, Path.GetFileNameWithoutExtension( path ) + ".obj" ); } ); //var modelPack = new ModelPack( @"D:\Programming\Repos\DDS3-Model-Studio\Source\DDS3ModelLibraryCLI\bin\Debug\unique_models\tiaki_DF1D93D2EC3ED36D63239A1A2B27ED1745DA9991F231884EA7BC5FAF291F74AA.PB" ); //modelPack.Save( @"D:\Programming\Repos\DDS3-Model-Studio\Source\DDS3ModelLibraryCLI\bin\Debug\unique_models\tiaki_DF1D93D2EC3ED36D63239A1A2B27ED1745DA9991F231884EA7BC5FAF291F74AA.PB" + ".out" ); //foreach ( var kvp in frequencyMap.OrderBy( x => x.Value ) ) //{ // Console.WriteLine( kvp.Value + " " + kvp.Key ); //} //var paths = Directory.EnumerateFiles( "unique_models", "*.PB", SearchOption.AllDirectories ).ToList(); //var done = 0; //Parallel.ForEach( paths, new ParallelOptions() { MaxDegreeOfParallelism = 8 }, ( path ) => //{ // //Console.WriteLine( Path.GetFileName( path ) ); // var modelPack = new ModelPack( path ); // //modelPack.Save( path + ".out" ); // //File.Delete( path + ".out" ); // //modelPack.Save(); //} ); //var done = 0; //Parallel.ForEach( paths, new ParallelOptions() { MaxDegreeOfParallelism = 8 }, ( path ) => //{ // Console.WriteLine( Path.GetFileName( path ) ); // var outPath = "unique_models\\" + Path.GetFileName( path ) + ".PB"; // if ( Path.GetExtension( path ) == ".PAC" ) // { // using ( var reader = new EndianBinaryReader( path, Endianness.Little ) ) // { // reader.Position = 0x24; // var size = reader.ReadInt32(); // reader.Position += 4; // var offset = reader.ReadInt32(); // using ( var fileStream = File.Create( outPath ) ) // new StreamView( reader.BaseStream, offset, size ).CopyTo( fileStream ); // } // } // else // { // File.Copy( path, outPath, true ); // } //} ); }
private static void OpenAndSaveModelPackTest() { var modelPack = new ModelPack(@"..\..\..\..\Resources\player_a.PB"); modelPack.Save(@"D:\Modding\DDS3\Nocturne\_HostRoot\dds3data\model\field\player_a.PB"); }
private static void Main(string[] args) { { File.Delete("test.fbx"); var modelPack = new ModelPack(@"D:\dumps\smt3_ntsc\DDS3\model\field\player_a.PB"); //var modelPack = new ModelPack( @"D:\dumps\smt3_ntsc\DDS3\model\devil\on\0x126_on.PB"); FbxModelExporter.Instance.Export(modelPack.Models[0], "test.fbx", modelPack.TexturePack); return; //AssimpModelExporter.Instance.Export( modelPack.Models[ 0 ], "player_a.dae", modelPack.TexturePack ); //for ( var i = 0; i < modelPack.MotionPacks[ 0 ].Motions.Count; i++ ) //{ // var motion = modelPack.MotionPacks[ 0 ].Motions[ i ]; // if ( motion == null ) // continue; // AssimpMotionExporter.Instance.Export( modelPack.Models[ 0 ], motion, $"player_a_motion_{i:D2}.dae" ); //} var newMotion = AssimpMotionImporter.Instance.Import(@"D:\Users\smart\Desktop\nocturne_player_a_fortnite.fbx", new AssimpMotionImporter.Config { NodeIndexResolver = n => modelPack.Models[0].Nodes.FindIndex(x => x.Name == n) }); for (int i = 0; i < modelPack.MotionPacks[0].Motions.Count; i++) { modelPack.MotionPacks[0].Motions[i] = newMotion; } modelPack.Save(@"D:\Modding\DDS3\Nocturne\_HostRoot\dds3data\model\field\player_a.PB"); } { var lb = new LBFileSystem(); lb.Load(@"D:\Modding\DDS3\Nocturne\_HostRoot\dds3data\fld\f\f037\_f037_027.LB"); var f1Handle = lb.GetHandle("F1"); FieldScene f1; using (var stream = lb.OpenFile(f1Handle)) f1 = new FieldScene(stream, true); foreach (var obj in f1.Objects) { switch (obj.ResourceType) { case FieldObjectResourceType.Model: { var model = ( Model )obj.Resource; foreach (var material in model.Materials) { if (material.TextureId.HasValue) { material.TextureId = 0; } material.Color1 = material.Color2 = material.Color3 = material.Color4 = material.Color5 = null; material.Float1 = null; material.FloatArray1 = material.FloatArray2 = material.FloatArray3 = null; } foreach (var node in model.Nodes) { if (node.Geometry == null) { continue; } foreach (var _mesh in node.Geometry.Meshes) { if (_mesh is MeshType1 mesh) { foreach (var batch in mesh.Batches) { batch.Flags &= ~MeshFlags.Normal; batch.Flags &= ~MeshFlags.Color; } } } } } break; case FieldObjectResourceType.Type3: break; case FieldObjectResourceType.TextureListFileName: break; case FieldObjectResourceType.Effect: break; case FieldObjectResourceType.Light: break; } } ExportObj(f1); lb.AddFile(f1Handle, f1.Save(), true, ConflictPolicy.Replace); var tbHandle = lb.GetHandle("TBN"); var texturePack = new TexturePack(); texturePack.Textures.Add(new Texture(new Bitmap(@"D:\Modding\Tools\magenta.png"))); lb.AddFile(tbHandle, texturePack.Save(), true, ConflictPolicy.Replace); lb.Save(@"D:\Modding\DDS3\Nocturne\_HostRoot\dds3data\fld\f\f037\f037_027.LB"); } //OpenAndSaveModelPackTest(); //ReplaceF1Test(); //ReplaceModelTest(); //OpenAndSaveModelPackBatchTest(); //return; //ExportObj( new ModelPack( @"D:\Modding\DDS3\Nocturne\_HostRoot\dds3data\model\field\player_b.PB" ) ); //return; //OpenAndSaveModelPackBatchTest();return; //OpenAndSaveFieldSceneBatchTest();return; //ReplaceModelTest();return; //var modelPack = new ModelPack( @"..\..\..\..\Resources\player_a.PB" ); //var modelPack = new ModelPack( @"D:\Modding\DDS3\Nocturne\_HostRoot\dds3data\model\field\player_a.PB" ); //using ( var writer = File.CreateText( "test.obj" ) ) //{ // var vertexBaseIndex = 0; // foreach ( var model in modelPack.Models ) // { // foreach ( var node in model.Nodes ) // { // if ( node.Geometry == null ) // continue; // for ( var meshIndex = 0; meshIndex < node.Geometry.Meshes.Count; meshIndex++ ) // { // var _mesh = node.Geometry.Meshes[ meshIndex ]; // if ( _mesh.Type != MeshType.Type7 ) // continue; // var mesh = ( MeshType7 ) _mesh; // var positions = new Vector3[mesh.VertexCount]; // var normals = new Vector3[positions.Length]; // var weights = new List<(short NodeIndex, float Weight)>[positions.Length]; // var texCoords = new Vector2[positions.Length]; // var batchVertexBaseIndex = 0; // foreach ( var batch in mesh.Batches ) // { // for ( var nodeBatchIndex = 0; nodeBatchIndex < batch.NodeBatches.Count; nodeBatchIndex++ ) // { // var nodeBatch = batch.NodeBatches[ nodeBatchIndex ]; // var nodeWorldTransform = model.Nodes[ nodeBatch.NodeIndex ].WorldTransform; // for ( int i = 0; i < nodeBatch.Positions.Length; i++ ) // { // var position = new Vector3( nodeBatch.Positions[i].X, nodeBatch.Positions[i].Y, // nodeBatch.Positions[i].Z ); // var weight = nodeBatch.Positions[i].W; // var weightedNodeWorldTransform = nodeWorldTransform * weight; // var weightedWorldPosition = Vector3.Transform( position, weightedNodeWorldTransform ); // positions[batchVertexBaseIndex + i] += weightedWorldPosition; // if ( weights[batchVertexBaseIndex + i] == null ) // weights[batchVertexBaseIndex + i] = new List<(short NodeIndex, float Weight)>(); // weights[batchVertexBaseIndex + i].Add( (nodeBatch.NodeIndex, weight) ); // normals[batchVertexBaseIndex + i] += Vector3.TransformNormal( nodeBatch.Normals[i], weightedNodeWorldTransform ); // } // } // Array.Copy( batch.TexCoords, 0, texCoords, batchVertexBaseIndex, batch.TexCoords.Length ); // //foreach ( var position in positions ) // //{ // // writer.WriteLine( $"v {position.X} {position.Y} {position.Z}" ); // //} // //foreach ( var normal in normals ) // //{ // // writer.WriteLine( $"vn {normal.X} {normal.Y} {normal.Z}" ); // //} // //foreach ( var texCoord in batch.TexCoords ) // //{ // // writer.WriteLine( $"vt {texCoord.X} {texCoord.Y}" ); // //} // batchVertexBaseIndex += batch.VertexCount; // } // foreach ( var position in positions ) // { // writer.WriteLine( $"v {position.X} {position.Y} {position.Z}" ); // } // foreach ( var normal in normals ) // { // writer.WriteLine( $"vn {normal.X} {normal.Y} {normal.Z}" ); // } // foreach ( var texCoord in texCoords ) // { // writer.WriteLine( $"vt {texCoord.X} {texCoord.Y}" ); // } // //// Find unique node indices used by the mesh (max 4 per mesh!) // //var usedNodeIndices = weights.SelectMany( x => x.Select( y => y.NodeIndex ) ).Distinct().ToList(); // //// Calculate node index usage frequency // //var usedNodeIndicesFrequency = new Dictionary<int, int>(); // //for ( int i = 0; i < usedNodeIndices.Count; i++ ) // // usedNodeIndicesFrequency[usedNodeIndices[i]] = 0; // //for ( int j = 0; j < positions.Length; j++ ) // //{ // // foreach ( var (nodeIndex, _) in weights[j] ) // // ++usedNodeIndicesFrequency[nodeIndex]; // //} // //// Sort used node indices by frequency // //usedNodeIndices = usedNodeIndices.OrderBy( x => usedNodeIndicesFrequency[ x ] ).ToList(); // //// Start building batches // //var vertexIndexRemap = new Dictionary<int, int>(); // //var batches = new List<MeshType7Batch>(); // //batchVertexBaseIndex = 0; // //while ( vertexIndexRemap.Count < positions.Length ) // //{ // // var batchVertexCount = Math.Min( 24, positions.Length - vertexIndexRemap.Count ); // // var batch = new MeshType7Batch(); // // var batchVertexIndexRemap = new Dictionary<int, int>(); // // var batchTexCoords = new List<Vector2>(); // // // req. all vertices to use the same set of node indices // // for ( var i = 0; i < usedNodeIndices.Count; i++ ) // // { // // var nodeIndex = usedNodeIndices[i]; // // var nodeWorldTransform = model.Nodes[nodeIndex].WorldTransform; // // var nodeWorldTransformInv = nodeWorldTransform.Inverted(); // // // get all verts with this index // // var nodePositions = new List<Vector4>(); // // var nodeNormals = new List<Vector3>(); // // for ( int j = 0; j < positions.Length; j++ ) // // { // // // Skip this vertex if it has already been processed before // // if ( vertexIndexRemap.ContainsKey( j ) ) // // continue; // // foreach ( (short NodeIndex, float Weight) in weights[j] ) // // { // // if ( NodeIndex != nodeIndex ) // // continue; // // // Transform position and normal to model space // // var position = Vector3.Transform( positions[j], nodeWorldTransformInv ); // // var normal = Vector3.TransformNormal( normals[j], nodeWorldTransformInv ); // // // Add entry to vertex remap, and add the model space positions and normals to our lists // // batchVertexIndexRemap[j] = batchVertexBaseIndex + nodePositions.Count; // // nodePositions.Add( new Vector4( position, Weight ) ); // // nodeNormals.Add( normal ); // // if ( i == 0 ) // // { // // // Only add this once, of course // // batchTexCoords.Add( texCoords[ j ] ); // // } // // // Stop looking if we've reached our vertex count // // if ( nodePositions.Count == batchVertexCount ) // // goto end; // // } // // } // // end: // // batch.NodeBatches.Add( new MeshType7NodeBatch() // // { // // NodeIndex = nodeIndex, // // Positions = nodePositions.ToArray(), // // Normals = nodeNormals.ToArray() // // }); // // } // // batch.TexCoords = batchTexCoords.ToArray(); // // foreach ( var i in batchVertexIndexRemap ) // // vertexIndexRemap.Add( i.Key, i.Value ); // // Debug.Assert( batch.NodeBatches.Count > 0 ); // // Debug.Assert( batch.NodeBatches.TrueForAll( x => x.VertexCount == batch.NodeBatches[ 0 ].VertexCount ) ); // // batches.Add( batch ); // // batchVertexBaseIndex += batchVertexCount; // //} // //var materialIndex = mesh.MaterialIndex; // //var triangles = new Triangle[mesh.TriangleCount]; // //for ( var i = 0; i < mesh.Triangles.Length; i++ ) // //{ // // ref var triangle = ref triangles[i]; // // triangle.A = ( ushort )vertexIndexRemap[mesh.Triangles[i].A]; // // triangle.B = ( ushort )vertexIndexRemap[mesh.Triangles[i].B]; // // triangle.C = ( ushort )vertexIndexRemap[mesh.Triangles[i].C]; // //} // //mesh = new MeshType7(); // //mesh.Batches.AddRange( batches ); // //mesh.Triangles = triangles; // //mesh.MaterialIndex = materialIndex; // //node.Geometry.Meshes[meshIndex] = mesh; // writer.WriteLine( $"o node_{node.Name}_mesh_{meshIndex}" ); // foreach ( var triangle in mesh.Triangles ) // { // writer.WriteLine( "f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}", vertexBaseIndex + triangle.A + 1, vertexBaseIndex + triangle.B + 1, vertexBaseIndex + triangle.C + 1 ); // } // vertexBaseIndex += mesh.VertexCount; // } // } // } //} //modelPack.Save( @"D:\Modding\DDS3\Nocturne\_HostRoot\dds3data\model\field\player_a.PB" ); //ReplaceModelTest(); //return; //GenerateMaterialPresets(); //return; //var modelPack = new ModelPack( @"..\..\..\..\Resources\player_a.PB" ); //foreach ( var material in modelPack.Models[0].Materials ) //{ // Console.WriteLine( MaterialPresetStore.GetPresetId( material ) ); //} //return; ////for ( var i = 0; i < modelPack.TexturePack.Count; i++ ) ////{ //// var texture = modelPack.TexturePack[ i ]; //// texture.GetBitmap().Save( $"player_a_{i}.png" ); ////} //modelPack.Save( @"D:\Modding\DDS3\Nocturne\_HostRoot\dds3data\model\field\player_a.PB" ); ////ReplaceModelTest(); //OpenAndSaveModelPackBatchTest(); }
private static void ConvertAssimpModel() { switch (Options.OutputFormat) { case OutputFormat.PB: { if (Options.PackedModel.ReplaceInput == null) { throw new Exception("You must specify a PB replacement input for conversion to PB"); } var modelPack = new ModelPack(); if (Options.PackedModel.ReplaceInput != null) { modelPack.Load(Options.PackedModel.ReplaceInput); } if (!Options.Assimp.TreatInputAsAnimation) { modelPack.Replace(Options.Input, Options.TmxScale, Options.Model.EnableMaterialOverlays, Options.Model.WeightedMeshType, Options.Model.UnweightedMeshType, Options.Model.MeshWeightLimit, Options.Model.BatchVertexLimit); } else { var newMotion = AssimpMotionImporter.Instance.Import(Options.Input, new AssimpMotionImporter.Config { NodeIndexResolver = n => modelPack.Models[Options.PackedModel.ReplaceMotionModelIndex].Nodes.FindIndex(x => x.Name == n) }); if (Options.PackedModel.ReplaceMotionIndex < 0 || (Options.PackedModel.ReplaceMotionIndex + 1) > modelPack.MotionPacks[Options.PackedModel.ReplaceMotionPackIndex].Motions.Count) { modelPack.MotionPacks[Options.PackedModel.ReplaceMotionPackIndex].Motions[Options.PackedModel.ReplaceMotionIndex] = newMotion; } } modelPack.Save(Options.Output); } break; case OutputFormat.F1: { var modelPack = new ModelPack(); var model = new Model(); model.Nodes.Add(new Node { Name = "model" }); modelPack.Models.Add(model); modelPack.Replace(Options.Input, Options.TmxScale, Options.Model.EnableMaterialOverlays, Options.Model.WeightedMeshType, Options.Model.UnweightedMeshType, Options.Model.MeshWeightLimit, Options.Model.BatchVertexLimit); var lb = new LBFileSystem(); lb.Load(Options.Field.LbReplaceInput); var f1Handle = lb.GetHandle("F1"); FieldScene f1; using (var stream = lb.OpenFile(f1Handle)) f1 = new FieldScene(stream, true); f1.Objects.RemoveAll(x => x.ResourceType == FieldObjectResourceType.Model); f1.Objects.Clear(); f1.Objects.Add(new FieldObject() { Id = 0, Name = "model", Transform = new FieldObjectTransform(), Resource = modelPack.Models[0] }); lb.AddFile(f1Handle, f1.Save(), true, ConflictPolicy.Replace); if (modelPack.TexturePack != null) { var tbHandle = lb.GetHandle("TBN"); lb.AddFile(tbHandle, modelPack.TexturePack.Save(), true, ConflictPolicy.Replace); } lb.Save(Options.Output); } break; default: throw new Exception("Unsupported output format"); } }