diff --git a/.gitignore b/.gitignore index 176345da..5f02d889 100644 --- a/.gitignore +++ b/.gitignore @@ -274,6 +274,7 @@ FakesAssemblies/ #ThirdParty/DXDecompiler # Include libreborb dll and hlsl exe !Charm/ThirdParty/*.dll +!Charm/ThirdParty/Alkahest/*.dll !Charm/ThirdParty/*.exe .vscode/tasks.json .vscode/launch.json diff --git a/AtlasSharp/AtlasView.xaml.cs b/AtlasSharp/AtlasView.xaml.cs index 21fd42cf..9b1350ff 100644 --- a/AtlasSharp/AtlasView.xaml.cs +++ b/AtlasSharp/AtlasView.xaml.cs @@ -262,7 +262,7 @@ private bool InitStaticMesh(FileHash staticHash) public struct InputSignature { - public InputSemantic Semantic; + public DXBCSemantic Semantic; public int SemanticIndex; public int DxgiFormat; public int BufferIndex; @@ -301,10 +301,10 @@ public PartMaterial(IMaterial material, List strides) throw new Exception(); } - Tiger.Schema.InputSignature[] inputSignatures = material.VertexShader.InputSignatures.ToArray(); + DXBCIOSignature[] inputSignatures = material.VertexShader.InputSignatures.ToArray(); Helpers.DecorateSignaturesWithBufferIndex(ref inputSignatures, strides); // absorb into the getter probs - foreach (Tiger.Schema.InputSignature signature in inputSignatures) + foreach (DXBCIOSignature signature in inputSignatures) { InputSignatures[sigIndex].Semantic = signature.Semantic; InputSignatures[sigIndex].SemanticIndex = (int)signature.SemanticIndex; @@ -312,7 +312,7 @@ public PartMaterial(IMaterial material, List strides) switch (signature.Mask) { case ComponentMask.XYZW: - if (signature.Semantic == InputSemantic.Colour) + if (signature.Semantic == DXBCSemantic.Colour) { InputSignatures[sigIndex].DxgiFormat = (int)DXGI_FORMAT.R8G8B8A8_UNORM; break; diff --git a/Charm.sln b/Charm.sln index 17c67887..8ae487c9 100644 --- a/Charm.sln +++ b/Charm.sln @@ -17,13 +17,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RevorbStd", "ThirdParty\rev EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CharmDepotDownloader", "CharmDepotDownloader\CharmDepotDownloader.csproj", "{D22CC98B-DE19-460A-B661-3155C55024AE}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AtlasSharp", "AtlasSharp\AtlasSharp.csproj", "{161DFC33-8147-4BD1-B72C-A08D53D831A3}" - ProjectSection(ProjectDependencies) = postProject - {1132E6A9-9AD9-405D-936A-7643392B0202} = {1132E6A9-9AD9-405D-936A-7643392B0202} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Atlas", "Atlas\Atlas.vcxproj", "{1132E6A9-9AD9-405D-936A-7643392B0202}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -58,14 +51,6 @@ Global {D22CC98B-DE19-460A-B661-3155C55024AE}.Debug|x64.Build.0 = Debug|Any CPU {D22CC98B-DE19-460A-B661-3155C55024AE}.Release|x64.ActiveCfg = Release|Any CPU {D22CC98B-DE19-460A-B661-3155C55024AE}.Release|x64.Build.0 = Release|Any CPU - {161DFC33-8147-4BD1-B72C-A08D53D831A3}.Debug|x64.ActiveCfg = Debug|Any CPU - {161DFC33-8147-4BD1-B72C-A08D53D831A3}.Debug|x64.Build.0 = Debug|Any CPU - {161DFC33-8147-4BD1-B72C-A08D53D831A3}.Release|x64.ActiveCfg = Release|Any CPU - {161DFC33-8147-4BD1-B72C-A08D53D831A3}.Release|x64.Build.0 = Release|Any CPU - {1132E6A9-9AD9-405D-936A-7643392B0202}.Debug|x64.ActiveCfg = Debug|x64 - {1132E6A9-9AD9-405D-936A-7643392B0202}.Debug|x64.Build.0 = Debug|x64 - {1132E6A9-9AD9-405D-936A-7643392B0202}.Release|x64.ActiveCfg = Release|x64 - {1132E6A9-9AD9-405D-936A-7643392B0202}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Charm/ActivityMapEntityView.xaml.cs b/Charm/ActivityMapEntityView.xaml.cs index fb98c80e..1fc21dfe 100644 --- a/Charm/ActivityMapEntityView.xaml.cs +++ b/Charm/ActivityMapEntityView.xaml.cs @@ -15,7 +15,10 @@ using Tiger.Schema.Activity; using Tiger.Schema.Activity.DESTINY1_RISE_OF_IRON; using Tiger.Schema.Activity.DESTINY2_SHADOWKEEP_2601; +using Tiger.Schema.Activity.DESTINY2_WITCHQUEEN_6307; using Tiger.Schema.Entity; +using Tiger.Schema.Havok; +using Tiger.Schema.Shaders; using Tiger.Schema.Static; namespace Charm; @@ -219,6 +222,28 @@ private void PopulateActivityEntityContainerList(IActivity activity, FileHash Un } } + var ambientTag = FileResourcer.Get().GetSchemaTag(activity.FileHash); + if (ambientTag.TagData.AmbientActivity != null) + { + var ambient = FileResourcer.Get().GetFileInterface(ambientTag.TagData.AmbientActivity.Hash); + foreach (var entry in ambient.EnumerateActivityEntities(UnkActivity)) + { + if (entry.DataTables.Count > 0) + { + DisplayEntityMap entityMap = new(); + entityMap.Name = $"(Ambient) {entry.BubbleName} {entry.ActivityPhaseName2}: {entry.DataTables.Count} Entries"; + entityMap.Hash = entry.Hash; + entityMap.Count = entry.DataTables.Count; + entityMap.EntityType = DisplayEntityMap.Type.Activity; + entityMap.DataTables = entry.DataTables; + entityMap.WorldIDs = entry.WorldIDs; + entityMap.Data = entityMap; + + items.Add(entityMap); + } + } + } + var sortedItems = new List(items); sortedItems.Sort((a, b) => a.Name.CompareTo(b.Name)); sortedItems.Insert(0, new DisplayEntityMap @@ -367,6 +392,7 @@ public static void ExportFull(List dataTables, string hash) Directory.CreateDirectory(savePath); ExtractDataTables(dataTables, hash, savePath); + if (_config.GetIndvidualStaticsEnabled()) { Directory.CreateDirectory(savePath + "/Entities"); @@ -382,115 +408,107 @@ public static void ExportFull(List dataTables, string hash) private static void ExtractDataTables(List dataTables, string hash, string savePath) { // todo these scenes can be combined - ExporterScene dynamicPointScene = Exporter.Get().CreateScene($"{hash}_EntityPoints", ExportType.EntityPoints); - ExporterScene dynamicScene = Exporter.Get().CreateScene($"{hash}_Entities", ExportType.Map); - ExporterScene skyScene = Exporter.Get().CreateScene($"{hash}_SkyEnts", ExportType.Map); + ExporterScene dynamicScene = Exporter.Get().CreateScene($"{hash}_Entities", ExportType.EntityInMap); + ExporterScene skyScene = Exporter.Get().CreateScene($"{hash}_SkyEnts", ExportType.EntityInMap); ExporterScene terrainScene = Exporter.Get().CreateScene($"{hash}_Terrain", ExportType.Terrain); + ExporterScene waterScene = Exporter.Get().CreateScene($"{hash}_Water", ExportType.EntityInMap); //Idk what to name this besides water Parallel.ForEach(dataTables, data => { - if (Strategy.CurrentStrategy == TigerStrategy.DESTINY1_RISE_OF_IRON && data.GetReferenceHash().Hash32 == 0x808003F6) - { - var dataEntries = FileResourcer.Get().GetSchemaTag(data).TagData.EntityResource.CollapseIntoDataEntry(); - foreach (var entry in dataEntries) - { - Entity entity = FileResourcer.Get().GetFile(entry.GetEntityHash()); - if (entity.HasGeometry()) - { - dynamicScene.AddMapEntity(entry, entity); - entity.SaveMaterialsFromParts(dynamicScene, entity.Load(ExportDetailLevel.MostDetailed)); - } - else - dynamicPointScene.AddEntityPoints(entry); - } - } - else + var dataTable = FileResourcer.Get().GetSchemaTag(data); + dataTable.TagData.DataEntries.ForEach(entry => { - var dataTable = FileResourcer.Get().GetSchemaTag(data); - dataTable.TagData.DataEntries.ForEach(entry => + Entity entity = FileResourcer.Get().GetFile(entry.GetEntityHash()); + if (entity.HasGeometry()) + dynamicScene.AddMapEntity(entry, entity); + + switch (entry.DataResource.GetValue(dataTable.GetReader())) { - Entity entity = FileResourcer.Get().GetFile(entry.GetEntityHash()); - if (entity.HasGeometry()) - { - dynamicScene.AddMapEntity(entry, entity); - entity.SaveMaterialsFromParts(dynamicScene, entity.Load(ExportDetailLevel.MostDetailed)); - } - else - { - //if (entry.Translation.ToVec3() == Tiger.Schema.Vector3.Zero) - // System.Console.WriteLine($"World origin resource {dataTable.Hash} Resource? {entry.DataResource.GetValue(dataTable.GetReader())}"); - dynamicPointScene.AddEntityPoints(entry); - } + case SMapSkyEntResource skyResource: + foreach (var element in skyResource.Unk10.TagData.Unk08) + { + if (element.Unk60.TagData.Unk08 is null) + continue; + Matrix4x4 matrix = new Matrix4x4( + element.Unk00.X, element.Unk00.Y, element.Unk00.Z, element.Unk00.W, + element.Unk10.X, element.Unk10.Y, element.Unk10.Z, element.Unk10.W, + element.Unk20.X, element.Unk20.Y, element.Unk20.Z, element.Unk20.W, + element.Unk30.X, element.Unk30.Y, element.Unk30.Z, element.Unk30.W + ); - switch (entry.DataResource.GetValue(dataTable.GetReader())) - { - case SMapSkyEntResource skyResource: - foreach (var element in skyResource.Unk10.TagData.Unk08) + System.Numerics.Vector3 scale = new(); + System.Numerics.Vector3 trans = new(); + Quaternion quat = new(); + Matrix4x4.Decompose(matrix, out scale, out quat, out trans); + + skyScene.AddMapModel(element.Unk60.TagData.Unk08, + new Tiger.Schema.Vector4(trans.X, trans.Y, trans.Z, 1.0f), + new Tiger.Schema.Vector4(quat.X, quat.Y, quat.Z, quat.W), + new Tiger.Schema.Vector3(scale.X, scale.Y, scale.Z)); + + foreach (DynamicMeshPart part in element.Unk60.TagData.Unk08.Load(ExportDetailLevel.MostDetailed, null)) { - if (element.Unk60.TagData.Unk08 is null) - continue; - - Matrix4x4 matrix = new Matrix4x4( - element.Unk00.X, element.Unk00.Y, element.Unk00.Z, element.Unk00.W, - element.Unk10.X, element.Unk10.Y, element.Unk10.Z, element.Unk10.W, - element.Unk20.X, element.Unk20.Y, element.Unk20.Z, element.Unk20.W, - element.Unk30.X, element.Unk30.Y, element.Unk30.Z, element.Unk30.W - ); - - System.Numerics.Vector3 scale = new(); - System.Numerics.Vector3 trans = new(); - Quaternion quat = new(); - Matrix4x4.Decompose(matrix, out scale, out quat, out trans); - - skyScene.AddMapModel(element.Unk60.TagData.Unk08, - new Tiger.Schema.Vector4(trans.X, trans.Y, trans.Z, 1.0f), - new Tiger.Schema.Vector4(quat.X, quat.Y, quat.Z, quat.W), - new Tiger.Schema.Vector3(scale.X, scale.Y, scale.Z)); - - foreach (DynamicMeshPart part in element.Unk60.TagData.Unk08.Load(ExportDetailLevel.MostDetailed, null)) - { - if (part.Material == null) continue; - skyScene.Materials.Add(new ExportMaterial(part.Material)); - } + if (part.Material == null) continue; + skyScene.Materials.Add(new ExportMaterial(part.Material, MaterialType.Transparent)); } - break; - case CubemapResource cubemap: - dynamicScene.AddCubemap(cubemap); - break; - case SMapLightResource mapLight: - dynamicScene.AddMapLight(mapLight); - break; - case SMapSpotLightResource spotLight: - if (spotLight.Unk10 is not null) - dynamicScene.AddMapSpotLight(entry, spotLight); - break; - case SMapDecalsResource decals: - decals.MapDecals?.Load(); - if (decals.MapDecals is null) - return; - - dynamicScene.AddDecals(decals); - foreach (var item in decals.MapDecals.TagData.DecalResources) + } + break; + case CubemapResource cubemap: + dynamicScene.AddCubemap(entry, cubemap); + dynamicScene.Textures.Add(cubemap.CubemapTexture); + break; + case SMapLightResource mapLight: + dynamicScene.AddMapLight(mapLight); + break; + case SMapShadowingLightResource spotLight: + if (spotLight.Unk10 is not null) + dynamicScene.AddMapSpotLight(entry, spotLight); + break; + case SMapDecalsResource decals: + if (decals.MapDecals is null || decals.MapDecals.TagData.DecalResources is null) + return; + + dynamicScene.AddDecals(decals); + foreach (var item in decals.MapDecals.TagData.DecalResources) + { + if (item.StartIndex >= 0 && item.StartIndex < decals.MapDecals.TagData.Locations.Count) { - if (item.StartIndex >= 0 && item.StartIndex < decals.MapDecals.TagData.Locations.Count) + for (int i = item.StartIndex; i < item.StartIndex + item.Count && i < decals.MapDecals.TagData.Locations.Count; i++) { - for (int i = item.StartIndex; i < item.StartIndex + item.Count && i < decals.MapDecals.TagData.Locations.Count; i++) - { - dynamicScene.Materials.Add(new ExportMaterial(item.Material)); - } + dynamicScene.Materials.Add(new ExportMaterial(item.Material, MaterialType.Transparent)); } } - break; - case SMapTerrainResource terrain: - terrain.Terrain.Load(); - terrain.Terrain.LoadIntoExporter(terrainScene, savePath, _config.GetUnrealInteropEnabled() || _config.GetS2ShaderExportEnabled()); - break; - default: - break; - } - }); - } + } + break; + case SMapTerrainResource terrain: + terrain.Terrain.LoadIntoExporter(terrainScene, savePath); + break; + case SMapWaterDecal water: + waterScene.AddMapModel(water.Model, + entry.Translation, + entry.Rotation, + new Tiger.Schema.Vector3(entry.Translation.W)); + foreach (DynamicMeshPart part in water.Model.Load(ExportDetailLevel.MostDetailed, null)) + { + if (part.Material == null) continue; + waterScene.Materials.Add(new ExportMaterial(part.Material, MaterialType.Transparent)); + } + break; + + case D2Class_7B918080 KillTurnbackHavok: + D2Class_21918080 havok = KillTurnbackHavok.Pointer.GetValue(dataTable.GetReader()); + DestinyHavok.SaveHavokShape(havok.HavokVolume, $"Turnback_{data}", entry.Translation, entry.Rotation); + break; + + case D2Class_C26A8080 unkHavok: + DestinyHavok.SaveHavokShape(unkHavok.Unk10.TagData.Unk08, data, entry.Translation, entry.Rotation); + break; + + default: + break; + } + }); }); } @@ -509,11 +527,6 @@ private static void ExportIndividual(List dataTables, string hash, str ExporterScene dynamicScene = Exporter.Get().CreateScene(entity.Hash, ExportType.EntityInMap); dynamicScene.AddEntity(entry.GetEntityHash(), entity.Load(ExportDetailLevel.MostDetailed), entity.Skeleton?.GetBoneNodes()); entity.SaveMaterialsFromParts(dynamicScene, entity.Load(ExportDetailLevel.MostDetailed)); - - if (_config.GetS2VMDLExportEnabled()) - { - Source2Handler.SaveEntityVMDL($"{savePath}/Entities", entity); - } } } } @@ -528,11 +541,6 @@ private static void ExportIndividual(List dataTables, string hash, str ExporterScene dynamicScene = Exporter.Get().CreateScene(entity.Hash, ExportType.EntityInMap); dynamicScene.AddEntity(entry.GetEntityHash(), entity.Load(ExportDetailLevel.MostDetailed), entity.Skeleton?.GetBoneNodes()); entity.SaveMaterialsFromParts(dynamicScene, entity.Load(ExportDetailLevel.MostDetailed)); - - if (_config.GetS2VMDLExportEnabled()) - { - Source2Handler.SaveEntityVMDL($"{savePath}/Entities", entity); - } } if (entry.DataResource.GetValue(dataTable.GetReader()) is SMapSkyEntResource skyResource) { @@ -543,18 +551,13 @@ private static void ExportIndividual(List dataTables, string hash, str ExporterScene skyScene = Exporter.Get().CreateScene(element.Unk60.TagData.Unk08.Hash, ExportType.EntityInMap); skyScene.AddModel(element.Unk60.TagData.Unk08); - - if (_config.GetS2VMDLExportEnabled()) - { - Source2Handler.SaveEntityVMDL($"{savePath}/Entities", element.Unk60.TagData.Unk08.Hash, element.Unk60.TagData.Unk08.Load(ExportDetailLevel.MostDetailed, null)); - } } } if (entry.DataResource.GetValue(dataTable.GetReader()) is SMapTerrainResource terrainArrangement) { ExporterScene staticScene = Exporter.Get().CreateScene($"{terrainArrangement.Terrain.Hash}_Terrain", ExportType.StaticInMap); terrainArrangement.Terrain.Load(); - terrainArrangement.Terrain.LoadIntoExporter(staticScene, savePath, _config.GetUnrealInteropEnabled() || _config.GetS2ShaderExportEnabled(), true); + terrainArrangement.Terrain.LoadIntoExporter(staticScene, savePath); } }); } diff --git a/Charm/App.xaml.cs b/Charm/App.xaml.cs index 6803f6fd..2b922a92 100644 --- a/Charm/App.xaml.cs +++ b/Charm/App.xaml.cs @@ -65,7 +65,7 @@ private void Application_Startup(object sender, StartupEventArgs e) // { // var dynamicParts = entity.Load(ExportDetailLevel.MostDetailed); // fbxHandler.AddEntityToScene(entity, dynamicParts, ExportDetailLevel.MostDetailed); - // entity.SaveMaterialsFromParts(savePath, dynamicParts, ConfigSubsystem.GetUnrealInteropEnabled() || ConfigSubsystem.GetS2ShaderExportEnabled()); + // entity.SaveMaterialsFromParts(savePath, dynamicParts, ConfigSubsystem.GetUnrealInteropEnabled() || ConfigSubsystem.GetSBoxShaderExportEnabled()); // entity.SaveTexturePlates(savePath); // } // diff --git a/Charm/Charm.csproj b/Charm/Charm.csproj index 59ad0d6f..4d7b4cb6 100644 --- a/Charm/Charm.csproj +++ b/Charm/Charm.csproj @@ -87,6 +87,9 @@ Always + + Always + Always @@ -107,16 +110,15 @@ + + + Shaders\Lighting.hlsl + Always + + - - - Shaders\Lighting.hlsl - Always - - - - - - + + + diff --git a/Charm/ConfigView.xaml b/Charm/ConfigView.xaml index d0ab5d94..52a18255 100644 --- a/Charm/ConfigView.xaml +++ b/Charm/ConfigView.xaml @@ -1,23 +1,24 @@ - + - - + + - - + + - diff --git a/Charm/DevView.xaml.cs b/Charm/DevView.xaml.cs index c7a4311a..a237b253 100644 --- a/Charm/DevView.xaml.cs +++ b/Charm/DevView.xaml.cs @@ -11,6 +11,7 @@ using System.Windows.Controls; using System.Windows.Input; using ConcurrentCollections; +using Arithmic; using Tiger; using Tiger.Exporters; using Tiger.Schema; @@ -18,6 +19,7 @@ using Tiger.Schema.Entity; using Tiger.Schema.Investment; using Tiger.Schema.Shaders; +using Tiger.Schema.Havok; using Tiger.Schema.Static; namespace Charm; @@ -185,6 +187,37 @@ private void AddWindow(FileHash hash) } _mainWindow.SetNewestTabSelected(); } + else if (fileMetadata.Type == 27 && fileMetadata.SubType == 0) // Havok + { + var shapeCollection = DestinyHavok.ReadShapeCollection(FileResourcer.Get().GetFile(hash).GetData()); + if (shapeCollection is null) + { + Log.Error("Havok shape collection is null"); + TagHashBox.Text = "NULL"; + return; + } + + Directory.CreateDirectory($"{ConfigSubsystem.Get().GetExportSavePath()}/HavokShapes"); + int i = 0; + foreach (var shape in shapeCollection) + { + var vertices = shape.Vertices; + var indices = shape.Indices; + + var sb = new StringBuilder(); + foreach (var vertex in vertices) + { + sb.AppendLine($"v {vertex.X} {vertex.Y} {vertex.Z}"); + } + foreach (var index in indices.Chunk(3)) + { + sb.AppendLine($"f {index[0] + 1} {index[1] + 1} {index[2] + 1}"); + } + + Console.WriteLine($"Writing 'HavokShapes/shape_{hash}_{i}.obj'"); + File.WriteAllText($"{ConfigSubsystem.Get().GetExportSavePath()}/HavokShapes/shape_{hash}_{i++}.obj", sb.ToString()); + } + } else if ((fileMetadata.Type == 8 || fileMetadata.Type == 16) && fileMetadata.SubType == 0) { switch (reference.Hash32) @@ -250,7 +283,7 @@ private void AddWindow(FileHash hash) foreach (DynamicMeshPart part in parts) { if (part.Material == null) continue; - scene.Materials.Add(new ExportMaterial(part.Material)); + scene.Materials.Add(new ExportMaterial(part.Material, MaterialType.Opaque)); } Exporter.Get().Export(); @@ -262,8 +295,8 @@ private void AddWindow(FileHash hash) case 0x8080714F: case 0x80806C81: Terrain terrain = FileResourcer.Get().GetFile(hash); - ExporterScene terrainScene = Exporter.Get().CreateScene(hash, ExportType.Terrain); - terrain.LoadIntoExporter(terrainScene, ConfigSubsystem.Get().GetExportSavePath(), false); + ExporterScene terrainScene = Exporter.Get().CreateScene(hash, ExportType.Static); + terrain.LoadIntoExporter(terrainScene, ConfigSubsystem.Get().GetExportSavePath()); Exporter.Get().Export(); break; default: diff --git a/Charm/EntityView.xaml.cs b/Charm/EntityView.xaml.cs index 24d7a768..273e1f21 100644 --- a/Charm/EntityView.xaml.cs +++ b/Charm/EntityView.xaml.cs @@ -137,10 +137,6 @@ public static void Export(List entities, string name, ExportTypeFlag exp entity.SaveMaterialsFromParts(scene, dynamicParts); entity.SaveTexturePlates(savePath); } - if (ConfigSubsystem.Get().GetS2VMDLExportEnabled()) - { - Source2Handler.SaveEntityVMDL($"{savePath}", entity); - } } if (exportType == ExportTypeFlag.Full) @@ -167,7 +163,7 @@ public static void ExportInventoryItem(ApiItem item) EntitySkeleton overrideSkeleton = null; if (Strategy.CurrentStrategy >= TigerStrategy.DESTINY2_WITCHQUEEN_6307) { - Entity playerBase = FileResourcer.Get().GetFile(new FileHash(Hash64Map.Get().GetHash32Checked("0000670F342E9595"))); // 64 bit more permanent + Entity playerBase = FileResourcer.Get().GetFile(new FileHash(Hash64Map.Get().GetHash32Checked("0000670F342E9595"))); // 64 bit more permanent overrideSkeleton = new EntitySkeleton(playerBase.Skeleton.Hash); } else if (Strategy.CurrentStrategy == TigerStrategy.DESTINY1_RISE_OF_IRON) diff --git a/Charm/GeneralConfigView.xaml.cs b/Charm/GeneralConfigView.xaml.cs index 15a9eec9..b7981ef1 100644 --- a/Charm/GeneralConfigView.xaml.cs +++ b/Charm/GeneralConfigView.xaml.cs @@ -80,7 +80,6 @@ private void PopulateConfigPanel() GeneralConfigPanel.Children.Add(sp); - // Save path ConfigSettingControl csp = new ConfigSettingControl(); csp.SettingName = "Export save path"; @@ -89,14 +88,6 @@ private void PopulateConfigPanel() csp.ChangeButton.Click += ExportSavePath_OnClick; GeneralConfigPanel.Children.Add(csp); - // Enable Blender interop //force people to use the addon now >:) - //ConfigSettingControl cbe = new ConfigSettingControl(); - //cbe.SettingName = "Generate Blender importing script"; - //bool bval2 = _config.GetBlenderInteropEnabled(); - //cbe.SettingValue = bval2.ToString(); - //cbe.ChangeButton.Click += BlenderInteropEnabled_OnClick; - //GeneralConfigPanel.Children.Add(cbe); - // Enable combined extraction folder for maps ConfigSettingControl cef = new ConfigSettingControl(); cef.SettingName = "Use single folder extraction for maps"; diff --git a/Charm/MainMenuView.xaml b/Charm/MainMenuView.xaml index c14d5854..c2c65364 100644 --- a/Charm/MainMenuView.xaml +++ b/Charm/MainMenuView.xaml @@ -187,61 +187,18 @@ - - - - - - + + + + + + diff --git a/Charm/MapView.xaml b/Charm/MapView.xaml index 81addf55..bd944bc9 100644 --- a/Charm/MapView.xaml +++ b/Charm/MapView.xaml @@ -4,6 +4,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Charm" + xmlns:atlasSharp="clr-namespace:AtlasSharp;assembly=AtlasSharp" mc:Ignorable="d" Loaded="OnControlLoaded" d:DesignHeight="300" d:DesignWidth="300"> @@ -15,5 +16,6 @@ - + + diff --git a/Charm/MapView.xaml.cs b/Charm/MapView.xaml.cs index cb22dfa0..e02f4285 100644 --- a/Charm/MapView.xaml.cs +++ b/Charm/MapView.xaml.cs @@ -23,8 +23,6 @@ public partial class MapView : UserControl private static MainWindow _mainWindow = null; private static ConfigSubsystem _config = CharmInstance.GetSubsystem(); - - private static bool source2Models = _config.GetS2VMDLExportEnabled(); private static bool exportStatics = _config.GetIndvidualStaticsEnabled(); private void OnControlLoaded(object sender, RoutedEventArgs routedEventArgs) @@ -155,7 +153,7 @@ public void Dispose() public static void ExportFullMap(Tag map, ExportTypeFlag exportTypeFlag) { - ExporterScene scene = Exporter.Get().CreateScene(map.Hash.ToString(), ExportType.Map); + ExporterScene scene = Exporter.Get().CreateScene(map.Hash.ToString(), ExportType.StaticInMap); string meshName = map.Hash.ToString(); string savePath = _config.GetExportSavePath() + $"/{meshName}"; @@ -199,11 +197,11 @@ public static void ExportTerrainMap(Tag map) if (entry.DataResource.GetValue(data.MapDataTable.GetReader()) is SMapTerrainResource terrainArrangement) // Terrain { terrainArrangement.Terrain.Load(); - terrainArrangement.Terrain.LoadIntoExporter(scene, savePath, _config.GetUnrealInteropEnabled() || _config.GetS2ShaderExportEnabled()); + terrainArrangement.Terrain.LoadIntoExporter(scene, savePath); if (exportStatics) { ExporterScene staticScene = Exporter.Get().CreateScene($"{terrainArrangement.Terrain.Hash}_Terrain", ExportType.StaticInMap); - terrainArrangement.Terrain.LoadIntoExporter(staticScene, savePath, _config.GetUnrealInteropEnabled() || _config.GetS2ShaderExportEnabled(), true); + terrainArrangement.Terrain.LoadIntoExporter(staticScene, savePath); } } }); @@ -277,14 +275,10 @@ private static void ExportStatics(string savePath, Tag map) if (File.Exists($"{savePath}/Statics/{part.Static.Hash}.fbx")) continue; string staticMeshName = part.Static.Hash.ToString(); - ExporterScene staticScene = Exporter.Get().CreateScene(staticMeshName, ExportType.StaticInMap); + ExporterScene staticScene = + Exporter.Get().CreateScene(staticMeshName, ExportType.StaticInMap); var staticmesh = part.Static.Load(ExportDetailLevel.MostDetailed); staticScene.AddStatic(part.Static.Hash, staticmesh); - - if (source2Models) - { - Source2Handler.SaveStaticVMDL($"{savePath}/Statics", staticMeshName, staticmesh); - } } } } @@ -356,7 +350,7 @@ private static void ExportStatics(string savePath, Tag map) List parts = new(); foreach (var partEntry in terrain.TagData.StaticParts) { - if (partEntry.DetailLevel == 0) + if (partEntry.Lod.DetailLevel == ELodCategory.MainGeom0) { var part = terrain.MakePart(partEntry); terrain.TransformPositions(part); diff --git a/Charm/MaterialView.xaml.cs b/Charm/MaterialView.xaml.cs index 44980ad3..3d47383f 100644 --- a/Charm/MaterialView.xaml.cs +++ b/Charm/MaterialView.xaml.cs @@ -58,15 +58,53 @@ public void Load(FileHash hash) if (material.VertexShader is not null) { - VertexShader.Text = material.Decompile(material.VertexShader.GetBytecode(), $"ps{material.VertexShader.Hash}"); + VertexShader.Text = material.Decompile(material.VertexShader.GetBytecode(), $"vs{material.VertexShader.Hash}"); VS_CBufferList.ItemsSource = GetCBufferDetails(material, true); + + var vs_test = material.VertexShader?.Resources; + Console.WriteLine($"----Vertex----"); + foreach (var vsr in vs_test) + { + Console.WriteLine($"Type: {vsr.ResourceType} | Index:{vsr.Index} | Count: {vsr.Count}"); + } + + var vs_in = material.VertexShader.InputSignatures; + foreach (var b in vs_in) + { + Console.WriteLine($"v{b.RegisterIndex}: {b.ToString()}{b.SemanticIndex}.{b.Mask}"); + } + + var vs_out = material.VertexShader.OutputSignatures; + foreach (var b in vs_out) + { + Console.WriteLine($"o{b.RegisterIndex}: {b.ToString()}{b.SemanticIndex}.{b.Mask}"); + } } if (material.PixelShader is not null) { PixelShader.Text = material.Decompile(material.PixelShader.GetBytecode(), $"ps{material.PixelShader.Hash}"); - PS_CBufferList.ItemsSource = GetCBufferDetails(material); - } + PS_CBufferList.ItemsSource = GetCBufferDetails(material); + + var ps_test = material.PixelShader?.Resources; + Console.WriteLine($"----Pixel----"); + foreach (var a in ps_test) + { + Console.WriteLine($"Type: {a.ResourceType} | Index:{a.Index} | Count: {a.Count}"); + } + + var ps_in = material.PixelShader.InputSignatures; + foreach (var b in ps_in) + { + Console.WriteLine($"v{b.RegisterIndex}: {b.ToString()}{b.SemanticIndex}.{b.Mask}"); + } + + var ps_out = material.PixelShader.OutputSignatures; + foreach (var b in ps_out) + { + Console.WriteLine($"o{b.RegisterIndex}: {b.ToString()}{b.SemanticIndex}.{b.Mask}"); + } + } } public List GetTextureDetails(IMaterial material) @@ -159,7 +197,7 @@ public List GetCBufferDetails(IMaterial material, bool bVertexSha { data.Add(vec.Vec); } - } + } } else { @@ -202,13 +240,13 @@ await Task.Run(() => Dispatcher.Invoke(() => { TfxBytecodeInterpreter bytecode = new(TfxBytecodeOp.ParseAll(dc.Stage == CBufferDetail.Shader.Pixel ? Material.PS_TFX_Bytecode : Material.VS_TFX_Bytecode)); - var bytecode_hlsl = bytecode.Evaluate(dc.Stage == CBufferDetail.Shader.Pixel ? Material.PS_TFX_Bytecode_Constants : Material.VS_TFX_Bytecode_Constants); + var bytecode_hlsl = bytecode.Evaluate(dc.Stage == CBufferDetail.Shader.Pixel ? Material.PS_TFX_Bytecode_Constants : Material.VS_TFX_Bytecode_Constants, true); ConcurrentBag items = new ConcurrentBag(); for (int i = 0; i < dc.Data.Count; i++) { CBufferDataDetail dataEntry = new(); - + dataEntry.Index = i; if(bytecode_hlsl.ContainsKey(i)) dataEntry.Vector = $"Bytecode Assigned"; diff --git a/Charm/SBoxConfigView.xaml b/Charm/SBoxConfigView.xaml new file mode 100644 index 00000000..962ab0cc --- /dev/null +++ b/Charm/SBoxConfigView.xaml @@ -0,0 +1,20 @@ + + + + + diff --git a/Charm/SBoxConfigView.xaml.cs b/Charm/SBoxConfigView.xaml.cs new file mode 100644 index 00000000..dd028a57 --- /dev/null +++ b/Charm/SBoxConfigView.xaml.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using Tiger; + +namespace Charm; + +public partial class SBoxConfigView : UserControl +{ + public SBoxConfigView() + { + InitializeComponent(); + _config = CharmInstance.GetSubsystem(); + } + + public void OnControlLoaded(object sender, RoutedEventArgs e) + { + PopulateConfigPanel(); + } + + private ConfigSubsystem _config; + + private void PopulateConfigPanel() + { + SBoxConfigPanel.Children.Clear(); + + TextBlock header = new TextBlock(); + header.Text = "S&Box Settings"; + header.FontSize = 30; + SBoxConfigPanel.Children.Add(header); + + //TextBlock lbl = new TextBlock(); + //lbl.Text = "Currently, only S&Box is supported"; + //lbl.FontSize = 10; + //S2ConfigPanel.Children.Add(lbl); + + // S&Box tools path + ConfigSettingControl cpp = new ConfigSettingControl(); + cpp.SettingName = "S&Box tools path"; + var val = _config.GetSBoxToolsPath(); + cpp.SettingValue = val == "" ? "Not set" : val; + cpp.ChangeButton.Click += SBoxToolsPath_OnClick; + SBoxConfigPanel.Children.Add(cpp); + + // S&Box content folder path + ConfigSettingControl content = new ConfigSettingControl(); + content.SettingName = "Content Folder path"; + var content_path = _config.GetSBoxContentPath(); + content.SettingValue = content_path == "" ? "Not set" : content_path; + content.ChangeButton.Click += SBoxContentPath_OnClick; + SBoxConfigPanel.Children.Add(content); + } + + private void SBoxToolsPath_OnClick(object sender, RoutedEventArgs e) + { + OpenSBoxToolsPathDialog(); + PopulateConfigPanel(); + } + + public void OpenSBoxToolsPathDialog() + { + using (var dialog = new System.Windows.Forms.FolderBrowserDialog()) + { + // Steam\steamapps\common\sbox\bin\win64 + dialog.Description = "Select the folder where your S&Box tools are located (sbox\\bin\\win64)"; + bool success = false; + while (!success) + { + System.Windows.Forms.DialogResult result = dialog.ShowDialog(); + if (result == System.Windows.Forms.DialogResult.OK) + { + success = _config.TrySetSBoxToolsPath(dialog.SelectedPath); + } + else + { + return; + } + + if (!success) + { + MessageBox.Show( + "Directory selected is invalid, please select the correct directory. (Steam/steamapps/common/sbox/bin/win64)"); + } + } + } + } + + private void SBoxContentPath_OnClick(object sender, RoutedEventArgs e) + { + OpenSBoxContentPathDialog(); + PopulateConfigPanel(); + } + + public void OpenSBoxContentPathDialog() + { + using (var dialog = new System.Windows.Forms.FolderBrowserDialog()) + { + dialog.Description = "Select the folder where compilied content will be stored"; + bool success = false; + while (!success) + { + System.Windows.Forms.DialogResult result = dialog.ShowDialog(); + if (result == System.Windows.Forms.DialogResult.OK) + { + success = _config.TrySetSBoxContentPath(dialog.SelectedPath); + } + else + { + return; + } + + if (!success) + { + MessageBox.Show( + "Directory selected is invalid, please select a valid directory"); + } + else + { + Directory.CreateDirectory(dialog.SelectedPath); + if(!File.Exists($"{dialog.SelectedPath}/.sbproj")) + { + CreateSBProject(dialog.SelectedPath); + } + } + } + } + } + + private void CreateSBProject(string path) + { + string project_template = "{\r\n \"Title\": \"Destiny Resources\",\r\n \"Type\": \"content\",\r\n \"Tags\": null,\r\n \"Schema\": 1,\r\n \"HasAssets\": true,\r\n \"AssetsPath\": \"\",\r\n \"Resources\": null,\r\n \"MenuResources\": null,\r\n \"HasCode\": false,\r\n \"CodePath\": null,\r\n \"PackageReferences\": [],\r\n \"EditorReferences\": null,\r\n \"Metadata\": {}\r\n}"; + + File.WriteAllText($"{path}/.sbproj", project_template); + } +} diff --git a/Charm/Source2ConfigView.xaml b/Charm/Source2ConfigView.xaml deleted file mode 100644 index 7def22dd..00000000 --- a/Charm/Source2ConfigView.xaml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - diff --git a/Charm/Source2ConfigView.xaml.cs b/Charm/Source2ConfigView.xaml.cs deleted file mode 100644 index 78710953..00000000 --- a/Charm/Source2ConfigView.xaml.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Media; -using Tiger; - -namespace Charm; - -public partial class Source2ConfigView : UserControl -{ - public Source2ConfigView() - { - InitializeComponent(); - _config = CharmInstance.GetSubsystem(); - } - - public void OnControlLoaded(object sender, RoutedEventArgs e) - { - PopulateConfigPanel(); - } - - private ConfigSubsystem _config; - - private void PopulateConfigPanel() - { - S2ConfigPanel.Children.Clear(); - - - TextBlock header = new TextBlock(); - header.Text = "Source 2 Settings"; - header.FontSize = 30; - S2ConfigPanel.Children.Add(header); - - TextBlock lbl = new TextBlock(); - lbl.Text = "Currently, only S&Box is supported"; - lbl.FontSize = 10; - S2ConfigPanel.Children.Add(lbl); - - // Packages path - ConfigSettingControl cpp = new ConfigSettingControl(); - cpp.SettingName = "Source 2 tools path"; - var val = _config.GetSource2Path(); - cpp.SettingValue = val == "" ? "Not set" : val; - cpp.ChangeButton.Click += Source2Path_OnClick; - S2ConfigPanel.Children.Add(cpp); - - TextBlock lbl2 = new TextBlock(); - lbl2.Text = "Currently not used for anything"; - lbl2.FontSize = 15; - S2ConfigPanel.Children.Add(lbl2); - - // Enable source 2 shader generation - ConfigSettingControl cbe = new ConfigSettingControl(); - cbe.SettingName = "Generate shaders (vfx)"; - bool bval2 = _config.GetS2ShaderExportEnabled(); - cbe.SettingValue = bval2.ToString(); - cbe.ChangeButton.Click += S2ShaderExportEnabled_OnClick; - S2ConfigPanel.Children.Add(cbe); - - // Enable vmat material generation - // ### Might as well just make vmats by default - // ConfigSettingControl cef = new ConfigSettingControl(); - // cef.SettingName = "Generate materials (vmat)"; - // bool bval = ConfigSubsystem.GetS2VMATExportEnabled(); - // cef.SettingValue = bval.ToString(); - // cef.ChangeButton.Click += S2VMATExportEnabled_OnClick; - // S2ConfigPanel.Children.Add(cef); - - // Enable vmdl model generation - ConfigSettingControl cfe = new ConfigSettingControl(); - cfe.SettingName = "Generate models (vmdl)"; - bool bval = _config.GetS2VMDLExportEnabled(); - cfe.SettingValue = bval.ToString(); - cfe.ChangeButton.Click += S2VMDLExportEnabled_OnClick; - S2ConfigPanel.Children.Add(cfe); - } - - private void Source2Path_OnClick(object sender, RoutedEventArgs e) - { - OpenSource2PathDialog(); - PopulateConfigPanel(); - } - - public void OpenSource2PathDialog() - { - using (var dialog = new System.Windows.Forms.FolderBrowserDialog()) - { - // Steam\steamapps\common\sbox\bin\win64 - dialog.Description = "Select the folder where your S&Box installation is located"; - bool success = false; - while (!success) - { - System.Windows.Forms.DialogResult result = dialog.ShowDialog(); - if (result == System.Windows.Forms.DialogResult.OK) - { - success = _config.TrySetSource2Path(dialog.SelectedPath); - } - else - { - return; - } - - if (!success) - { - MessageBox.Show( - "Directory selected is invalid, please select the correct directory. (Steam/steamapps/common/sbox/bin/win64)"); - } - } - } - } - - private void S2ShaderExportEnabled_OnClick(object sender, RoutedEventArgs e) - { - _config.SetS2ShaderExportEnabled(!_config.GetS2ShaderExportEnabled()); - PopulateConfigPanel(); - } - - private void S2VMATExportEnabled_OnClick(object sender, RoutedEventArgs e) - { - _config.SetS2VMATExportEnabled(!_config.GetS2VMATExportEnabled()); - PopulateConfigPanel(); - } - - private void S2VMDLExportEnabled_OnClick(object sender, RoutedEventArgs e) - { - _config.SetS2VMDLExportEnabled(!_config.GetS2VMDLExportEnabled()); - PopulateConfigPanel(); - } -} diff --git a/Charm/StaticView.xaml b/Charm/StaticView.xaml index ed00c350..8e0f04c8 100644 --- a/Charm/StaticView.xaml +++ b/Charm/StaticView.xaml @@ -1,21 +1,21 @@ - + - + - - + diff --git a/Charm/StaticView.xaml.cs b/Charm/StaticView.xaml.cs index b5832209..c2a00efa 100644 --- a/Charm/StaticView.xaml.cs +++ b/Charm/StaticView.xaml.cs @@ -23,37 +23,25 @@ public StaticView() public void LoadStatic(FileHash hash, ExportDetailLevel detailLevel, Window window) { - if (Strategy.CurrentStrategy == TigerStrategy.DESTINY2_LATEST && ConfigSubsystem.Get().GetUseCustomRenderer() == true) - { - AtlasView.Visibility = Visibility.Visible; - ModelView.Visibility = Visibility.Collapsed; - AtlasView.LoadStatic(hash, window); - } - else - { - AtlasView.Visibility = Visibility.Collapsed; - ModelView.Visibility = Visibility.Visible; - StaticMesh staticMesh = FileResourcer.Get().GetFile(hash); - var parts = staticMesh.Load(detailLevel); - // await Task.Run(() => - // { - // parts = staticMesh.Load(detailLevel); - // }); - MainViewModel MVM = (MainViewModel)ModelView.UCModelView.Resources["MVM"]; - MVM.Clear(); - var displayParts = MakeDisplayParts(parts); - MVM.SetChildren(displayParts); - MVM.Title = hash; - MVM.SubTitle = $"{displayParts.Sum(p => p.BasePart.Indices.Count)} triangles"; - } + ModelView.Visibility = Visibility.Visible; + StaticMesh staticMesh = FileResourcer.Get().GetFile(hash); + var parts = staticMesh.Load(detailLevel); + // await Task.Run(() => + // { + // parts = staticMesh.Load(detailLevel); + // }); + MainViewModel MVM = (MainViewModel)ModelView.UCModelView.Resources["MVM"]; + MVM.Clear(); + var displayParts = MakeDisplayParts(parts); + MVM.SetChildren(displayParts); + MVM.Title = hash; + MVM.SubTitle = $"{displayParts.Sum(p => p.BasePart.Indices.Count)} triangles"; } public static void ExportStatic(FileHash hash, string name, ExportTypeFlag exportType, string extraPath = "") { ExporterScene scene = Exporter.Get().CreateScene(name, ExportType.Static); - bool lodexport = false; ConfigSubsystem config = ConfigSubsystem.Get(); - bool source2Models = config.GetS2VMDLExportEnabled(); string savePath = config.GetExportSavePath() + "/" + extraPath + "/"; string meshName = hash; @@ -67,26 +55,15 @@ public static void ExportStatic(FileHash hash, string name, ExportTypeFlag expor scene.AddStatic(hash, parts); Directory.CreateDirectory(savePath); if (exportType == ExportTypeFlag.Full) - { staticMesh.SaveMaterialsFromParts(scene, parts); - if (config.GetUnrealInteropEnabled()) - { - AutomatedExporter.SaveInteropUnrealPythonFile(savePath, meshName, AutomatedExporter.ImportType.Static, config.GetOutputTextureFormat()); - AutomatedExporter.SaveInteropBlenderPythonFile(savePath, meshName, AutomatedExporter.ImportType.Static, config.GetOutputTextureFormat()); - } - - if (source2Models) - { - Source2Handler.SaveStaticVMDL($"{savePath}", meshName, parts); - } - } + bool lodexport = true; if (lodexport) { ExporterScene lodScene = Exporter.Get().CreateScene($"{name}_LOD", ExportType.Static); List lodparts = staticMesh.Load(ExportDetailLevel.LeastDetailed); - Directory.CreateDirectory(savePath + "/LOD"); + staticMesh.SaveMaterialsFromParts(lodScene, lodparts); foreach (StaticPart lodpart in lodparts) { diff --git a/Charm/TagListView.xaml.cs b/Charm/TagListView.xaml.cs index 934aa904..054225cb 100644 --- a/Charm/TagListView.xaml.cs +++ b/Charm/TagListView.xaml.cs @@ -106,9 +106,9 @@ public enum ETagListType BKHDGroupList, [Description("BKHD Group [Final]")] BKHDGroup, - [Description("Weapon Audio List")] + [Description("BKHD Audio List")] BKHDAudioList, - [Description("Weapon Audio [Final]")] + [Description("BKHD Audio [Final]")] BKHDAudio, [Description("Material List [Packages]")] MaterialList, diff --git a/Charm/ThirdParty/Alkahest/destiny_havok.dll b/Charm/ThirdParty/Alkahest/destiny_havok.dll new file mode 100644 index 00000000..e9859608 Binary files /dev/null and b/Charm/ThirdParty/Alkahest/destiny_havok.dll differ diff --git a/Charm/charm.ico b/Charm/charm.ico index a2f86e46..eb52d349 100644 Binary files a/Charm/charm.ico and b/Charm/charm.ico differ diff --git a/README.md b/README.md index 200b2572..9f462cd7 100644 --- a/README.md +++ b/README.md @@ -1,77 +1,10 @@ -# Charm - the Destiny tool that does everything +## !! OUTDATED !! +# S&Charm (CharmBox?) ## What is this? +A fork of [Charm](https://github.com/MontagueM/Charm) solely focused on exporting to S&Box -A new, flashier, fully C# version of my previous tool Phonon. - -It is designed to support many versions of the Tiger engine, including many game versions of Destiny 2. - -The tool focuses on providing as much access to the information in the game files as possible, ideal for artists and content preservation. - -## How do I install and use it? - -You'll first need at least one game installation. -Charm currently supports: - -| Version | Description | Where | Main manifest id | Language manifest id | -|---------|--------------------------|-----------------|---------------------|----------------------| -| 2.6.0.1 | Shadowkeep first update | DepotDownloader | 7002268313830901797 | 2399965969279284756 | -| 2.9.9.9 | Shadowkeep last update | DepotDownloader | 4160053308690659072 | 4651412338057797072 | -| 3.4.0.2 | Beyond Light last update | DepotDownloader | 5631185797932644936 | 3832609057880895101 | -| 6.3.0.7 | Witch Queen last update | DepotDownloader | 6051526863119423207 | 1078048403901153652 | -| N/A | Lightfall latest | Steam | N/A | N/A | - -If you just want to look at the latest release, you only need Destiny 2 downloaded on Steam. - -Otherwise, you can download the DepotDownloader versions by -- Downloading [DepotDownloader](https://github.com/SteamRE/DepotDownloader/releases) -- Running it with the following arguments: -``` -dotnet DepotDownloader.dll -app 1085660 -depot 1085661 -manifest {main_manifest_id} -username -password -dir -validate -dotnet DepotDownloader.dll -app 1085660 -depot 1085662 -manifest {language_manifest_id} -username -password -dir -validate - -e.g. -dotnet DepotDownloader.dll -app 1085660 -depot 1085661 -manifest 4160053308690659072 -username myusername -password mypassword -dir "D:/DestinyCharmStore/v2601/" -validate -dotnet DepotDownloader.dll -app 1085660 -depot 1085662 -manifest 4651412338057797072 -username myusername -password mypassword -dir "D:/DestinyCharmStore/v2601/" -validate -``` - -After you've downloaded the version(s) you want: - -- You'll need [.NET 7.0 x64](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/sdk-7.0.400-windows-x64-installer) installed. -- Download the [latest release]() and run Charm.exe. -- Set the packages and export paths first. - -If you encounter any problems, take a look in the `Logs/` folder, find the latest log file, and look at the exception. -Feel free to raise an issue on this repository on in DMR `#charm-tool-help` if you need help. - -Also take a look at the [Charm wiki](https://github.com/MontagueM/DestinyDocs/blob/main/Charm/Home.md) for more info. - -## Source 2: **Only supports S&Box** +## WIP - [Import guide](https://github.com/DeltaDesigns/Charm/wiki/Source-2-Importing) - - Generates .shader files for semi-accurate game shaders (similar to UE5 shaders) - - Generates .vmat (material) and .vmdl (model) files for statics and maps - -### Some tricks - -* Middle click tabs to close them. -* In a packages view, you can type in any hash and it will take you to it. No need to look through all the packages. - -## Reporting issues - -If you experience any issue, you can register an issue in this repository. If the program has crashed, it is extremely valuable to provide the charm.log file. - -## Learning and Contributing - -To learn about how Charm works or to contribute, check out the [wiki](https://github.com/MontagueM/Charm/wiki). - -## Sponsor - -I put a sponsor on this project as some people wanted to contribute, if you want to you can help me out :) - -## License - -The Charm source code is licensed under GPLv3. All other used code and DLLs are subject to their own licenses. - -## Credits - -Thanks to Alcidine, BIOS, Carson Reed, Delta, and nblock for testing, feedback, and help throughout the project's development. HighRTT for audio help (RevorbStd and librevorb). + - Generates .shader files from decompiled game shaders + - Generates .vmat (material) and .vmdl (model) files for exported objects diff --git a/Tiger/ConfigSubsystem.cs b/Tiger/ConfigSubsystem.cs index a6797e57..dfda57a4 100644 --- a/Tiger/ConfigSubsystem.cs +++ b/Tiger/ConfigSubsystem.cs @@ -26,7 +26,7 @@ public struct Settings public CommonSettings Common; public UnrealSettings Unreal; public BlenderSettings Blender; - public Source2Settings Source2; + public SBoxSettings SBox; } public class CommonSettings @@ -54,59 +54,14 @@ public class BlenderSettings } // [ConfigSubsystem] -public class Source2Settings +public class SBoxSettings { - public bool Source2VShaderExportsEnabled { get; set; } = false; - public bool Source2VMATExportsEnabled { get; set; } = false; - public bool Source2VMDLExportsEnabled { get; set; } = false; - public string Source2Path { get; set; } = ""; + public string SBoxToolsPath { get; set; } = ""; + public string SBoxContentPath { get; set; } = ""; } -// class TypeExtensions -// { -// public bool HasAttributeOfType(this Type type) where T : Attribute -// { -// return type.GetCustomAttributes(true).Any(); -// } -// } - public class ConfigSubsystem : Subsystem { - // private Configuration _config = - // ConfigurationManager.OpenExeConfiguration(System.Windows.Forms.Application.ExecutablePath); - - // private Dictionary _settings = new(); - - - // protected override bool Initialise() - // { - // // FillSettingsCache(); - // return true; - // } - - // private void FillSettingsCache() - // { - // HashSet allSettings = AppDomain.CurrentDomain.GetAssemblies() - // .SelectMany(a => a.GetTypes()) - // .Where(t => t.HasAttributeOfType()) - // .ToHashSet(); - // - // foreach (Type settingType in allSettings) - // { - // dynamic? settings = Activator.CreateInstance(settingType); - // _settings.Common.Common.Add(settingType, settings); - // } - // } - - // public T? GetSettings() where T : struct - // { - // if (_settings.Common.Common.TryGetValue(typeof(T), out dynamic? settings)) - // { - // return (T) settings; - // } - // - // return null; - // } #region packagesPath @@ -175,72 +130,45 @@ private TigerStrategy FindEnumValueStrategy(string description) #endregion - #region source2Path + #region S&Box - public string GetSource2Path() + public string GetSBoxToolsPath() { - return _settings.Source2.Source2Path; + return _settings.SBox.SBoxToolsPath; } - public bool TrySetSource2Path(string path) + public bool TrySetSBoxToolsPath(string path) { if (path == "") - { return false; - } if (!path.EndsWith("win64")) - { return false; - } - _settings.Source2.Source2Path = path; + _settings.SBox.SBoxToolsPath = path; Save(); return true; } - #endregion - - #region source2ExportsEnabled - - public void SetS2ShaderExportEnabled(bool bS2ShaderExportEnabled) - { - _settings.Source2.Source2VShaderExportsEnabled = bS2ShaderExportEnabled; - Save(); - } - - public bool GetS2ShaderExportEnabled() + public string GetSBoxContentPath() { - return _settings.Source2.Source2VShaderExportsEnabled; + return _settings.SBox.SBoxContentPath; } - // - public void SetS2VMATExportEnabled(bool bS2VMATExportEnabled) + public bool TrySetSBoxContentPath(string path) { - _settings.Source2.Source2VMATExportsEnabled = bS2VMATExportEnabled; - Save(); - } + if (path == "") + return false; - public bool GetS2VMATExportEnabled() - { - return _settings.Source2.Source2VMATExportsEnabled; - } + _settings.SBox.SBoxContentPath = path; - public void SetS2VMDLExportEnabled(bool bS2VMDLExportEnabled) - { - _settings.Source2.Source2VMDLExportsEnabled = bS2VMDLExportEnabled; Save(); - } - - public bool GetS2VMDLExportEnabled() - { - return _settings.Source2.Source2VMDLExportsEnabled; + return true; } #endregion - #region exportSavePath public string GetExportSavePath() @@ -387,7 +315,6 @@ public bool GetUseCustomRenderer() } private string _configFilePath = "./config.json"; - // private Dictionary _settings; private Settings _settings; public ConfigSubsystem() @@ -425,22 +352,12 @@ private bool LoadConfig() Log.Error($"Failed to load config file {_configFilePath}: {e.Message}"); } - // bool configIsMissingField = false; - // foreach (var field in GetConfigFields()) - // { - // configIsMissingField |= SetFieldValueFromConfig(field.Key, field, deserializedSettings); - // } - // - // if (configIsMissingField) - // { - // return WriteConfig(); - // } if (_settings.Common == null) { _settings.Common = new CommonSettings(); _settings.Blender = new BlenderSettings(); + _settings.SBox = new SBoxSettings(); _settings.Unreal = new UnrealSettings(); - _settings.Source2 = new Source2Settings(); WriteConfig(); } @@ -484,8 +401,6 @@ private bool ConfigFileExists() private void Save() { WriteConfig(); - // _config.Save(ConfigurationSaveMode.Modified); - // ConfigurationManager.RefreshSection("appSettings"); } protected internal override bool Initialise() diff --git a/Tiger/Exporters/AutomatedExporter.cs b/Tiger/Exporters/AutomatedExporter.cs index 2ab9492e..30b2b75d 100644 --- a/Tiger/Exporters/AutomatedExporter.cs +++ b/Tiger/Exporters/AutomatedExporter.cs @@ -15,7 +15,7 @@ public enum ImportType API } - public static void SaveInteropUnrealPythonFile(string saveDirectory, string meshName, ImportType importType, TextureExportFormat textureFormat, bool bSingleFolder = true) + public static void SaveInteropUnrealPythonFile(string saveDirectory, string meshName, ImportType importType, TextureExportFormat textureFormat, bool bSingleFolder = true) { // Copy and rename file File.Copy("Exporters/import_to_ue5.py", $"{saveDirectory}/{meshName}_import_to_ue5.py", true); @@ -46,39 +46,6 @@ public static void SaveInteropUnrealPythonFile(string saveDirectory, string mesh File.WriteAllText($"{saveDirectory}/{meshName}_import_to_ue5.py", textExtensions); } - public static void SaveInteropBlenderPythonFile(string saveDirectory, string meshName, ImportType importType, TextureExportFormat textureFormat) - { - //Not gonna delete just in case - - //// Copy and rename file - //saveDirectory = saveDirectory.Replace("\\", "/"); - //File.Copy("Exporters/import_to_blender.py", $"{saveDirectory}/{meshName}_import_to_blender.py", true); - - ////Lets just make a py for all exports now because why not - //string text = File.ReadAllText($"{saveDirectory}/{meshName}_import_to_blender.py"); - //text = text.Replace("HASH", $"{meshName}"); - //text = text.Replace("OUTPUT_DIR", $"{saveDirectory}"); - //text = text.Replace("IMPORT_TYPE", $"{importType.ToString().Replace("ImportType.", "")}"); - //File.WriteAllText($"{saveDirectory}/{meshName}_import_to_blender.py", text); - - //// change extension - //string textExtensions = File.ReadAllText($"{saveDirectory}/{meshName}_import_to_blender.py"); - //switch (textureFormat) - //{ - // case ETextureFormat.PNG: - // textExtensions = textExtensions.Replace("TEX_EXT", ".png"); - // break; - // case ETextureFormat.TGA: - // textExtensions = textExtensions.Replace("TEX_EXT", ".tga"); - // break; - // default: - // textExtensions = textExtensions.Replace("TEX_EXT", ".dds"); - // break; - //} - //File.WriteAllText($"{saveDirectory}/{meshName}_import_to_blender.py", textExtensions); - } - - public static void SaveBlenderApiFile(string saveDirectory, string meshName, TextureExportFormat outputTextureFormat, List dyes, string fileSuffix = "") { File.Copy($"Exporters/blender_api_template.py", $"{saveDirectory}/{meshName}{fileSuffix}.py", true); diff --git a/Tiger/Exporters/Exporter.cs b/Tiger/Exporters/Exporter.cs index 35e6acaa..3d33c49f 100644 --- a/Tiger/Exporters/Exporter.cs +++ b/Tiger/Exporters/Exporter.cs @@ -60,12 +60,14 @@ public void Export() public struct ExportMaterial { public readonly IMaterial Material; + public readonly MaterialType Type; public readonly bool IsTerrain; - public ExportMaterial(IMaterial material, bool isTerrain = false) + public ExportMaterial(IMaterial material, MaterialType type = MaterialType.Opaque, bool isTerrain = false) { Material = material; IsTerrain = isTerrain; + Type = type; } public override int GetHashCode() @@ -84,13 +86,14 @@ public class ExporterScene public string Name { get; set; } public ExportType Type { get; set; } public ConcurrentBag StaticMeshes = new(); + public ConcurrentBag TerrainMeshes = new(); public ConcurrentBag Entities = new(); public ConcurrentDictionary> StaticMeshInstances = new(); public ConcurrentDictionary> ArrangedStaticMeshInstances = new(); public ConcurrentDictionary> EntityInstances = new(); public ConcurrentBag ExternalMaterialTextures = new(); public ConcurrentBag EntityPoints = new(); - public ConcurrentBag Cubemaps = new(); + public ConcurrentDictionary Cubemaps = new(); public ConcurrentBag MapLights = new(); public ConcurrentDictionary> MapSpotLights = new(); public ConcurrentBag Decals = new(); @@ -110,6 +113,17 @@ public void AddStatic(FileHash meshHash, List parts) StaticMeshes.Add(mesh); } + public void AddTerrain(string meshHash, List parts) + { + ExporterMesh mesh = new(meshHash); + for (int i = 0; i < parts.Count; i++) + { + StaticPart part = parts[i]; + mesh.AddPart(meshHash, part, i); + } + TerrainMeshes.Add(mesh); + } + public void AddStaticInstancesAndParts(FileHash meshHash, List parts, IEnumerable instances) { ExporterMesh mesh = new(meshHash); @@ -226,7 +240,7 @@ public void AddMapEntity(SMapDataEntry dynamicResource, Entity entity, Transform { if (!_addedEntities.Contains(entity.Hash)) //Dont want duplicate entities being added { - ExporterMesh mesh = new(dynamicResource.GetEntityHash()); + ExporterMesh mesh = new(entity.Hash); _addedEntities.Add(entity.Hash); var parts = entity.Model.Load(ExportDetailLevel.MostDetailed, entity.ModelParentResource); @@ -235,36 +249,32 @@ public void AddMapEntity(SMapDataEntry dynamicResource, Entity entity, Transform DynamicMeshPart part = parts[i]; if (part.Material == null) continue; - if (part.Material.EnumeratePSTextures().Any()) //Dont know if this will 100% "fix" the duplicate meshs that come with entities - { - mesh.AddPart(dynamicResource.GetEntityHash(), part, i); - } + + Materials.Add(new ExportMaterial(part.Material, MaterialType.Opaque)); + mesh.AddPart(entity.Hash, part, i); } Entities.Add(new ExporterEntity { Mesh = mesh, BoneNodes = entity.Skeleton?.GetBoneNodes() }); } - if (!EntityInstances.ContainsKey(dynamicResource.GetEntityHash())) + if (!EntityInstances.ContainsKey(entity.Hash)) { - EntityInstances.TryAdd(dynamicResource.GetEntityHash(), new()); + EntityInstances.TryAdd(entity.Hash, new()); } - if (transform is null) + EntityInstances[entity.Hash].Add(new Transform { - transform = new Transform - { - Position = dynamicResource.Translation.ToVec3(), - Rotation = Vector4.QuaternionToEulerAngles(dynamicResource.Rotation), - Quaternion = dynamicResource.Rotation, - Scale = new Vector3(dynamicResource.Translation.W, dynamicResource.Translation.W, dynamicResource.Translation.W) - }; - } + Position = dynamicResource.Translation.ToVec3(), + Rotation = Vector4.QuaternionToEulerAngles(dynamicResource.Rotation), + Quaternion = dynamicResource.Rotation, + Scale = new Vector3(dynamicResource.Translation.W, dynamicResource.Translation.W, + dynamicResource.Translation.W) + }); EntityInstances[dynamicResource.GetEntityHash()].Add((Transform)transform); } public void AddMapModel(EntityModel model, Vector4 translation, Vector4 rotation, Vector3 scale, bool transparentsOnly = false) { ExporterMesh mesh = new(model.Hash); - if (!_addedEntities.Contains(model.Hash)) //Dont want duplicate entities being added { _addedEntities.Add(model.Hash); @@ -273,10 +283,8 @@ public void AddMapModel(EntityModel model, Vector4 translation, Vector4 rotation { DynamicMeshPart part = parts[i]; - if (part.Material != null && !part.Material.EnumeratePSTextures().Any()) //Dont know if this will 100% "fix" the duplicate meshs that come with entities - { + if (part.Material == null) continue; - } mesh.AddPart(model.Hash, part, i); } @@ -309,16 +317,22 @@ public void AddModel(EntityModel model) Entities.Add(new ExporterEntity { Mesh = mesh, BoneNodes = null }); } - public void AddCubemap(CubemapResource cubemap) + public void AddCubemap(SMapDataEntry entry, CubemapResource cubemap) { - Cubemaps.Add(cubemap); + Cubemaps.TryAdd(cubemap, new Transform + { + Position = entry.Translation.ToVec3(), + Rotation = Vector4.QuaternionToEulerAngles(entry.Rotation), + Quaternion = entry.Rotation, + Scale = Vector3.One + }); } public void AddMapLight(SMapLightResource mapLight) //Point { MapLights.Add(mapLight); } - public void AddMapSpotLight(SMapDataEntry spotLightEntry, SMapSpotLightResource spotLightResource) //Spot + public void AddMapSpotLight(SMapDataEntry spotLightEntry, SMapShadowingLightResource spotLightResource) //Spot { if (!MapSpotLights.ContainsKey(spotLightResource.Unk10.Hash)) { @@ -357,10 +371,10 @@ public class ExporterEntity public class ExporterMesh { - public FileHash Hash { get; set; } + public string Hash { get; set; } public List Parts { get; } = new(); - public ExporterMesh(FileHash hash) + public ExporterMesh(string hash) { Hash = hash; } diff --git a/Tiger/Exporters/FbxExporter.cs b/Tiger/Exporters/FbxExporter.cs index 9f798309..4b2f214b 100644 --- a/Tiger/Exporters/FbxExporter.cs +++ b/Tiger/Exporters/FbxExporter.cs @@ -16,50 +16,76 @@ public override void Export(Exporter.ExportEventArgs args) { foreach (ExporterScene scene in args.Scenes) { - FbxScene fbxScene = FbxScene.Create(_manager, scene.Name); + string outputDirectory = args.OutputDirectory; + switch (scene.Type) + { + case ExportType.Static: + case ExportType.Entity: + case ExportType.API: + outputDirectory = Path.Join(outputDirectory, scene.Name); + break; + case ExportType.StaticInMap: + outputDirectory = Path.Join(outputDirectory, "Maps", "Models", "Statics"); + break; + case ExportType.EntityInMap: + outputDirectory = Path.Join(outputDirectory, "Maps", "Models", "Entities"); + break; + case ExportType.Terrain: + outputDirectory = Path.Join(outputDirectory, "Maps", "Models", "Terrain"); + break; + default: + outputDirectory = Path.Join(outputDirectory, "Maps"); + break; + } foreach (ExporterMesh mesh in scene.StaticMeshes) { + FbxScene fbxScene = FbxScene.Create(_manager, mesh.Hash); AddMesh(fbxScene, mesh); + ExportScene(fbxScene, Path.Join(outputDirectory, mesh.Hash)); + SBoxHandler.SaveStaticVMDL(outputDirectory, mesh); + } - foreach (var meshInstance in scene.ArrangedStaticMeshInstances) + foreach (ExporterMesh mesh in scene.TerrainMeshes) { - if (scene.StaticMeshes.Count(s => s.Hash == meshInstance.Key) != 1) - { - var a = 0; - } - // Debug.Assert(scene.StaticMeshes.Count(s => s.Hash == meshInstance.Key) == 1); - AddInstancedMesh(fbxScene, scene.StaticMeshes.First(s => s.Hash == meshInstance.Key).Parts, meshInstance.Value); + FbxScene fbxScene = FbxScene.Create(_manager, mesh.Hash); + AddMesh(fbxScene, mesh); + ExportScene(fbxScene, Path.Join(outputDirectory, mesh.Hash)); } foreach (ExporterEntity entity in scene.Entities) { - AddEntity(fbxScene, entity); - } - - foreach (var p in scene.EntityPoints) - { - AddDynamicPoint(fbxScene, p); + if (scene.Type != ExportType.API) + { + FbxScene fbxScene = FbxScene.Create(_manager, entity.Mesh.Hash); + AddEntity(fbxScene, entity); + ExportScene(fbxScene, Path.Join(outputDirectory, entity.Mesh.Hash)); + SBoxHandler.SaveEntityVMDL(outputDirectory, entity); + } } - string outputDirectory = args.OutputDirectory; - if (scene.Type is ExportType.Static or ExportType.Entity or ExportType.API or ExportType.D1API) - { - outputDirectory = Path.Join(outputDirectory, scene.Name); - } - else if (scene.Type is ExportType.Map or ExportType.Terrain or ExportType.EntityPoints) - { - outputDirectory = Path.Join(outputDirectory, "Maps"); - } - else if (scene.Type is ExportType.StaticInMap) - { - outputDirectory = Path.Join(outputDirectory, "Maps", "Statics"); - } - else if (scene.Type is ExportType.EntityInMap) + if (scene.Type == ExportType.API) { - outputDirectory = Path.Join(outputDirectory, "Maps", "Entities"); + FbxScene fbxScene = FbxScene.Create(_manager, scene.Name); + foreach (ExporterEntity entity in scene.Entities) + { + AddEntity(fbxScene, entity); + } + ExportScene(fbxScene, Path.Join(outputDirectory, scene.Name)); + //SBoxHandler.SaveEntityVMDL(outputDirectory, entity); } - ExportScene(fbxScene, Path.Join(outputDirectory, scene.Name)); + //foreach (var meshInstance in scene.ArrangedStaticMeshInstances) + //{ + // FbxScene fbxScene = FbxScene.Create(_manager, scene.Name); + // AddInstancedMesh(fbxScene, scene.StaticMeshes.First(s => s.Hash == meshInstance.Key).Parts, meshInstance.Value); + // ExportScene(fbxScene, Path.Join(outputDirectory, scene.Name)); + //} + //foreach (var p in scene.EntityPoints) + //{ + // FbxScene fbxScene = FbxScene.Create(_manager, scene.Name); + // AddDynamicPoint(fbxScene, p); + // ExportScene(fbxScene, Path.Join(outputDirectory, scene.Name)); + //} } } @@ -98,7 +124,7 @@ private void ExportScene(FbxScene fbxScene, string outputPath) _manager.GetIOSettings().SetBoolProp(FbxWrapperNative.EXP_FBX_ANIMATION, true); _manager.GetIOSettings().SetBoolProp(FbxWrapperNative.EXP_FBX_GLOBAL_SETTINGS, true); var exporter = Internal.Fbx.FbxExporter.Create(_manager, ""); - exporter.Initialize(outputPath + ".fbx", -1); // -1 == detect via extension ie binary not ascii, binary is more space efficient + exporter.Initialize(outputPath + ".fbx", -1); // -1 == detect via extension ie binary not ascii, binary is more space efficient if (fbxScene.GetRootNode().GetChildCount() > 0) // Only export if theres actually something to export exporter.Export(fbxScene); exporter.Destroy(); diff --git a/Tiger/Exporters/MaterialExporter.cs b/Tiger/Exporters/MaterialExporter.cs index de43c3ef..b3417367 100644 --- a/Tiger/Exporters/MaterialExporter.cs +++ b/Tiger/Exporters/MaterialExporter.cs @@ -9,7 +9,6 @@ public override void Export(Exporter.ExportEventArgs args) { ConcurrentHashSet mapTextures = new(); ConcurrentHashSet mapMaterials = new(); - bool saveShaders = ConfigSubsystem.Get().GetUnrealInteropEnabled() || ConfigSubsystem.Get().GetS2ShaderExportEnabled(); Parallel.ForEach(args.Scenes, scene => { @@ -36,12 +35,9 @@ public override void Export(Exporter.ExportEventArgs args) textures.Add(texture.Texture); } - if (saveShaders) - { - string shaderSaveDirectory = $"{args.OutputDirectory}/{scene.Name}/Shaders"; - material.Material.SavePixelShader(shaderSaveDirectory, material.IsTerrain); - material.Material.SaveVertexShader(shaderSaveDirectory); - } + string shaderSaveDirectory = $"{args.OutputDirectory}/{scene.Name}"; + material.Material.SaveShaders(shaderSaveDirectory, material.Type, material.IsTerrain); + material.Material.SaveVertexShader(shaderSaveDirectory); } string textureSaveDirectory = $"{args.OutputDirectory}/{scene.Name}/Textures"; @@ -80,17 +76,17 @@ public override void Export(Exporter.ExportEventArgs args) if (texture is null) continue; texture.SavetoFile($"{textureSaveDirectory}/{texture.Hash}"); + if (texture.IsCubemap()) + { + SBoxHandler.SaveVTEX(texture, textureSaveDirectory); + } } - if (saveShaders) + string shaderSaveDirectory = $"{args.OutputDirectory}/Maps"; + Directory.CreateDirectory(shaderSaveDirectory); + foreach (ExportMaterial material in mapMaterials) { - string shaderSaveDirectory = $"{args.OutputDirectory}/Maps/Shaders"; - Directory.CreateDirectory(shaderSaveDirectory); - foreach (ExportMaterial material in mapMaterials) - { - material.Material.SavePixelShader(shaderSaveDirectory, material.IsTerrain); - material.Material.SaveVertexShader(shaderSaveDirectory); - } + material.Material.SaveShaders(shaderSaveDirectory, material.Type, material.IsTerrain); } } } diff --git a/Tiger/Exporters/MetadataExporter.cs b/Tiger/Exporters/MetadataExporter.cs index e9ca4a89..f66b0d37 100644 --- a/Tiger/Exporters/MetadataExporter.cs +++ b/Tiger/Exporters/MetadataExporter.cs @@ -41,11 +41,6 @@ public MetadataScene(ExporterScene scene) ConcurrentDictionary> terrainDyemaps = new ConcurrentDictionary>(); _config.TryAdd("TerrainDyemaps", terrainDyemaps); - if (ConfigSubsystem.Get().GetUnrealInteropEnabled()) - { - SetUnrealInteropPath(ConfigSubsystem.Get().GetUnrealInteropPath()); - } - SetType(scene.Type.ToString()); _exportType = scene.Type; SetMeshName(scene.Name); @@ -55,9 +50,19 @@ public MetadataScene(ExporterScene scene) foreach (var part in mesh.Parts) { if (part.Material != null) - { AddMaterial(part.Material); - } + + AddPart(part, part.Name); + } + } + + foreach (var mesh in scene.TerrainMeshes) + { + foreach (var part in mesh.Parts) + { + if (part.Material != null) + AddMaterial(part.Material); + AddPart(part, part.Name); } } @@ -88,13 +93,13 @@ public MetadataScene(ExporterScene scene) AddTextureToMaterial(texture.Material, texture.Index, texture.Texture); } - foreach (CubemapResource cubemap in scene.Cubemaps) + foreach (var cubemap in scene.Cubemaps) { - AddCubemap(cubemap.CubemapName, - cubemap.CubemapSize.ToVec3(), - cubemap.CubemapRotation, - cubemap.CubemapPosition.ToVec3(), - cubemap.CubemapTexture != null ? cubemap.CubemapTexture.Hash : ""); + AddCubemap(cubemap.Key.CubemapName, + cubemap.Key.CubemapSize.ToVec3(), + cubemap.Value.Quaternion, + cubemap.Value.Position, + cubemap.Key.CubemapTexture != null ? cubemap.Key.CubemapTexture.Hash : ""); } foreach (var mapLight in scene.MapLights) { @@ -114,8 +119,8 @@ public MetadataScene(ExporterScene scene) "Point", mapLight.Lights.TagData.Transforms[i].Translation, mapLight.Lights.TagData.Transforms[i].Rotation, - new Vector2(1, 1), - color, + new Vector2(1,1), //new Vector2(mapLight.Unk10.TagData.Unk30[i].UnkA0.W, mapLight.Unk10.TagData.Unk30[i].UnkB0.W), //Not right + (data.TagData.Buffer1.Count > 0 ? data.TagData.Buffer1[0].Vec : data.TagData.Buffer2[0].Vec), mapLight.Lights.TagData.Bounds.TagData.InstanceBounds[i].Corner2.X - mapLight.Lights.TagData.Bounds.TagData.InstanceBounds[i].Corner1.X); } } @@ -146,13 +151,15 @@ public MetadataScene(ExporterScene scene) { foreach (var entry in mapLight.Value) { + var data = FileResourcer.Get().GetSchemaTag(mapLight.Key); AddLight( mapLight.Key, "Spot", new Vector4(entry.Position.X, entry.Position.Y, entry.Position.Z, 1), entry.Quaternion, - new Vector2(1.0, 1.0), - new Vector4(1.0, 1.0, 1.0, 1.0)); + new Vector2(1, 1), + data.TagData.UnkE8.TagData.Buffer1.Count > 0 ? data.TagData.UnkE8.TagData.Buffer1[0].Vec : data.TagData.UnkE8.TagData.Buffer2[0].Vec); + } } @@ -264,9 +271,9 @@ public void AddCubemap(string name, Vector3 scale, Vector4 quatRotation, Vector3 public void AddLight(string name, string type, Vector4 translation, Vector4 quatRotation, Vector2 size, Vector4 color, float range = 13) { //Idk how color/intensity is handled, so if its above 1 just bring it down - //float R = color.X > 1 ? color.X / 100 : color.X; - //float G = color.Y > 1 ? color.Y / 100 : color.Y; - //float B = color.Z > 1 ? color.Z / 100 : color.Z; + float R = color.X > 1 ? color.X / 100 : color.X; + float G = color.Y > 1 ? color.Y / 100 : color.Y; + float B = color.Z > 1 ? color.Z / 100 : color.Z; if (!_config["Lights"].ContainsKey(name)) { @@ -278,7 +285,7 @@ public void AddLight(string name, string type, Vector4 translation, Vector4 quat Translation = new[] { translation.X, translation.Y, translation.Z }, Rotation = new[] { quatRotation.X, quatRotation.Y, quatRotation.Z, quatRotation.W }, Size = new[] { size.X, size.Y }, - Color = new[] { color.X, color.Y, color.Z }, + Color = new[] { R, G, B }, Range = range }); } @@ -323,29 +330,29 @@ public void WriteToFile(string path) { path = Path.Join(path, _config["MeshName"]); } - else if (_exportType is ExportType.Map or ExportType.Terrain or ExportType.EntityPoints) + else //if (_exportType is ExportType.Map or ExportType.Terrain or ExportType.EntityPoints or ExportType.MapResource) { path = Path.Join(path, "Maps"); } - else if (_exportType is ExportType.StaticInMap or ExportType.EntityInMap) - { - return; - } - - // If theres only 1 part, we need to rename it + the instance to the name of the mesh (unreal imports to fbx name if only 1 mesh inside) - if (_config["Parts"].Count == 1) - { - var part = _config["Parts"][_config["Parts"].Keys[0]]; - //I'm not sure what to do if it's 0, so I guess I'll leave that to fix it in the future if something breakes. - if (_config["Instances"].Count != 0) - { - var instance = _config["Instances"][_config["Instances"].Keys[0]]; - _config["Instances"] = new ConcurrentDictionary>(); - _config["Instances"][_config["MeshName"]] = instance; - } - _config["Parts"] = new ConcurrentDictionary(); - _config["Parts"][_config["MeshName"]] = part; - } + //else if (_exportType is ExportType.StaticInMap or ExportType.EntityInMap) + //{ + // return; + //} + + //// If theres only 1 part, we need to rename it + the instance to the name of the mesh (unreal imports to fbx name if only 1 mesh inside) + //if (_config["Parts"].Count == 1) + //{ + // var part = _config["Parts"][_config["Parts"].Keys[0]]; + // //I'm not sure what to do if it's 0, so I guess I'll leave that to fix it in the future if something breakes. + // if (_config["Instances"].Count != 0) + // { + // var instance = _config["Instances"][_config["Instances"].Keys[0]]; + // _config["Instances"] = new ConcurrentDictionary>(); + // _config["Instances"][_config["MeshName"]] = instance; + // } + // _config["Parts"] = new ConcurrentDictionary(); + // _config["Parts"][_config["MeshName"]] = part; + //} //this just sorts the "instances" part of the cfg so its ordered by scale @@ -396,6 +403,7 @@ private struct JsonLight public float[] Size; public float[] Color; public float Range; + public bool Shadowing; } private struct JsonDecal { diff --git a/Tiger/Exporters/Source2Handler.cs b/Tiger/Exporters/Source2Handler.cs index 18c0aeaa..c85206cb 100644 --- a/Tiger/Exporters/Source2Handler.cs +++ b/Tiger/Exporters/Source2Handler.cs @@ -1,55 +1,49 @@ -using System; -using System.Text; +using System.Text; +using System.Text.Json; using Arithmic; using Tiger.Schema; -using Tiger.Schema.Entity; using Tiger.Schema.Shaders; using Tiger.Schema.Static; +using Texture = Tiger.Schema.Texture; namespace Tiger.Exporters; -public class Source2Handler +public class SBoxHandler { - private static ConfigSubsystem _config = CharmInstance.GetSubsystem(); - public static bool source2Shaders = _config.GetS2ShaderExportEnabled(); - public static bool source2Models = _config.GetS2VMDLExportEnabled(); - public static bool source2Materials = _config.GetS2VMATExportEnabled(); - - public static void SaveStaticVMDL(string savePath, string staticMeshName, List staticMesh) + public static void SaveStaticVMDL(string savePath, ExporterMesh mesh) { try { - if (!File.Exists($"{savePath}/{staticMeshName}.vmdl")) + if (!File.Exists($"{savePath}/{mesh.Hash}.vmdl")) { - //Source 2 shit - File.Copy("Exporters/template.vmdl", $"{savePath}/{staticMeshName}.vmdl", true); - string text = File.ReadAllText($"{savePath}/{staticMeshName}.vmdl"); + File.Copy("Exporters/template.vmdl", $"{savePath}/{mesh.Hash}.vmdl", true); + string text = File.ReadAllText($"{savePath}/{mesh.Hash}.vmdl"); StringBuilder mats = new StringBuilder(); int i = 0; - foreach (MeshPart staticpart in staticMesh) + foreach (var part in mesh.Parts) { mats.AppendLine("{"); - if (staticpart.Material == null) + if (part.Material == null) { - mats.AppendLine($" from = \"{staticMeshName}_Group{staticpart.GroupIndex}_Index{staticpart.Index}_{i}_{staticpart.LodCategory}.vmat\""); + mats.AppendLine($" from = \"{mesh.Hash}_Group{part.MeshPart.GroupIndex}_Index{part.Index}_{i}_{part.MeshPart.LodCategory}.vmat\""); mats.AppendLine($" to = \"materials/black_matte.vmat\""); } else { - mats.AppendLine($" from = \"{staticpart.Material.FileHash}.vmat\""); - mats.AppendLine($" to = \"materials/{staticpart.Material.FileHash}.vmat\""); + mats.AppendLine($" from = \"{part.Material.FileHash}.vmat\""); + mats.AppendLine($" to = \"materials/{part.Material.FileHash}.vmat\""); } mats.AppendLine("},\n"); i++; } text = text.Replace("%MATERIALS%", mats.ToString()); - text = text.Replace("%FILENAME%", $"models/{staticMeshName}.fbx"); - text = text.Replace("%MESHNAME%", staticMeshName); + text = text.Replace("%FILENAME%", $"Models/Statics/{mesh.Hash}.fbx"); + text = text.Replace("%MESHNAME%", mesh.Hash); - File.WriteAllText($"{savePath}/{staticMeshName}.vmdl", text); + File.WriteAllText($"{savePath}/{mesh.Hash}.vmdl", text); } } catch (Exception e) @@ -58,30 +52,30 @@ public static void SaveStaticVMDL(string savePath, string staticMeshName, List parts) + public static void SaveEntityVMDL(string savePath, ExporterEntity entity) { try { - if (!File.Exists($"{savePath}/{hash}.vmdl")) + if (!File.Exists($"{savePath}/{entity.Mesh.Hash}.vmdl")) { - File.Copy("Exporters/template.vmdl", $"{savePath}/{hash}.vmdl", true); - string text = File.ReadAllText($"{savePath}/{hash}.vmdl"); + File.Copy("Exporters/template.vmdl", $"{savePath}/{entity.Mesh.Hash}.vmdl", true); + string text = File.ReadAllText($"{savePath}/{entity.Mesh.Hash}.vmdl"); StringBuilder mats = new StringBuilder(); int i = 0; - foreach (var part in parts) + foreach (var part in entity.Mesh.Parts) { mats.AppendLine("{"); if (part.Material == null) { - mats.AppendLine($" from = \"{hash}_Group{part.GroupIndex}_Index{part.Index}_{i}_{part.LodCategory}.vmat\""); + mats.AppendLine($" from = \"{entity.Mesh.Hash}_Group{part.MeshPart.GroupIndex}_Index{part.MeshPart.Index}_{i}_{part.MeshPart.LodCategory}.vmat\""); mats.AppendLine($" to = \"materials/black_matte.vmat\""); } else @@ -94,10 +88,10 @@ public static void SaveEntityVMDL(string savePath, string hash, List parts, STerrain terrainHeader) + public static void SaveTerrainVMDL(string name, string savePath, List parts) { - Directory.CreateDirectory($"{savePath}/Statics/"); - File.Copy("Exporters/template.vmdl", $"{savePath}/Statics/{hash}_Terrain.vmdl", true); - if (File.Exists($"{savePath}/Statics/{hash}_Terrain.vmdl")) + Directory.CreateDirectory($"{savePath}/Models/Terrain/"); + File.Copy("Exporters/template.vmdl", $"{savePath}/Models/Terrain/{name}.vmdl", true); + if (File.Exists($"{savePath}/Models/Terrain/{name}.vmdl")) { - string text = File.ReadAllText($"{savePath}/Statics/{hash}_Terrain.vmdl"); + string text = File.ReadAllText($"{savePath}/Models/Terrain/{name}.vmdl"); StringBuilder mats = new StringBuilder(); int i = 0; foreach (var staticpart in parts) { - if (terrainHeader.MeshGroups[staticpart.GroupIndex].Dyemap != null) - { - mats.AppendLine("{"); - mats.AppendLine($" from = \"{staticpart.Material.FileHash}.vmat\""); - mats.AppendLine($" to = \"materials/Terrain/{hash}_{staticpart.Material.FileHash}.vmat\""); - mats.AppendLine("},\n"); - i++; - } + mats.AppendLine("{"); + mats.AppendLine($" from = \"{staticpart.Material.FileHash}.vmat\""); + mats.AppendLine($" to = \"materials/Terrain/{staticpart.Material.FileHash}.vmat\""); + mats.AppendLine("},\n"); + i++; } text = text.Replace("%MATERIALS%", mats.ToString()); - text = text.Replace("%FILENAME%", $"models/{hash}_Terrain.fbx"); - text = text.Replace("%MESHNAME%", hash); + text = text.Replace("%FILENAME%", $"Models/Terrain/{name}.fbx"); + text = text.Replace("%MESHNAME%", name); - File.WriteAllText($"{savePath}/Statics/{hash}_Terrain.vmdl", text); + File.WriteAllText($"{savePath}/Models/Terrain/{name}.vmdl", text); } } - public static void SaveVMAT(string savePath, string hash, IMaterial materialHeader, bool isTerrain = false) + public static void SaveVMAT(string savePath, string hash, IMaterial materialHeader, bool isTerrain = false, List terrainDyemaps = null) { - StringBuilder vmat = new StringBuilder(); - vmat.AppendLine("Layer0 \n{"); - - //If the shader doesnt exist, just use the default complex.shader - if (!File.Exists($"{savePath}/Source2/PS_{materialHeader.PixelShader?.Hash}.shader")) - { - vmat.AppendLine($" shader \"complex.shader\""); - - //Use just the first texture for the diffuse - if (materialHeader.EnumeratePSTextures().Any()) - { - if (materialHeader.EnumeratePSTextures().ElementAt(0).Texture is not null) - vmat.AppendLine($" TextureColor \"materials/Textures/{materialHeader.EnumeratePSTextures().ElementAt(0).Texture.Hash}.png\""); - } - } + string path; + if (isTerrain) + path = $"{savePath}/Materials/Terrain"; else - { - vmat.AppendLine($"\tshader \"ps_{materialHeader.PixelShader.Hash}.shader\""); - vmat.AppendLine($"\tF_ALPHA_TEST 1"); - vmat.AppendLine($"\tF_ADDITIVE_BLEND 1"); + path = $"{savePath}/Materials"; - if (materialHeader.Unk0C != 0) - { - vmat.AppendLine($"\tF_RENDER_BACKFACES 1"); - } - - TfxBytecodeInterpreter bytecode = new(TfxBytecodeOp.ParseAll(materialHeader.PS_TFX_Bytecode)); - var bytecode_hlsl = bytecode.Evaluate(materialHeader.PS_TFX_Bytecode_Constants); + Directory.CreateDirectory(path); + StringBuilder vmat = new StringBuilder(); - vmat.AppendLine($"\tDynamicParams\r\n\t{{"); - foreach (var entry in bytecode_hlsl) - { - vmat.AppendLine($"\t\tcb0_{entry.Key} \"{entry.Value}\""); - } - vmat.AppendLine($"\t\tcb13_0 \"Time\""); - vmat.AppendLine($"\t}}"); - } + vmat.AppendLine("Layer0\n{"); + //If the shader doesnt exist, just use the default complex.shader + //if (!File.Exists($"{savePath}/Shaders/PS_{materialHeader.PixelShader?.Hash}.shader")) + //{ + // vmat.AppendLine($" shader \"complex.shader\""); + + // //Use just the first texture for the diffuse + // if (materialHeader.EnumeratePSTextures().Any()) + // { + // if (materialHeader.EnumeratePSTextures().ElementAt(0).Texture is not null) + // vmat.AppendLine($" TextureColor \"Textures/{materialHeader.EnumeratePSTextures().ElementAt(0).Texture.Hash}.png\""); + // } + //} + + //Material parameters + vmat.AppendLine($"\tshader \"ps_{materialHeader.PixelShader.Hash}.shader\""); + vmat.AppendLine($"\tF_ALPHA_TEST 1"); + vmat.AppendLine($"\tF_ADDITIVE_BLEND 1"); + + if (materialHeader.Unk0C != 0) + vmat.AppendLine($"\tF_RENDER_BACKFACES 1"); + + //Textures foreach (var e in materialHeader.EnumeratePSTextures()) { if (e.Texture == null) continue; - vmat.AppendLine($"\tTextureT{e.TextureIndex} \"materials/Textures/{e.Texture.Hash}.png\""); + vmat.AppendLine($"\tTextureT{e.TextureIndex} \"Textures/{e.Texture.Hash}.png\""); } //vmat.AppendLine(PopulateCBuffers(materialHeader.Decompile(materialHeader.VertexShader.GetBytecode(), $"vs{materialHeader.VertexShader.Hash}"), materialHeader, true).ToString()); vmat.AppendLine(PopulateCBuffers(materialHeader).ToString()); - vmat.AppendLine("}"); - string terrainDir = isTerrain ? "/Terrain/" : ""; - if (isTerrain) - Directory.CreateDirectory($"{savePath}/Source2/materials/{terrainDir}"); + //Dynamic expressions + TfxBytecodeInterpreter bytecode = new(TfxBytecodeOp.ParseAll(materialHeader.PS_TFX_Bytecode)); + var bytecode_hlsl = bytecode.Evaluate(materialHeader.PS_TFX_Bytecode_Constants); - if (!File.Exists($"{savePath}/Source2/materials/{terrainDir}{hash}.vmat")) + vmat.AppendLine($"\tDynamicParams\r\n\t{{"); + foreach (var entry in bytecode_hlsl) { - try + vmat.AppendLine($"\t\tcb0_{entry.Key} \"{entry.Value}\""); + } + + foreach (var resource in materialHeader.PixelShader.Resources) + { + if (resource.ResourceType == Schema.ResourceType.CBuffer) { - File.WriteAllText($"{savePath}/Source2/materials/{terrainDir}{hash}.vmat", vmat.ToString()); + switch (resource.Index) + { + case 2: //Transparent scope + for (int i = 0; i < resource.Count; i++) + { + vmat.AppendLine($"\t\tcb2_{i} \"float4(0,1,1,1)\""); + } + break; + case 8: //??? scope + for (int i = 0; i < resource.Count; i++) + { + if (i < 5) + vmat.AppendLine($"\t\tcb8_{i} \"float4(0,0,0,0)\""); + else + vmat.AppendLine($"\t\tcb8_{i} \"float4(1,1,1,1)\""); + } + break; + case 13: //Frame scope + vmat.AppendLine($"\t\tcb13_0 \"Time\""); + vmat.AppendLine($"\t\tcb13_1 \"float4(0.25,1,1,1)\""); + break; + } } - catch (IOException) + } + + vmat.AppendLine($"\t}}"); + vmat.AppendLine("}"); + + if (terrainDyemaps is not null) + foreach (var tex in terrainDyemaps) { + SaveVTEX(tex, $"{savePath}/Textures"); } + + try + { + File.WriteAllText($"{path}/{hash}.vmat", vmat.ToString()); + } + catch (IOException) + { } } @@ -216,31 +242,26 @@ public static void SaveDecalVMAT(string savePath, string hash, IMaterial materia if (materialHeader.EnumeratePSTextures().Any()) { if (materialHeader.EnumeratePSTextures().ElementAt(0).Texture is not null) - vmat.AppendLine($" TextureColor \"materials/Textures/{materialHeader.EnumeratePSTextures().ElementAt(0).Texture.Hash}.png\""); + vmat.AppendLine($" TextureColor \"Textures/{materialHeader.EnumeratePSTextures().ElementAt(0).Texture.Hash}.png\""); } foreach (var e in materialHeader.EnumeratePSTextures()) { if (e.Texture == null) - { continue; - } - vmat.AppendLine($" TextureT{e.TextureIndex} \"materials/Textures/{e.Texture.Hash}.png\""); + vmat.AppendLine($" TextureT{e.TextureIndex} \"Textures/{e.Texture.Hash}.png\""); } vmat.AppendLine("}"); - if (!File.Exists($"{savePath}/Source2/materials/{hash}_decal.vmat")) + try + { + Directory.CreateDirectory($"{savePath}/materials/"); + File.WriteAllText($"{savePath}/materials/{hash}_decal.vmat", vmat.ToString()); + } + catch (IOException) { - try - { - Directory.CreateDirectory($"{savePath}/Source2/materials/"); - File.WriteAllText($"{savePath}/Source2/materials/{hash}_decal.vmat", vmat.ToString()); - } - catch (IOException) - { - } } } @@ -287,4 +308,49 @@ public static StringBuilder PopulateCBuffers(IMaterial materialHeader, bool isVe return cbuffers; } + + public static void SaveVTEX(Texture tex, string savePath) + { + if (!Directory.Exists(savePath)) + Directory.CreateDirectory(savePath); + + var file = TextureFile.CreateDefault(tex, tex.IsCubemap() ? ImageDimension.CUBEARRAY : ImageDimension._2D); + var json = JsonSerializer.Serialize(file, JsonSerializerOptions.Default); + File.WriteAllText($"{savePath}/{tex.Hash}.vtex", json); + } + + public static void SaveGearVMAT(string saveDirectory, string meshName, TextureExportFormat outputTextureFormat, List dyes, string fileSuffix = "") + { + File.Copy($"Exporters/template.vmat", $"{saveDirectory}/{meshName}{fileSuffix}.vmat", true); + string text = File.ReadAllText($"{saveDirectory}/{meshName}{fileSuffix}.vmat"); + + string[] components = { "X", "Y", "Z", "W" }; + + int dyeIndex = 1; + foreach (var dye in dyes) + { + var dyeInfo = dye.GetDyeInfo(); + foreach (var fieldInfo in dyeInfo.GetType().GetFields()) + { + Vector4 value = (Vector4)fieldInfo.GetValue(dyeInfo); + if (!fieldInfo.CustomAttributes.Any()) + continue; + string valueName = fieldInfo.CustomAttributes.First().ConstructorArguments[0].Value.ToString(); + for (int i = 0; i < 4; i++) + { + text = text.Replace($"{valueName}{dyeIndex}.{components[i]}", $"{value[i].ToString().Replace(",", ".")}"); + } + } + + var diff = dye.TagData.DyeTextures[0]; + text = text.Replace($"DiffMap{dyeIndex}", $"{diff.Texture.Hash}.{TextureExtractor.GetExtension(outputTextureFormat)}"); + var norm = dye.TagData.DyeTextures[1]; + text = text.Replace($"NormMap{dyeIndex}", $"{norm.Texture.Hash}.{TextureExtractor.GetExtension(outputTextureFormat)}"); + dyeIndex++; + } + + text = text.Replace("OUTPUTPATH", $"Textures"); + text = text.Replace("SHADERNAMEENUM", $"{meshName}{fileSuffix}"); + File.WriteAllText($"{saveDirectory}/{meshName}{fileSuffix}.vmat", text); + } } diff --git a/Tiger/Exporters/VTEXTextureFile.cs b/Tiger/Exporters/VTEXTextureFile.cs new file mode 100644 index 00000000..76660667 --- /dev/null +++ b/Tiger/Exporters/VTEXTextureFile.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; +using Tiger.Schema; +using Tiger.Schema.Entity; + +namespace Tiger.Exporters +{ + public enum GammaType + { + Linear, + SRGB, + } + + public enum ImageFormatType + { + DXT5, + DXT1, + RGBA8888, + BC7, + BC6H, + } + + public enum ImageDimension + { + [Description("1D")] + _1D, + [Description("2D")] + _2D, + [Description("3D")] + _3D, + [Description("1DArray")] + _1DARRAY, + [Description("2DArray")] + _2DARRAY, + [Description("3DArray")] + _3DARRAY, + [Description("CUBE")] + CUBE, + [Description("CUBEARRAY")] + CUBEARRAY + } + + public class TextureFile + { + public List Images { get; set; } + + public string InputColorSpace { get; set; } + + public string OutputFormat { get; set; } + + public string OutputColorSpace { get; set; } + + public string OutputTypeString { get; set; } + + public static TextureFile CreateDefault(Texture texture, ImageDimension dimension) + { + return new TextureFile + { + Images = new List { $"textures/{texture.Hash}.png" }, + OutputFormat = ImageFormatType.RGBA8888.ToString(), + OutputColorSpace = (texture.IsSrgb() ? GammaType.SRGB : GammaType.Linear).ToString(), + InputColorSpace = (texture.IsSrgb() ? GammaType.SRGB : GammaType.Linear).ToString(), + OutputTypeString = GetDisplayName(dimension) + }; + } + + private static string GetDisplayName(ImageDimension value) + { + var field = value.GetType().GetField(value.ToString()); + var attribute = (DescriptionAttribute)Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)); + + return attribute == null ? value.ToString() : attribute.Description; + } + } +} diff --git a/Tiger/Exporters/blender_api_template.py b/Tiger/Exporters/blender_api_template.py index 342853a2..4079ca02 100644 --- a/Tiger/Exporters/blender_api_template.py +++ b/Tiger/Exporters/blender_api_template.py @@ -302,7 +302,7 @@ def create_Shader_Preset(context, operator, group_name, riplocation): armor_primary_detail_diffuse_map.extension = 'REPEAT' armor_primary_detail_diffuse_map.hide = True - bpy.data.images.load(os.path.join(riplocation, "OUTPUTPATH/DiffMap1"), check_existing=False) + bpy.data.images.load(os.path.join(riplocation, "OUTPUTPATH/DetailTextures/DiffMap1"), check_existing=False) DetailDiffuse01 = bpy.data.images.get("DiffMap1") DetailDiffuse01.colorspace_settings.name = "sRGB" DetailDiffuse01.alpha_mode = "CHANNEL_PACKED" @@ -317,7 +317,7 @@ def create_Shader_Preset(context, operator, group_name, riplocation): armor_primary_detail_normal_map.extension = 'REPEAT' armor_primary_detail_normal_map.hide = True - bpy.data.images.load(os.path.join(riplocation, "OUTPUTPATH/NormMap1"), check_existing=False) + bpy.data.images.load(os.path.join(riplocation, "OUTPUTPATH/DetailTextures/NormMap1"), check_existing=False) DetailNormal01 = bpy.data.images.get("NormMap1") DetailNormal01.colorspace_settings.name = "Non-Color" DetailNormal01.alpha_mode = "CHANNEL_PACKED" @@ -332,7 +332,7 @@ def create_Shader_Preset(context, operator, group_name, riplocation): cloth_primary_detail_diffuse_map.extension = 'REPEAT' cloth_primary_detail_diffuse_map.hide = True - bpy.data.images.load(os.path.join(riplocation,"OUTPUTPATH/DiffMap2"), check_existing=False) + bpy.data.images.load(os.path.join(riplocation,"OUTPUTPATH/DetailTextures/DiffMap2"), check_existing=False) DetailDiffuse02 = bpy.data.images.get("DiffMap2") DetailDiffuse02.colorspace_settings.name = "sRGB" DetailDiffuse02.alpha_mode = "CHANNEL_PACKED" @@ -347,7 +347,7 @@ def create_Shader_Preset(context, operator, group_name, riplocation): cloth_primary_detail_normal_map.extension = 'REPEAT' cloth_primary_detail_normal_map.hide = True - bpy.data.images.load(os.path.join(riplocation,"OUTPUTPATH/NormMap2"), check_existing=False) + bpy.data.images.load(os.path.join(riplocation,"OUTPUTPATH/DetailTextures/NormMap2"), check_existing=False) DetailNormal02 = bpy.data.images.get("NormMap2") DetailNormal02.colorspace_settings.name = "Non-Color" DetailNormal02.alpha_mode = "CHANNEL_PACKED" @@ -362,7 +362,7 @@ def create_Shader_Preset(context, operator, group_name, riplocation): suit_primary_detail_diffuse_map.extension = 'REPEAT' suit_primary_detail_diffuse_map.hide = True - bpy.data.images.load(os.path.join(riplocation, "OUTPUTPATH/DiffMap3"), check_existing=False) + bpy.data.images.load(os.path.join(riplocation, "OUTPUTPATH/DetailTextures/DiffMap3"), check_existing=False) DetailDiffuse03 = bpy.data.images.get("DiffMap3") DetailDiffuse03.colorspace_settings.name = "sRGB" DetailDiffuse03.alpha_mode = "CHANNEL_PACKED" @@ -377,7 +377,7 @@ def create_Shader_Preset(context, operator, group_name, riplocation): suit_primary_detail_normal_map.extension = 'REPEAT' suit_primary_detail_normal_map.hide = True - bpy.data.images.load(os.path.join(riplocation, "OUTPUTPATH/NormMap3"), check_existing=False) + bpy.data.images.load(os.path.join(riplocation, "OUTPUTPATH/DetailTextures/NormMap3"), check_existing=False) DetailNormal03 = bpy.data.images.get("NormMap3") DetailNormal03.colorspace_settings.name = "Non-Color" DetailNormal03.alpha_mode = "CHANNEL_PACKED" diff --git a/Tiger/Exporters/template.vmat b/Tiger/Exporters/template.vmat new file mode 100644 index 00000000..4d287e91 --- /dev/null +++ b/Tiger/Exporters/template.vmat @@ -0,0 +1,177 @@ +// THIS FILE IS AUTO-GENERATED + +Layer0 +{ + shader "Destiny_2_Gear_Shader.shader" + + g_flMaskclipvalue "0.5" + + //---- Armor ---- + g_Armor_DetailDiffuseTransform "[DiffTrans1.X DiffTrans1.Y DiffTrans1.Z DiffTrans1.W ]" + g_Armor_DetailNormalTransform "[NormTrans1.X NormTrans1.Y NormTrans1.Z NormTrans1.W]" + g_ArmorPrimary_Color "[CPrime1.X CPrime1.Y CPrime1.Z 1.000000]" + g_ArmorPrimary_DetailDiffuseBlend "PrimeMatParams1.X" + g_ArmorPrimary_DetailNormalBlend "PrimeMatParams1.Y" + g_ArmorPrimary_DetailRoughnessBlend "PrimeMatParams1.Z" + g_ArmorPrimary_Emission "[CPrimeEmit1.X CPrimeEmit1.Y CPrimeEmit1.Z 1.000000]" + g_ArmorPrimary_Fuzz "PrimeAdvMatParams1.Y" + g_ArmorPrimary_Iridescence "PrimeAdvMatParams1.X" + g_ArmorPrimary_Metalness "PrimeMatParams1.W" + g_ArmorPrimary_RoughnessRemap "[PrimeRoughMap1.X PrimeRoughMap1.Y PrimeRoughMap1.Z PrimeRoughMap1.W]" + g_ArmorPrimary_Transmission "PrimeAdvMatParams1.Z" + g_ArmorPrimary_WearRemap "[PrimeWearMap1.X PrimeWearMap1.Y PrimeWearMap1.Z PrimeWearMap1.W]" + + g_WornArmorPrimary_Color "[CPrimeWear1.X CPrimeWear1.Y CPrimeWear1.Z 1.0]" + g_WornArmorPrimary_DetailDiffuseBlend "PrimeWornMatParams1.X" + g_WornArmorPrimary_DetailNormalBlend "PrimeWornMatParams1.Y" + g_WornArmorPrimary_DetailRoughnessBlend "PrimeWornMatParams1.Z" + g_WornArmorPrimary_Metalness "PrimeWornMatParams1.W" + g_WornArmorPrimary_RoughnessRemap "[PrimeWornRoughMap1.X PrimeWornRoughMap1.Y PrimeWornRoughMap1.Z PrimeWornRoughMap1.W]" + + g_ArmorSecondary_Color "[CSecon1.X CSecon1.Y CSecon1.Z 1.000000]" + g_ArmorSecondary_DetailDiffuseBlend "SeconMatParams1.X" + g_ArmorSecondary_DetailNormalBlend "SeconMatParams1.Y" + g_ArmorSecondary_DetailRoughnessBlend "SeconMatParams1.Z" + g_ArmorSecondary_Emission "[CSeconEmit1.X CSeconEmit1.Y CSeconEmit1.Z 1.000000]" + g_ArmorSecondary_Fuzz "SeconAdvMatParams1.Y" + g_ArmorSecondary_Iridescence "SeconAdvMatParams1.X" + g_ArmorSecondary_Metalness "SeconMatParams1.W" + g_ArmorSecondary_RoughnessRemap "[SeconRoughMap1.X SeconRoughMap1.Y SeconRoughMap1.Z SeconRoughMap1.W]" + g_ArmorSecondary_Transmission "SeconAdvMatParams1.Z" + g_ArmorSecondary_WearRemap "[SeconWearMap1.X SeconWearMap1.Y SeconWearMap1.Z SeconWearMap1.W]" + + g_WornArmorSecondary_Color "[CSeconWear1.X CSeconWear1.Y CSeconWear1.Z 1.000000]" + g_WornArmorSecondary_DetailDiffuseBlend "SeconWornMatParams1.X" + g_WornArmorSecondary_DetailNormalBlend "SeconWornMatParams1.Y" + g_WornArmorSecondary_DetailRoughnessBlend "SeconWornMatParams1.Z" + g_WornArmorSecondary_Metalness "SeconWornMatParams1.W" + g_WornArmorSecondary_RoughnessRemap "[SeconWornRoughMap1.X SeconWornRoughMap1.Y SeconWornRoughMap1.Z SeconWornRoughMap1.W]" + + //---- Cloth ---- + g_Cloth_DetailDiffuseTransform "[DiffTrans2.X DiffTrans2.Y DiffTrans2.Z DiffTrans2.W]" + g_Cloth_DetailNormalTransform "[NormTrans2.X NormTrans2.Y NormTrans2.Z NormTrans2.W]" + g_ClothPrimary_Color "[CPrime2.X CPrime2.Y CPrime2.Z 1.000000]" + g_ClothPrimary_DetailDiffuseBlend "PrimeMatParams2.X" + g_ClothPrimary_DetailNormalBlend "PrimeMatParams2.Y" + g_ClothPrimary_DetailRoughnessBlend "PrimeMatParams2.Z" + g_ClothPrimary_Emission "[CPrimeEmit2.X CPrimeEmit2.Y CPrimeEmit2.Z 1.000000]" + g_ClothPrimary_Fuzz "PrimeAdvMatParams2.Y" + g_ClothPrimary_Iridescence "PrimeAdvMatParams2.X" + g_ClothPrimary_Metalness "PrimeMatParams2.W" + g_ClothPrimary_RoughnessRemap "[PrimeRoughMap2.X PrimeRoughMap2.Y PrimeRoughMap2.Z PrimeRoughMap2.W]" + g_ClothPrimary_Transmission "PrimeAdvMatParams2.Z" + g_ClothPrimary_WearRemap "[PrimeWearMap2.X PrimeWearMap2.Y PrimeWearMap2.Z PrimeWearMap2.W]" + + g_WornClothPrimary_Color "[CPrimeWear2.X CPrimeWear2.Y CPrimeWear2.Z 1.000000]" + g_WornClothPrimary_DetailDiffuseBlend "PrimeWornMatParams1.X" + g_WornClothPrimary_DetailNormalBlend "PrimeWornMatParams1.Y" + g_WornClothPrimary_DetailRoughnessBlend "PrimeWornMatParams1.Z" + g_WornClothPrimary_Metalness "PrimeWornMatParams1.W" + g_WornClothPrimary_RoughnessRemap "[PrimeWornRoughMap2.X PrimeWornRoughMap2.Y PrimeWornRoughMap2.Z PrimeWornRoughMap2.W]" + g_WornClothPrimary_WearRemap "[0.000 1.000 0.000 1.000]" + + g_ClothSecondary_Color "[CSecon2.X CSecon2.Y CSecon2.Z 1.000000]" + g_ClothSecondary_DetailDiffuseBlend "SeconMatParams2.X" + g_ClothSecondary_DetailNormalBlend "SeconMatParams2.Y" + g_ClothSecondary_DetailRoughnessBlend "SeconMatParams2.Z" + g_ClothSecondary_Emission "[CSeconEmit2.X CSeconEmit2.Y CSeconEmit2.Z 1.000000]" + g_ClothSecondary_Fuzz "SeconAdvMatParams2.Y" + g_ClothSecondary_Iridescence "SeconAdvMatParams2.X" + g_ClothSecondary_Metalness "SeconMatParams2.W" + g_ClothSecondary_RoughnessRemap "[SeconRoughMap2.X SeconRoughMap2.Y SeconRoughMap2.Z SeconRoughMap2.W]" + g_ClothSecondary_Transmission "SeconAdvMatParams2.Z" + g_ClothSecondary_WearRemap "[SeconWearMap2.X SeconWearMap2.Y SeconWearMap2.Z SeconWearMap2.W]" + + g_WornClothSecondary_Color "[CSeconWear2.X CSeconWear2.Y CSeconWear2.Z 1.000000]" + g_WornClothSecondary_DetailDiffuseBlend "SeconWornMatParams2.X" + g_WornClothSecondary_DetailNormalBlend "SeconWornMatParams2.Y" + g_WornClothSecondary_DetailRoughnessBlend "SeconWornMatParams2.Z" + g_WornClothSecondary_Metalness "SeconWornMatParams2.W" + g_WornClothSecondary_RoughnessRemap "[SeconWornRoughMap2.X SeconWornRoughMap2.Y SeconWornRoughMap2.Z SeconWornRoughMap2.W]" + g_WornClothSecondary_WearRemap "[0.000 1.000 0.000 1.000]" + + //---- Color ---- + g_flModelTintAmount "1.000" + g_vColorTint "[1.000000 1.000000 1.000000 0.000000]" + + //---- Detail Textures ---- + TextureDetailDiffuse01 "OUTPUTPATH/DetailTextures/DiffMap1" //Armor Detail Diffuse + TextureDetailDiffuse02 "OUTPUTPATH/DetailTextures/DiffMap2" //Cloth Detail Diffuse + TextureDetailDiffuse03 "OUTPUTPATH/DetailTextures/DiffMap3" //Suit Detail Diffuse + + TextureDetailNormal01 "OUTPUTPATH/DetailTextures/NormMap1" //Armor Detail Normal + TextureDetailNormal02 "OUTPUTPATH/DetailTextures/NormMap2" //Cloth Detail Normal + TextureDetailNormal03 "OUTPUTPATH/DetailTextures/NormMap3" //Suit Detail Normal + + //---- Fade ---- + g_flFadeExponent "1.000" + + //---- Fog ---- + g_bFogEnabled "1" + + //---- Lighting ---- + g_flDirectionalLightmapMinZ "0.050" + g_flDirectionalLightmapStrength "1.000" + + //---- Material ---- + TextureColor "materials/default/default_color.tga" + TextureDyeMap "materials/default/default.tga" + TextureGStack "materials/default/default.tga" + TextureIridescence "materials/Textures/Iridescence_Lookup.tga" + TextureNormal "materials/default/default_normal.tga" + TextureTranslucency "materials/default/default_trans.tga" + + //---- Suit ---- + g_Suit_DetailDiffuseTransform "[DiffTrans3.X DiffTrans3.Y DiffTrans3.Z DiffTrans3.W]" + g_Suit_DetailNormalTransform "[NormTrans3.X NormTrans3.Y NormTrans3.Z NormTrans3.W]" + g_SuitPrimary_Color "[CPrime3.X CPrime3.Y CPrime3.Z 1.000000]" + g_SuitPrimary_DetailDiffuseBlend "PrimeMatParams3.X" + g_SuitPrimary_DetailNormalBlend "PrimeMatParams3.Y" + g_SuitPrimary_DetailRoughnessBlend "PrimeMatParams3.Z" + g_SuitPrimary_Emission "[CPrimeEmit3.X CPrimeEmit3.Y CPrimeEmit3.Z 1.000000]" + g_SuitPrimary_Fuzz "PrimeAdvMatParams3.Y" + g_SuitPrimary_Iridescence "PrimeAdvMatParams3.X" + g_SuitPrimary_Metalness "PrimeMatParams3.W" + g_SuitPrimary_RoughnessRemap "[PrimeRoughMap3.X PrimeRoughMap3.Y PrimeRoughMap3.Z PrimeRoughMap3.W]" + g_SuitPrimary_Transmission "PrimeAdvMatParams3.Z" + g_SuitPrimary_WearRemap "[PrimeWearMap3.X PrimeWearMap3.Y PrimeWearMap3.Z PrimeWearMap3.W]" + + g_WornSuitPrimary_Color "[CPrimeWear3.X CPrimeWear3.Y CPrimeWear3.Z 1.000000]" + g_WornSuitPrimary_DetailDiffuseBlend "PrimeWornMatParams3.X" + g_WornSuitPrimary_DetailNormalBlend "PrimeWornMatParams3.Y" + g_WornSuitPrimary_DetailRoughnessBlend "PrimeWornMatParams3.Z" + g_WornSuitPrimary_Metalness "PrimeWornMatParams3.W" + g_WornSuitPrimary_RoughnessRemap "[PrimeWornRoughMap3.X PrimeWornRoughMap3.Y PrimeWornRoughMap3.Z PrimeWornRoughMap3.W]" + g_WornSuitPrimary_WearRemap "[0.000 1.000 0.000 1.000]" + + g_SuitSecondary_Color "[CSecon3.X CSecon3.Y CSecon3.Z 1.000000]" + g_SuitSecondary_DetailDiffuseBlend "SeconMatParams3.X" + g_SuitSecondary_DetailNormalBlend "SeconMatParams3.Y" + g_SuitSecondary_DetailRoughnessBlend "SeconMatParams3.Z" + g_SuitSecondary_Emission "[CSeconEmit3.X CSeconEmit3.Y CSeconEmit3.Z 1.000000]" + g_SuitSecondary_Fuzz "SeconAdvMatParams3.Y" + g_SuitSecondary_Iridescence "SeconAdvMatParams3.X" + g_SuitSecondary_Metalness "SeconMatParams3.W" + g_SuitSecondary_RoughnessRemap "[SeconRoughMap3.X SeconRoughMap3.Y SeconRoughMap3.Z SeconRoughMap3.W]" + g_SuitSecondary_Transmission "SeconAdvMatParams3.Z" + g_SuitSecondary_WearRemap "[SeconWearMap3.X SeconWearMap3.Y SeconWearMap3.Z SeconWearMap3.W]" + + g_WornSuitSecondary_Color "[CSeconWear3.X CSeconWear3.Y CSeconWear3.Z 1.000000]" + g_WornSuitSecondary_DetailDiffuseBlend "SeconWornMatParams3.X" + g_WornSuitSecondary_DetailNormalBlend "SeconWornMatParams3.Y" + g_WornSuitSecondary_DetailRoughnessBlend "SeconWornMatParams3.Z" + g_WornSuitSecondary_Metalness "SeconWornMatParams3.W" + g_WornSuitSecondary_RoughnessRemap "[SeconWornRoughMap3.X SeconWornRoughMap3.Y SeconWornRoughMap3.Z SeconWornRoughMap3.W]" + g_WornSuitSecondary_WearRemap "[0.000 1.000 0.000 1.000]" + + //---- Texture Coordinates ---- + g_nScaleTexCoordUByModelScaleAxis "0" + g_nScaleTexCoordVByModelScaleAxis "0" + g_vTexCoordOffset "[0.000 0.000]" + g_vTexCoordScale "[1.000 1.000]" + g_vTexCoordScrollSpeed "[0.000 0.000]" + + //---- Translucent ---- + g_flAlphaTestReference "0.700" + g_flAntiAliasedEdgeStrength "1.000" +} diff --git a/Tiger/Helpers.cs b/Tiger/Helpers.cs index cce635d0..7dc7d12d 100644 --- a/Tiger/Helpers.cs +++ b/Tiger/Helpers.cs @@ -38,7 +38,7 @@ public static string DebugString(this List value) return sb.ToString(); } - public static void DecorateSignaturesWithBufferIndex(ref InputSignature[] inputSignatures, List strides) + public static void DecorateSignaturesWithBufferIndex(ref DXBCIOSignature[] inputSignatures, List strides) { if (!strides.Any()) { @@ -47,7 +47,7 @@ public static void DecorateSignaturesWithBufferIndex(ref InputSignature[] inputS int bufferIndex = 0; int offset = 0; int strideBound = strides[bufferIndex]; - foreach (ref InputSignature inputSignature in inputSignatures.AsSpan()) + foreach (ref DXBCIOSignature inputSignature in inputSignatures.AsSpan()) { if (offset < strideBound) { @@ -59,7 +59,7 @@ public static void DecorateSignaturesWithBufferIndex(ref InputSignature[] inputS inputSignature.BufferIndex = bufferIndex; } - if (inputSignature.Semantic == InputSemantic.Colour) + if (inputSignature.Semantic == DXBCSemantic.Colour) { offset += inputSignature.GetNumberOfComponents() * 1; // 1 byte per component } diff --git a/Tiger/Schema/Activity/Activity.cs b/Tiger/Schema/Activity/Activity.cs index 900be852..39d033e4 100644 --- a/Tiger/Schema/Activity/Activity.cs +++ b/Tiger/Schema/Activity/Activity.cs @@ -337,8 +337,7 @@ private List CollapseResourceParent(FileHash hash) var resourceValue = resource.EntityResourceParent.TagData.EntityResource.TagData.Unk18.GetValue(resource.EntityResourceParent.TagData.EntityResource.GetReader()); switch (resourceValue) { - case D2Class_D8928080: - var tag = (D2Class_D8928080)resourceValue; + case D2Class_D8928080 tag: if (tag.Unk84 is not null) { if (tag.Unk84.TagData.DataEntries.Count > 0) @@ -348,13 +347,24 @@ private List CollapseResourceParent(FileHash hash) } break; - case D2Class_EF8C8080: - var tag2 = (D2Class_EF8C8080)resourceValue; - if (tag2.Unk58 is not null) + case D2Class_EF8C8080 tag: + if (tag.Unk58 is not null) { - if (tag2.Unk58.TagData.DataEntries.Count > 0) + if (tag.Unk58.TagData.DataEntries.Count > 0) { - items.Add(tag2.Unk58.Hash); + items.Add(tag.Unk58.Hash); + } + } + break; + case D2Class_E58C8080 tag: + foreach(var tag2 in tag.Unk68) + { + if(tag2.Unk10.GetValue(resource.EntityResourceParent.TagData.EntityResource.GetReader()) is D2Class_C48C8080 d2class_C48C8080) + { + if(d2class_C48C8080.DataTable is not null && d2class_C48C8080.DataTable.TagData.DataEntries.Count > 0) + { + items.Add(d2class_C48C8080.DataTable.Hash); + } } } break; diff --git a/Tiger/Schema/Activity/ActivityStructsWQ.cs b/Tiger/Schema/Activity/ActivityStructsWQ.cs index b700b7dc..04142f2c 100644 --- a/Tiger/Schema/Activity/ActivityStructsWQ.cs +++ b/Tiger/Schema/Activity/ActivityStructsWQ.cs @@ -31,9 +31,9 @@ public struct SActivity_WQ public TigerHash Unk60; public FileHash Unk64; // an entity thing //[SchemaField(0xA0, TigerStrategy.DESTINY2_BEYONDLIGHT_3402)] - //[SchemaField(0x68, TigerStrategy.DESTINY2_WITCHQUEEN_6307)] - //[SchemaField(0x78, TigerStrategy.DESTINY2_LATEST)] - //public FileHash64 UnkActivity68; // todo this uses an unknown hash64 system in the package + [Tag64, SchemaField(0x68, TigerStrategy.DESTINY2_WITCHQUEEN_6307)] + [Tag64, SchemaField(0x78, TigerStrategy.DESTINY2_LATEST)] + public Tag AmbientActivity; } [SchemaStruct(TigerStrategy.DESTINY2_BEYONDLIGHT_3402, "8B8E8080", 0xD0)] diff --git a/Tiger/Schema/Entity/Entity.cs b/Tiger/Schema/Entity/Entity.cs index cc779c9b..c285feb1 100644 --- a/Tiger/Schema/Entity/Entity.cs +++ b/Tiger/Schema/Entity/Entity.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using Tiger.Exporters; +using Tiger.Schema.Shaders; namespace Tiger.Schema.Entity; @@ -119,7 +120,7 @@ public void SaveMaterialsFromParts(ExporterScene scene, List dy foreach (var dynamicPart in dynamicParts) { if (dynamicPart.Material == null) continue; - scene.Materials.Add(new ExportMaterial(dynamicPart.Material)); + scene.Materials.Add(new ExportMaterial(dynamicPart.Material, MaterialType.Opaque)); } } diff --git a/Tiger/Schema/Entity/EntityModel.cs b/Tiger/Schema/Entity/EntityModel.cs index 8d6f61d1..da19ac30 100644 --- a/Tiger/Schema/Entity/EntityModel.cs +++ b/Tiger/Schema/Entity/EntityModel.cs @@ -1,4 +1,5 @@ -using System.Diagnostics; +using System; +using System.Diagnostics; using Arithmic; using Tiger.Schema.Shaders; @@ -70,11 +71,11 @@ private Dictionary> GetPartsOfDetailLevel } else { - if (eDetailLevel == ExportDetailLevel.MostDetailed && part.LodCategory is ELodCategory.MainGeom0 or ELodCategory.GripStock0 or ELodCategory.Stickers0 or ELodCategory.InternalGeom0 or ELodCategory.Detail0) + if (eDetailLevel == ExportDetailLevel.MostDetailed && part.Lod.IsHighestLevel()) { parts[meshIndex].Add(partIndex++, part); } - else if (eDetailLevel == ExportDetailLevel.LeastDetailed && part.LodCategory == ELodCategory.LowPolyGeom3) + else if (eDetailLevel == ExportDetailLevel.LeastDetailed && !part.Lod.IsHighestLevel()) { parts[meshIndex].Add(partIndex++, part); } @@ -115,8 +116,8 @@ private List GenerateParts(Dictionary GenerateParts(Dictionary TigerStrategy.DESTINY1_RISE_OF_IRON) { if (dynamicMeshPart.Material is null || - dynamicMeshPart.Material.VertexShader is null || - dynamicMeshPart.Material.PixelShader is null || - dynamicMeshPart.Material.Unk08 != 1 || - (dynamicMeshPart.Material.Unk20 & 0x8000) != 0) + dynamicMeshPart.Material.VertexShader is null || + dynamicMeshPart.Material.PixelShader is null || + dynamicMeshPart.Material.Unk08 != 1 || + (dynamicMeshPart.Material.Unk20 & 0x8000) != 0) continue; } else { if (dynamicMeshPart.Material is null || - dynamicMeshPart.Material.VertexShader is null || - dynamicMeshPart.Material.PixelShader is null) // || dynamicMeshPart.Material.Unk08 != 1) + dynamicMeshPart.Material.VertexShader is null || + dynamicMeshPart.Material.PixelShader is null) // || dynamicMeshPart.Material.Unk08 != 1) continue; //if (dynamicMeshPart.Material.Unk08 != 1) // Console.WriteLine($"{dynamicMeshPart.Material.FileHash}"); } - dynamicMeshPart.GetAllData(mesh, _tag); parts.Add(dynamicMeshPart); } - meshIndex++; } - return parts; } - } public class DynamicMeshPart : MeshPart @@ -212,20 +209,20 @@ public void GetAllData(SEntityModelMesh mesh, SEntityModel model) if (Strategy.CurrentStrategy <= TigerStrategy.DESTINY2_SHADOWKEEP_2999 && Strategy.CurrentStrategy != TigerStrategy.DESTINY1_RISE_OF_IRON) { - InputSignature[] inputSignatures = Material.VertexShader.InputSignatures.ToArray(); + DXBCIOSignature[] inputSignatures = Material.VertexShader.InputSignatures.ToArray(); int b0Stride = mesh.Vertices1.TagData.Stride; int b1Stride = mesh.Vertices2?.TagData.Stride ?? 0; - List inputSignatures0 = new(); - List inputSignatures1 = new(); + List inputSignatures0 = new(); + List inputSignatures1 = new(); int stride = 0; - foreach (InputSignature inputSignature in inputSignatures) + foreach (DXBCIOSignature inputSignature in inputSignatures) { if (stride < b0Stride) inputSignatures0.Add(inputSignature); else inputSignatures1.Add(inputSignature); - if (inputSignature.Semantic == InputSemantic.Colour || inputSignature.Semantic == InputSemantic.BlendIndices || inputSignature.Semantic == InputSemantic.BlendWeight) + if (inputSignature.Semantic == DXBCSemantic.Colour || inputSignature.Semantic == DXBCSemantic.BlendIndices || inputSignature.Semantic == DXBCSemantic.BlendWeight) stride += inputSignature.GetNumberOfComponents() * 1; // 1 byte per component else stride += inputSignature.GetNumberOfComponents() * 2; // 2 bytes per component diff --git a/Tiger/Schema/Entity/EntityStructs.cs b/Tiger/Schema/Entity/EntityStructs.cs index 87ddb70e..01615f5c 100644 --- a/Tiger/Schema/Entity/EntityStructs.cs +++ b/Tiger/Schema/Entity/EntityStructs.cs @@ -545,7 +545,7 @@ public struct D2Class_CB6E8080 // TODO use DCG to figure out what this is [SchemaField(0x1A, TigerStrategy.DESTINY2_SHADOWKEEP_2601)] [SchemaField(0x1C, TigerStrategy.DESTINY2_BEYONDLIGHT_3402)] public byte GearDyeChangeColorIndex; // sbyte gear_dye_change_color_index - public ELodCategory LodCategory; + public ELod Lod; public byte Unk1E; public byte LodRun; // lod_run [SchemaField(TigerStrategy.DESTINY2_BEYONDLIGHT_3402)] @@ -1767,6 +1767,36 @@ public struct D2Class_4D898080 public StringPointer Name; } +[SchemaStruct(TigerStrategy.DESTINY2_WITCHQUEEN_6307, "E58C8080", 0xF0)] +public struct D2Class_E58C8080 +{ + [SchemaField(0x68)] + public DynamicArray Unk68; +} + +[SchemaStruct(TigerStrategy.DESTINY2_WITCHQUEEN_6307, "56998080", 0x18)] +public struct D2Class_56998080 +{ + [SchemaField(0x10)] + public ResourcePointer Unk10; // C88C8080, C48C8080 +} + +[SchemaStruct(TigerStrategy.DESTINY2_WITCHQUEEN_6307, "C88C8080", 0x38)] +public struct D2Class_C88C8080 +{ +} + + +/// +/// Datatable for Kill/Turnback Havok volumes? +/// +[SchemaStruct(TigerStrategy.DESTINY2_WITCHQUEEN_6307, "C48C8080", 0x38)] +public struct D2Class_C48C8080 +{ + [SchemaField(0x18)] + public Tag DataTable; +} + [SchemaStruct(TigerStrategy.DESTINY1_RISE_OF_IRON, "E3138080", 0x1190)] [SchemaStruct(TigerStrategy.DESTINY2_BEYONDLIGHT_3402, "357C8080", 0x1BD0)] public struct D2Class_357C8080 diff --git a/Tiger/Schema/Enums.cs b/Tiger/Schema/Enums.cs index 4f87ee0b..84a009c6 100644 --- a/Tiger/Schema/Enums.cs +++ b/Tiger/Schema/Enums.cs @@ -1,4 +1,6 @@ -namespace Tiger.Schema; +using System.Runtime.InteropServices; + +namespace Tiger.Schema; public enum PrimitiveType // name comes from bungie { @@ -18,3 +20,18 @@ public enum ELodCategory : byte LowPolyGeom3 = 9, // low poly geom lod3 Detail0 = 10 // detail lod0 } + +[StructLayout(LayoutKind.Sequential)] +public struct ELod +{ + public ELodCategory DetailLevel; + + public bool IsHighestLevel() + { + return DetailLevel == ELodCategory.MainGeom0 || + DetailLevel == ELodCategory.GripStock0 || + DetailLevel == ELodCategory.Stickers0 || + DetailLevel == ELodCategory.InternalGeom0 || + DetailLevel == ELodCategory.Detail0; + } +} diff --git a/Tiger/Schema/InfoConfigHandler.cs b/Tiger/Schema/InfoConfigHandler.cs index 902b17e0..b4acc94e 100644 --- a/Tiger/Schema/InfoConfigHandler.cs +++ b/Tiger/Schema/InfoConfigHandler.cs @@ -79,15 +79,6 @@ public void SetMeshName(string meshName) _config["MeshName"] = meshName; } - public void SetUnrealInteropPath(string interopPath) - { - _config["UnrealInteropPath"] = new string(interopPath.Split("\\Content").Last().ToArray()).TrimStart('\\'); - if (_config["UnrealInteropPath"] == "") - { - _config["UnrealInteropPath"] = "Content"; - } - } - public void AddInstance(string modelHash, float scale, Vector4 quatRotation, Vector3 translation) { if (!_config["Instances"].ContainsKey(modelHash)) diff --git a/Tiger/Schema/Investment/Dye.cs b/Tiger/Schema/Investment/Dye.cs index d9199b29..7b7cc88d 100644 --- a/Tiger/Schema/Investment/Dye.cs +++ b/Tiger/Schema/Investment/Dye.cs @@ -68,6 +68,7 @@ public static string GetChannelName(TigerHash channelHash) public void ExportTextures(string savePath, TextureExportFormat outputTextureFormat) { + Directory.CreateDirectory(savePath); TextureExtractor.SetTextureFormat(outputTextureFormat); foreach (var entry in _tag.DyeTextures) { diff --git a/Tiger/Schema/Investment/InvestmentHandler.cs b/Tiger/Schema/Investment/InvestmentHandler.cs index f8246855..d836f623 100644 --- a/Tiger/Schema/Investment/InvestmentHandler.cs +++ b/Tiger/Schema/Investment/InvestmentHandler.cs @@ -843,45 +843,50 @@ private class IndexSet public void ExportShader(InventoryItem item, string savePath, string name, TextureExportFormat outputTextureFormat) { - if (Strategy.CurrentStrategy == TigerStrategy.DESTINY1_RISE_OF_IRON) - { - Dictionary dyes = new(); - if (item.TagData.Unk90.GetValue(item.GetReader()) is D2Class_77738080 translationBlock) - { - foreach (var dyeEntry in translationBlock.CustomDyes) - { - DyeD1 dye = GetD1DyeFromIndex(dyeEntry.DyeIndex); - dye.ExportTextures(savePath + "/Textures", outputTextureFormat); - dyes.Add(DyeD1.GetChannelName(GetChannelHashFromIndex(dyeEntry.ChannelIndex)), dye); - } - } - // appliable shaders in D1 only supported armor - AutomatedExporter.SaveD1ShaderInfo(savePath, name, outputTextureFormat, new List { dyes["ArmorPlate"], dyes["ArmorSuit"], dyes["ArmorCloth"] }, "_armor"); // imagine spelling armor with a 'u' (laughs in freedom units) - } - else + Dictionary dyes = new(); + + // export all the customDyes + if (item.TagData.Unk90.GetValue(item.GetReader()) is D2Class_77738080 translationBlock) { - Dictionary dyes = new(); - // export all the customDyes - if (item.TagData.Unk90.GetValue(item.GetReader()) is D2Class_77738080 translationBlock) + foreach (var dyeEntry in translationBlock.CustomDyes) { - foreach (var dyeEntry in translationBlock.CustomDyes) - { - Dye dye = GetDyeFromIndex(dyeEntry.DyeIndex); - dye.ExportTextures(savePath + "/Textures", outputTextureFormat); - dyes.Add(Dye.GetChannelName(GetChannelHashFromIndex(dyeEntry.ChannelIndex)), dye); - } + Dye dye = GetDyeFromIndex(dyeEntry.DyeIndex); + dye.ExportTextures(savePath + "/Textures/DetailTextures/", outputTextureFormat); + dyes.Add(Dye.GetChannelName(GetChannelHashFromIndex(dyeEntry.ChannelIndex)), dye); } - // armor - AutomatedExporter.SaveBlenderApiFile(savePath, name, outputTextureFormat, new List { dyes["ArmorPlate"], dyes["ArmorSuit"], dyes["ArmorCloth"] }, "_armour"); - // ghost - AutomatedExporter.SaveBlenderApiFile(savePath, name, outputTextureFormat, new List { dyes["GhostMain"], dyes["GhostHighlights"], dyes["GhostDecals"] }, "_ghost"); - // ship - AutomatedExporter.SaveBlenderApiFile(savePath, name, outputTextureFormat, new List { dyes["ShipUpper"], dyes["ShipDecals"], dyes["ShipLower"] }, "_ship"); - // sparrow - AutomatedExporter.SaveBlenderApiFile(savePath, name, outputTextureFormat, new List { dyes["SparrowUpper"], dyes["SparrowEngine"], dyes["SparrowLower"] }, "_sparrow"); - // weapon - AutomatedExporter.SaveBlenderApiFile(savePath, name, outputTextureFormat, new List { dyes["Weapon1"], dyes["Weapon2"], dyes["Weapon3"] }, "_weapon"); } + + // armour + AutomatedExporter.SaveBlenderApiFile(savePath, name, outputTextureFormat, new List { dyes["ArmorPlate"], dyes["ArmorSuit"], dyes["ArmorCloth"] }, "_armour"); + + // ghost + AutomatedExporter.SaveBlenderApiFile(savePath, name, outputTextureFormat, new List { dyes["GhostMain"], dyes["GhostHighlights"], dyes["GhostDecals"] }, "_ghost"); + + // ship + AutomatedExporter.SaveBlenderApiFile(savePath, name, outputTextureFormat, new List { dyes["ShipUpper"], dyes["ShipDecals"], dyes["ShipLower"] }, "_ship"); + + // sparrow + AutomatedExporter.SaveBlenderApiFile(savePath, name, outputTextureFormat, new List { dyes["SparrowUpper"], dyes["SparrowEngine"], dyes["SparrowLower"] }, "_sparrow"); + + // weapon + AutomatedExporter.SaveBlenderApiFile(savePath, name, outputTextureFormat, new List { dyes["Weapon1"], dyes["Weapon2"], dyes["Weapon3"] }, "_weapon"); + + // S&Box // + + // armour + SBoxHandler.SaveGearVMAT(savePath, name, outputTextureFormat, new List { dyes["ArmorPlate"], dyes["ArmorSuit"], dyes["ArmorCloth"] }, "_armour"); + + // ghost + SBoxHandler.SaveGearVMAT(savePath, name, outputTextureFormat, new List { dyes["GhostMain"], dyes["GhostHighlights"], dyes["GhostDecals"] }, "_ghost"); + + // ship + SBoxHandler.SaveGearVMAT(savePath, name, outputTextureFormat, new List { dyes["ShipUpper"], dyes["ShipDecals"], dyes["ShipLower"] }, "_ship"); + + // sparrow + SBoxHandler.SaveGearVMAT(savePath, name, outputTextureFormat, new List { dyes["SparrowUpper"], dyes["SparrowEngine"], dyes["SparrowLower"] }, "_sparrow"); + + // weapon + SBoxHandler.SaveGearVMAT(savePath, name, outputTextureFormat, new List { dyes["Weapon1"], dyes["Weapon2"], dyes["Weapon3"] }, "_weapon"); } } diff --git a/Tiger/Schema/Model/Havok/HavokMesh.cs b/Tiger/Schema/Model/Havok/HavokMesh.cs new file mode 100644 index 00000000..68aa55bc --- /dev/null +++ b/Tiger/Schema/Model/Havok/HavokMesh.cs @@ -0,0 +1,130 @@ +using System.Runtime.InteropServices; +using Arithmic; +using System.Text; +using System.Numerics; + +namespace Tiger.Schema.Havok; + +public unsafe class DestinyHavok +{ + [StructLayout(LayoutKind.Sequential)] + public struct HavokVector3 + { + public float X, Y, Z; + } + + [StructLayout(LayoutKind.Sequential)] + private struct CArray where T : struct + { + public T* Data; + public ulong Length; + } + + [StructLayout(LayoutKind.Sequential)] + private struct CShape + { + public CArray Vertices; + public CArray Indices; + } + + [DllImport("ThirdParty/Alkahest/destiny_havok.dll", EntryPoint = "destinyhavok_read_shape_collection")] + private static extern CArray* ReadShapeCollection(IntPtr data, ulong length); + + [DllImport("ThirdParty/Alkahest/destiny_havok.dll", EntryPoint = "destinyhavok_free_shape_collection")] + private static extern void FreeShapeCollection(CArray* p); + + public struct HavokShape + { + public HavokVector3[] Vertices; + public ushort[] Indices; + } + + public static HavokShape[] ReadShapeCollection(byte[] data) + { + var bufferPtr = Marshal.AllocCoTaskMem(data.Length); + Marshal.Copy(data, 0, bufferPtr, data.Length); + + var shapeCollectionPtr = ReadShapeCollection(bufferPtr, (ulong)data.Length); + if (shapeCollectionPtr == null) + return null; //throw new Exception("Failed to read shape collection, destiny-havok returned null."); + + var shapeCollection = *shapeCollectionPtr; + + var shapes = new HavokShape[shapeCollection.Length]; + for (ulong i = 0; i < shapeCollection.Length; i++) + { + var shape = shapeCollection.Data[i]; + var vertices = new HavokVector3[shape.Vertices.Length]; + var indices = new ushort[shape.Indices.Length]; + + for (ulong j = 0; j < shape.Vertices.Length; j++) + { + vertices[j] = shape.Vertices.Data[j]; + } + + for (ulong j = 0; j < shape.Indices.Length; j++) + { + indices[j] = shape.Indices.Data[j]; + } + + shapes[i] = new HavokShape + { + Vertices = vertices, + Indices = indices + }; + } + + FreeShapeCollection(shapeCollectionPtr); + return shapes; + } + + public static void SaveHavokShape(FileHash hash, string name, Vector4 transforms, Vector4 quat) + { + var shapeCollection = DestinyHavok.ReadShapeCollection(FileResourcer.Get().GetFile(hash).GetData()); + if (shapeCollection is null) + { + Log.Error("Havok shape collection is null"); + return; + } + + Directory.CreateDirectory($"{ConfigSubsystem.Get().GetExportSavePath()}/HavokShapes"); + int i = 0; + foreach (var shape in shapeCollection) + { + var vertices = shape.Vertices; + var indices = shape.Indices; + + var sb = new StringBuilder(); + foreach (var vertex in vertices) + { + System.Numerics.Vector3 rotatedVertex = RotateVertex(vertex, new Quaternion(quat.X, quat.Y, quat.Z, quat.W)); + sb.AppendLine($"v {(rotatedVertex.X + transforms.X) * transforms.W} {(rotatedVertex.Y + transforms.Y) * transforms.W} {(rotatedVertex.Z + transforms.Z) * transforms.W}"); + } + foreach (var index in indices.Chunk(3)) + { + sb.AppendLine($"f {index[0] + 1} {index[1] + 1} {index[2] + 1}"); + } + + Console.WriteLine($"Writing 'HavokShapes/{hash}_{i}.obj'"); + File.WriteAllText($"{ConfigSubsystem.Get().GetExportSavePath()}/HavokShapes/{name}_{hash}_{i++}.obj", sb.ToString()); + } + } + + private static System.Numerics.Vector3 RotateVertex(HavokVector3 vertex, Quaternion rotationQuaternion) + { + // Ensure the quaternion is normalized + rotationQuaternion = Quaternion.Normalize(rotationQuaternion); + + // Convert the vertex to a quaternion + Quaternion vertexQuaternion = new Quaternion(vertex.X, vertex.Y, vertex.Z, 0.0f); + + // Rotate the vertex + Quaternion rotatedVertexQuaternion = Quaternion.Multiply(rotationQuaternion, vertexQuaternion); + rotatedVertexQuaternion = Quaternion.Multiply(rotatedVertexQuaternion, Quaternion.Conjugate(rotationQuaternion)); + + // Extract the rotated vertex + System.Numerics.Vector3 rotatedVertex = new System.Numerics.Vector3(rotatedVertexQuaternion.X, rotatedVertexQuaternion.Y, rotatedVertexQuaternion.Z); + + return rotatedVertex; + } +} diff --git a/Tiger/Schema/Model/VertexBuffer.cs b/Tiger/Schema/Model/VertexBuffer.cs index 70d0c6a0..fb109ef7 100644 --- a/Tiger/Schema/Model/VertexBuffer.cs +++ b/Tiger/Schema/Model/VertexBuffer.cs @@ -210,7 +210,7 @@ private void ReadVertexData(TigerReader handle, MeshPart part, uint vertexIndex, switch (_tag.Type) { case 0: - status = ReadVertexDataType0(handle, part); + status = ReadVertexDataType0(handle, part, isTerrain); break; case 1: status = ReadVertexDataType1(handle, part); @@ -233,28 +233,25 @@ private void ReadVertexData(TigerReader handle, MeshPart part, uint vertexIndex, } } - private bool ReadVertexDataType0(TigerReader handle, MeshPart part) + private bool ReadVertexDataType0(TigerReader handle, MeshPart part, bool isTerrain) { switch (_tag.Stride) { case 0x4: part.VertexTexcoords0.Add(new Vector2(handle.ReadInt16(), handle.ReadInt16())); break; - case 0x8: // all terrain-specific - var v = new Vector4(handle.ReadUInt16(), handle.ReadUInt16(), handle.ReadInt16(), handle.ReadUInt16(), - true); - if (v.W > 0 && Math.Abs(v.W - 0x7F_FF) > 0.1f) - { - v.Z += 2 * v.W; // terrain uses a z precision extension. - } - - part.VertexPositions.Add(v); - + case 0x8: // terrain-specific + part.VertexPositions.Add(new Vector4((float)handle.ReadInt16(), (float)handle.ReadInt16(), (float)handle.ReadInt16(), + (float)handle.ReadInt16())); break; case 0xC: part.VertexNormals.Add(new Vector4(handle.ReadInt16(), handle.ReadInt16(), handle.ReadInt16(), handle.ReadInt16(), true)); - part.VertexTexcoords0.Add(new Vector2(handle.ReadInt16(), handle.ReadInt16())); + + if (isTerrain) + part.VertexTexcoords0.Add(new Vector2(handle.ReadHalf(), handle.ReadHalf())); + else + part.VertexTexcoords0.Add(new Vector2(handle.ReadInt16(), handle.ReadInt16())); break; case 0x10: part.VertexPositions.Add(new Vector4(handle.ReadInt16(), handle.ReadInt16(), handle.ReadInt16(), @@ -271,6 +268,15 @@ private bool ReadVertexDataType0(TigerReader handle, MeshPart part) part.VertexTangents.Add(new Vector4(handle.ReadInt16(), handle.ReadInt16(), handle.ReadInt16(), handle.ReadInt16(), true)); break; + case 0x1C: + part.VertexPositions.Add(new Vector4(handle.ReadSingle(), handle.ReadSingle(), handle.ReadSingle(), 1.0f)); + + var texcoord = new Vector4(handle.ReadInt16(), handle.ReadInt16(), handle.ReadInt16(), + handle.ReadInt16(), true); + var normal = new Vector4(handle.ReadInt16(), handle.ReadInt16(), handle.ReadInt16(), + handle.ReadInt16(), true); + //var texcoord1 = new Vector2(handle.ReadInt16(), handle.ReadInt16()); + break; default: return false; } @@ -471,7 +477,7 @@ private bool ReadVertexDataType6(TigerReader handle, DynamicMeshPart dynamicPart } public void ReadVertexDataSignatures(MeshPart part, HashSet uniqueVertexIndices, - List inputSignatures, bool isTerrain = false) + List inputSignatures, bool isTerrain = false) { using var reader = GetReferenceReader(); foreach (uint vertexIndex in uniqueVertexIndices) @@ -481,7 +487,7 @@ public void ReadVertexDataSignatures(MeshPart part, HashSet uniqueVertexIn } private void ReadVertexDataSignature(TigerReader reader, MeshPart part, uint vertexIndex, - List inputSignatures, bool isTerrain = false) + List inputSignatures, bool isTerrain = false) { reader.Seek(vertexIndex * _tag.Stride, SeekOrigin.Begin); @@ -489,11 +495,14 @@ private void ReadVertexDataSignature(TigerReader reader, MeshPart part, uint ver IntVector4 WeightValue = new(); IntVector4 WeightIndex = new(); - foreach (InputSignature inputSignature in inputSignatures) + foreach (DXBCIOSignature inputSignature in inputSignatures) { + if (inputSignature.ToString().Contains("SV_")) + continue; + switch (inputSignature.Semantic) { - case InputSemantic.Position: + case DXBCSemantic.Position: if (isTerrain) //has to be a float { part.VertexPositions.Add(new Vector4((float)reader.ReadInt16(), (float)reader.ReadInt16(), (float)reader.ReadInt16(), @@ -526,7 +535,7 @@ private void ReadVertexDataSignature(TigerReader reader, MeshPart part, uint ver } break; - case InputSemantic.Texcoord: + case DXBCSemantic.Texcoord: switch (inputSignature.Mask) { case ComponentMask.XY: @@ -545,7 +554,7 @@ private void ReadVertexDataSignature(TigerReader reader, MeshPart part, uint ver } break; - case InputSemantic.Normal: + case DXBCSemantic.Normal: switch (inputSignature.Mask) { // euler @@ -563,7 +572,7 @@ private void ReadVertexDataSignature(TigerReader reader, MeshPart part, uint ver } break; - case InputSemantic.Tangent: + case DXBCSemantic.Tangent: switch (inputSignature.Mask) { // euler @@ -581,20 +590,23 @@ private void ReadVertexDataSignature(TigerReader reader, MeshPart part, uint ver } break; - case InputSemantic.Colour: + case DXBCSemantic.Colour: part.VertexColours.Add(new Vector4(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte())); break; - case InputSemantic.BlendIndices: - HasWeights = true; - WeightIndex = new IntVector4(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); + case DXBCSemantic.BlendIndices: + //Indices get set in BlendWeight break; - case InputSemantic.BlendWeight: - HasWeights = true; - WeightValue = new IntVector4(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); + case DXBCSemantic.BlendWeight: + //VertexWeight vw = new() + //{ + // WeightIndices = new IntVector4(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()), + // WeightValues = new IntVector4(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()), + //}; + //(part as DynamicMeshPart).VertexWeights.Add(vw); break; default: - throw new NotImplementedException(); + throw new NotImplementedException($"Not Implemented {inputSignature.Semantic}"); } } diff --git a/Tiger/Schema/Shaders/Material.cs b/Tiger/Schema/Shaders/Material.cs index 5e2166ec..30098799 100644 --- a/Tiger/Schema/Shaders/Material.cs +++ b/Tiger/Schema/Shaders/Material.cs @@ -3,44 +3,10 @@ namespace Tiger.Schema.Shaders { - - public struct TextureView - { - public string Dimension; - public string Type; - public string Variable; - public int Index; - } - - public struct Buffer - { - public string Variable; - public string Type; - public int Index; - } - - public struct Cbuffer - { - public string Variable; - public string Type; - public int Count; - public int Index; - } - - public struct Input - { - public string Variable; - public string Type; - public int Index; - public string Semantic; - } - - public struct Output + public enum MaterialType { - public string Variable; - public string Type; - public int Index; - public string Semantic; + Opaque, + Transparent } public interface IMaterial : ISchema @@ -50,30 +16,22 @@ public interface IMaterial : ISchema public uint Unk10 { get; } public uint Unk0C { get; } //Seems to be backface culling public ushort Unk20 { get; } - - // Vertex + public IEnumerable EnumerateVSTextures(); + public IEnumerable EnumeratePSTextures(); + public IEnumerable EnumerateCSTextures(); public ShaderBytecode? VertexShader { get; } + public ShaderBytecode? PixelShader { get; } + public ShaderBytecode? ComputeShader { get; } + public FileHash PSVector4Container { get; } public FileHash VSVector4Container { get; } + public List PS_Samplers { get; } public List VS_Samplers { get; } public DynamicArray VS_TFX_Bytecode { get; } public DynamicArray VS_TFX_Bytecode_Constants { get; } public DynamicArray VS_CBuffers { get; } - public IEnumerable EnumerateVSTextures(); - - // Pixel - public ShaderBytecode? PixelShader { get; } - public FileHash PSVector4Container { get; } - public List PS_Samplers { get; } public DynamicArray PS_TFX_Bytecode { get; } public DynamicArray PS_TFX_Bytecode_Constants { get; } public DynamicArray PS_CBuffers { get; } - public IEnumerable EnumeratePSTextures(); - - // Compute - public IEnumerable EnumerateCSTextures(); - public ShaderBytecode? ComputeShader { get; } - - public static object _lock = new object(); private static ConfigSubsystem _config = CharmInstance.GetSubsystem(); @@ -133,71 +91,54 @@ public string Decompile(byte[] shaderBytecode, string name, string savePath = "h return hlsl; } - public void SavePixelShader(string saveDirectory, bool isTerrain = false) + public void SaveShaders(string saveDirectory, MaterialType type, bool isTerrain = false) { - if (Strategy.CurrentStrategy == TigerStrategy.DESTINY1_RISE_OF_IRON) - return; - + Directory.CreateDirectory($"{saveDirectory}/Shaders"); if (PixelShader != null && PixelShader.Hash.IsValid()) { string pixel = Decompile(PixelShader.GetBytecode(), $"ps{PixelShader.Hash}"); string vertex = Decompile(VertexShader.GetBytecode(), $"vs{VertexShader.Hash}"); - string usf = _config.GetUnrealInteropEnabled() ? new UsfConverter().HlslToUsf(this, pixel, false) : ""; - string vfx = Source2Handler.source2Shaders ? new S2ShaderConverter().HlslToVfx(this, pixel, vertex, isTerrain) : ""; - - Directory.CreateDirectory($"{saveDirectory}/Unreal"); - if (Source2Handler.source2Shaders) - { - Directory.CreateDirectory($"{saveDirectory}/Source2"); - Directory.CreateDirectory($"{saveDirectory}/Source2/materials"); - } + string vfx = new S2ShaderConverter().HlslToVfx(this, pixel, vertex, type, isTerrain); try { - if (usf != String.Empty && !File.Exists($"{saveDirectory}/Unreal/PS_{FileHash}.usf")) - { - File.WriteAllText($"{saveDirectory}/Unreal/PS_{FileHash}.usf", usf); - } - if (vfx != String.Empty && !File.Exists($"{saveDirectory}/Source2/PS_{PixelShader.Hash}.shader")) + if (vfx != String.Empty) { - File.WriteAllText($"{saveDirectory}/Source2/PS_{PixelShader.Hash}.shader", vfx); + File.WriteAllText($"{saveDirectory}/Shaders/PS_{PixelShader.Hash}.shader", vfx); + if (!isTerrain) + SBoxHandler.SaveVMAT(saveDirectory, FileHash, this); } } catch (IOException) // threading error { } - - //Need to save vmat after shader has be exported, to check if it exists - if (Source2Handler.source2Shaders) - Source2Handler.SaveVMAT(saveDirectory, FileHash, this, isTerrain); } } public void SaveVertexShader(string saveDirectory) { - Directory.CreateDirectory($"{saveDirectory}"); - if (VertexShader != null && VertexShader.Hash.IsValid()) - { - string hlsl = Decompile(VertexShader.GetBytecode(), $"vs{VertexShader.Hash}"); - string usf = _config.GetUnrealInteropEnabled() ? new UsfConverter().HlslToUsf(this, hlsl, true) : ""; - if (usf != String.Empty) - { - try - { - File.WriteAllText($"{saveDirectory}/VS_{FileHash}.usf", usf); - Console.WriteLine($"Saved vertex shader {FileHash}"); - } - catch (IOException) // threading error - { - } - } - } + //Directory.CreateDirectory($"{saveDirectory}"); + //if (VertexShader != null && VertexShader.Hash.IsValid()) + //{ + // string hlsl = Decompile(VertexShader.GetBytecode(), $"vs{VertexShader.Hash}"); + // if (usf != String.Empty) + // { + // try + // { + // File.WriteAllText($"{saveDirectory}/VS_{FileHash}.usf", usf); + // Console.WriteLine($"Saved vertex shader {FileHash}"); + // } + // catch (IOException) // threading error + // { + // } + // } + //} } //Only useful for saving single material from DevView or MaterialView, better control for output compared to scene system public void SaveMaterial(string saveDirectory) { - var hlslPath = $"{saveDirectory}/Shaders/Raw"; + var hlslPath = $"{saveDirectory}/Raw_Shaders"; var texturePath = $"{saveDirectory}/Textures"; Directory.CreateDirectory(hlslPath); Directory.CreateDirectory(texturePath); @@ -205,12 +146,12 @@ public void SaveMaterial(string saveDirectory) if (PixelShader != null) { Decompile(PixelShader.GetBytecode(), $"ps{PixelShader.Hash}", hlslPath); - SavePixelShader($"{saveDirectory}/Shaders/"); + SaveShaders($"{saveDirectory}", MaterialType.Opaque); } if (VertexShader != null) { Decompile(VertexShader.GetBytecode(), $"vs{VertexShader.Hash}", hlslPath); - SaveVertexShader($"{saveDirectory}/Shaders/"); + SaveVertexShader($"{saveDirectory}"); } foreach (STextureTag texture in EnumerateVSTextures()) @@ -226,6 +167,10 @@ public void SaveMaterial(string saveDirectory) continue; texture.Texture.SavetoFile($"{saveDirectory}/Textures/{texture.Texture.Hash}"); + if (texture.Texture.IsCubemap()) + { + SBoxHandler.SaveVTEX(texture.Texture, $"{saveDirectory}/Textures"); + } } } @@ -255,57 +200,6 @@ public List GetVec4Container(FileHash containerHash) // public List VSConstantBuffers; // } -namespace Tiger.Schema.Shaders.DESTINY1_RISE_OF_IRON -{ - public class Material : Tag, IMaterial - { - public FileHash FileHash => Hash; - public uint Unk08 => _tag.Unk08; - public uint Unk10 => _tag.Unk10; - public uint Unk0C => _tag.Unk0C; - public ushort Unk20 => _tag.Unk20; - // Leaving shaders null until they (if ever) can be decompiled to hlsl - public ShaderBytecode VertexShader => _tag.VertexShader; // null; - public ShaderBytecode PixelShader => _tag.PixelShader; // null; - public ShaderBytecode ComputeShader => null; - public FileHash PSVector4Container => _tag.PSVector4Container; - public FileHash VSVector4Container => _tag.VSVector4Container; - public DynamicArray VS_TFX_Bytecode => _tag.VS_TFX_Bytecode; - public DynamicArray VS_TFX_Bytecode_Constants => _tag.VS_TFX_Bytecode_Constants; - public DynamicArray VS_CBuffers => _tag.VS_CBuffers; - public DynamicArray PS_TFX_Bytecode => _tag.PS_TFX_Bytecode; - public DynamicArray PS_TFX_Bytecode_Constants => _tag.PS_TFX_Bytecode_Constants; - public DynamicArray PS_CBuffers => _tag.PS_CBuffers; - public List VS_Samplers => _tag.VS_Samplers.Select(x => x.Samplers).ToList(); - public List PS_Samplers => _tag.PS_Samplers.Select(x => x.Samplers).ToList(); - - public IEnumerable EnumerateVSTextures() - { - foreach (STextureTag texture in _tag.VSTextures) - { - yield return texture; - } - } - - public IEnumerable EnumeratePSTextures() - { - foreach (STextureTag texture in _tag.PSTextures) - { - yield return texture; - } - } - - public IEnumerable EnumerateCSTextures() - { - return null; - } - - public Material(FileHash fileHash) : base(fileHash) - { - } - } -} - namespace Tiger.Schema.Shaders.DESTINY2_SHADOWKEEP_2601 { public class Material : Tag, IMaterial @@ -314,7 +208,7 @@ public class Material : Tag, IMaterial public uint Unk08 => _tag.Unk08; public uint Unk10 => _tag.Unk10; public uint Unk0C => _tag.Unk0C; - public ushort Unk20 => _tag.Unk18; + public ushort Unk20 => _tag.Unk20; public ShaderBytecode VertexShader => _tag.VertexShader; public ShaderBytecode PixelShader => _tag.PixelShader; public ShaderBytecode ComputeShader => _tag.ComputeShader; @@ -463,34 +357,26 @@ public IEnumerable EnumerateCSTextures() public Material(FileHash fileHash) : base(fileHash) { } - } -} -//TODO: Move this -public enum TfxRenderStage -{ - GenerateGbuffer = 0, - Decals = 1, - InvestmentDecals = 2, - ShadowGenerate = 3, - LightingApply = 4, - LightProbeApply = 5, - DecalsAdditive = 6, - Transparents = 7, - Distortion = 8, - LightShaftOcclusion = 9, - SkinPrepass = 10, - LensFlares = 11, - DepthPrepass = 12, - WaterReflection = 13, - PostprocessTransparentStencil = 14, - Impulse = 15, - Reticle = 16, - WaterRipples = 17, - MaskSunLight = 18, - Volumetrics = 19, - Cubemaps = 20, - PostprocessScreen = 21, - WorldForces = 22, - ComputeSkinning = 23, + // public void SaveComputeShader(string saveDirectory) + // { + // Directory.CreateDirectory($"{saveDirectory}"); + // if (_tag.ComputeShader != null && !File.Exists($"{saveDirectory}/CS_{Hash}.usf")) + // { + // string hlsl = Decompile(_tag.ComputeShader.GetBytecode(), "cs"); + // string usf = new UsfConverter().HlslToUsf(this, hlsl, false); + // if (usf != String.Empty) + // { + // try + // { + // File.WriteAllText($"{saveDirectory}/CS_{Hash}.usf", usf); + // Console.WriteLine($"Saved compute shader {Hash}"); + // } + // catch (IOException) // threading error + // { + // } + // } + // } + // } + } } diff --git a/Tiger/Schema/Shaders/MaterialStructs.cs b/Tiger/Schema/Shaders/MaterialStructs.cs index 8090b7a1..04a83831 100644 --- a/Tiger/Schema/Shaders/MaterialStructs.cs +++ b/Tiger/Schema/Shaders/MaterialStructs.cs @@ -61,8 +61,8 @@ public struct SMaterial_SK public uint Unk08; public uint Unk0C; public uint Unk10; - [SchemaField(0x18)] - public ushort Unk18; // ?? + [SchemaField(0x20)] + public ushort Unk20; // ?? [SchemaField(0x48)] public ShaderBytecode VertexShader; [SchemaField(0x50)] diff --git a/Tiger/Schema/Shaders/S2ShaderConverter.cs b/Tiger/Schema/Shaders/S2ShaderConverter.cs index d003ca93..b864ccca 100644 --- a/Tiger/Schema/Shaders/S2ShaderConverter.cs +++ b/Tiger/Schema/Shaders/S2ShaderConverter.cs @@ -1,11 +1,5 @@ -using System; -using System.Diagnostics; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; +using System.Text; using System.Text.RegularExpressions; -using Tiger; -using Tiger.Schema; using Tiger.Schema.Shaders; namespace Tiger.Schema; @@ -14,42 +8,37 @@ public class S2ShaderConverter { private StringReader hlsl; private StringBuilder vfx; - private readonly List textures = new List(); - private readonly List samplers = new List(); - private readonly List cbuffers = new List(); - private readonly List buffers = new List(); - private readonly List inputs = new List(); - private readonly List outputs = new List(); - private static bool isTerrain = false; - private bool bOpacityEnabled = false; - private bool bTranslucent = true; - private bool bUsesFrontFace = false; + private List inputs = new(); + private List outputs = new(); + private List resources = new(); + + private bool isTerrain = false; + private bool bRT0 = true; + private bool bTranslucent = false; private bool bFixRoughness = false; + private bool bUsesNormalBuffer = false; + private bool bUsesFrameBuffer = false; + private bool bUsesDepthBuffer = false; public string vfxStructure = $@"HEADER {{ - Description = ""Charm Auto-Generated Source 2 Shader""; + Description = ""Charm Auto-Generated S&Box Shader""; }} MODES {{ VrForward(); - - Depth(); - + Depth(); ToolsVis( S_MODE_TOOLS_VIS ); ToolsWireframe( ""vr_tools_wireframe.shader"" ); ToolsShadingComplexity( ""tools_shading_complexity.shader"" ); - - //Reflection( S_MODE_REFLECTIONS ); }} FEATURES {{ #include ""common/features.hlsl"" - //Feature( F_DYNAMIC_REFLECTIONS, 0..1, ""Rendering"" ); }} COMMON @@ -72,7 +61,14 @@ struct VertexInput struct PixelInput {{ float4 vBlendValues : TEXCOORD14; - float3 v5 : TEXCOORD15; //terrain specific + float3 v3 : TEXCOORD15; //terrain specific + float3 v4 : TEXCOORD16; //terrain specific + float3 v5 : TEXCOORD17; //terrain specific + + float3 vPositionOs : TEXCOORD18; + float3 vNormalOs : TEXCOORD19; + float4 vTangentUOs_flTangentVSign : TANGENT < Semantic( TangentU_SignV ); >; + #include ""common/pixelinput.hlsl"" }}; @@ -92,9 +88,11 @@ PixelInput MainVs( VertexInput i ) float4 r0,r1,r2; o.vBlendValues = i.vColorBlendValues; o.vBlendValues.a = i.vColorBlendValues.a; + o.vPositionOs = i.vPositionOs.xyz; + VS_DecodeObjectSpaceNormalAndTangent( i, o.vNormalOs, o.vTangentUOs_flTangentVSign ); //vs_Function - {(isTerrain ? " r1.xyz = abs(i.vNormalOs.xyz) * abs(i.vNormalOs.xyz);\r\n r1.xyz = r1.xyz * r1.xyz;\r\n r2.xyz = r1.xyz * r1.xyz;\r\n r2.xyz = r2.xyz * r2.xyz;\r\n r1.xyz = r2.xyz * r1.xyz;\r\n r0.z = dot(r1.xyz, float3(1,1,1));\r\n o.v5.xyz = r1.xyz / r0.zzz;" : "")} + return FinalizeVertex( o ); }} }} @@ -107,7 +105,7 @@ PixelInput MainVs( VertexInput i ) #define cmp - //RenderState - // #if ( S_MODE_REFLECTIONS ) + //#if ( S_MODE_REFLECTIONS ) // #define FinalOutput ReflectionOutput //#else // #define FinalOutput float4 @@ -125,20 +123,22 @@ float4 MainPs( PixelInput i ) : SV_Target0 }} }}"; - public string HlslToVfx(IMaterial material, string pixel, string vertex, bool bIsTerrain = false) + public string HlslToVfx(IMaterial material, string pixel, string vertex, MaterialType type, bool bIsTerrain = false) { //Pixel Shader StringBuilder texSamples = new StringBuilder(); hlsl = new StringReader(pixel); vfx = new StringBuilder(); isTerrain = bIsTerrain; + inputs = material.PixelShader.InputSignatures; + outputs = material.PixelShader.OutputSignatures; + resources = material.PixelShader.Resources; + bTranslucent = type == MaterialType.Transparent || outputs.Count == 1; - ProcessHlslData(); + //ProcessHlslData(); - if (bUsesFrontFace) - { + if (inputs.Any(input => input.Semantic == DXBCSemantic.SystemIsFrontFace)) vfxStructure = vfxStructure.Replace("//frontface", "#define S_RENDER_BACKFACES 1"); - } for (int i = 0; i < material.PS_Samplers.Count; i++) { @@ -146,7 +146,7 @@ public string HlslToVfx(IMaterial material, string pixel, string vertex, bool bI continue; var sampler = material.PS_Samplers[i].Sampler; - texSamples.AppendLine($"\tSamplerState g_s{i + 1} < Filter({sampler.Filter}); AddressU({sampler.AddressU}); AddressV({sampler.AddressV}); AddressW({sampler.AddressW}); ComparisonFunc({sampler.ComparisonFunc}); MaxAniso({sampler.MaxAnisotropy}); >;"); + texSamples.AppendLine($"\tSamplerState s_s{i + 1} < Filter({sampler.Filter}); AddressU({sampler.AddressU}); AddressV({sampler.AddressV}); AddressW({sampler.AddressW}); ComparisonFunc({sampler.ComparisonFunc}); MaxAniso({sampler.MaxAnisotropy}); >;"); } vfxStructure = vfxStructure.Replace("//ps_samplers", texSamples.ToString()); @@ -155,21 +155,19 @@ public string HlslToVfx(IMaterial material, string pixel, string vertex, bool bI hlsl = new StringReader(pixel); StringBuilder instructions = ConvertInstructions(material, false); if (instructions.ToString().Length == 0) - { return ""; - } + vfxStructure = vfxStructure.Replace("//ps_Function", instructions.ToString()); vfxStructure = vfxStructure.Replace("//ps_Inputs", WriteFunctionDefinition(material, false).ToString()); - if (bOpacityEnabled || bTranslucent) //This way is stupid but it works - { - vfxStructure = vfxStructure.Replace("//alpha", $"#ifndef S_ALPHA_TEST\r\n\t#define S_ALPHA_TEST {((bUsesNormalBuffer || bTranslucent) ? "0" : "1")}\r\n\t#endif\r\n\t#ifndef S_TRANSLUCENT\r\n\t#define S_TRANSLUCENT {((bUsesNormalBuffer || bTranslucent) ? "1" : "0")}\r\n\t#endif"); - if (bTranslucent) - vfxStructure = vfxStructure.Replace("FinalOutput MainPs", "float4 MainPs"); - } + if (bTranslucent) //This way is stupid but it works + vfxStructure = vfxStructure.Replace("//alpha", $"#ifndef S_ALPHA_TEST\r\n\t#define S_ALPHA_TEST 0\r\n\t#endif\r\n\t#ifndef S_TRANSLUCENT\r\n\t#define S_TRANSLUCENT 1\r\n\t#endif"); vfxStructure = vfxStructure.Replace("//ps_output", AddOutput(material).ToString()); + if (isTerrain) + vfxStructure = vfxStructure.Replace("//vs_Function", "// Terrain specific\r\n\t\tr1.xyz = float3(0,1,0) * i.vNormalOs.yzx;\r\n\t\tr1.xyz = i.vNormalOs.zxy * float3(0,0,1) + -r1.xyz;\r\n\t\tr0.z = dot(r1.yz, r1.yz);\r\n\t\tr0.z = rsqrt(r0.z);\r\n\t\tr1.xyz = r1.xyz * r0.zzz;\r\n\t\tr2.xyz = i.vNormalOs.zxy * r1.yzx;\r\n\t\tr2.xyz = i.vNormalOs.yzx * r1.zxy + -r2.xyz;\r\n\t\to.v4.xyz = r1.xyz;\r\n\t\tr0.z = dot(r2.xyz, r2.xyz);\r\n\t\tr0.z = rsqrt(r0.z);\r\n\t\to.v3.xyz = r2.xyz * r0.zzz;\r\n\t\tr1.xyz = abs(i.vNormalOs.xyz) * abs(i.vNormalOs.xyz);\r\n\t\tr1.xyz = r1.xyz * r1.xyz;\r\n\t\tr2.xyz = r1.xyz * r1.xyz;\r\n\t\tr2.xyz = r2.xyz * r2.xyz;\r\n\t\tr1.xyz = r2.xyz * r1.xyz;\r\n\t\tr0.z = dot(r1.xyz, float3(1,1,1));\r\n\t\to.v5.xyz = r1.xyz / r0.zzz;"); + //------------------------------------------------------------------------------ //Vertex Shader - Commented out for now @@ -201,121 +199,124 @@ public string HlslToVfx(IMaterial material, string pixel, string vertex, bool bI return vfx.ToString(); } - private void ProcessHlslData() - { - string line = string.Empty; - cbuffers.Clear(); - textures.Clear(); - inputs.Clear(); - bool bFindOpacity = false; - do - { - line = hlsl.ReadLine(); - if (line != null) - { - if (line.Contains("r0,r1")) // at end of function definition - { - bFindOpacity = true; - } - - if (bFindOpacity) - { - if (line.Contains("discard") || line.Contains("o0.w = r")) - { - bOpacityEnabled = true; - break; - } - continue; - } - - if (line.Contains("Texture")) - { - TextureView texture = new TextureView(); - texture.Dimension = line.Split("<")[0]; - texture.Type = line.Split("<")[1].Split(">")[0]; - texture.Variable = line.Split("> ")[1].Split(" :")[0]; - texture.Index = Int32.TryParse(new string(texture.Variable.Skip(1).ToArray()), out int index) ? index : -1; - textures.Add(texture); - } - else if (line.Contains("SamplerState")) - { - samplers.Add(line.Split("(")[1].Split(")")[0].Last() - 48); - } - else if (line.Contains("Buffer")) - { - Shaders.Buffer buffer = new Shaders.Buffer(); - buffer.Type = line.Split('<', '>')[1]; - buffer.Variable = line.Split(' ')[1]; - buffer.Index = Int32.TryParse(new string(buffer.Variable.Skip(1).ToArray()), out int index) ? index : -1; - buffers.Add(buffer); - } - else if (line.Contains("cbuffer")) - { - hlsl.ReadLine(); - line = hlsl.ReadLine(); - Cbuffer cbuffer = new Cbuffer(); - cbuffer.Variable = "cb" + line.Split("cb")[1].Split("[")[0]; - cbuffer.Index = Int32.TryParse(new string(cbuffer.Variable.Skip(2).ToArray()), out int index) ? index : -1; - cbuffer.Count = Int32.TryParse(new string(line.Split("[")[1].Split("]")[0]), out int count) ? count : -1; - cbuffer.Type = line.Split("cb")[0].Trim(); - cbuffers.Add(cbuffer); - } - else if (line.Contains(" v") && line.Contains(" : ") && !line.Contains("?")) - { - Input input = new Input(); - input.Variable = "v" + line.Split("v")[1].Split(" : ")[0]; - input.Index = Int32.TryParse(new string(input.Variable.Skip(1).ToArray()), out int index) ? index : -1; - input.Semantic = line.Split(" : ")[1].Split(",")[0]; - if (input.Semantic == "SV_isFrontFace0") - bUsesFrontFace = true; - input.Type = line.Split(" v")[0].Trim(); - inputs.Add(input); - } - else if (line.Contains("out") && line.Contains(" : ")) - { - Output output = new Output(); - output.Variable = "o" + line.Split(" o")[2].Split(" : ")[0]; - output.Index = Int32.TryParse(new string(output.Variable.Skip(1).ToArray()), out int index) ? index : -1; - output.Semantic = line.Split(" : ")[1].Split(",")[0]; - output.Type = line.Split("out ")[1].Split(" o")[0]; - outputs.Add(output); - } - } - - } while (line != null); - } + //private void ProcessHlslData() + //{ + // string line = string.Empty; + // cbuffers.Clear(); + // textures.Clear(); + // inputs.Clear(); + // bool bFindOpacity = false; + // do + // { + // line = hlsl.ReadLine(); + // if (line != null) + // { + // if (line.Contains("r0,r1")) // at end of function definition + // { + // bFindOpacity = true; + // } + + // if (bFindOpacity) + // { + // if (line.Contains("discard") || line.Contains("o0.w = r")) + // { + // //bOpacityEnabled = true; + // break; + // } + // continue; + // } + + // if (line.Contains("Texture")) + // { + // TextureView texture = new TextureView(); + // texture.Dimension = line.Split("<")[0]; + // texture.Type = line.Split("<")[1].Split(">")[0]; + // texture.Variable = line.Split("> ")[1].Split(" :")[0]; + // texture.Index = Int32.TryParse(new string(texture.Variable.Skip(1).ToArray()), out int index) ? index : -1; + // textures.Add(texture); + // } + // else if (line.Contains("SamplerState")) + // { + // samplers.Add(line.Split("(")[1].Split(")")[0].Last() - 48); + // } + // else if (line.Contains("Buffer")) + // { + // Shaders.Buffer buffer = new Shaders.Buffer(); + // buffer.Type = line.Split('<', '>')[1]; + // buffer.Variable = line.Split(' ')[1]; + // buffer.Index = Int32.TryParse(new string(buffer.Variable.Skip(1).ToArray()), out int index) ? index : -1; + // buffers.Add(buffer); + // } + // else if (line.Contains("cbuffer")) + // { + // hlsl.ReadLine(); + // line = hlsl.ReadLine(); + // Cbuffer cbuffer = new Cbuffer(); + // cbuffer.Variable = "cb" + line.Split("cb")[1].Split("[")[0]; + // cbuffer.Index = Int32.TryParse(new string(cbuffer.Variable.Skip(2).ToArray()), out int index) ? index : -1; + // cbuffer.Count = Int32.TryParse(new string(line.Split("[")[1].Split("]")[0]), out int count) ? count : -1; + // cbuffer.Type = line.Split("cb")[0].Trim(); + // cbuffers.Add(cbuffer); + // } + // else if (line.Contains(" v") && line.Contains(" : ") && !line.Contains("?")) + // { + // Input input = new Input(); + // input.Variable = "v" + line.Split("v")[1].Split(" : ")[0]; + // input.Index = Int32.TryParse(new string(input.Variable.Skip(1).ToArray()), out int index) ? index : -1; + // input.Semantic = line.Split(" : ")[1].Split(",")[0]; + // if (input.Semantic == "SV_isFrontFace0") + // bUsesFrontFace = true; + // input.Type = line.Split(" v")[0].Trim(); + // inputs.Add(input); + // } + // else if (line.Contains("out") && line.Contains(" : ")) + // { + // Output output = new Output(); + // output.Variable = "o" + line.Split(" o")[2].Split(" : ")[0]; + // output.Index = Int32.TryParse(new string(output.Variable.Skip(1).ToArray()), out int index) ? index : -1; + // output.Semantic = line.Split(" : ")[1].Split(",")[0]; + // output.Type = line.Split("out ")[1].Split(" o")[0]; + // outputs.Add(output); + // } + // } + + // } while (line != null); + //} private StringBuilder WriteCbuffers(IMaterial material, bool bIsVertexShader) { StringBuilder CBuffers = new(); - foreach (var buffer in buffers) + foreach (var resource in resources) { - CBuffers.AppendLine($"\tBuffer<{buffer.Type}> b_{buffer.Variable} : register({buffer.Variable});"); - } - - foreach (var cbuffer in cbuffers) - { - //CBuffers.AppendLine($"\tstatic {cbuffer.Type} {cbuffer.Variable}[{cbuffer.Count}] = ").AppendLine("\t{"); - - dynamic data = null; - if (bIsVertexShader) - { - for (int i = 0; i < cbuffer.Count; i++) - { - CBuffers.AppendLine($"\tfloat4 vs_cb{cbuffer.Index}_{i} < Default4( 0.0f, 0.0f, 0.0f, 0.0f ); UiGroup( \"vs_cb{cbuffer.Index}/{i}\"); >;"); - } - } - else + switch (resource.ResourceType) { - for (int i = 0; i < cbuffer.Count; i++) - { - CBuffers.AppendLine($"\tfloat4 cb{cbuffer.Index}_{i} < Default4( 0.0f, 0.0f, 0.0f, 0.0f ); UiGroup( \"cb{cbuffer.Index}/{i}\"); >;"); - } + case ResourceType.Buffer: + CBuffers.AppendLine($"\tBuffer b_t{resource.Index} : register(t{resource.Index});"); + break; + case ResourceType.CBuffer: + if (bIsVertexShader) + { + for (int i = 0; i < resource.Count; i++) + { + CBuffers.AppendLine($"\tfloat4 vs_cb{resource.Index}_{i} < Default4( 0.0f, 0.0f, 0.0f, 0.0f ); UiGroup( \"vs_cb{resource.Index}/{i}\"); >;"); + } + } + else + { + if (resource.Index != 12) + { + for (int i = 0; i < resource.Count; i++) + { + CBuffers.AppendLine($"\tfloat4 cb{resource.Index}_{i} < Default4( 0.0f, 0.0f, 0.0f, 0.0f ); UiGroup( \"cb{resource.Index}/{i}\"); >;"); + } + } + } + break; } } - return CBuffers.Replace("∞", "1.#INF"); + return CBuffers; } private StringBuilder WriteFunctionDefinition(IMaterial material, bool bIsVertexShader) @@ -352,28 +353,14 @@ private StringBuilder WriteFunctionDefinition(IMaterial material, bool bIsVertex if (isTerrain) //Terrain has 4 dyemaps per shader, from what ive seen { - funcDef.AppendLine($"\tCreateInputTexture2D( TextureT14_0, Linear, 8, \"\", \"\", \"Textures,10/14\", Default3( 1.0, 1.0, 1.0 ));"); - funcDef.AppendLine($"\tTexture2D g_t14_0 < Channel( RGBA, Box( TextureT14_0 ), Linear ); OutputFormat( RGBA8888 ); SrgbRead( False ); >; "); - funcDef.AppendLine($"\tTextureAttribute(g_t14_0, g_t14_0);\n"); - - funcDef.AppendLine($"\tCreateInputTexture2D( TextureT14_1, Linear, 8, \"\", \"\", \"Textures,10/15\", Default3( 1.0, 1.0, 1.0 ));"); - funcDef.AppendLine($"\tTexture2D g_t14_1 < Channel( RGBA, Box( TextureT14_1 ), Linear ); OutputFormat( RGBA8888 ); SrgbRead( False ); >; "); - funcDef.AppendLine($"\tTextureAttribute(g_t14_1, g_t14_1);\n"); - - funcDef.AppendLine($"\tCreateInputTexture2D( TextureT14_2, Linear, 8, \"\", \"\", \"Textures,10/16\", Default3( 1.0, 1.0, 1.0 ));"); - funcDef.AppendLine($"\tTexture2D g_t14_2 < Channel( RGBA, Box( TextureT14_2 ), Linear ); OutputFormat( RGBA8888 ); SrgbRead( False ); >; "); - funcDef.AppendLine($"\tTextureAttribute(g_t14_2, g_t14_2);\n"); - - funcDef.AppendLine($"\tCreateInputTexture2D( TextureT14_3, Linear, 8, \"\", \"\", \"Textures,10/17\", Default3( 1.0, 1.0, 1.0 ));"); - funcDef.AppendLine($"\tTexture2D g_t14_3 < Channel( RGBA, Box( TextureT14_3 ), Linear ); OutputFormat( RGBA8888 ); SrgbRead( False ); >; "); - funcDef.AppendLine($"\tTextureAttribute(g_t14_3, g_t14_3);\n"); + funcDef.AppendLine($"\tCreateTexture2DWithoutSampler( g_t14 ) < Attribute( \"TerrainDyemap\" ); SrgbRead( false ); >;\r\n\n"); } - //if(bUsesNormalBuffer) - //{ - // funcDef.AppendLine($"\tBoolAttribute( bWantsFBCopyTexture, true );"); - // funcDef.AppendLine($"\tCreateTexture2D( g_tFrameBufferCopyTexture ) < Attribute( \"FrameBufferCopyTexture\" ); SrgbRead( true ); Filter( MIN_MAG_MIP_LINEAR ); AddressU( CLAMP ); AddressV( CLAMP ); >;"); - //} + if (bUsesFrameBuffer) + { + funcDef.AppendLine($"\tBoolAttribute( bWantsFBCopyTexture, true );"); + funcDef.AppendLine($"\tCreateTexture2D( g_tFrameBufferCopyTexture ) < Attribute( \"FrameBufferCopyTexture\" ); SrgbRead( true ); Filter( MIN_MAG_MIP_LINEAR ); AddressU( CLAMP ); AddressV( CLAMP ); >;"); + } } return funcDef; } @@ -386,28 +373,28 @@ private StringBuilder ConvertInstructions(IMaterial material, bool isVertexShade { foreach (var i in inputs) { - switch(i.Semantic) + switch (i.Semantic) { - case "POSITION0": - funcDef.AppendLine($"\t\tfloat4 {i.Variable} = float4(i.vPositionOs, 0); //{i.Semantic}"); + case DXBCSemantic.Position: + funcDef.AppendLine($"\t\tfloat4 v{i.BufferIndex} = float4(i.vPositionOs, 0); //{i.ToString()}"); break; - case "TANGENT0": - funcDef.AppendLine($"\t\tfloat4 {i.Variable} = i.vTangentUOs; //{i.Semantic}"); + case DXBCSemantic.Tangent: + funcDef.AppendLine($"\t\tfloat4 v{i.BufferIndex} = i.vTangentUOs; //{i.ToString()}"); break; - case "TEXCOORD0": - funcDef.AppendLine($"\t\tfloat4 {i.Variable} = float4(i.vTexCoord, 0, 0); //{i.Semantic}"); + case DXBCSemantic.Texcoord: + funcDef.AppendLine($"\t\tfloat4 v{i.BufferIndex} = float4(i.vTexCoord, 0, 0); //{i.ToString()}"); break; - case "NORMAL0": - funcDef.AppendLine($"\t\tfloat4 {i.Variable} = i.vNormalOs; //{i.Semantic}"); + case DXBCSemantic.Normal: + funcDef.AppendLine($"\t\tfloat4 v{i.BufferIndex} = i.vNormalOs; //{i.ToString()}"); break; - case "SV_VERTEXID0": - funcDef.AppendLine($"\t\tuint {i.Variable} = i.vVertexID; //{i.Semantic}"); + case DXBCSemantic.SystemVertexId: + funcDef.AppendLine($"\t\tuint v{i.BufferIndex} = i.vVertexID; //{i.ToString()}"); break; - case "SV_InstanceID0": - funcDef.AppendLine($"\t\tuint {i.Variable} = i.vInstanceID; //{i.Semantic}"); + case DXBCSemantic.SystemInstanceId: + funcDef.AppendLine($"\t\tuint v{i.BufferIndex} = i.vInstanceID; //{i.ToString()}"); break; default: - funcDef.AppendLine($"\t\t{i.Type} {i.Variable} = {i.Variable}; //{i.Semantic}"); + funcDef.AppendLine($"\t\t{i.GetMaskType()} v{i.BufferIndex} = float4(1,1,1,1).{i.Mask.ToString().ToLower()}; //{i.Semantic}"); break; } } @@ -444,7 +431,7 @@ private StringBuilder ConvertInstructions(IMaterial material, bool isVertexShade var dotAfter = line.Split(").")[1]; // todo add dimension - funcDef.AppendLine($"\t\t{equal.TrimStart()}= Tex2DS(g_t{texIndex}, g_s{sampleIndex}, {sampleUv}).{dotAfter}"); + funcDef.AppendLine($"\t\t{equal.TrimStart()}= Tex2DS(g_t{texIndex}, s_s{sampleIndex}, {sampleUv}).{dotAfter}"); } else if (line.Contains("Load")) { @@ -464,56 +451,60 @@ private StringBuilder ConvertInstructions(IMaterial material, bool isVertexShade } else //Pixel { - funcDef.AppendLine("\t\tfloat3 vPositionWs = i.vPositionWithOffsetWs.xyz + g_vHighPrecisionLightingOffsetWs.xyz;"); - funcDef.AppendLine("\t\tfloat3 vCameraToPositionDirWs = CalculateCameraToPositionDirWs( vPositionWs.xyz );"); + //Need to divde by 39.37 to convert to meters + funcDef.AppendLine("\t\tfloat3 vPositionWs = (i.vPositionWithOffsetWs.xyz + g_vHighPrecisionLightingOffsetWs.xyz) / 39.37;"); funcDef.AppendLine("\t\tfloat alpha = 1;"); - if (isTerrain) //variables are different for terrain for whatever reason, kinda have to guess + if (isTerrain) // Input variables are different for terrain { - funcDef.AppendLine("\t\tfloat4 v0 = {vPositionWs/39.37, 1};"); //Detail uv? - funcDef.AppendLine("\t\tfloat4 v1 = {i.vTextureCoords, 1, 1};"); //Main uv? - funcDef.AppendLine("\t\tfloat4 v2 = {i.vNormalWs,1};"); - funcDef.AppendLine("\t\tfloat4 v3 = {i.vTangentUWs,1};"); - funcDef.AppendLine("\t\tfloat4 v4 = {i.vTangentVWs,1};"); - funcDef.AppendLine("\t\tfloat4 v5 = {i.v5,1};"); + funcDef.AppendLine("\t\tfloat4 v0 = {vPositionWs, 1};"); // World Pos + funcDef.AppendLine("\t\tfloat4 v1 = {i.vTextureCoords, 1, 1};"); // UVs + funcDef.AppendLine("\t\tfloat4 v2 = {i.vNormalWs,1};"); // Mesh world normals + funcDef.AppendLine("\t\tfloat4 v3 = {i.v3,1};"); // From VS, Used for normals + funcDef.AppendLine("\t\tfloat4 v4 = {i.v4,1};"); // From VS, Used for normals + funcDef.AppendLine("\t\tfloat4 v5 = {i.v5,1};"); // From VS, Used for tri-planar mapping? Mainly seen on vertical terrain } else { - funcDef.AppendLine("\t\tfloat4 v0 = {i.vNormalWs,1};"); //Mesh world normals - funcDef.AppendLine("\t\tfloat4 v1 = {i.vTangentUWs,1};"); - funcDef.AppendLine("\t\tfloat4 v2 = {i.vTangentVWs,1};"); - funcDef.AppendLine("\t\tfloat4 v3 = {i.vTextureCoords,0,0};"); //UVs - funcDef.AppendLine("\t\tfloat4 v4 = {(vPositionWs)/39.37,0};"); //Don't really know, just guessing its world offset or something - funcDef.AppendLine("\t\tfloat4 v5 = i.vBlendValues;"); //Vertex color. - //funcDef.AppendLine("uint v6 = 1;"); //Usually FrontFace but can also be v7 + funcDef.AppendLine("\t\tfloat4 v0 = {i.vNormalWs,1};"); // Mesh world normals + funcDef.AppendLine("\t\tfloat4 v1 = {i.vTangentUWs,1};"); // Tangent U + funcDef.AppendLine("\t\tfloat4 v2 = {i.vTangentVWs,1};"); // Tangent V + funcDef.AppendLine("\t\tfloat4 v3 = {i.vTextureCoords,0,0};"); // UVs + funcDef.AppendLine("\t\tfloat4 v4 = {vPositionWs,0};"); // World Pos } foreach (var i in inputs) { - switch(i.Type) + switch (i.GetMaskType()) { case "uint": - if (i.Semantic == "SV_isFrontFace0") - funcDef.AppendLine($"\t\tint {i.Variable} = i.face;"); + if (i.Semantic == DXBCSemantic.SystemIsFrontFace) + funcDef.AppendLine($"\t\tint v{i.RegisterIndex} = i.face;"); else - funcDef.AppendLine($"\t\tint {i.Variable} = 1;"); + funcDef.AppendLine($"\t\tint v{i.RegisterIndex} = 1;"); break; case "float4": - if (i.Semantic == "SV_POSITION0" && i.Variable != "v5") - funcDef.AppendLine($"\t\tfloat4 {i.Variable} = i.vPositionSs;"); + if (i.Semantic == DXBCSemantic.SystemPosition) + funcDef.AppendLine($"\t\tfloat4 v{i.RegisterIndex} = i.vPositionSs;"); + else if (i.RegisterIndex == 5 && i.Semantic == DXBCSemantic.Texcoord && !isTerrain) + funcDef.AppendLine($"\t\tfloat4 v5 = i.vBlendValues;"); + //else + // funcDef.AppendLine($"\t\tfloat4 {i.Variable} = float4(1,1,1,1);"); + break; + case "float": + funcDef.AppendLine($"\t\tfloat v{i.RegisterIndex} = 1;"); break; } } funcDef.AppendLine("\t\tfloat4 o0 = float4(0,0,0,0);"); funcDef.AppendLine("\t\tfloat4 o1 = float4(PackNormal3D(v0.xyz),1);"); - funcDef.AppendLine("\t\tfloat4 o2 = float4(0,0.5,0,0);\n"); + funcDef.AppendLine("\t\tfloat4 o2 = float4(0,0.5,0,0);"); funcDef.AppendLine("\t\tfloat4 o3 = float4(0,0,0,0);\n"); - //if (cbuffers.Any(cbuffer => cbuffer.Index == 13 && cbuffer.Count == 2)) //Should be time (g_flTime) but probably gets manipulated somehow - //{ - // funcDef.AppendLine("\t\tcb13[0] = 1;"); - // funcDef.AppendLine("\t\tcb13[1] = 1;"); - //} + if (resources.Any(cbuffer => (cbuffer.ResourceType == ResourceType.CBuffer && cbuffer.Index == 12))) + { + funcDef.AppendLine(AddViewScope()); + } string line = hlsl.ReadLine(); if (line == null) @@ -533,7 +524,7 @@ private StringBuilder ConvertInstructions(IMaterial material, bool isVertexShade while (!line.Contains("{")) { if (line.Contains("SV_TARGET2")) - bTranslucent = false; + bRT0 = false; line = hlsl.ReadLine(); } do @@ -542,40 +533,33 @@ private StringBuilder ConvertInstructions(IMaterial material, bool isVertexShade line = hlsl.ReadLine(); if (line != null) { - if (line.Contains("cb12[7].xyz") || line.Contains("cb12[14].xyz")) //cb12 is view scope - { - funcDef.AppendLine($"\t\t{line.TrimStart() - .Replace("cb12[7].xyz", "vCameraToPositionDirWs") - .Replace("v4.xyz", "float3(0,0,0)") - .Replace("cb12[14].xyz", "-vCameraToPositionDirWs")}"); - } - else if (line.Contains("cb")) + if (line.Contains("cb") && !line.Contains("Sample")) { - if(line.Contains("Sample")) //rare case where a cbuffer value is directly used as a texcoord - { - string pattern = @"cb(\d+)\[(\d+)\]"; // Matches cb#[#] - - var equal = line.Split("=")[0]; - var texIndex = Int32.Parse(line.Split(".Sample")[0].Split("t")[1]); - var sampleIndex = Int32.Parse(line.Split("(s")[1].Split("_s,")[0]); - var sampleUv = line.Split(", ")[1].Split(").")[0]; - sampleUv = Regex.Replace(sampleUv, pattern, isVertexShader ? "vs_cb$1_$2" : "cb$1_$2"); - var dotAfter = line.Split(").")[1]; - - funcDef.AppendLine($"\t\t{equal.TrimStart()}= g_t{texIndex}.Sample(g_s{sampleIndex}, {sampleUv}).{dotAfter}"); - } - else + string pattern = @"cb(\d+)\[(\d+)\]"; // Matches cb#[#] + string output = Regex.Replace(line, pattern, match => { - string pattern = @"cb(\d+)\[(\d+)\]"; // Matches cb#[#] - string output = Regex.Replace(line, pattern, isVertexShader ? "vs_cb$1_$2" : "cb$1_$2"); - - funcDef.AppendLine($"\t\t{output.TrimStart()}"); - } - + // Extract the values from the matched groups + string group1 = match.Groups[1].Value; // cbuffer index + string group2 = match.Groups[2].Value; // cbuffer array index + + if (group1 != "12") + { + // Replace with the actual values of group1 and group2 + return isVertexShader ? $"vs_cb{group1}_{group2}" : $"cb{group1}_{group2}"; + } + else + { + // If group1 is "12", don't replace + return match.Value; + } + }); + + // Append the modified line to funcDef + funcDef.AppendLine($"\t\t{output.TrimStart()}"); } else if (line.Contains("while (true)")) { - funcDef.AppendLine($"\t\t{line.TrimStart().Replace("while (true)", "[unroll(20)] while (true)")}"); + funcDef.AppendLine($"\t\t{line.TrimStart().Replace("while (true)", "[loop] while (true)")}"); } else if (line.Contains("return;")) { @@ -584,40 +568,69 @@ private StringBuilder ConvertInstructions(IMaterial material, bool isVertexShade else if (line.Contains("Sample")) { var equal = line.Split("=")[0]; + var equal_post = line.Split("=")[1]; var texIndex = Int32.Parse(line.Split(".Sample")[0].Split("t")[1]); var sampleIndex = Int32.Parse(line.Split("(s")[1].Split("_s,")[0]); var sampleUv = line.Split(", ")[1].Split(").")[0]; var dotAfter = line.Split(").")[1]; - if (texIndex == 14 && isTerrain) //THIS IS SO SO BAD + if (sampleUv.Contains("cb")) //Rare case where a cbuffer value is used as a texcoord + { + string pattern = @"cb(\d+)\[(\d+)\]"; // Matches cb#[#] + sampleUv = Regex.Replace(sampleUv, pattern, match => + { + // Extract the values from the matched groups + string group1 = match.Groups[1].Value; // cbuffer index + string group2 = match.Groups[2].Value; // cbuffer array index + + if (group1 != "12") + { + // Replace with the actual values of group1 and group2 + return isVertexShader ? $"vs_cb{group1}_{group2}" : $"cb{group1}_{group2}"; + } + else + { + // If group1 is "12", don't replace + return match.Value; + } + }); + } + + + if (texIndex == 14 && isTerrain) // Terrain dyemap, not defined in the material itself { - funcDef.AppendLine($"\t\tbool red = i.vBlendValues.x > 0.5;\r\n" + - $" bool green = i.vBlendValues.y > 0.5;\r\n" + - $" bool blue = i.vBlendValues.z > 0.5;\r\n\r\n" + - $" if (red && !green && !blue)\r\n" + - $" {{\r\n" + - $" {equal} = g_t{texIndex}_0.Sample(g_s{sampleIndex}, {sampleUv}).{dotAfter}\r\n" + - $" }}\r\n" + - $" else if (!red && green && !blue)\r\n" + - $" {{\r\n" + - $" {equal} = g_t{texIndex}_1.Sample(g_s{sampleIndex}, {sampleUv}).{dotAfter}\r\n" + - $" }}\r\n" + - $" else if (!red && !green && blue)\r\n" + - $" {{\r\n" + - $" {equal} = g_t{texIndex}_2.Sample(g_s{sampleIndex}, {sampleUv}).{dotAfter}\r\n" + - $" }}\r\n" + - $" else if (red && green && blue)\r\n" + - $" {{\r\n" + - $" {equal} = g_t{texIndex}_3.Sample(g_s{sampleIndex}, {sampleUv}).{dotAfter}\r\n" + - $" }}"); + funcDef.AppendLine($"\t\t{equal.TrimStart()} = g_t{texIndex}.Sample(s_s{sampleIndex}, {sampleUv}).{dotAfter}"); } - else if (!material.EnumeratePSTextures().Any(texture => texture.TextureIndex == texIndex)) //Some kind of buffer texture + else if (!material.EnumeratePSTextures().Any(texture => texture.TextureIndex == texIndex)) // Some kind of buffer texture or not defined in the material { - funcDef.AppendLine($"\t\t{equal.TrimStart()}= float4(1,1,1,1).{dotAfter} //t{texIndex}"); + switch (texIndex) + { + case 10: // Depth buffer + bUsesDepthBuffer = true; + funcDef.AppendLine($"\t\t{equal.TrimStart()}= Depth::Get({sampleUv}).xxxx; //{equal_post}"); + break; + case 11: + case 13: + case 23: // Framebuffer? Usually uses SampleLevel but shouldnt be an issue? + bUsesFrameBuffer = true; + funcDef.AppendLine($"\t\t{equal.TrimStart()}= g_tFrameBufferCopyTexture.Sample(s_s{sampleIndex}, {sampleUv}).{dotAfter} //{equal_post}"); + break; + case 15: + case 20: // Unknown + funcDef.AppendLine($"\t\t{equal.TrimStart()}= float4(0.3137,0.3137,0.3137,0.3137).{dotAfter} //{equal_post}"); + break; + case 0: + case 21: // Unknown + funcDef.AppendLine($"\t\t{equal.TrimStart()}= float4(0.1882,0.1882,0.1882,0.1882).{dotAfter} //{equal_post}"); + break; + default: // Unknown + funcDef.AppendLine($"\t\t{equal.TrimStart()}= float4(0.5,0.5,0.5,0.5).{dotAfter} //{equal_post}"); + break; + } } - else + else // Textures defined by the material { - funcDef.AppendLine($"\t\t{equal.TrimStart()}= g_t{texIndex}.Sample(g_s{sampleIndex}, {sampleUv}).{dotAfter}"); + funcDef.AppendLine($"\t\t{equal.TrimStart()}= g_t{texIndex}.Sample(s_s{sampleIndex}, {sampleUv}).{dotAfter}"); } } else if (line.Contains("CalculateLevelOfDetail")) @@ -627,39 +640,70 @@ private StringBuilder ConvertInstructions(IMaterial material, bool isVertexShade var sampleIndex = Int32.Parse(line.Split("(s")[1].Split("_s,")[0]); var sampleUv = line.Split(", ")[1].Split(")")[0]; - funcDef.AppendLine($"\t\t{equal.TrimStart()}= g_t{texIndex}.CalculateLevelOfDetail(g_s{sampleIndex}, {sampleUv});"); + funcDef.AppendLine($"\t\t{equal.TrimStart()}= g_t{texIndex}.CalculateLevelOfDetail(s_s{sampleIndex}, {sampleUv});"); } else if (line.Contains("Load")) { var equal = line.Split("=")[0]; + var equal_post = line.Split("=")[1]; var texIndex = Int32.Parse(line.Split(".Load")[0].Split("t")[1]); var sampleUv = line.Split("(")[1].Split(")")[0]; var dotAfter = line.Split(").")[1]; - if (texIndex == 2) //Pretty sure this is normal buffer, cant get/use in Forward Rendering... + if (!material.EnumeratePSTextures().Any(texture => texture.TextureIndex == texIndex)) //Some kind of buffer texture { - bUsesNormalBuffer = true; - funcDef.AppendLine($"\t\t{equal.TrimStart()}= v0.{dotAfter}"); + switch (texIndex) + { + case 2: + bUsesNormalBuffer = true; + funcDef.AppendLine($"\t\t{equal.TrimStart()}= v0.{dotAfter}"); + break; + case 10: + bUsesDepthBuffer = true; + funcDef.AppendLine($"\t\t{equal.TrimStart()}= Depth::Get({sampleUv}).xxxx; //{equal_post}"); + break; + case 11: + case 13: + case 23: //Usually uses SampleLevel but shouldnt be an issue? + bUsesFrameBuffer = true; + funcDef.AppendLine($"\t\t{equal.TrimStart()}= g_tFrameBufferCopyTexture.Load({sampleUv}).{dotAfter} //{equal_post}"); + break; + case 15: + case 20: + funcDef.AppendLine($"\t\t{equal.TrimStart()}= float4(0.3137,0.3137,0.3137,0.3137).{dotAfter} //{equal_post}"); + break; + case 0: + case 21: + funcDef.AppendLine($"\t\t{equal.TrimStart()}= float4(0.1882,0.1882,0.1882,0.1882).{dotAfter} //{equal_post}"); + break; + default: + funcDef.AppendLine($"\t\t{equal.TrimStart()}= float4(0.5,0.5,0.5,0.5).{dotAfter} //{equal_post}"); + break; + } } - else if (!material.EnumeratePSTextures().Any(texture => texture.TextureIndex == texIndex)) //Some kind of buffer texture + else { - funcDef.AppendLine($"\t\t{equal.TrimStart()}= float4(1,1,1,1).{dotAfter} //t{texIndex}.Load"); + funcDef.AppendLine($"\t\t{equal.TrimStart()}= g_t{texIndex}.Load({sampleUv}).{dotAfter}"); } } - else if (line.Contains("o0.w = r")) //o0.w = r(?) - { - funcDef.AppendLine($"\t\t{line.TrimStart()}"); - funcDef.AppendLine($"\t\talpha = 1 - o0.w;"); - } - else if (line.Contains("discard")) - { - funcDef.AppendLine(line.Replace("discard", "\t\t{ alpha = 0; }")); //sometimes o0.w is used for alpha instead on some shaders - } + //else if (line.Contains("o0.w = r")) //o0.w = r(?) + //{ + // funcDef.AppendLine($"\t\t{line.TrimStart()}"); + // //funcDef.AppendLine($"\t\talpha = 1 - o0.w;"); + //} + //else if (line.Contains("discard")) + //{ + // funcDef.AppendLine(line.Replace("discard", "\t\t{ alpha = 0; }")); + //} else if (line.Contains("o1.xyzw = float4(0,0,0,0);")) { funcDef.AppendLine(line.Replace("o1.xyzw = float4(0,0,0,0);", "\t\to1.xyzw = float4(PackNormal3D(v0.xyz),0);")); //decals(?) have 0 normals sometimes, dont want that bFixRoughness = true; } + else if (line.Contains("GetDimensions")) // Uhhhh + { + funcDef.AppendLine($"\t\t//{line.TrimStart()}"); + } else { funcDef.AppendLine($"\t\t{line.TrimStart()}"); @@ -675,7 +719,7 @@ private StringBuilder AddOutput(IMaterial material) { StringBuilder output = new StringBuilder(); - if(!bTranslucent) //uses o1,o2 + if (!bRT0) //uses o1,o2 { //this is fine... output.Append($"\t\t// Normal\r\n " + @@ -692,6 +736,7 @@ private StringBuilder AddOutput(IMaterial material) $"mat.Transmission = o2.z;\r\n " + $"mat.Normal = normal_in_world_space; //Normal is already in world space so no need to convert in Material::From"); + output.AppendLine($"// for some toolvis shit\r\n\t\tmat.WorldTangentU = i.vTangentUWs;\r\n\t\tmat.WorldTangentV = i.vTangentVWs;\r\n mat.TextureCoords = i.vTextureCoords.xy;"); output.Append($"\n\t\treturn ShadingModelStandard::Shade(i, mat);"); @@ -703,20 +748,46 @@ private StringBuilder AddOutput(IMaterial material) } else //only uses o0 { - output.AppendLine($"\t\tMaterial mat = Material::From(i, float4(o0.xyz, alpha), float4(0.5, 0.5, 1, 1), float4(0.5, 0, 1, 1), float3(1.0f, 1.0f, 1.0f), 0);"); - output.AppendLine($"\t\treturn ShadingModelStandard::Shade(i, mat);"); - - //if (material.Unk0C == 2) //Unlit? - //{ - // output.Append($"\t\treturn float4(o0.xyz, alpha);"); - //} - //else - //{ - // output.AppendLine($"\t\tMaterial mat = Material::From(i, float4(o0.xyz, alpha), float4(0.5, 0.5, 1, 1), float4(0.5, 0, 1, 1), float3(1.0f, 1.0f, 1.0f), 0);"); - // output.AppendLine($"\t\treturn ShadingModelStandard::Shade(i, mat);"); - //} + bool a = bUsesNormalBuffer || bTranslucent || bUsesFrameBuffer || bUsesDepthBuffer; + + if (a) //?? + { + //output.Append($"\t\treturn float4(o0.xyz, {(bUsesFrameBuffer ? "1" : "alpha")});"); + output.Append($"\t\treturn float4(o0.xyz, 1);"); + } + else + { + output.AppendLine($"\t\tMaterial mat = Material::From(i, float4(o0.xyz, 1), float4(0.5, 0.5, 1, 1), float4(0.5, 0, 1, 1), float3(1.0f, 1.0f, 1.0f), 0);"); + output.AppendLine($"\t\treturn ShadingModelStandard::Shade(i, mat);"); + } } return output; } + + private string AddViewScope() + { + StringBuilder viewScope = new StringBuilder(); + viewScope.AppendLine($"\t\tfloat4 cb12[15] = {{"); + + viewScope.AppendLine($"\t\tg_matWorldToProjection,"); //0 + + viewScope.AppendLine($"\t\tfloat4(1,0,0,0),"); //4 + viewScope.AppendLine($"\t\tfloat4(0,1,0,0),"); + viewScope.AppendLine($"\t\tfloat4(0,0,1,0),"); + viewScope.AppendLine($"\t\tfloat4(g_vCameraPositionWs/39.37,1),"); + + viewScope.AppendLine($"\t\tfloat4(0.5,0,0,0),"); //8 + viewScope.AppendLine($"\t\tfloat4(0,1,0,0),"); + viewScope.AppendLine($"\t\tfloat4(0,0,1,0),"); + viewScope.AppendLine($"\t\tfloat4(-100,-100,-100,1),"); + + viewScope.AppendLine($"\t\tfloat4(g_vFrameBufferCopyInvSizeAndUvScale.xy, 1/g_vFrameBufferCopyInvSizeAndUvScale.xy),"); //12 + viewScope.AppendLine($"\t\tfloat4(1,0,0,0),"); //13 + viewScope.AppendLine($"\t\tfloat4(g_vCameraPositionWs/39.37,1)"); //14 + + viewScope.AppendLine($"\t\t}};"); + + return viewScope.ToString(); + } } diff --git a/Tiger/Schema/Shaders/ShaderBytecode.cs b/Tiger/Schema/Shaders/ShaderBytecode.cs index 505903d7..a9e5ab3f 100644 --- a/Tiger/Schema/Shaders/ShaderBytecode.cs +++ b/Tiger/Schema/Shaders/ShaderBytecode.cs @@ -1,13 +1,12 @@ -using System.Diagnostics; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; using Arithmic; namespace Tiger.Schema; public class ShaderBytecode : TigerReferenceFile { - private List? _inputSignatures; - public List InputSignatures + private List? _inputSignatures; + public List InputSignatures { get { @@ -18,7 +17,7 @@ public List InputSignatures _inputSignatures = GetInputSignatures(); Log.Debug($"Input signatures for shader {Hash} ({_inputSignatures.Count}):"); - foreach (InputSignature inputSignature in _inputSignatures) + foreach (DXBCIOSignature inputSignature in _inputSignatures) { Log.Debug(inputSignature.DebugString()); } @@ -26,6 +25,46 @@ public List InputSignatures } } + private List? _outputSignatures; + public List OutputSignatures + { + get + { + if (_outputSignatures != null) + { + return _outputSignatures; + } + + _outputSignatures = GetOutputSignatures(); + Log.Debug($"Output signatures for shader {Hash} ({_outputSignatures.Count}):"); + foreach (DXBCIOSignature outputSignature in _outputSignatures) + { + Log.Debug(outputSignature.DebugString()); + } + return _outputSignatures; + } + } + + private List _resources; + public List Resources + { + get + { + if (_resources != null) + { + return _resources; + } + + _resources = GetShaderResources(); + Log.Debug($"Shader Resources for shader {Hash} ({_resources.Count}):"); + foreach (DXBCShaderResource resource in _resources) + { + Log.Debug(resource.DebugString()); + } + return _resources; + } + } + public ShaderBytecode(FileHash hash) : base(hash) { } @@ -36,29 +75,153 @@ public byte[] GetBytecode() return reader.ReadBytes((int)_tag.BytecodeSize); } - public List GetInputSignatures() + //These are kinda messy and can probably be simplified + public List GetInputSignatures() { using TigerReader reader = GetReferenceReader(); -#if DEBUG - reader.Seek(0x2C, SeekOrigin.Begin); - uint inputSignatureCC = reader.ReadUInt32(); - Debug.Assert(inputSignatureCC == 1313297225); - uint chunkSize = reader.ReadUInt32(); -#endif + //#if DEBUG + // reader.Seek(0x2C, SeekOrigin.Begin); + // uint inputSignatureCC = reader.ReadUInt32(); + // Debug.Assert(inputSignatureCC == 1313297225); + // uint chunkSize = reader.ReadUInt32(); + //#endif reader.Seek(0x34, SeekOrigin.Begin); uint inputSignatureCount = reader.ReadUInt32(); reader.Seek(0x4, SeekOrigin.Current); - List inputSignatures = new(); + List inputSignatures = new(); for (int i = 0; i < inputSignatureCount; i++) { - DXBCInputSignature signature = reader.ReadType(); - if (signature.SystemValueType == 0) // we only want non-system value inputs + DXBCIOElement signature = reader.ReadType(); + inputSignatures.Add(new DXBCIOSignature(reader, 0x34, signature)); + } + + return inputSignatures; + } + + public List GetOutputSignatures() + { + using TigerReader reader = GetReferenceReader(); + + reader.Seek(0x30, SeekOrigin.Begin); + uint chunkStart = reader.ReadUInt32(); + reader.Seek(chunkStart + 0x8, SeekOrigin.Current); + + uint outputSignatureCount = reader.ReadUInt32(); + reader.Seek(0x4, SeekOrigin.Current); + List outputSignatures = new(); + for (int i = 0; i < outputSignatureCount; i++) + { + DXBCIOElement signature = reader.ReadType(); + outputSignatures.Add(new DXBCIOSignature(reader, chunkStart + 0x8 + 0x34, signature)); + } + + return outputSignatures; + } + + public List GetShaderResources() + { + using TigerReader reader = GetReferenceReader(); + // Go to ISGN chunk + reader.Seek(0x30, SeekOrigin.Begin); + // Get the chunk size of ISGN + uint sizeISGN = reader.ReadUInt32(); + + // Go to OSGN chunk + reader.Seek(sizeISGN + 0x4, SeekOrigin.Current); + // Get the chunk size of OSGN + uint sizeOSGN = reader.ReadUInt32(); + + // Go to SHEX chunk + reader.Seek(sizeOSGN + 0x14, SeekOrigin.Current); + + //uint a = reader.ReadUInt32(); + //Debug.Assert(a == 1480935507); + + List shaderResources = new(); + ResourceType type = (ResourceType)reader.ReadUInt32(); + try + { + do { - inputSignatures.Add(new InputSignature(reader, 0x34, signature)); + switch (type) + { + case ResourceType.CBuffer: + case ResourceType.CBuffer1: //Dynamically Linked or some shit, i forgot the name + reader.Seek(0x4, SeekOrigin.Current); + shaderResources.Add(new DXBCShaderResource + { + ResourceType = ResourceType.CBuffer, + Index = reader.ReadUInt32(), + Count = reader.ReadUInt32() + }); + break; + case ResourceType.Buffer: + reader.Seek(0x4, SeekOrigin.Current); + shaderResources.Add(new DXBCShaderResource + { + ResourceType = ResourceType.Buffer, + Index = reader.ReadUInt32(), + Count = 0 + }); + reader.Seek(0x4, SeekOrigin.Current); + break; + case ResourceType.Texture2D: + reader.Seek(0x4, SeekOrigin.Current); + shaderResources.Add(new DXBCShaderResource + { + ResourceType = ResourceType.Texture2D, + Index = reader.ReadUInt32(), + Count = 0 + }); + reader.Seek(0x4, SeekOrigin.Current); + break; + case ResourceType.Texture3D: + reader.Seek(0x4, SeekOrigin.Current); + shaderResources.Add(new DXBCShaderResource + { + ResourceType = ResourceType.Texture3D, + Index = reader.ReadUInt32(), + Count = 0 + }); + reader.Seek(0x4, SeekOrigin.Current); + break; + case ResourceType.TextureCube: + reader.Seek(0x4, SeekOrigin.Current); + shaderResources.Add(new DXBCShaderResource + { + ResourceType = ResourceType.TextureCube, + Index = reader.ReadUInt32(), + Count = 0 + }); + reader.Seek(0x4, SeekOrigin.Current); + break; + case ResourceType.SamplerState: + reader.Seek(0x4, SeekOrigin.Current); + shaderResources.Add(new DXBCShaderResource + { + ResourceType = ResourceType.SamplerState, + Index = reader.ReadUInt32(), + Count = 0 + }); + break; + //default: + // throw new NotSupportedException($"Unknown Type {type}"); + } + + type = (ResourceType)reader.ReadUInt32(); + } + while (type != ResourceType.None + && type != ResourceType.Output + && type != ResourceType.PSInput + && type != ResourceType.VSInput); + } + catch (Exception ex) + { + Log.Error($"{Hash}: {ex}"); } - return inputSignatures; + return shaderResources; } } @@ -70,28 +233,37 @@ public struct SShaderBytecode public TigerHash Unk0C; } -public enum InputSemantic +public enum DXBCSemantic { None, Position, Texcoord, Normal, + Binormal, Tangent, BlendIndices, BlendWeight, - Colour + Colour, + + //System semantics + SystemVertexId, + SystemInstanceId, + SystemTarget, + SystemPosition, + SystemIsFrontFace } -public struct InputSignature + +public struct DXBCIOSignature { - public InputSemantic Semantic; + public DXBCSemantic Semantic; public uint SemanticIndex; public RegisterComponentType ComponentType; public ComponentMask Mask; public uint RegisterIndex; public int BufferIndex = -1; // gets set in a decorator - public InputSignature(TigerReader reader, long chunkStart, DXBCInputSignature inputSignature) + public DXBCIOSignature(TigerReader reader, long chunkStart, DXBCIOElement inputSignature) { SemanticIndex = inputSignature.SemanticIndex; ComponentType = inputSignature.ComponentType; @@ -104,25 +276,47 @@ public InputSignature(TigerReader reader, long chunkStart, DXBCInputSignature in switch (semanticName) { case "POSITION": - Semantic = InputSemantic.Position; + Semantic = DXBCSemantic.Position; break; case "TEXCOORD": - Semantic = InputSemantic.Texcoord; + Semantic = DXBCSemantic.Texcoord; break; case "NORMAL": - Semantic = InputSemantic.Normal; + Semantic = DXBCSemantic.Normal; + break; + case "BINORMAL": + Semantic = DXBCSemantic.Binormal; break; case "TANGENT": - Semantic = InputSemantic.Tangent; + Semantic = DXBCSemantic.Tangent; break; case "BLENDINDICES": - Semantic = InputSemantic.BlendIndices; + Semantic = DXBCSemantic.BlendIndices; break; case "BLENDWEIGHT": - Semantic = InputSemantic.BlendWeight; + Semantic = DXBCSemantic.BlendWeight; break; case "COLOR": - Semantic = InputSemantic.Colour; + Semantic = DXBCSemantic.Colour; + break; + + //System + case "SV_POSITION": + Semantic = DXBCSemantic.SystemPosition; + break; + case "SV_isFrontFace": + Semantic = DXBCSemantic.SystemIsFrontFace; + break; + case "SV_VertexID": //Does case matter here? + case "SV_VERTEXID": + Semantic = DXBCSemantic.SystemVertexId; + break; + case "SV_InstanceID": + Semantic = DXBCSemantic.SystemInstanceId; + break; + case "SV_Target": + case "SV_TARGET": + Semantic = DXBCSemantic.SystemTarget; break; default: throw new NotImplementedException($"Unknown semantic {semanticName}"); @@ -145,10 +339,70 @@ public int GetNumberOfComponents() _ => throw new NotImplementedException($"Unknown component mask {Mask}") }; } + + public string GetMaskType() + { + switch (GetNumberOfComponents()) + { + case 1: + return "uint"; + case 2: + return "float2"; + case 3: + return "float3"; + case 4: + return "float4"; + default: + return "float4"; + } + } + + public override string ToString() + { + switch (Semantic) + { + case DXBCSemantic.Position: + return "POSITION"; + case DXBCSemantic.Texcoord: + return "TEXCOORD"; + case DXBCSemantic.Normal: + return "NORMAL"; + case DXBCSemantic.Binormal: + return "BINORMAL"; + case DXBCSemantic.Tangent: + return "TANGENT"; + case DXBCSemantic.BlendIndices: + return "BLENDINDICES"; + case DXBCSemantic.BlendWeight: + return "BLENDWEIGHT"; + case DXBCSemantic.Colour: + return "COLOR"; + + case DXBCSemantic.SystemPosition: + return "SV_POSITION"; + case DXBCSemantic.SystemIsFrontFace: + return "SV_ISFRONTFACE"; + case DXBCSemantic.SystemVertexId: + return "SV_VERTEXID"; + case DXBCSemantic.SystemInstanceId: + return "SV_INSTANCEID"; + case DXBCSemantic.SystemTarget: + return "SV_TARGET"; + default: + throw new NotImplementedException($"Unknown Semantic {Semantic}"); + } + } +} + +public struct DXBCShaderResource +{ + public ResourceType ResourceType; + public uint Index; + public uint Count; } [StructLayout(LayoutKind.Sequential, Size = 0x18)] -public struct DXBCInputSignature +public struct DXBCIOElement { public int SemanticNameOffset; public uint SemanticIndex; @@ -182,3 +436,21 @@ public enum ComponentMask : byte XYZW = X | Y | Z | W } + +public enum ResourceType +{ + CBuffer = 0x04000059, + CBuffer1 = 0x04000859, + Buffer = 0x04000858, + SamplerState = 0x0300005A, + Texture2D = 0x04001858, + Texture3D = 0x04002858, + TextureCube = 0x04003058, + + // Can already get through IOSignatures + // But need to know where to stop reading + None = 0x02000068, + VSInput = 0x0300005F, + PSInput = 0x03001062, + Output = 0x03000065 +} diff --git a/Tiger/Schema/Shaders/TFX Bytecode/Externs.cs b/Tiger/Schema/Shaders/TFX Bytecode/Externs.cs index f8238af2..7311688c 100644 --- a/Tiger/Schema/Shaders/TFX Bytecode/Externs.cs +++ b/Tiger/Schema/Shaders/TFX Bytecode/Externs.cs @@ -98,3 +98,17 @@ PlayerCenteredCascadedGrid = 95, SoftDeform = 96, } + +//Based on CBuffer index +public enum TfxScope : byte +{ + //Material = 0, + Instance = 1, + Transparent = 2, + Unk3 = 3, + Unk8 = 8, + Decal = 9, + View = 12, + Frame = 13, +} + diff --git a/Tiger/Schema/Shaders/TFX Bytecode/Interpreter.cs b/Tiger/Schema/Shaders/TFX Bytecode/Interpreter.cs index dddd3935..cd50282b 100644 --- a/Tiger/Schema/Shaders/TFX Bytecode/Interpreter.cs +++ b/Tiger/Schema/Shaders/TFX Bytecode/Interpreter.cs @@ -56,15 +56,17 @@ private string StackTop() return top; } - public Dictionary Evaluate(DynamicArray constants) + public Dictionary Evaluate(DynamicArray constants, bool print = false) { Dictionary hlsl = new(); try { - Console.WriteLine($"--------Evaluating Bytecode:"); + if(print) + Console.WriteLine($"--------Evaluating Bytecode:"); foreach ((int _ip, var op) in Opcodes.Select((value, index) => (index, value))) { - Console.WriteLine($"{op.op} : {TfxBytecodeOp.TfxToString(op, constants)}"); + if (print) + Console.WriteLine($"{op.op} : {TfxBytecodeOp.TfxToString(op, constants)}"); switch (op.op) { case TfxBytecode.Add: @@ -270,7 +272,9 @@ public Dictionary Evaluate(DynamicArray constants) case TfxBytecode.PopOutput: //Temp.AddRange(Stack); - Console.WriteLine($"----Output Stack Count: {Stack.Count}"); + if (print) + Console.WriteLine($"----Output Stack Count: {Stack.Count}"); + if(Stack.Count == 0) //Shouldnt happen hlsl.TryAdd(((PopOutputData)op.data).slot, "float4(0, 0, 0, 0)"); else if(Stack.Count > 1) //Shouldnt happen @@ -304,7 +308,8 @@ public Dictionary Evaluate(DynamicArray constants) break; default: - Console.WriteLine($"Not Implemented: {op.op}"); + if (print) + Console.WriteLine($"Not Implemented: {op.op}"); break; } diff --git a/Tiger/Schema/Shaders/UsfConverter.cs b/Tiger/Schema/Shaders/UsfConverter.cs index df6a5672..c762e3fe 100644 --- a/Tiger/Schema/Shaders/UsfConverter.cs +++ b/Tiger/Schema/Shaders/UsfConverter.cs @@ -3,6 +3,45 @@ namespace Tiger.Schema; +public struct TextureView +{ + public string Dimension; + public string Type; + public string Variable; + public int Index; +} + +public struct Buffer +{ + public string Variable; + public string Type; + public int Index; +} + +public struct Cbuffer +{ + public string Variable; + public string Type; + public int Count; + public int Index; +} + +public struct Input +{ + public string Variable; + public string Type; + public int Index; + public string Semantic; +} + +public struct Output +{ + public string Variable; + public string Type; + public int Index; + public string Semantic; +} + public class UsfConverter { private StringReader hlsl; diff --git a/Tiger/Schema/Static/StaticMapData.cs b/Tiger/Schema/Static/StaticMapData.cs index 81d2ba61..28e4ddbc 100644 --- a/Tiger/Schema/Static/StaticMapData.cs +++ b/Tiger/Schema/Static/StaticMapData.cs @@ -679,15 +679,15 @@ public struct D2Class_706C8080 //public Vector4 Unk40; //public Vector4 Unk50; - //[SchemaField(0x80, TigerStrategy.DESTINY2_SHADOWKEEP_2601)] - //[SchemaField(0xC0, TigerStrategy.DESTINY2_WITCHQUEEN_6307)] - //public IMaterial UnkC0; - //[SchemaField(0x84, TigerStrategy.DESTINY2_SHADOWKEEP_2601)] - //[SchemaField(0xC4, TigerStrategy.DESTINY2_WITCHQUEEN_6307)] - //public IMaterial UnkC4; - //[SchemaField(TigerStrategy.DESTINY2_SHADOWKEEP_2601, Obsolete = true)] - //[SchemaField(0xC8, TigerStrategy.DESTINY2_WITCHQUEEN_6307)] - //public IMaterial UnkC8; + [SchemaField(0x80, TigerStrategy.DESTINY2_SHADOWKEEP_2601)] + [SchemaField(0xC0, TigerStrategy.DESTINY2_BEYONDLIGHT_3402)] + public FileHash Shading; + [SchemaField(0x84, TigerStrategy.DESTINY2_SHADOWKEEP_2601)] + [SchemaField(0xC4, TigerStrategy.DESTINY2_BEYONDLIGHT_3402)] + public FileHash Volumetric; + [SchemaField(TigerStrategy.DESTINY2_SHADOWKEEP_2601, Obsolete = true)] + [SchemaField(0xC8, TigerStrategy.DESTINY2_WITCHQUEEN_6307)] + public FileHash Lightprobe; [SchemaField(0x84, TigerStrategy.DESTINY1_RISE_OF_IRON)] [SchemaField(0x88, TigerStrategy.DESTINY2_SHADOWKEEP_2601)] @@ -851,15 +851,70 @@ public struct D2Class_BA6C8080 public Vector4 Unk40; } +/// +/// A light that casts shadows +/// [SchemaStruct("5E6C8080", 0x20)] -public struct SMapSpotLightResource +public struct SMapShadowingLightResource { [SchemaField(0x10)] - public Tag Unk10; // D2Class_716C8080, might be related to lights for entities? - [SchemaField(0x1C)] + public Tag Unk10; // D2Class_716C8080, might be related to lights for entities? [SchemaField(0x1C)] public TigerHash Unk1C; } +[SchemaStruct(TigerStrategy.DESTINY2_WITCHQUEEN_6307, "716C8080", 0x110)] +public struct D2Class_716C8080 +{ + [SchemaField(0xE8, TigerStrategy.DESTINY2_WITCHQUEEN_6307)] + public Tag UnkE8; + [SchemaField(0xEC, TigerStrategy.DESTINY2_WITCHQUEEN_6307)] + public Tag UnkEC; +} + +/// +/// Usually a flat plane for screen-space reflected water +/// +[SchemaStruct(TigerStrategy.DESTINY2_WITCHQUEEN_6307, "D4688080", 0x70)] +public struct SMapWaterDecal +{ + [SchemaField(0x10)] + public EntityModel Model; +} + +[SchemaStruct(TigerStrategy.DESTINY2_WITCHQUEEN_6307, "7B918080", 0x1C)] +public struct D2Class_7B918080 +{ + public ResourcePointer Pointer; //21918080 +} + +/// +/// Havok volume data resource. +/// +[SchemaStruct(TigerStrategy.DESTINY2_WITCHQUEEN_6307, "21918080", 0x20)] +public struct D2Class_21918080 +{ + [SchemaField(0x10)] + public FileHash HavokVolume; // type 27 subtype 0 + public TigerHash Unk14; +} + +/// +/// Unk Havok data resource. +/// +[SchemaStruct("C26A8080", 0x18)] +public struct D2Class_C26A8080 +{ + [SchemaField(0x10)] + public Tag Unk10; // C46A8080 +} + +[SchemaStruct("C46A8080", 0x18)] +public struct D2Class_C46A8080 +{ + [SchemaField(0x8)] + public FileHash Unk08; // Havok Volume +} + // /// // /// Boss entity data resource? // /// @@ -944,16 +999,6 @@ public struct D2Class_19808080 // public dynamic? Unk00; // } // -// /// -// /// Havok volume data resource. -// /// -// [SchemaStruct("21918080", 0x20)] -// public struct D2Class_21918080 -// { -// [SchemaField(0x10), DestinyField(FieldType.FileHash)] -// public Tag HavokVolume; // type 27 subtype 0 -// public TigerHash Unk14; -// } // // /// // /// Unk data resource. @@ -965,16 +1010,6 @@ public struct D2Class_19808080 // public Tag Unk10; // C2858080 // } // -// /// -// /// Unk data resource. -// /// -// [SchemaStruct("C26A8080", 0x18)] -// public struct D2Class_C26A8080 -// { -// [SchemaField(0x10), DestinyField(FieldType.FileHash)] -// public Tag Unk10; // C46A8080 -// } -// // // /// // /// Unk data resource. diff --git a/Tiger/Schema/Static/StaticMesh.cs b/Tiger/Schema/Static/StaticMesh.cs index 4142b0dd..526cfd68 100644 --- a/Tiger/Schema/Static/StaticMesh.cs +++ b/Tiger/Schema/Static/StaticMesh.cs @@ -42,6 +42,7 @@ public class MeshPart public List VertexTangents = new List(); public List VertexColours = new List(); public IMaterial? Material; + public MaterialType MaterialType; public int GroupIndex = 0; } @@ -65,10 +66,8 @@ public void SaveMaterialsFromParts(ExporterScene scene, List parts) foreach (var part in parts) { if (part.Material == null) - { continue; - } - scene.Materials.Add(new ExportMaterial(part.Material)); + scene.Materials.Add(new ExportMaterial(part.Material, part.MaterialType)); } } @@ -92,14 +91,14 @@ private List LoadDecals(ExportDetailLevel detailLevel) { if (detailLevel == ExportDetailLevel.MostDetailed) { - if (decalPartEntry.LODLevel != 1 && decalPartEntry.LODLevel != 2 && decalPartEntry.LODLevel != 10) + if (!decalPartEntry.Lod.IsHighestLevel()) { continue; } } else if (detailLevel == ExportDetailLevel.LeastDetailed) { - if (decalPartEntry.LODLevel == 1 || decalPartEntry.LODLevel == 2 || decalPartEntry.LODLevel == 10) + if (decalPartEntry.Lod.IsHighestLevel()) { continue; } @@ -107,6 +106,7 @@ private List LoadDecals(ExportDetailLevel detailLevel) StaticPart part = new StaticPart(decalPartEntry); part.GetDecalData(decalPartEntry, _tag); part.Material = decalPartEntry.Material; + part.MaterialType = MaterialType.Transparent; parts.Add(part); } diff --git a/Tiger/Schema/Static/StaticMeshData.cs b/Tiger/Schema/Static/StaticMeshData.cs index 38659351..fc452b56 100644 --- a/Tiger/Schema/Static/StaticMeshData.cs +++ b/Tiger/Schema/Static/StaticMeshData.cs @@ -84,6 +84,15 @@ private List GenerateParts(Dictionary staticPa { StaticPart part = new(staticPartEntry); part.Material = materialMap[i]; + part.MaterialType = MaterialType.Opaque; + + if (part.Material is null || + part.Material.VertexShader is null || + part.Material.PixelShader is null || + part.Material.Unk08 != 1 || + (part.Material.Unk20 & 0x8000) != 0) + continue; + part.GetAllData(_tag.Buffers[staticPartEntry.BufferIndex], parent); parts.Add(part); } @@ -101,7 +110,7 @@ public Dictionary GetPartsOfDetailLevel(ExportDetailLevel for (int i = 0; i < _tag.Parts.Count; i++) { var staticPartEntry = _tag.Parts[i]; - if (staticPartEntry.DetailLevel == 1 || staticPartEntry.DetailLevel == 2 || staticPartEntry.DetailLevel == 10) + if (staticPartEntry.Lod.IsHighestLevel()) { staticPartEntries.Add(i, staticPartEntry); } @@ -112,7 +121,7 @@ public Dictionary GetPartsOfDetailLevel(ExportDetailLevel for (int i = 0; i < _tag.Parts.Count; i++) { var staticPartEntry = _tag.Parts[i]; - if (staticPartEntry.DetailLevel != 1 && staticPartEntry.DetailLevel != 2 && staticPartEntry.DetailLevel != 10) + if (!staticPartEntry.Lod.IsHighestLevel()) { staticPartEntries.Add(i, staticPartEntry); } @@ -196,16 +205,44 @@ private List GenerateParts(Dictionary staticPa if (_tag.Meshes.Count == 0) return new List(); SStaticMeshBuffers mesh = _tag.Meshes[0]; + // Get material map + int lowestDetail = 0xFF; + foreach (var d2Class386D8080 in _tag.MaterialAssignments) + { + if (d2Class386D8080.RenderStage < lowestDetail) + { + lowestDetail = d2Class386D8080.RenderStage; + } + } + + Dictionary materialMap = new(); + for (var i = 0; i < _tag.MaterialAssignments.Count; i++) + { + var entry = _tag.MaterialAssignments[i]; + if (entry.RenderStage == lowestDetail) + { + materialMap.Add(entry.PartIndex, parent.Materials[i].Material); + } + } + foreach (var (i, staticPartEntry) in staticPartEntries) { - var material = parent.Materials[i].Material; - if (material is null || material.Unk08 != 1) - continue; - - StaticPart part = new StaticPart(staticPartEntry); - part.Material = material; - part.GetAllData(mesh, parent); - parts.Add(part); + if (materialMap.ContainsKey(i)) + { + StaticPart part = new StaticPart(staticPartEntry); + part.Material = materialMap[i]; + part.MaterialType = MaterialType.Opaque; + + if (part.Material is null || + part.Material.VertexShader is null || + part.Material.PixelShader is null || + part.Material.Unk08 != 1 || + (part.Material.Unk20 & 0x8000) != 0) + continue; + + part.GetAllData(mesh, parent); + parts.Add(part); + } } return parts; } @@ -220,26 +257,14 @@ public Dictionary GetPartsOfDetailLevel(ExportDetailLevel var part = _tag.Parts[mat.PartIndex]; if (part.BufferIndex == 0) { - switch (detailLevel) + var staticPartEntry = _tag.Parts[i]; + if (staticPartEntry.Lod.IsHighestLevel()) { - case ExportDetailLevel.MostDetailed: - if (part.DetailLevel == 1 || part.DetailLevel == 2 || part.DetailLevel == 10) - { - staticPartEntries.Add(i, part); - } - break; - case ExportDetailLevel.LeastDetailed: - if (part.DetailLevel != 1 && part.DetailLevel != 2 && part.DetailLevel != 10) - { - staticPartEntries.Add(i, part); - } - break; - default: - staticPartEntries.Add(i, part); - break; + staticPartEntries.Add(i, staticPartEntry); } } } + return staticPartEntries; } } diff --git a/Tiger/Schema/Static/StaticMeshStructs.cs b/Tiger/Schema/Static/StaticMeshStructs.cs index b04a2172..18c9c737 100644 --- a/Tiger/Schema/Static/StaticMeshStructs.cs +++ b/Tiger/Schema/Static/StaticMeshStructs.cs @@ -41,7 +41,7 @@ public struct SStaticMeshDecal [SchemaField(TigerStrategy.DESTINY2_SHADOWKEEP_2601)] [SchemaField(TigerStrategy.DESTINY2_WITCHQUEEN_6307, Obsolete = true)] public short Unk02; - public sbyte LODLevel; + public ELod Lod; public sbyte Unk03; public short PrimitiveType; [SchemaField(TigerStrategy.DESTINY2_WITCHQUEEN_6307)] @@ -106,7 +106,7 @@ public struct SStaticMeshPart public uint IndexOffset; public uint IndexCount; public ushort BufferIndex; - public sbyte DetailLevel; + public ELod Lod; public sbyte PrimitiveType; } diff --git a/Tiger/Schema/Static/StaticPart.cs b/Tiger/Schema/Static/StaticPart.cs index 29cd1c6d..4d4d6505 100644 --- a/Tiger/Schema/Static/StaticPart.cs +++ b/Tiger/Schema/Static/StaticPart.cs @@ -9,24 +9,36 @@ public StaticPart(SStaticPart terrainPartEntry) : base() { IndexOffset = terrainPartEntry.IndexOffset; IndexCount = terrainPartEntry.IndexCount; - LodCategory = (ELodCategory)terrainPartEntry.DetailLevel; + LodCategory = terrainPartEntry.Lod.DetailLevel; PrimitiveType = PrimitiveType.TriangleStrip; + MaterialType = Shaders.MaterialType.Opaque; + } + + public StaticPart(SMeshGroup terrainPartEntry) : base() + { + IndexOffset = terrainPartEntry.IndexOffset; + IndexCount = terrainPartEntry.IndexCount; + LodCategory = ELodCategory.LowPolyGeom1; + PrimitiveType = PrimitiveType.TriangleStrip; + MaterialType = Shaders.MaterialType.Opaque; } public StaticPart(SStaticMeshPart staticPartEntry) : base() { IndexOffset = staticPartEntry.IndexOffset; IndexCount = staticPartEntry.IndexCount; - LodCategory = (ELodCategory)staticPartEntry.DetailLevel; + LodCategory = staticPartEntry.Lod.DetailLevel; PrimitiveType = (PrimitiveType)staticPartEntry.PrimitiveType; + MaterialType = Shaders.MaterialType.Opaque; } public StaticPart(SStaticMeshDecal decalPartEntry) : base() { IndexOffset = decalPartEntry.IndexOffset; IndexCount = decalPartEntry.IndexCount; - LodCategory = (ELodCategory)decalPartEntry.LODLevel; + LodCategory = decalPartEntry.Lod.DetailLevel; PrimitiveType = (PrimitiveType)decalPartEntry.PrimitiveType; + MaterialType = Shaders.MaterialType.Transparent; } public StaticPart(SStaticMeshData_D1 staticPartEntry) : base() @@ -71,20 +83,20 @@ public void GetAllData(SStaticMeshBuffers buffers, SStaticMesh container) if (Strategy.CurrentStrategy <= TigerStrategy.DESTINY2_SHADOWKEEP_2999) { - InputSignature[] inputSignatures = Material.VertexShader.InputSignatures.ToArray(); + DXBCIOSignature[] inputSignatures = Material.VertexShader.InputSignatures.ToArray(); int b0Stride = buffers.Vertices0.TagData.Stride; int b1Stride = buffers.Vertices1?.TagData.Stride ?? 0; - List inputSignatures0 = new(); - List inputSignatures1 = new(); + List inputSignatures0 = new(); + List inputSignatures1 = new(); int stride = 0; - foreach (InputSignature inputSignature in inputSignatures) + foreach (DXBCIOSignature inputSignature in inputSignatures) { if (stride < b0Stride) inputSignatures0.Add(inputSignature); else inputSignatures1.Add(inputSignature); - if (inputSignature.Semantic == InputSemantic.Colour || inputSignature.Semantic == InputSemantic.BlendIndices || inputSignature.Semantic == InputSemantic.BlendWeight) + if (inputSignature.Semantic == DXBCSemantic.Colour || inputSignature.Semantic == DXBCSemantic.BlendIndices || inputSignature.Semantic == DXBCSemantic.BlendWeight) stride += inputSignature.GetNumberOfComponents() * 1; // 1 byte per component else stride += inputSignature.GetNumberOfComponents() * 2; // 2 bytes per component @@ -127,13 +139,13 @@ public void GetDecalData(SStaticMeshDecal mesh, SStaticMesh container) // Have to call it like this b/c we don't know the format of the vertex data here if (Strategy.CurrentStrategy <= TigerStrategy.DESTINY2_SHADOWKEEP_2999) { - List inputSignatures = mesh.Material.VertexShader.InputSignatures; + List inputSignatures = mesh.Material.VertexShader.InputSignatures; int b0Stride = mesh.Vertices0.TagData.Stride; int b1Stride = mesh.Vertices1?.TagData.Stride ?? 0; - List inputSignatures0 = new(); - List inputSignatures1 = new(); + List inputSignatures0 = new(); + List inputSignatures1 = new(); int stride = 0; - foreach (InputSignature inputSignature in inputSignatures) + foreach (DXBCIOSignature inputSignature in inputSignatures) { if (stride < b0Stride) { @@ -144,7 +156,7 @@ public void GetDecalData(SStaticMeshDecal mesh, SStaticMesh container) inputSignatures1.Add(inputSignature); } - if (inputSignature.Semantic == InputSemantic.Colour) + if (inputSignature.Semantic == DXBCSemantic.Colour) { stride += inputSignature.GetNumberOfComponents() * 1; // 1 byte per component } diff --git a/Tiger/Schema/Static/Terrain.cs b/Tiger/Schema/Static/Terrain.cs index 3f317ce3..b7030071 100644 --- a/Tiger/Schema/Static/Terrain.cs +++ b/Tiger/Schema/Static/Terrain.cs @@ -15,35 +15,20 @@ public Terrain(FileHash hash) : base(hash) } // To test use edz.strike_hmyn and alleys_a adf6ae80 - public void LoadIntoExporter(ExporterScene scene, string saveDirectory, bool bSaveShaders, bool exportStatic = false) + public void LoadIntoExporter(ExporterScene scene, string saveDirectory) { // Uses triangle strip + only using first set of vertices and indices Dictionary parts = new Dictionary(); List dyeMaps = new List(); - foreach (var partEntry in _tag.StaticParts) - { - if (partEntry.DetailLevel == 0) - { - if ((partEntry.Material is null || partEntry.Material.VertexShader is null) && Strategy.CurrentStrategy != TigerStrategy.DESTINY1_RISE_OF_IRON) - continue; - - var part = MakePart(partEntry); - parts.TryAdd(part, partEntry.Material); - - scene.Materials.Add(new ExportMaterial(partEntry.Material, true)); - part.Material = partEntry.Material; - - if (exportStatic) //Need access to material early, before scene system exports - partEntry.Material.SavePixelShader($"{saveDirectory}/Shaders", true); - } - } int terrainTextureIndex = 14; Texture lastValidEntry = null; for (int i = 0; i < _tag.MeshGroups.Count; i++) { - var partEntry = _tag.MeshGroups[i]; - if (partEntry.Dyemap == null) + var meshGroup = _tag.MeshGroups[i]; + // Check if the current Dyemap is null + + if (meshGroup.Dyemap == null) { if (lastValidEntry != null) { @@ -53,7 +38,7 @@ public void LoadIntoExporter(ExporterScene scene, string saveDirectory, bool bSa } else // Use the first valid dyemap if it gets to this point { - var firstValidDyemap = _tag.MeshGroups.FirstOrDefault(x => x.Dyemap != null).Dyemap; + var firstValidDyemap = _tag.MeshGroups.First(x => x.Dyemap != null).Dyemap; if (firstValidDyemap != null) { scene.Textures.Add(firstValidDyemap); @@ -64,33 +49,62 @@ public void LoadIntoExporter(ExporterScene scene, string saveDirectory, bool bSa else { // Update lastValidEntry with the current Dyemap - lastValidEntry = partEntry.Dyemap; - scene.Textures.Add(partEntry.Dyemap); - dyeMaps.Add(partEntry.Dyemap); + lastValidEntry = meshGroup.Dyemap; + scene.Textures.Add(meshGroup.Dyemap); + dyeMaps.Add(meshGroup.Dyemap); } - } - foreach (var part in parts) - { - TransformPositions(part.Key); - TransformTexcoords(part.Key); - TransformVertexColors(part.Key); - } + foreach (var partEntry in _tag.StaticParts.Where(x => x.GroupIndex == i)) + { + // MainGeom0 LOD0, GripStock0 LOD1, Stickers0 LOD2? + if (partEntry.Lod.DetailLevel == ELodCategory.MainGeom0) + { + if (partEntry.Material != null || partEntry.Material.VertexShader != null) + { + var part = MakePart(partEntry); - scene.AddStatic(Hash, parts.Keys.ToList()); - // For now we pre-transform it - if (!exportStatic) - { - scene.AddStaticInstance(Hash, 1, Vector4.Zero, Vector3.Zero); + scene.Materials.Add(new ExportMaterial(partEntry.Material, MaterialType.Opaque, true)); + part.Material = partEntry.Material; - for (int i = 0; i < dyeMaps.Count; i++) - { - scene.AddTerrainDyemap(Hash, dyeMaps[i].Hash); + //Need access to material early, before scene system exports + partEntry.Material.SaveShaders($"{saveDirectory}", MaterialType.Opaque, true); + + TransformPositions(part); + TransformTexcoords(part); + TransformVertexColors(part); + + SBoxHandler.SaveVMAT(saveDirectory, $"{part.Material.FileHash}", part.Material, true, dyeMaps); + + parts.TryAdd(part, partEntry.Material); + } + } } + + //{ // LOD3? + // var part = MakeLODPart(meshGroup, i); + + // scene.Materials.Add(new ExportMaterial(_tag.Unk6C, MaterialType.Opaque, true)); + // part.Material = _tag.Unk6C; + + // TransformPositions(part); + // TransformTexcoords(part); + // TransformVertexColors(part); + + // parts.TryAdd(part, _tag.Unk6C); + //} + + + scene.AddTerrain($"{Hash}_{i}", parts.Keys.ToList()); + SBoxHandler.SaveTerrainVMDL($"{Hash}_{i}", saveDirectory, parts.Keys.ToList()); + + parts.Clear(); } - if (CharmInstance.GetSubsystem().GetS2VMDLExportEnabled()) - Source2Handler.SaveTerrainVMDL(saveDirectory, Hash, parts.Keys.ToList(), TagData); + //scene.AddStaticInstance(Hash, 1, Vector4.Zero, Vector3.Zero); + for (int i = 0; i < dyeMaps.Count; i++) + { + scene.AddTerrainDyemap(Hash, dyeMaps[i].Hash); + } } public StaticPart MakePart(SStaticPart entry) @@ -98,6 +112,9 @@ public StaticPart MakePart(SStaticPart entry) StaticPart part = new(entry); part.GroupIndex = entry.GroupIndex; part.Indices = _tag.Indices1.GetIndexData(PrimitiveType.TriangleStrip, entry.IndexOffset, entry.IndexCount); + + //Console.WriteLine($"{_tag.Indices2.GetIndexData(PrimitiveType.TriangleStrip, _tag.MeshGroups[part.GroupIndex].Unk48, _tag.MeshGroups[part.GroupIndex].unk).Count}"); + // Get unique vertex indices we need to get data for HashSet uniqueVertexIndices = new HashSet(); foreach (UIntVector3 index in part.Indices) @@ -108,37 +125,55 @@ public StaticPart MakePart(SStaticPart entry) } part.VertexIndices = uniqueVertexIndices.ToList(); - if (Strategy.CurrentStrategy != TigerStrategy.DESTINY1_RISE_OF_IRON) + List inputSignatures = entry.Material.VertexShader.InputSignatures; + int b0Stride = _tag.Vertices1.TagData.Stride; + int b1Stride = _tag.Vertices2?.TagData.Stride ?? 0; + List inputSignatures0 = new(); + List inputSignatures1 = new(); + int stride = 0; + foreach (DXBCIOSignature inputSignature in inputSignatures) { - List inputSignatures = entry.Material.VertexShader.InputSignatures; - int b0Stride = _tag.Vertices1.TagData.Stride; - int b1Stride = _tag.Vertices2?.TagData.Stride ?? 0; - List inputSignatures0 = new(); - List inputSignatures1 = new(); - int stride = 0; - foreach (InputSignature inputSignature in inputSignatures) - { - if (stride < b0Stride) - inputSignatures0.Add(inputSignature); - else - inputSignatures1.Add(inputSignature); - - if (inputSignature.Semantic == InputSemantic.Colour) - stride += inputSignature.GetNumberOfComponents() * 1; // 1 byte per component - else - stride += inputSignature.GetNumberOfComponents() * 2; // 2 bytes per component - } - - Log.Debug($"Reading vertex buffers {_tag.Vertices1.Hash}/{_tag.Vertices1.TagData.Stride}/{inputSignatures.Where(s => s.BufferIndex == 0).DebugString()} and {_tag.Vertices2?.Hash}/{_tag.Vertices2?.TagData.Stride}/{inputSignatures.Where(s => s.BufferIndex == 1).DebugString()}"); - _tag.Vertices1.ReadVertexDataSignatures(part, uniqueVertexIndices, inputSignatures0, true); - _tag.Vertices2.ReadVertexDataSignatures(part, uniqueVertexIndices, inputSignatures1, true); + if (stride < b0Stride) + inputSignatures0.Add(inputSignature); + else + inputSignatures1.Add(inputSignature); + if (inputSignature.Semantic == DXBCSemantic.Colour) + stride += inputSignature.GetNumberOfComponents() * 1; // 1 byte per component + else + stride += inputSignature.GetNumberOfComponents() * 2; // 2 bytes per component } - else // Can't get input semantics (yet) for D1 / PS4 + + Log.Debug($"Reading vertex buffers {_tag.Vertices1.Hash}/{_tag.Vertices1.TagData.Stride}/{inputSignatures.Where(s => s.BufferIndex == 0).DebugString()} and {_tag.Vertices2?.Hash}/{_tag.Vertices2?.TagData.Stride}/{inputSignatures.Where(s => s.BufferIndex == 1).DebugString()}"); + _tag.Vertices1.ReadVertexDataSignatures(part, uniqueVertexIndices, inputSignatures0, true); + _tag.Vertices2.ReadVertexDataSignatures(part, uniqueVertexIndices, inputSignatures1, true); + + //_tag.Vertices1.ReadVertexData(part, uniqueVertexIndices, 0, -1, true); + //_tag.Vertices2.ReadVertexData(part, uniqueVertexIndices, 0, -1, true); + + return part; + } + + public StaticPart MakeLODPart(SMeshGroup entry, int groupIndex) + { + StaticPart part = new(entry); + + part.GroupIndex = groupIndex; + part.Indices = _tag.Indices2.GetIndexData(PrimitiveType.TriangleStrip, entry.IndexOffset, entry.IndexCount); + + // Get unique vertex indices we need to get data for + HashSet uniqueVertexIndices = new HashSet(); + foreach (UIntVector3 index in part.Indices) { - _tag.Vertices1.ReadVertexData(part, uniqueVertexIndices, 0, _tag.Vertices2 != null ? _tag.Vertices2.TagData.Stride : -1, true); - _tag.Vertices2?.ReadVertexData(part, uniqueVertexIndices, 1, _tag.Vertices1.TagData.Stride, true); + uniqueVertexIndices.Add(index.X); + uniqueVertexIndices.Add(index.Y); + uniqueVertexIndices.Add(index.Z); } + part.VertexIndices = uniqueVertexIndices.ToList(); + + //Log.Debug($"Reading vertex buffers {_tag.Vertices3.Hash}/{_tag.Vertices3.TagData.Stride}/{inputSignatures.Where(s => s.BufferIndex == 0).DebugString()} and {_tag.Vertices4?.Hash}/{_tag.Vertices4?.TagData.Stride}/{inputSignatures.Where(s => s.BufferIndex == 1).DebugString()}"); + _tag.Vertices3.ReadVertexData(part, uniqueVertexIndices, 0, -1, true); + _tag.Vertices4.ReadVertexData(part, uniqueVertexIndices, 0, -1, true); return part; } @@ -216,14 +251,31 @@ public void TransformTexcoords(StaticPart part) } } - public void TransformVertexColors(StaticPart part) + private void TransformVertexColors(StaticPart part) { //Helper for dyemap assignment - //ROI and Pre-BL can have a max of 16 per terrain part - float alpha = part.GroupIndex / 15.0f; + //SK can have up to index 15, maybe more? for (int i = 0; i < part.VertexPositions.Count; i++) { - part.VertexColours.Add(new Vector4(0.0f, 0.0f, 0.0f, alpha)); + int colorIndex = part.GroupIndex % 4; + switch (colorIndex) + { + case 0: + part.VertexColours.Add(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); + break; + case 1: + part.VertexColours.Add(new Vector4(0.0f, 1.0f, 0.0f, 1.0f)); + break; + case 2: + part.VertexColours.Add(new Vector4(0.0f, 0.0f, 1.0f, 1.0f)); + break; + case 3: + part.VertexColours.Add(new Vector4(1.0f, 1.0f, 1.0f, 1.0f)); + break; + default: + part.VertexColours.Add(new Vector4(0.0f, 0.0f, 0.0f, 0.0f)); + break; + }; } } } @@ -231,7 +283,6 @@ public void TransformVertexColors(StaticPart part) /// /// Terrain data resource. /// -[SchemaStruct(TigerStrategy.DESTINY1_RISE_OF_IRON, "371C8080", 0x20)] [SchemaStruct(TigerStrategy.DESTINY2_SHADOWKEEP_2601, "4B718080", 0x20)] [SchemaStruct(TigerStrategy.DESTINY2_BEYONDLIGHT_3402, "7D6C8080", 0x20)] public struct SMapTerrainResource @@ -240,7 +291,6 @@ public struct SMapTerrainResource public short Unk10; // tile x-y coords? public short Unk12; public TigerHash Unk14; - [NoLoad] public Terrain Terrain; public Tag TerrainBounds; } @@ -248,7 +298,6 @@ public struct SMapTerrainResource /// /// Terrain _tag. /// -[SchemaStruct(TigerStrategy.DESTINY1_RISE_OF_IRON, "2E1B8080", 0xB0)] [SchemaStruct(TigerStrategy.DESTINY2_SHADOWKEEP_2601, "4F718080", 0xB0)] [SchemaStruct(TigerStrategy.DESTINY2_BEYONDLIGHT_3402, "816C8080", 0xB0)] public struct STerrain @@ -258,7 +307,7 @@ public struct STerrain public Vector4 Unk10; public Vector4 Unk20; public Vector4 Unk30; - [SchemaField(0x58, TigerStrategy.DESTINY1_RISE_OF_IRON)] + [SchemaField(0x58, TigerStrategy.DESTINY2_SHADOWKEEP_2601)] [SchemaField(0x50, TigerStrategy.DESTINY2_BEYONDLIGHT_3402)] public DynamicArray MeshGroups; @@ -267,21 +316,19 @@ public struct STerrain public IndexBuffer Indices1; public IMaterial Unk6C; public IMaterial Unk70; - [SchemaField(0x80, TigerStrategy.DESTINY1_RISE_OF_IRON)] + [SchemaField(0x80, TigerStrategy.DESTINY2_SHADOWKEEP_2601)] [SchemaField(0x78, TigerStrategy.DESTINY2_BEYONDLIGHT_3402)] public DynamicArray StaticParts; public VertexBuffer Vertices3; public VertexBuffer Vertices4; public IndexBuffer Indices2; - - [SchemaField(0xA4, TigerStrategy.DESTINY1_RISE_OF_IRON)] - [SchemaField(TigerStrategy.DESTINY2_SHADOWKEEP_2601, Obsolete = true)] - public IMaterial UnkA4; - [SchemaField(TigerStrategy.DESTINY2_SHADOWKEEP_2601, Obsolete = true)] - public Texture UnkA8; // A top down view of the terrain in-game (assuming for LOD) + [SchemaField(0xA0, TigerStrategy.DESTINY2_SHADOWKEEP_2601)] + [SchemaField(0x98, TigerStrategy.DESTINY2_BEYONDLIGHT_3402)] + public int Unk98; + public int Unk9C; + public int UnkA0; } -[SchemaStruct(TigerStrategy.DESTINY1_RISE_OF_IRON, "7F1A8080", 0x60)] [SchemaStruct(TigerStrategy.DESTINY2_SHADOWKEEP_2601, "54718080", 0x60)] [SchemaStruct(TigerStrategy.DESTINY2_BEYONDLIGHT_3402, "866C8080", 0x60)] public struct SMeshGroup @@ -296,14 +343,11 @@ public struct SMeshGroup public uint Unk3C; public uint Unk40; public uint Unk44; - public uint Unk48; - public uint Unk4C; - [SchemaField(0x58, TigerStrategy.DESTINY1_RISE_OF_IRON)] - [SchemaField(0x50, TigerStrategy.DESTINY2_SHADOWKEEP_2601)] + public uint IndexOffset; // 75% sure this is right + public uint IndexCount; public Texture Dyemap; } -[SchemaStruct(TigerStrategy.DESTINY1_RISE_OF_IRON, "481A8080", 0x0C)] [SchemaStruct(TigerStrategy.DESTINY2_SHADOWKEEP_2601, "52718080", 0x0C)] [SchemaStruct(TigerStrategy.DESTINY2_BEYONDLIGHT_3402, "846C8080", 0x0C)] public struct SStaticPart @@ -312,5 +356,5 @@ public struct SStaticPart public uint IndexOffset; public ushort IndexCount; public byte GroupIndex; - public byte DetailLevel; + public ELod Lod; } diff --git a/Tiger/Schema/Test.cs b/Tiger/Schema/Test.cs deleted file mode 100644 index 5b70c8a9..00000000 --- a/Tiger/Schema/Test.cs +++ /dev/null @@ -1,33 +0,0 @@ -//namespace Tiger.Schema; - -//[SchemaStruct("5B698080", 0x70)] -//public struct UnkLights -//{ -// public long ThisSize; -// public DynamicArray Unk0x08; -// public DynamicArray Unk0x18; -// public FileHash Unk0x28; -// public FileHash Unk0x2C; -// public ushort Unk0x30; -// public ushort Unk0x32; -// public ushort Unk0x34; -// public ushort Unk0x36; -// public FileHash Unk0x38; -// [SchemaField(0x40)] -// public Vector4 Unk0x40; -// public Vector4 Unk0x50; -//} - -//[SchemaStruct("63698080", 0x8)] -//public struct Unk63698080 -//{ -// public FileHash Unk00; -// public ushort StartIndex; -// public ushort Count; -//} - -//[SchemaStruct("64698080", 0x10)] -//public struct Unk64698080 -//{ -// public Vector4 Translation; -//} diff --git a/Tiger/Schema/Vector.cs b/Tiger/Schema/Vector.cs index 07fa92a9..28716ee0 100644 --- a/Tiger/Schema/Vector.cs +++ b/Tiger/Schema/Vector.cs @@ -311,6 +311,18 @@ public double Magnitude } } + public Vector4 Normalize() + { + float magnitude = (float)Magnitude; + + X /= magnitude; + Y /= magnitude; + Z /= magnitude; + W /= magnitude; + + return new Vector4(X, Y, Z, W); + } + public Vector3 ToVec3() { return new Vector3(X, Y, Z); @@ -361,6 +373,11 @@ public float this[int index] } } + public static Vector4 operator -(Vector4 x, Vector4 y) + { + return new Vector4(x.X - y.X, x.Y - y.Y, x.Z - y.Z, x.W - y.W); + } + public static bool operator ==(Vector4 x, Vector4 y) { return x.X == y.X && diff --git a/Tiger/Tiger.csproj b/Tiger/Tiger.csproj index 63fed26f..28488cf8 100644 --- a/Tiger/Tiger.csproj +++ b/Tiger/Tiger.csproj @@ -23,6 +23,8 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + + @@ -51,6 +53,9 @@ Always + + Always + Always diff --git a/Tomograph/AtlasTests.cs b/Tomograph/AtlasTests.cs deleted file mode 100644 index 30fba3fd..00000000 --- a/Tomograph/AtlasTests.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Tomograph; - -[TestClass] -public class AtlasTests -{ - - [TestMethod] - public void StaticMesh_WithValidHash_ExportsAllAtlasInformation() - { - // Method intentionally left empty. - } - - [TestMethod] - public void StaticMap_WithValidHash_ExportsAllAtlasInformation() - { - // Method intentionally left empty. - } -} diff --git a/Tomograph/Tomograph.csproj b/Tomograph/Tomograph.csproj index 5f1ab336..51585586 100644 --- a/Tomograph/Tomograph.csproj +++ b/Tomograph/Tomograph.csproj @@ -31,7 +31,6 @@ -