Merge branch 'dev' of https://github.com/4sval/FModel into dev

This commit is contained in:
Marlon 2022-12-16 19:54:04 +01:00
commit cdc987b984
No known key found for this signature in database
GPG Key ID: 5C65CA190C38792F
22 changed files with 461 additions and 334 deletions

View File

@ -101,20 +101,8 @@ public class BaseCommunity : BaseIcon
{
if (!bShort) return base.GetCosmeticSeason(seasonNumber);
var s = seasonNumber["Cosmetics.Filter.Season.".Length..];
var number = int.Parse(s);
switch (number)
{
case 10:
s = "X";
break;
case > 18:
number += 2;
s = number.ToString();
break;
}
return $"C{number / 10 + 1} S{s[^1..]}";
(int chapterIdx, int seasonIdx) = GetInternalSID(int.Parse(s));
return $"C{chapterIdx} S{seasonIdx}";
}
private new void DrawBackground(SKCanvas c)

View File

@ -193,15 +193,18 @@ public class BaseIcon : UCreator
return string.Format(format, name);
}
protected string GetCosmeticSeason(string seasonNumber)
protected (int, int) GetInternalSID(int number)
{
var s = seasonNumber["Cosmetics.Filter.Season.".Length..];
var initial = int.Parse(s);
var number = initial;
static int GetSeasonsInChapter(int chapter) => chapter switch
{
1 => 10,
2 => 8,
3 => 4,
_ => 10
};
var chapterIdx = 0;
var seasonIdx = 0;
while (number > 0)
{
var seasonsInChapter = GetSeasonsInChapter(++chapterIdx);
@ -213,14 +216,14 @@ public class BaseIcon : UCreator
number = 0;
}
}
return (chapterIdx, seasonIdx);
}
static int GetSeasonsInChapter(int chapter) => chapter switch
{
1 => 10,
2 => 8,
3 => 4,
_ => 10
};
protected string GetCosmeticSeason(string seasonNumber)
{
var s = seasonNumber["Cosmetics.Filter.Season.".Length..];
var initial = int.Parse(s);
(int chapterIdx, int seasonIdx) = GetInternalSID(initial);
var season = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "SeasonTextFormat", "Season {0}");
var introduced = Utils.GetLocalizedResource("Fort.Cosmetics", "CosmeticItemDescription_Season", "\nIntroduced in <SeasonText>{0}</>.");

View File

@ -90,6 +90,7 @@ public class CreatorPackage : IDisposable
case "FortPlaysetPropItemDefinition":
case "FortHomebaseNodeItemDefinition":
case "FortNeverPersistItemDefinition":
case "FortPlayerAugmentItemDefinition":
case "RadioContentSourceItemDefinition":
case "FortPlaysetGrenadeItemDefinition":
case "FortPersonalVehicleItemDefinition":

View File

@ -94,7 +94,7 @@
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Export Raw Data" Command="{Binding RightClickMenuCommand}">
<MenuItem Header="Export Raw Data (.uasset)" Command="{Binding RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Export_Data" />
@ -109,7 +109,7 @@
</Viewbox>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Save Property" Command="{Binding RightClickMenuCommand}">
<MenuItem Header="Save Property (.json)" Command="{Binding RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Save_Properties" />
@ -124,10 +124,10 @@
</Viewbox>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Save Texture" Command="{Binding RightClickMenuCommand}">
<MenuItem Header="Save Texture (.png)" Command="{Binding RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Save_Texture" />
<Binding Source="Assets_Save_Textures" />
<Binding Path="SelectedItems" ElementName="AssetsListName" />
</MultiBinding>
</MenuItem.CommandParameter>
@ -139,24 +139,8 @@
</Viewbox>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Auto">
<MenuItem.Icon>
<Viewbox Width="16" Height="16">
<Canvas Width="24" Height="24">
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource StatusBarIcon}" />
</Canvas>
</Viewbox>
</MenuItem.Icon>
<MenuItem Header="Save Properties" IsCheckable="True" StaysOpenOnClick="True"
InputGestureText="{Binding AutoSaveProps, Source={x:Static local:Settings.UserSettings.Default}}"
IsChecked="{Binding IsAutoSaveProps, Source={x:Static local:Settings.UserSettings.Default}}" />
<MenuItem Header="Save Textures" IsCheckable="True" StaysOpenOnClick="True"
InputGestureText="{Binding AutoSaveTextures, Source={x:Static local:Settings.UserSettings.Default}}"
IsChecked="{Binding IsAutoSaveTextures, Source={x:Static local:Settings.UserSettings.Default}}" />
<MenuItem Header="Open Sounds" IsCheckable="True" StaysOpenOnClick="True"
InputGestureText="{Binding AutoOpenSounds, Source={x:Static local:Settings.UserSettings.Default}}"
IsChecked="{Binding IsAutoOpenSounds, Source={x:Static local:Settings.UserSettings.Default}}" />
</MenuItem>
<MenuItem Header="Auto Open Sounds" IsCheckable="True" StaysOpenOnClick="True"
IsChecked="{Binding IsAutoOpenSounds, Source={x:Static local:Settings.UserSettings.Default}}" />
</MenuItem>
<MenuItem Header="Views">
<MenuItem Header="3D Viewer" Command="{Binding MenuCommand}" CommandParameter="Views_3dViewer">
@ -400,7 +384,7 @@
</Viewbox>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Export Folder's Packages Raw Data" Click="OnFolderExportClick">
<MenuItem Header="Export Folder's Packages Raw Data (.uasset)" Click="OnFolderExportClick">
<MenuItem.Icon>
<Viewbox Width="16" Height="16">
<Canvas Width="24" Height="24">
@ -409,7 +393,7 @@
</Viewbox>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Save Folder's Packages Properties" Click="OnFolderSaveClick">
<MenuItem Header="Save Folder's Packages Properties (.json)" Click="OnFolderSaveClick">
<MenuItem.Icon>
<Viewbox Width="16" Height="16">
<Canvas Width="24" Height="24">
@ -418,6 +402,15 @@
</Viewbox>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Save Folder's Packages Textures (.png)" Click="OnFolderTextureClick">
<MenuItem.Icon>
<Viewbox Width="16" Height="16">
<Canvas Width="24" Height="24">
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource TextureIcon}" />
</Canvas>
</Viewbox>
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Save Directory" Click="OnSaveDirectoryClick">
<MenuItem.Icon>
@ -528,7 +521,7 @@
</Viewbox>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Export Raw Data" Command="{Binding DataContext.RightClickMenuCommand}">
<MenuItem Header="Export Raw Data (.uasset)" Command="{Binding DataContext.RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Export_Data" />
@ -543,7 +536,7 @@
</Viewbox>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Save Properties" Command="{Binding DataContext.RightClickMenuCommand}">
<MenuItem Header="Save Properties (.json)" Command="{Binding DataContext.RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Save_Properties" />
@ -558,10 +551,10 @@
</Viewbox>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Save Texture" Command="{Binding DataContext.RightClickMenuCommand}">
<MenuItem Header="Save Texture (.png)" Command="{Binding DataContext.RightClickMenuCommand}">
<MenuItem.CommandParameter>
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
<Binding Source="Assets_Save_Texture" />
<Binding Source="Assets_Save_Textures" />
<Binding Path="SelectedItems" />
</MultiBinding>
</MenuItem.CommandParameter>
@ -803,32 +796,6 @@
</Viewbox>
</StatusBarItem>
<StatusBarItem Width="30" HorizontalContentAlignment="Stretch" ToolTip="Auto Save Properties Enabled">
<StatusBarItem.Style>
<Style TargetType="StatusBarItem">
<Style.Triggers>
<DataTrigger Binding="{Binding IsAutoSaveProps, Source={x:Static local:Settings.UserSettings.Default}}" Value="False">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</StatusBarItem.Style>
<TextBlock HorizontalAlignment="Center" FontWeight="SemiBold" Text="PRP" />
</StatusBarItem>
<StatusBarItem Width="30" HorizontalContentAlignment="Stretch" ToolTip="Auto Save Textures Enabled">
<StatusBarItem.Style>
<Style TargetType="StatusBarItem">
<Style.Triggers>
<DataTrigger Binding="{Binding IsAutoSaveTextures, Source={x:Static local:Settings.UserSettings.Default}}" Value="False">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</StatusBarItem.Style>
<TextBlock HorizontalAlignment="Center" FontWeight="SemiBold" Text="TEX" />
</StatusBarItem>
<StatusBarItem Width="30" HorizontalContentAlignment="Stretch" ToolTip="Auto Open Sounds Enabled">
<StatusBarItem.Style>
<Style TargetType="StatusBarItem">

View File

@ -27,9 +27,6 @@ public partial class MainWindow
public MainWindow()
{
CommandBindings.Add(new CommandBinding(new RoutedCommand("AutoSaveProps", typeof(MainWindow), new InputGestureCollection { new KeyGesture(UserSettings.Default.AutoSaveProps.Key, UserSettings.Default.AutoSaveProps.Modifiers) }), OnAutoTriggerExecuted));
CommandBindings.Add(new CommandBinding(new RoutedCommand("AutoSaveTextures", typeof(MainWindow), new InputGestureCollection { new KeyGesture(UserSettings.Default.AutoSaveTextures.Key, UserSettings.Default.AutoSaveTextures.Modifiers) }), OnAutoTriggerExecuted));
CommandBindings.Add(new CommandBinding(new RoutedCommand("AutoOpenSounds", typeof(MainWindow), new InputGestureCollection { new KeyGesture(UserSettings.Default.AutoOpenSounds.Key, UserSettings.Default.AutoOpenSounds.Modifiers) }), OnAutoTriggerExecuted));
CommandBindings.Add(new CommandBinding(new RoutedCommand("ReloadMappings", typeof(MainWindow), new InputGestureCollection { new KeyGesture(Key.F12) }), OnMappingsReload));
CommandBindings.Add(new CommandBinding(ApplicationCommands.Find, (_, _) => OnOpenAvalonFinder()));
@ -143,22 +140,6 @@ public partial class MainWindow
await _applicationView.CUE4Parse.InitMappings();
}
private void OnAutoTriggerExecuted(object sender, ExecutedRoutedEventArgs e)
{
switch ((e.Command as RoutedCommand)?.Name)
{
case "AutoSaveProps":
UserSettings.Default.IsAutoSaveProps = !UserSettings.Default.IsAutoSaveProps;
break;
case "AutoSaveTextures":
UserSettings.Default.IsAutoSaveTextures = !UserSettings.Default.IsAutoSaveTextures;
break;
case "AutoOpenSounds":
UserSettings.Default.IsAutoOpenSounds = !UserSettings.Default.IsAutoOpenSounds;
break;
}
}
private void OnOpenAvalonFinder()
{
_applicationView.CUE4Parse.TabControl.SelectedTab.HasSearchOpen = true;
@ -205,6 +186,14 @@ public partial class MainWindow
}
}
private async void OnFolderTextureClick(object sender, RoutedEventArgs e)
{
if (AssetsFolderName.SelectedItem is TreeItem folder)
{
await _threadWorkerView.Begin(cancellationToken => { _applicationView.CUE4Parse.TextureFolder(cancellationToken, folder); });
}
}
private void OnSaveDirectoryClick(object sender, RoutedEventArgs e)
{
if (AssetsFolderName.SelectedItem is not TreeItem folder) return;

View File

@ -42,6 +42,7 @@ struct Parameters
AoParams Ao;
bool HasAo;
float Specular;
float Roughness;
float EmissiveMult;
float UVScale;
@ -66,6 +67,7 @@ uniform Parameters uParameters;
uniform Light uLights[MAX_LIGHT_COUNT];
uniform int uNumLights;
uniform int uNumTexCoords;
uniform bool uHasVertexColors;
uniform vec3 uViewPos;
uniform vec3 uViewDir;
uniform bool bVertexColors[6];
@ -74,7 +76,7 @@ out vec4 FragColor;
int LayerToIndex()
{
return min(int(fTexLayer), uNumTexCoords - 1);
return clamp(int(fTexLayer), 0, uNumTexCoords - 1);
}
vec4 SamplerToVector(sampler2D s)
@ -94,54 +96,54 @@ vec3 ComputeNormals(int layer)
return normalize(tbn * normal);
}
vec3 schlickFresnel(int layer, float vDotH)
vec3 schlickFresnel(vec3 fLambert, float metallic, float hDotv)
{
vec3 f0 = vec3(0.04f);
return f0 + (1.0f - f0) * pow(clamp(1.0f - vDotH, 0.0f, 1.0f), 5);
vec3 f0 = vec3(0.04);
f0 = mix(f0, fLambert, metallic);
return f0 + (1.0 - f0) * pow(clamp(1.0 - hDotv, 0.0, 1.0), 5);
}
float ggxDistribution(float roughness, float nDoth)
{
float alpha2 = roughness * roughness * roughness * roughness;
float d = nDoth * nDoth * (alpha2- 1.0f) + 1.0f;
float d = nDoth * nDoth * (alpha2- 1.0) + 1.0;
return alpha2 / (PI * d * d);
}
float geomSmith(float roughness, float dp)
{
float k = (roughness + 1.0f) * (roughness + 1.0f) / 8.0f;
float denom = dp * (1.0f - k) + k;
float k = (roughness + 1.0) * (roughness + 1.0) / 8.0;
float denom = dp * (1.0 - k) + k;
return dp / denom;
}
vec3 CalcCameraLight(int layer, vec3 normals)
{
vec3 fLambert = SamplerToVector(uParameters.Diffuse[layer].Sampler).rgb * uParameters.Diffuse[layer].Color.rgb;
vec3 specular_masks = SamplerToVector(uParameters.SpecularMasks[layer].Sampler).rgb;
float roughness = max(0.0f, mix(specular_masks.r, specular_masks.b, uParameters.Roughness));
float roughness = max(0.0f, specular_masks.b * uParameters.Roughness);
vec3 intensity = vec3(1.0f) * 1.0f;
vec3 intensity = vec3(1.0f) * 1.0;
vec3 l = -uViewDir;
vec3 n = normals;
vec3 v = normalize(uViewPos - fPos);
vec3 h = normalize(v + l);
float nDotH = max(dot(n, h), 0.0f);
float vDotH = max(dot(v, h), 0.0f);
float nDotL = max(dot(n, l), 0.0f);
float nDotV = max(dot(n, v), 0.0f);
float nDotH = max(dot(n, h), 0.0);
float hDotv = max(dot(h, v), 0.0);
float nDotL = max(dot(n, l), 0.0);
float nDotV = max(dot(n, v), 0.0);
vec3 f = schlickFresnel(layer, vDotH);
vec3 f = schlickFresnel(fLambert, specular_masks.g, hDotv);
vec3 kS = f;
vec3 kD = 1.0 - kS;
kD *= max(0.0f, dot(v, reflect(-v, normals)) * specular_masks.g);
kD *= 1.0 - specular_masks.g;
vec3 specBrdfNom = ggxDistribution(roughness, nDotH) * f * geomSmith(roughness, nDotL) * geomSmith(roughness, nDotV);
float specBrdfDenom = 4.0f * nDotV * nDotL + 0.0001f;
vec3 specBrdf = specBrdfNom / specBrdfDenom;
vec3 fLambert = SamplerToVector(uParameters.Diffuse[layer].Sampler).rgb * uParameters.Diffuse[layer].Color.rgb;
vec3 specBrdfNom = ggxDistribution(roughness, nDotH) * geomSmith(roughness, nDotL) * geomSmith(roughness, nDotV) * f;
float specBrdfDenom = 4.0 * nDotV * nDotL + 0.0001;
vec3 specBrdf = uParameters.Specular * specular_masks.r * specBrdfNom / specBrdfDenom;
vec3 diffuseBrdf = kD * fLambert / PI;
return (diffuseBrdf + specBrdf) * intensity * nDotL;
@ -149,7 +151,7 @@ vec3 CalcCameraLight(int layer, vec3 normals)
void main()
{
if (bVertexColors[2])
if (bVertexColors[2] && uHasVertexColors)
{
FragColor = fColor;
}
@ -215,7 +217,7 @@ void main()
result *= lights; // use * to darken the scene, + to lighten it
}
result = result / (result + vec3(1.0f));
FragColor = vec4(pow(result, vec3(1.0f / 2.2f)), 1.0f);
result = result / (result + vec3(1.0));
FragColor = vec4(pow(result, vec3(1.0 / 2.2)), 1.0);
}
}

View File

@ -112,20 +112,6 @@ namespace FModel.Settings
set => SetProperty(ref _lastOpenedSettingTab, value);
}
private bool _isAutoSaveProps;
public bool IsAutoSaveProps
{
get => _isAutoSaveProps;
set => SetProperty(ref _isAutoSaveProps, value);
}
private bool _isAutoSaveTextures;
public bool IsAutoSaveTextures
{
get => _isAutoSaveTextures;
set => SetProperty(ref _isAutoSaveTextures, value);
}
private bool _isAutoOpenSounds = true;
public bool IsAutoOpenSounds
{
@ -557,27 +543,6 @@ namespace FModel.Settings
set => SetProperty(ref _assetRemoveTab, value);
}
private Hotkey _autoSaveProps = new(Key.F1);
public Hotkey AutoSaveProps
{
get => _autoSaveProps;
set => SetProperty(ref _autoSaveProps, value);
}
private Hotkey _autoSaveTextures = new(Key.F2);
public Hotkey AutoSaveTextures
{
get => _autoSaveTextures;
set => SetProperty(ref _autoSaveTextures, value);
}
private Hotkey _autoOpenSounds = new(Key.F3);
public Hotkey AutoOpenSounds
{
get => _autoOpenSounds;
set => SetProperty(ref _autoOpenSounds, value);
}
private Hotkey _addAudio = new(Key.N, ModifierKeys.Control);
public Hotkey AddAudio
{

View File

@ -62,7 +62,7 @@ public class EpicApiEndpoint : AbstractApiProvider
if (string.IsNullOrEmpty(UserSettings.Default.LastAuthResponse.AccessToken)) return true;
var request = new FRestRequest("https://account-public-service-prod.ol.epicgames.com/account/api/oauth/verify");
request.AddHeader("Authorization", $"bearer {UserSettings.Default.LastAuthResponse.AccessToken}");
var response = await _client.ExecuteAsync(request).ConfigureAwait(false);
return response.StatusCode != HttpStatusCode.OK;
var response = await _client.ExecuteGetAsync(request).ConfigureAwait(false);
return !response.IsSuccessful;
}
}

View File

@ -488,56 +488,6 @@ public class CUE4ParseViewModel : ViewModel
});
}
public void ExtractFolder(CancellationToken cancellationToken, TreeItem folder)
{
foreach (var asset in folder.AssetsList.Assets)
{
Thread.Sleep(10);
cancellationToken.ThrowIfCancellationRequested();
try
{
Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs);
}
catch
{
// ignore
}
}
foreach (var f in folder.Folders) ExtractFolder(cancellationToken, f);
}
public void ExportFolder(CancellationToken cancellationToken, TreeItem folder)
{
foreach (var asset in folder.AssetsList.Assets)
{
Thread.Sleep(10);
cancellationToken.ThrowIfCancellationRequested();
ExportData(asset.FullPath);
}
foreach (var f in folder.Folders) ExportFolder(cancellationToken, f);
}
public void SaveFolder(CancellationToken cancellationToken, TreeItem folder)
{
foreach (var asset in folder.AssetsList.Assets)
{
Thread.Sleep(10);
cancellationToken.ThrowIfCancellationRequested();
try
{
Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs, true);
}
catch
{
// ignore
}
}
foreach (var f in folder.Folders) SaveFolder(cancellationToken, f);
}
public void ExtractSelected(CancellationToken cancellationToken, IEnumerable<AssetItem> assetItems)
{
foreach (var asset in assetItems)
@ -548,7 +498,41 @@ public class CUE4ParseViewModel : ViewModel
}
}
public void Extract(CancellationToken cancellationToken, string fullPath, bool addNewTab = false, bool bulkSave = false)
private void BulkFolder(CancellationToken cancellationToken, TreeItem folder, Action<AssetItem> action)
{
foreach (var asset in folder.AssetsList.Assets)
{
Thread.Sleep(10);
cancellationToken.ThrowIfCancellationRequested();
try
{
action(asset);
}
catch
{
// ignore
}
}
foreach (var f in folder.Folders) BulkFolder(cancellationToken, f, action);
}
public void ExportFolder(CancellationToken cancellationToken, TreeItem folder)
=> BulkFolder(cancellationToken, folder, asset => ExportData(asset.FullPath));
public void ExtractFolder(CancellationToken cancellationToken, TreeItem folder)
=> BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs));
public void SaveFolder(CancellationToken cancellationToken, TreeItem folder)
=> BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs, true));
public void TextureFolder(CancellationToken cancellationToken, TreeItem folder)
=> BulkFolder(cancellationToken, folder, asset =>
{
Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs, false, true);
});
public void Extract(CancellationToken cancellationToken, string fullPath, bool addNewTab = false, bool bulkSave = false, bool bulkTexture = false)
{
Log.Information("User DOUBLE-CLICKED to extract '{FullPath}'", fullPath);
@ -581,7 +565,7 @@ public class CUE4ParseViewModel : ViewModel
foreach (var e in exports)
{
if (CheckExport(cancellationToken, e))
if (CheckExport(cancellationToken, e, bulkTexture))
break;
}
@ -691,7 +675,7 @@ public class CUE4ParseViewModel : ViewModel
if (Provider.TrySaveAsset(fullPath, out var data))
{
using var stream = new MemoryStream(data) { Position = 0 };
TabControl.SelectedTab.AddImage(fileName.SubstringBeforeLast("."), false, SKBitmap.Decode(stream));
TabControl.SelectedTab.AddImage(fileName.SubstringBeforeLast("."), false, SKBitmap.Decode(stream), bulkTexture);
}
break;
@ -711,7 +695,7 @@ public class CUE4ParseViewModel : ViewModel
canvas.DrawPicture(svg.Picture, paint);
}
TabControl.SelectedTab.AddImage(fileName.SubstringBeforeLast("."), false, bitmap);
TabControl.SelectedTab.AddImage(fileName.SubstringBeforeLast("."), false, bitmap, bulkTexture);
}
break;
@ -759,11 +743,11 @@ public class CUE4ParseViewModel : ViewModel
}
}
private bool CheckExport(CancellationToken cancellationToken, UObject export) // return true once you wanna stop searching for exports
private bool CheckExport(CancellationToken cancellationToken, UObject export, bool bulkTexture = false) // return true once you wanna stop searching for exports
{
switch (export)
{
case USolarisDigest solarisDigest:
case USolarisDigest solarisDigest when !bulkTexture:
{
if (!TabControl.CanAddTabs) return false;
@ -774,11 +758,11 @@ public class CUE4ParseViewModel : ViewModel
}
case UTexture2D texture:
{
TabControl.SelectedTab.AddImage(texture);
TabControl.SelectedTab.AddImage(texture, bulkTexture);
return false;
}
case UAkMediaAssetData:
case USoundWave:
case UAkMediaAssetData when !bulkTexture:
case USoundWave when !bulkTexture:
{
var shouldDecompress = UserSettings.Default.CompressedAudioMode == ECompressedAudio.PlayDecompressed;
export.Decode(shouldDecompress, out var audioFormat, out var data);
@ -788,10 +772,10 @@ public class CUE4ParseViewModel : ViewModel
SaveAndPlaySound(Path.Combine(TabControl.SelectedTab.Directory, TabControl.SelectedTab.Header.SubstringBeforeLast('.')).Replace('\\', '/'), audioFormat, data);
return false;
}
case UWorld when UserSettings.Default.PreviewWorlds:
case UStaticMesh when UserSettings.Default.PreviewStaticMeshes:
case USkeletalMesh when UserSettings.Default.PreviewSkeletalMeshes:
case UMaterialInstance when UserSettings.Default.PreviewMaterials && !ModelIsOverwritingMaterial &&
case UWorld when !bulkTexture && UserSettings.Default.PreviewWorlds:
case UStaticMesh when !bulkTexture && UserSettings.Default.PreviewStaticMeshes:
case USkeletalMesh when !bulkTexture && UserSettings.Default.PreviewSkeletalMeshes:
case UMaterialInstance when !bulkTexture && UserSettings.Default.PreviewMaterials && !ModelIsOverwritingMaterial &&
!(Game == FGame.FortniteGame && export.Owner != null && (export.Owner.Name.EndsWith($"/MI_OfferImages/{export.Name}", StringComparison.OrdinalIgnoreCase) ||
export.Owner.Name.EndsWith($"/RenderSwitch_Materials/{export.Name}", StringComparison.OrdinalIgnoreCase) ||
export.Owner.Name.EndsWith($"/MI_BPTile/{export.Name}", StringComparison.OrdinalIgnoreCase))):
@ -800,23 +784,23 @@ public class CUE4ParseViewModel : ViewModel
SnooperViewer.Run();
return true;
}
case UMaterialInstance m when ModelIsOverwritingMaterial:
case UMaterialInstance m when !bulkTexture && ModelIsOverwritingMaterial:
{
SnooperViewer.Renderer.Swap(m);
SnooperViewer.Run();
return true;
}
case UAnimSequence a when ModelIsWaitingAnimation:
case UAnimSequence a when !bulkTexture && ModelIsWaitingAnimation:
{
SnooperViewer.Renderer.Animate(a);
SnooperViewer.Run();
return true;
}
case UStaticMesh when UserSettings.Default.SaveStaticMeshes:
case USkeletalMesh when UserSettings.Default.SaveSkeletalMeshes:
case UMaterialInstance when UserSettings.Default.SaveMaterials:
case USkeleton when UserSettings.Default.SaveSkeletonAsMesh:
case UAnimSequence when UserSettings.Default.SaveAnimations:
case UStaticMesh when !bulkTexture && UserSettings.Default.SaveStaticMeshes:
case USkeletalMesh when !bulkTexture && UserSettings.Default.SaveSkeletalMeshes:
case UMaterialInstance when !bulkTexture && UserSettings.Default.SaveMaterials:
case USkeleton when !bulkTexture && UserSettings.Default.SaveSkeletonAsMesh:
case UAnimSequence when !bulkTexture && UserSettings.Default.SaveAnimations:
{
SaveExport(export);
return true;
@ -827,7 +811,7 @@ public class CUE4ParseViewModel : ViewModel
if (!package.TryConstructCreator(out var creator)) return false;
creator.ParseForInfo();
TabControl.SelectedTab.AddImage(export.Name, false, creator.Draw());
TabControl.SelectedTab.AddImage(export.Name, false, creator.Draw(), bulkTexture);
return true;
}
}

View File

@ -50,12 +50,12 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
}
break;
case "Assets_Save_Texture":
case "Assets_Save_Textures":
foreach (var asset in assetItems)
{
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath);
contextViewModel.CUE4Parse.TabControl.SelectedTab.SaveImage(false);
contextViewModel.CUE4Parse.TabControl.SelectedTab.SaveImages(false);
}
break;

View File

@ -110,6 +110,7 @@ public class GameSelectorViewModel : ViewModel
yield return GetUnrealEngineGame("a99769d95d8f400baad1f67ab5dfe508");
yield return GetUnrealEngineGame("Nebula");
yield return GetUnrealEngineGame("711c5e95dc094ca58e5f16bd48e751d6");
yield return GetUnrealEngineGame("9361c8c6d2f34b42b5f2f61093eedf48");
yield return GetRiotGame("VALORANT", "ShooterGame\\Content\\Paks");
yield return new DetectedGame { GameName = "Valorant [LIVE]", GameDirectory = Constants._VAL_LIVE_TRIGGER };
yield return GetMojangGame("MinecraftDungeons", "\\dungeons\\dungeons\\Dungeons\\Content\\Paks");

View File

@ -17,6 +17,7 @@ using System.Windows;
using System.Windows.Media.Imaging;
using CUE4Parse.UE4.Assets.Exports.Texture;
using CUE4Parse_Conversion.Textures;
using Ookii.Dialogs.Wpf;
namespace FModel.ViewModels;
@ -227,19 +228,20 @@ public class TabItem : ViewModel
});
}
public void AddImage(UTexture2D texture) => AddImage(texture.Name, texture.bRenderNearestNeighbor, texture.Decode(UserSettings.Default.OverridedPlatform));
public void AddImage(UTexture2D texture, bool bulkTexture)
=> AddImage(texture.Name, texture.bRenderNearestNeighbor, texture.Decode(UserSettings.Default.OverridedPlatform), bulkTexture);
public void AddImage(string name, bool rnn, SKBitmap[] img)
public void AddImage(string name, bool rnn, SKBitmap[] img, bool bulkTexture)
{
foreach (var i in img) AddImage(name, rnn, i);
foreach (var i in img) AddImage(name, rnn, i, bulkTexture);
}
public void AddImage(string name, bool rnn, SKBitmap img)
public void AddImage(string name, bool rnn, SKBitmap img, bool bulkTexture)
{
Application.Current.Dispatcher.Invoke(() =>
{
var t = new TabImage(name, rnn, img);
if (UserSettings.Default.IsAutoSaveTextures)
if (bulkTexture)
SaveImage(t, true);
_images.Add(t);
@ -259,7 +261,7 @@ public class TabItem : ViewModel
Document ??= new TextDocument();
Document.Text = text;
if (UserSettings.Default.IsAutoSaveProps || bulkSave)
if (bulkSave)
SaveProperty(true);
});
}
@ -273,16 +275,43 @@ public class TabItem : ViewModel
});
}
public void SaveImage(bool autoSave) => SaveImage(SelectedImage, autoSave);
public void SaveImages(bool bulkTexture)
{
if (_images.Count == 1)
{
SaveImage(bulkTexture);
return;
}
private void SaveImage(TabImage image, bool autoSave)
var directory = Path.Combine(UserSettings.Default.TextureDirectory,
UserSettings.Default.KeepDirectoryStructure ? Directory : "").Replace('\\', '/');
if (!bulkTexture)
{
var folderBrowser = new VistaFolderBrowserDialog();
if (folderBrowser.ShowDialog() == true)
directory = folderBrowser.SelectedPath;
else return;
}
else System.IO.Directory.CreateDirectory(directory);
foreach (var image in _images)
{
if (image == null) return;
var fileName = $"{image.ExportName}.png";
SaveImage(image, Path.Combine(directory, fileName), fileName);
}
}
public void SaveImage(bool bulkTexture) => SaveImage(SelectedImage, bulkTexture);
private void SaveImage(TabImage image, bool bulkTexture)
{
if (image == null) return;
var fileName = $"{image.ExportName}.png";
var directory = Path.Combine(UserSettings.Default.TextureDirectory,
var path = Path.Combine(UserSettings.Default.TextureDirectory,
UserSettings.Default.KeepDirectoryStructure ? Directory : "", fileName!).Replace('\\', '/');
if (!autoSave)
if (!bulkTexture)
{
var saveFileDialog = new SaveFileDialog
{
@ -293,19 +322,23 @@ public class TabItem : ViewModel
};
var result = saveFileDialog.ShowDialog();
if (!result.HasValue || !result.Value) return;
directory = saveFileDialog.FileName;
}
else
{
System.IO.Directory.CreateDirectory(directory.SubstringBeforeLast('/'));
path = saveFileDialog.FileName;
}
else System.IO.Directory.CreateDirectory(path.SubstringBeforeLast('/'));
using (var fs = new FileStream(directory, FileMode.Create, FileAccess.Write, FileShare.Read))
{
fs.Write(image.ImageBuffer, 0, image.ImageBuffer.Length);
}
SaveImage(image, path, fileName);
}
SaveCheck(directory, fileName);
private void SaveImage(TabImage image, string path, string fileName)
{
SaveImage(image, path);
SaveCheck(path, fileName);
}
private void SaveImage(TabImage image, string path)
{
using var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read);
fs.Write(image.ImageBuffer, 0, image.ImageBuffer.Length);
}
public void SaveProperty(bool autoSave)

View File

@ -27,6 +27,7 @@ public class StringToGameConverter : IValueConverter
"shoebill" => FGame.SwGame,
"a99769d95d8f400baad1f67ab5dfe508" => FGame.Platform,
"711c5e95dc094ca58e5f16bd48e751d6" => FGame.PandaGame,
"9361c8c6d2f34b42b5f2f61093eedf48" => FGame.TslGame,
381210 => FGame.DeadByDaylight,
578080 => FGame.TslGame,
677620 => FGame.PortalWars,

View File

@ -420,10 +420,6 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
@ -455,29 +451,17 @@
<Separator Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="3" Style="{StaticResource CustomSeparator}" />
<TextBlock Grid.Row="8" Grid.Column="0" Text="Auto Save Properties *" VerticalAlignment="Center" Margin="0 0 0 5" />
<TextBlock Grid.Row="8" Grid.Column="0" Text="Add Audio File" VerticalAlignment="Center" Margin="0 0 0 5" />
<controls:HotkeyTextBox Grid.Row="8" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
HotKey="{Binding AutoSaveProps, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
<TextBlock Grid.Row="9" Grid.Column="0" Text="Auto Save Textures *" VerticalAlignment="Center" Margin="0 0 0 5" />
<controls:HotkeyTextBox Grid.Row="9" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
HotKey="{Binding AutoSaveTextures, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
<TextBlock Grid.Row="10" Grid.Column="0" Text="Auto Open Sounds *" VerticalAlignment="Center" />
<controls:HotkeyTextBox Grid.Row="10" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}"
HotKey="{Binding AutoOpenSounds, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
<Separator Grid.Row="11" Grid.Column="0" Grid.ColumnSpan="3" Style="{StaticResource CustomSeparator}" />
<TextBlock Grid.Row="12" Grid.Column="0" Text="Add Audio File" VerticalAlignment="Center" Margin="0 0 0 5" />
<controls:HotkeyTextBox Grid.Row="12" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
HotKey="{Binding AddAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
<TextBlock Grid.Row="13" Grid.Column="0" Text="Play / Pause Current Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
<controls:HotkeyTextBox Grid.Row="13" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
<TextBlock Grid.Row="9" Grid.Column="0" Text="Play / Pause Current Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
<controls:HotkeyTextBox Grid.Row="9" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
HotKey="{Binding PlayPauseAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
<TextBlock Grid.Row="14" Grid.Column="0" Text="Previous Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
<controls:HotkeyTextBox Grid.Row="14" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
<TextBlock Grid.Row="10" Grid.Column="0" Text="Previous Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
<controls:HotkeyTextBox Grid.Row="10" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
HotKey="{Binding PreviousAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
<TextBlock Grid.Row="15" Grid.Column="0" Text="Next Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
<controls:HotkeyTextBox Grid.Row="15" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
<TextBlock Grid.Row="11" Grid.Column="0" Text="Next Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
<controls:HotkeyTextBox Grid.Row="11" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
HotKey="{Binding NextAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
</Grid>
</DataTemplate>

View File

@ -5,6 +5,7 @@ using System.Numerics;
using CUE4Parse.UE4.Assets.Exports.Material;
using CUE4Parse.UE4.Assets.Exports.Texture;
using CUE4Parse.UE4.Objects.Core.Math;
using CUE4Parse.UE4.Objects.Core.Misc;
using ImGuiNET;
using OpenTK.Graphics.OpenGL4;
@ -31,6 +32,7 @@ public class Material : IDisposable
public AoParams Ao;
public bool HasAo;
public float Specular = 1f;
public float Roughness = 0.5f;
public float EmissiveMult = 1f;
public float UVScale = 1f;
@ -89,7 +91,7 @@ public class Material : IDisposable
}
{ // scalars
if (Parameters.TryGetTexture2d(out var original, "M", "PackedTexture", "AEM") &&
if (Parameters.TryGetTexture2d(out var original, "M", "AEM", "AO") &&
!original.Name.Equals("T_BlackMask") && options.TryGetTexture(original, false, out var transformed))
{
HasAo = true;
@ -101,14 +103,19 @@ public class Material : IDisposable
}
}
if (Parameters.TryGetScalar(out var specular, "Specular", "Specular Intensity", "Spec"))
Specular = specular;
if (Parameters.TryGetScalar(out var roughnessMin, "RoughnessMin", "SpecRoughnessMin") &&
Parameters.TryGetScalar(out var roughnessMax, "RoughnessMax", "SpecRoughnessMax"))
Roughness = (roughnessMin + roughnessMax) / 2f;
if (Parameters.TryGetScalar(out var roughness, "Rough", "Roughness"))
if (Parameters.TryGetScalar(out var roughness, "Rough", "Roughness", "Ro Multiplier", "RO_mul", "Roughness_Mult"))
Roughness = roughness;
if (Parameters.TryGetScalar(out var emissiveMult, "emissive mult", "Emissive_Mult"))
EmissiveMult = emissiveMult;
if (Parameters.TryGetScalar(out var emissiveMultScalar, "emissive mult", "Emissive_Mult"))
EmissiveMult = emissiveMultScalar;
else if (Parameters.TryGetLinearColor(out var emissiveMultColor, "Emissive Multiplier", "EmissiveMultiplier"))
EmissiveMult = emissiveMultColor.R;
if (Parameters.TryGetScalar(out var uvScale, "UV Scale"))
UVScale = uvScale;
@ -209,6 +216,7 @@ public class Material : IDisposable
shader.SetUniform("uParameters.Ao.AmbientOcclusion", Ao.AmbientOcclusion);
shader.SetUniform("uParameters.HasAo", HasAo);
shader.SetUniform("uParameters.Specular", Specular);
shader.SetUniform("uParameters.Roughness", Roughness);
shader.SetUniform("uParameters.EmissiveMult", EmissiveMult);
shader.SetUniform("uParameters.UVScale", UVScale);
@ -223,23 +231,25 @@ public class Material : IDisposable
{
if (ImGui.BeginTable("parameters", 2))
{
SnimGui.Layout("Roughness");ImGui.PushID(1);
SnimGui.Layout("Specular");ImGui.PushID(1);
ImGui.DragFloat("", ref Specular, _step, _zero, 1.0f, _mult, _clamp);
ImGui.PopID();SnimGui.Layout("Roughness");ImGui.PushID(2);
ImGui.DragFloat("", ref Roughness, _step, _zero, 1.0f, _mult, _clamp);
SnimGui.Layout("Emissive Multiplier");ImGui.PushID(2);
ImGui.PopID();SnimGui.Layout("Emissive Multiplier");ImGui.PushID(3);
ImGui.DragFloat("", ref EmissiveMult, _step, _zero, _infinite, _mult, _clamp);
ImGui.PopID();SnimGui.Layout("UV Scale");ImGui.PushID(3);
ImGui.PopID();SnimGui.Layout("UV Scale");ImGui.PushID(4);
ImGui.DragFloat("", ref UVScale, _step, _zero, _infinite, _mult, _clamp);
ImGui.PopID();
if (HasAo)
{
SnimGui.Layout("Ambient Occlusion");ImGui.PushID(4);
SnimGui.Layout("Ambient Occlusion");ImGui.PushID(5);
ImGui.DragFloat("", ref Ao.AmbientOcclusion, _step, _zero, 1.0f, _mult, _clamp);ImGui.PopID();
if (Ao.HasColorBoost)
{
SnimGui.Layout("Color Boost");ImGui.PushID(5);
SnimGui.Layout("Color Boost");ImGui.PushID(6);
ImGui.ColorEdit3("", ref Ao.ColorBoost.Color);ImGui.PopID();
SnimGui.Layout("Color Boost Exponent");ImGui.PushID(6);
SnimGui.Layout("Color Boost Exponent");ImGui.PushID(7);
ImGui.DragFloat("", ref Ao.ColorBoost.Exponent, _step, _zero, _infinite, _mult, _clamp);
ImGui.PopID();
}
@ -273,7 +283,7 @@ public class Material : IDisposable
}
}
public void ImGuiTextures(Dictionary<string, Texture> icons, Model model)
public bool ImGuiTextures(Dictionary<string, Texture> icons, Model model)
{
if (ImGui.BeginTable("material_textures", 2))
{
@ -300,21 +310,46 @@ public class Material : IDisposable
ImGui.EndTable();
}
ImGui.Image(GetSelectedTexture() ?? icons["noimage"].GetPointer(),
var texture = GetSelectedTexture() ?? icons["noimage"];
ImGui.Image(texture.GetPointer(),
new Vector2(ImGui.GetContentRegionAvail().X - ImGui.GetScrollX()),
Vector2.Zero, Vector2.One, Vector4.One, new Vector4(1.0f, 1.0f, 1.0f, 0.25f));
ImGui.Spacing();
return ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(ImGuiMouseButton.Left);
}
private IntPtr? GetSelectedTexture()
public Vector2[] ImGuiTextureInspector(Texture fallback)
{
var texture = GetSelectedTexture() ?? fallback;
if (ImGui.BeginTable("texture_inspector", 2, ImGuiTableFlags.SizingStretchProp))
{
SnimGui.Layout("Type");ImGui.Text($" : ({texture.Format}) {texture.Name}");
SnimGui.TooltipCopy("(?) Click to Copy Path", texture.Path);
SnimGui.Layout("Guid");ImGui.Text($" : {texture.Guid.ToString(EGuidFormats.UniqueObjectGuid)}");
SnimGui.Layout("Import");ImGui.Text($" : {texture.ImportedWidth}x{texture.ImportedHeight}");
SnimGui.Layout("Export");ImGui.Text($" : {texture.Width}x{texture.Height}");
ImGui.EndTable();
}
var largest = ImGui.GetContentRegionAvail();
largest.X -= ImGui.GetScrollX();
largest.Y -= ImGui.GetScrollY();
var ratio = Math.Min(largest.X / texture.Width, largest.Y / texture.Height);
var size = new Vector2(texture.Width * ratio, texture.Height * ratio);
var pos = ImGui.GetCursorPos();
ImGui.Image(texture.GetPointer(),size, Vector2.Zero, Vector2.One, Vector4.One, new Vector4(1.0f, 1.0f, 1.0f, 0.25f));
return new[] { size, pos };
}
private Texture GetSelectedTexture()
{
return SelectedTexture switch
{
0 => Diffuse[SelectedChannel]?.GetPointer(),
1 => Normals[SelectedChannel]?.GetPointer(),
2 => SpecularMasks[SelectedChannel]?.GetPointer(),
3 => Ao.Texture?.GetPointer(),
4 => Emissive[SelectedChannel]?.GetPointer(),
0 => Diffuse[SelectedChannel],
1 => Normals[SelectedChannel],
2 => SpecularMasks[SelectedChannel],
3 => Ao.Texture,
4 => Emissive[SelectedChannel],
_ => null
};
}

View File

@ -211,6 +211,7 @@ public class Model : IDisposable
public void Setup(Options options)
{
_handle = GL.CreateProgram();
var broken = GL.GetInteger(GetPName.MaxTextureUnits) == 0;
_ebo = new BufferObject<uint>(Indices, BufferTarget.ElementArrayBuffer);
_vbo = new BufferObject<float>(Vertices, BufferTarget.ArrayBuffer);
@ -221,7 +222,7 @@ public class Model : IDisposable
_vao.VertexAttributePointer(2, 3, VertexAttribPointerType.Float, _vertexSize, 4); // normal
_vao.VertexAttributePointer(3, 3, VertexAttribPointerType.Float, _vertexSize, 7); // tangent
_vao.VertexAttributePointer(4, 2, VertexAttribPointerType.Float, _vertexSize, 10); // uv
_vao.VertexAttributePointer(5, 1, VertexAttribPointerType.Float, _vertexSize, 12); // texture index
if (!broken) _vao.VertexAttributePointer(5, 1, VertexAttribPointerType.Float, _vertexSize, 12); // texture index
_vao.VertexAttributePointer(6, 4, VertexAttribPointerType.Float, _vertexSize, 13); // color
_vao.VertexAttributePointer(7, 4, VertexAttribPointerType.Float, _vertexSize, 17); // boneids
_vao.VertexAttributePointer(8, 4, VertexAttribPointerType.Float, _vertexSize, 21); // boneweights
@ -232,7 +233,7 @@ public class Model : IDisposable
for (var i = 0; i < Materials.Length; i++)
{
if (!Materials[i].IsUsed) continue;
Materials[i].Setup(options, NumTexCoords);
Materials[i].Setup(options, broken ? 1 : NumTexCoords);
}
if (HasMorphTargets)
@ -268,6 +269,7 @@ public class Model : IDisposable
_vao.Bind();
shader.SetUniform("uMorphTime", MorphTime);
shader.SetUniform("uNumTexCoords", NumTexCoords);
shader.SetUniform("uHasVertexColors", HasVertexColors);
GL.PolygonMode(MaterialFace.FrontAndBack, Wireframe ? PolygonMode.Line : PolygonMode.Fill);
for (int section = 0; section < Sections.Length; section++)

View File

@ -102,7 +102,7 @@ public class Options
}
/// <summary>
/// Red : Specular
/// Red : Specular (if possible)
/// Blue : Roughness
/// Green : Metallic
/// </summary>
@ -112,6 +112,8 @@ public class Options
switch (_game)
{
case "hk_project":
case "gameface":
case "divineknockout":
{
unsafe
{
@ -120,13 +122,73 @@ public class Options
{
for (var i = 0; i < mip.SizeX * mip.SizeY; i++)
{
(d[offset + 1], d[offset + 2]) = (d[offset + 2], d[offset + 1]); // swap G and B
(d[offset + 1], d[offset + 2]) = (d[offset + 2], d[offset + 1]); // RBG
offset += 4;
}
}
}
break;
}
// R: Roughness
// G: Metallic
// B: Whatever (AO / S / E / ...)
case "ccff7r":
{
unsafe
{
var offset = 0;
fixed (byte* d = data)
{
for (var i = 0; i < mip.SizeX * mip.SizeY; i++)
{
(d[offset + 1], d[offset + 2]) = (d[offset + 2], d[offset + 1]); // RBG
(d[offset], d[offset + 1]) = (d[offset + 1], d[offset]); // BRG
offset += 4;
}
}
}
break;
}
case "shootergame":
{
var packedPBRType = o.Name[(o.Name.LastIndexOf('_') + 1)..];
switch (packedPBRType)
{
case "MRAE": // R: Metallic, G: Roughness, B: AO (0-127) & Emissive (128-255) (Character PBR)
unsafe
{
var offset = 0;
fixed (byte* d = data)
{
for (var i = 0; i < mip.SizeX * mip.SizeY; i++)
{
(d[offset], d[offset + 1]) = (d[offset + 1], d[offset]); // RMAE
// (d[offset], d[offset + 2]) = (d[offset + 2], d[offset]); // AEMR
offset += 4;
}
}
}
break;
case "MRAS": // R: Metallic, G: Roughness, B: AO, A: Specular (Legacy PBR)
case "MRA": // R: Metallic, G: Roughness, B: AO (Environment PBR)
case "MRS": // R: Metallic, G: Roughness, B: Specular (Weapon PBR)
unsafe
{
var offset = 0;
fixed (byte* d = data)
{
for (var i = 0; i < mip.SizeX * mip.SizeY; i++)
{
(d[offset], d[offset + 2]) = (d[offset + 2], d[offset]); // SRM
(d[offset + 1], d[offset + 2]) = (d[offset + 2], d[offset + 1]); // SMR
offset += 4;
}
}
}
break;
}
break;
}
}
}

View File

@ -1,4 +1,5 @@
using System;
using System;
using System.Linq;
using System.Numerics;
using System.Threading;
using System.Windows;
@ -258,27 +259,50 @@ public class Renderer : IDisposable
else if (m.TryConvert(out var mesh))
{
model = new Model(m, mesh, t);
if (actor.TryGetAllValues(out FPackageIndex[] textureData, "TextureData"))
if (actor.TryGetValue(out FPackageIndex baseMaterial, "BaseMaterial") &&
actor.TryGetAllValues(out FPackageIndex[] textureData, "TextureData"))
{
for (int j = 0; j < textureData.Length; j++)
var material = model.Materials.FirstOrDefault(x => x.Name == baseMaterial.Name);
if (material is { IsUsed: true })
{
if (!model.Materials[model.Sections[j].MaterialIndex].IsUsed ||
textureData[j].Load() is not { } textureDataIdx)
continue;
for (int j = 0; j < textureData.Length; j++)
{
var diffuse_key = j switch
{
0 => "Diffuse",
> 0 => $"Diffuse_Texture_{j + 1}",
_ => CMaterialParams2.FallbackDiffuse
};
var normal_key = j switch
{
0 => "Normals",
> 0 => $"Normals_Texture_{j + 1}",
_ => CMaterialParams2.FallbackNormals
};
var specularmasks_key = j switch
{
0 => "SpecularMasks",
> 0 => $"SpecularMasks_{j + 1}",
_ => CMaterialParams2.FallbackNormals
};
if (textureDataIdx.TryGetValue(out FPackageIndex overrideMaterial, "OverrideMaterial") &&
overrideMaterial.TryLoad(out var material) && material is UMaterialInterface unrealMaterial)
model.Materials[model.Sections[j].MaterialIndex].SwapMaterial(unrealMaterial);
if (textureData[j]?.Load() is not { } textureDataIdx)
continue;
if (textureDataIdx.TryGetValue(out FPackageIndex diffuse, "Diffuse") &&
diffuse.Load() is UTexture2D diffuseTexture)
model.Materials[model.Sections[j].MaterialIndex].Parameters.Textures[CMaterialParams2.Diffuse[0][0]] = diffuseTexture;
if (textureDataIdx.TryGetValue(out FPackageIndex normal, "Normal") &&
normal.Load() is UTexture2D normalTexture)
model.Materials[model.Sections[j].MaterialIndex].Parameters.Textures[CMaterialParams2.Normals[0][0]] = normalTexture;
if (textureDataIdx.TryGetValue(out FPackageIndex specular, "Specular") &&
specular.Load() is UTexture2D specularTexture)
model.Materials[model.Sections[j].MaterialIndex].Parameters.Textures[CMaterialParams2.SpecularMasks[0][0]] = specularTexture;
if (textureDataIdx.TryGetValue(out FPackageIndex overrideMaterial, "OverrideMaterial") &&
overrideMaterial.TryLoad(out var oMaterial) && oMaterial is UMaterialInterface oUnrealMaterial)
material.SwapMaterial(oUnrealMaterial);
if (textureDataIdx.TryGetValue(out FPackageIndex diffuse, "Diffuse") &&
diffuse.Load() is UTexture2D diffuseTexture)
material.Parameters.Textures[diffuse_key] = diffuseTexture;
if (textureDataIdx.TryGetValue(out FPackageIndex normal, "Normal") &&
normal.Load() is UTexture2D normalTexture)
material.Parameters.Textures[normal_key] = normalTexture;
if (textureDataIdx.TryGetValue(out FPackageIndex specular, "Specular") &&
specular.Load() is UTexture2D specularTexture)
material.Parameters.Textures[specularmasks_key] = specularTexture;
}
}
}
if (staticMeshComp.TryGetValue(out FPackageIndex[] overrideMaterials, "OverrideMaterials"))

View File

@ -30,25 +30,43 @@ public class Skeleton : IDisposable
continue;
var transform = Transform.Identity;
var matrix = Matrix4x4.Identity;
while (boneIndex > -1)
{
var t = RefSkel.ReferenceSkeleton.FinalRefBonePose[boneIndex];
transform.Position += t.Rotation.RotateVector(t.Translation.ToMapVector()) * Constants.SCALE_DOWN_RATIO;
var bone = RefSkel.ReferenceSkeleton.FinalRefBonePose[boneIndex];
boneIndex = RefSkel.ReferenceSkeleton.FinalRefBoneInfo[boneIndex].ParentIndex;
var parentBone = RefSkel.ReferenceSkeleton.FinalRefBonePose[boneIndex < 0 ? 0 : boneIndex];
var orig_loc = bone.Translation;
parentBone.Rotation.Conjugate();
orig_loc = parentBone.Rotation.RotateVector(orig_loc);
var orig_quat = bone.Rotation;
orig_quat *= parentBone.Rotation;
orig_quat.Conjugate();
var p_rotated = orig_quat * orig_loc;
orig_quat.Conjugate();
p_rotated *= orig_quat;
matrix *=
Matrix4x4.CreateFromQuaternion(orig_quat) *
Matrix4x4.CreateTranslation(p_rotated);
// Console.WriteLine(matrix.Translation);
}
// for (int j = 0; j <= 3; j++)
// for (int j = 0; j <= boneIndex; j++)
// {
// var t = RefSkel.ReferenceSkeleton.FinalRefBonePose[j];
// // var matrix = Matrix4x4.CreateScale(t.Scale3D.ToMapVector());
// // matrix *= Matrix4x4.CreateFromQuaternion(t.Rotation);
// transform.Position += t.Rotation.UnrotateVector(t.Translation.ToMapVector()) * Constants.SCALE_DOWN_RATIO;
// var r = RefSkel.ReferenceSkeleton.FinalRefBonePose[j - (j == 0 ? 0 : 1)].Rotation;
// r.Conjugate();
// matrix *= Matrix4x4.CreateFromQuaternion(r) * Matrix4x4.CreateTranslation(t.Translation);
//
// // Console.WriteLine($@"{t.Translation}");
// // transform.Relation *= matrix;
// Console.WriteLine($@"{t.Translation}");
// transform.Relation *= matrix;
// }
Sockets[i] = new Socket(socket, transform);
Sockets[i] = new Socket(socket, matrix.Translation);
}
}

View File

@ -6,6 +6,7 @@ using FModel.Framework;
using ImGuiNET;
using OpenTK.Windowing.Common;
using System.Numerics;
using System.Text;
using FModel.Settings;
using OpenTK.Graphics.OpenGL4;
@ -43,6 +44,8 @@ public class SnimGui
private readonly Save _saver = new ();
private readonly string _renderer;
private readonly string _version;
private bool _ti_open;
private bool _ti_overlayUv;
private bool _viewportFocus;
private readonly Vector4 _accentColor = new (0.125f, 0.42f, 0.831f, 1.0f);
@ -75,6 +78,7 @@ public class SnimGui
Draw3DViewport(s);
DrawNavbar();
if (_ti_open) DrawTextureInspector(s);
Controller.Render();
}
@ -238,6 +242,30 @@ public class SnimGui
}
});
Modal("GPU OpenGL Info", ImGui.MenuItem("GPU Info"), () =>
{
var s = new StringBuilder();
s.AppendLine($"MaxTextureImageUnits: {GL.GetInteger(GetPName.MaxTextureImageUnits)}");
s.AppendLine($"MaxTextureUnits: {GL.GetInteger(GetPName.MaxTextureUnits)}");
s.AppendLine($"MaxVertexTextureImageUnits: {GL.GetInteger(GetPName.MaxVertexTextureImageUnits)}");
s.AppendLine($"MaxCombinedTextureImageUnits: {GL.GetInteger(GetPName.MaxCombinedTextureImageUnits)}");
s.AppendLine($"MaxGeometryTextureImageUnits: {GL.GetInteger(GetPName.MaxGeometryTextureImageUnits)}");
s.AppendLine($"MaxTextureCoords: {GL.GetInteger(GetPName.MaxTextureCoords)}");
s.AppendLine($"Renderer: {_renderer}");
s.AppendLine($"Version: {_version}");
ImGui.TextWrapped(s.ToString());
ImGui.Separator();
var size = new Vector2(120, 0);
ImGui.InvisibleButton("", size * 4);
ImGui.SameLine();
ImGui.SetItemDefaultFocus();
if (ImGui.Button("OK", size))
{
ImGui.CloseCurrentPopup();
}
});
Modal("About Snooper", ImGui.MenuItem("About"), () =>
{
ImGui.TextWrapped(
@ -556,9 +584,9 @@ hello world!
}
ImGui.SetNextItemOpen(true, ImGuiCond.Appearing);
if (ImGui.CollapsingHeader("Textures"))
if (ImGui.CollapsingHeader("Textures") && material.ImGuiTextures(icons, model))
{
material.ImGuiTextures(icons, model);
_ti_open = true;
}
ImGui.SetNextItemOpen(true, ImGuiCond.Appearing);
@ -590,6 +618,32 @@ hello world!
}
}
private void DrawTextureInspector(Snooper s)
{
if (ImGui.Begin("Texture Inspector", ref _ti_open, ImGuiWindowFlags.NoScrollbar) &&
s.Renderer.Options.TryGetModel(out var model) &&
s.Renderer.Options.TryGetSection(model, out var section))
{
var vectors = model.Materials[section.MaterialIndex].ImGuiTextureInspector(s.Renderer.Options.Icons["noimage"]);
if (_ti_overlayUv)
{
var size = vectors[0];
var drawList = ImGui.GetWindowDrawList();
drawList.PushClipRect(size, size, true);
ImGui.SetCursorPos(vectors[1]);
ImGui.InvisibleButton("canvas", size, ImGuiButtonFlags.MouseButtonLeft | ImGuiButtonFlags.MouseButtonRight);
drawList.AddLine(new Vector2(0, 0), size, 255, 2f);
drawList.PopClipRect();
}
Popup(() =>
{
if (ImGui.MenuItem("Overlay UVs", null, _ti_overlayUv))
_ti_overlayUv = !_ti_overlayUv;
});
}
ImGui.End(); // if window is collapsed
}
private void Draw3DViewport(Snooper s)
{
ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero);
@ -727,15 +781,15 @@ hello world!
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
}
public static void TooltipCopy(string name)
public static void TooltipCopy(string label, string text = null)
{
if (ImGui.IsItemHovered())
{
ImGui.BeginTooltip();
ImGui.Text(name);
ImGui.Text(label);
ImGui.EndTooltip();
}
if (ImGui.IsItemClicked()) ImGui.SetClipboardText(name);
if (ImGui.IsItemClicked()) ImGui.SetClipboardText(text ?? label);
}
private void Theme()

View File

@ -1,5 +1,7 @@
using System;
using System.Numerics;
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
using CUE4Parse.UE4.Objects.Core.Math;
namespace FModel.Views.Snooper;
@ -19,6 +21,15 @@ public class Socket : IDisposable
// Transform.Scale = socket.RelativeScale.ToMapVector();
}
public Socket(USkeletalMeshSocket socket, Vector3 position)
{
Name = socket.SocketName.Text;
Bone = socket.BoneName.Text;
Transform = Transform.Identity;
var pos = position /*+ socket.RelativeRotation.RotateVector(socket.RelativeLocation)*/;
Transform.Position = new FVector(pos.X, pos.Z, pos.Y) * Constants.SCALE_DOWN_RATIO;
}
public void Dispose()
{
throw new NotImplementedException();

View File

@ -2,6 +2,7 @@ using System;
using System.Windows;
using CUE4Parse.UE4.Assets.Exports.Texture;
using CUE4Parse.UE4.Objects.Core.Math;
using CUE4Parse.UE4.Objects.Core.Misc;
using OpenTK.Graphics.OpenGL4;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
@ -15,6 +16,7 @@ public class Texture : IDisposable
private readonly TextureTarget _target;
public readonly string Type;
public readonly FGuid Guid;
public readonly string Name;
public readonly string Path;
public readonly EPixelFormat Format;
@ -22,7 +24,6 @@ public class Texture : IDisposable
public readonly uint ImportedHeight;
public int Width;
public int Height;
public string Label;
public Texture(TextureType type)
{
@ -34,7 +35,8 @@ public class Texture : IDisposable
TextureType.MsaaFramebuffer => TextureTarget.Texture2DMultisample,
_ => TextureTarget.Texture2D
};
Label = "(?) Click to Copy Path";
Guid = new FGuid();
}
public Texture(uint width, uint height) : this(TextureType.MsaaFramebuffer)
@ -72,8 +74,9 @@ public class Texture : IDisposable
public Texture(byte[] data, int width, int height, UTexture2D texture2D) : this(TextureType.Normal)
{
Type = texture2D.ExportType;
Guid = texture2D.LightingGuid;
Name = texture2D.Name;
Path = texture2D.Owner?.Name;
Path = texture2D.GetPathName();
Format = texture2D.Format;
ImportedWidth = texture2D.ImportedSize.X;
ImportedHeight = texture2D.ImportedSize.Y;