-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMeshTextureDuplicationPlugin.cs
More file actions
225 lines (192 loc) · 10.7 KB
/
MeshTextureDuplicationPlugin.cs
File metadata and controls
225 lines (192 loc) · 10.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
using DuplicationPluginInjectorPlugin;
using Frosty.Controls;
using Frosty.Core;
using Frosty.Core.Controls;
using Frosty.Core.Viewport;
using Frosty.Core.Windows;
using FrostySdk;
using FrostySdk.Ebx;
using FrostySdk.IO;
using FrostySdk.Managers;
using FrostySdk.Resources;
using MeshSetPlugin.Resources;
using MeshTextureDuplicationPlugin.Windows;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
using System.Linq;
using static DuplicationPlugin.DuplicationTool;
namespace MeshTextureDuplicationPlugin
{
[RegisterDuplicationExtension]
public class MeshTextureDuplicationExtension : DuplicateAssetExtension {
public override string AssetType => "MeshAsset";
public override EbxAssetEntry DuplicateAsset(EbxAssetEntry entry, string newName, bool createNew, Type newType)
{
//2017 battlefront meshes always have lowercase names. This doesn't apply to all games, but its still safer to do so
newName = newName.ToLower();
// Duplicate the ebx
EbxAssetEntry newEntry = base.DuplicateAsset(entry, newName, createNew, newType);
EbxAsset newAsset = App.AssetManager.GetEbx(newEntry);
// Get the original asset root object data
EbxAsset asset = App.AssetManager.GetEbx(entry);
dynamic meshProperties = asset.RootObject;
dynamic newMeshProperties = newAsset.RootObject;
//Get the original res entry and duplicate it
ResAssetEntry resAsset = App.AssetManager.GetResEntry(meshProperties.MeshSetResource);
ResAssetEntry newResAsset = DuplicateRes(resAsset, newName, ResourceType.MeshSet);
//Since this is a mesh we need to get the meshSet for the duplicated entry and set it up
MeshSet meshSet = App.AssetManager.GetResAs<MeshSet>(newResAsset);
meshSet.FullName = newResAsset.Name;
//Go through all of the lods and duplicate their chunks
foreach (MeshSetLod lod in meshSet.Lods)
{
lod.Name = newResAsset.Name;
//Double check that the lod actually has a chunk id. If it doesn't this means the data is inline and we don't need to worry
if (lod.ChunkId != Guid.Empty)
{
//Get the original chunk and dupe it
ChunkAssetEntry chunk = App.AssetManager.GetChunkEntry(lod.ChunkId);
ChunkAssetEntry newChunk = DuplicateChunk(chunk);
//Now set the params for the lod
lod.ChunkId = newChunk.Id;
//Link the res and chunk
newResAsset.LinkAsset(newChunk);
foreach (var section in lod.Sections)
{
string sectionName = section.Name;
}
}
}
//Set our new mesh's properties
newMeshProperties.MeshSetResource = newResAsset.ResRid;
newMeshProperties.NameHash = (uint)Utils.HashString(newName);
//Link the res and ebx
newEntry.LinkAsset(newResAsset);
// Stuff for SBDs since SWBF2 is weird
if (ProfilesLibrary.IsLoaded(ProfileVersion.StarWarsBattlefrontII))
{
// Restore the texture params
MeshVariation meshVariation = MeshVariationDb.GetVariations(entry.Guid).Variations[0];
List<MeshVariationMaterial> mats = new List<MeshVariationMaterial>();
List<string> sectionNames = new List<string>();
Dictionary<MeshVariationMaterial, string> idDict = new Dictionary<MeshVariationMaterial, string>();
for (int i = 0; i < newMeshProperties.Materials.Count; i++) // For each material in the new variation
{
PointerRef _matObject = newMeshProperties.Materials[i];
object matObject = _matObject.Internal;
string sectionName = meshSet.Lods[0].Sections[i].Name;
sectionNames.Add(sectionName);
// Check if this is actually a material
if (TypeLibrary.IsSubClassOf(matObject.GetType(), "MeshMaterial") && ((dynamic)matObject).Shader.TextureParameters.Count == 0)
{
// Get the material object as a dynamic, so we can see and edit its properties
dynamic matProperties = matObject as dynamic;
AssetClassGuid guid = matProperties.GetInstanceGuid();
foreach (MeshVariationMaterial mvm in meshVariation.Materials) // For each material in the original assets MVDB Entry
{
if (mvm.MaterialGuid == guid.ExportedGuid) // If it has the same guid
{
mats.Add(mvm);
// We then use its texture params as the texture params in the variation
foreach (dynamic texParam in (dynamic)mvm.TextureParameters)
{
// do NOT do this! it will make a references internally to the same class, making all
// instances the same! bad wannkunst bad!
//matProperties.Shader.TextureParameters.Add(texParam);
dynamic newTexParam = TypeLibrary.CreateObject("TextureShaderParameter");
newTexParam.ParameterName = texParam.ParameterName;
newTexParam.Value = texParam.Value;
matProperties.Shader.TextureParameters.Add(newTexParam);
}
break;
}
}
}
}
MeshDuplicationOptions opts = new MeshDuplicationOptions();
opts.Load();
if (opts.DupeMeshTextures)
{
Application.Current.Dispatcher.Invoke(() =>
{
DuplicateMeshTexturesConfigWindow configWindow = new DuplicateMeshTexturesConfigWindow(mats, entry, sectionNames);
configWindow.Show();
configWindow.Closed += (_, __) =>
{
// FIXME: Account for placeholders in target path.
// FIXME: Ensure unique names.
//App.Logger.Log($"Closed with value {configWindow.shouldDupe}");
//App.Logger.Log($"Enabled value: {configWindow.parameters[0].Enabled}");
if (!configWindow.shouldDupe) { return; } // cancel button clicked
TextureExtension ext = new TextureExtension();
EbxAssetEntry lastTexture = null;
foreach (TextureParameterRepresentation tpr in configWindow.parameters)
{
if (!tpr.Enabled)
{
continue; // box was not checked
}
string[] splitName = newEntry.Name.Split('/');
string targetPath = string.Join("/", splitName.Take(splitName.Length - 1).ToArray()) + "/" + tpr.TargetName;
lastTexture = ext.DuplicateAsset(tpr.OriginalTextureEntry, targetPath, false, null);
}
try
{
App.EditorWindow.DataExplorer.SelectAsset(lastTexture);
}
catch (Exception ex)
{
App.Logger.Log("Unable to show duped texture in data explorer for some reason. Refresh the data explorer by toggling \"Show Modified\".");
}
};
});
if (opts.ShowWarningPopup)
{
FrostyMessageBox.Show("Please finish duplicating the textures (or hit cancel) before moving on to other tasks.\n\nYou can disable this popup in settings.", "Duplication Plugin");
}
}
// Duplicate the sbd
ResAssetEntry oldShaderBlock = App.AssetManager.GetResEntry(entry.Name.ToLower() + "_mesh/blocks");
ResAssetEntry newShaderBlock = DuplicateRes(oldShaderBlock, newResAsset.Name + "_mesh/blocks", ResourceType.ShaderBlockDepot);
ShaderBlockDepot newShaderBlockDepot = App.AssetManager.GetResAs<ShaderBlockDepot>(newShaderBlock);
// TODO: hacky way to generate unique hashes
for (int i = 0; i < newShaderBlockDepot.ResourceCount; i++)
{
ShaderBlockResource res = newShaderBlockDepot.GetResource(i);
res.ChangeHash(meshSet.NameHash);
}
// Change the references in the sbd
for (int lod = 0; lod < meshSet.Lods.Count; lod++)
{
ShaderBlockEntry sbEntry = newShaderBlockDepot.GetSectionEntry(lod);
ShaderBlockMeshVariationEntry sbMvEntry = newShaderBlockDepot.GetResource(sbEntry.Index + 1) as ShaderBlockMeshVariationEntry;
// calculate new entry hash
sbEntry.SetHash(meshSet.NameHash, 0, lod);
sbMvEntry.SetHash(meshSet.NameHash, 0, lod);
// Update the mesh guid
for (int section = 0; section < meshSet.Lods[lod].Sections.Count; section++)
{
MeshParamDbBlock mesh = sbEntry.GetMeshParams(section);
mesh.MeshAssetGuid = newAsset.RootInstanceGuid;
}
}
App.AssetManager.ModifyRes(newShaderBlock.Name, newShaderBlockDepot);
newResAsset.LinkAsset(newShaderBlock);
}
//Modify the res and ebx
App.AssetManager.ModifyRes(newResAsset.Name, meshSet);
App.AssetManager.ModifyEbx(newName, newAsset);
return newEntry;
}
}
public class CheatSheetMenuExtension : MenuExtension
{
public override string MenuItemName => "Texture Placeholder Cheat Sheet";
public override string TopLevelMenuName => "Help";
}
}