mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
restore monogltf
This commit is contained in:
1 parent
4b0035345e
commit
39ffc6ae51
5 files changed
+394
-332
No files matched your search
+2
-1
@@ -8,7 +8,8 @@
|
|||||||
"blitzkit",
|
"blitzkit",
|
||||||
"webp",
|
"webp",
|
||||||
"hocs",
|
"hocs",
|
||||||
"cooldown"
|
"cooldown",
|
||||||
|
"Lods"
|
||||||
],
|
],
|
||||||
"userWords": [
|
"userWords": [
|
||||||
"WOTB",
|
"WOTB",
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"isRoot": true,
|
||||||
|
"tools": {
|
||||||
|
"csharpier": {
|
||||||
|
"version": "1.2.6",
|
||||||
|
"commands": [
|
||||||
|
"csharpier"
|
||||||
|
],
|
||||||
|
"rollForward": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,150 +1,175 @@
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using BlitzKit.Game.Models;
|
using BlitzKit.Game.Models;
|
||||||
|
using CUE4Parse_Conversion;
|
||||||
|
using CUE4Parse_Conversion.Meshes;
|
||||||
using CUE4Parse_Conversion.Textures;
|
using CUE4Parse_Conversion.Textures;
|
||||||
using CUE4Parse.Compression;
|
using CUE4Parse_Conversion.UEFormat.Enums;
|
||||||
using CUE4Parse.UE4.Assets.Exports;
|
using CUE4Parse.UE4.Assets.Exports;
|
||||||
|
using CUE4Parse.UE4.Assets.Exports.Engine;
|
||||||
|
using CUE4Parse.UE4.Assets.Exports.StaticMesh;
|
||||||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||||
using CUE4Parse.UE4.Assets.Objects.Properties;
|
using CUE4Parse.UE4.Assets.Objects;
|
||||||
using CUE4Parse.UE4.IO.Objects;
|
|
||||||
using CUE4Parse.UE4.Objects.Engine;
|
using CUE4Parse.UE4.Objects.Engine;
|
||||||
using CUE4Parse.UE4.Objects.UObject;
|
using CUE4Parse.UE4.Objects.UObject;
|
||||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
||||||
using Microsoft.JavaScript.NodeApi;
|
using Microsoft.JavaScript.NodeApi;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
using CUE4Parse.UE4.AssetRegistry.Objects;
|
|
||||||
using CUE4Parse.UE4.Readers;
|
|
||||||
using CUE4Parse.UE4.AssetRegistry.Readers;
|
|
||||||
using CUE4Parse.UE4.AssetRegistry;
|
|
||||||
using CUE4Parse.UE4.Assets.Exports.Engine;
|
|
||||||
using CUE4Parse.UE4.Assets.Objects;
|
|
||||||
using CUE4Parse.UE4.Assets.Exports.StaticMesh;
|
|
||||||
using CUE4Parse_Conversion.Meshes;
|
|
||||||
using CUE4Parse_Conversion;
|
|
||||||
|
|
||||||
namespace BlitzKit.Game.JSExport;
|
namespace BlitzKit.Game.JSExport;
|
||||||
|
|
||||||
[JSExport]
|
[JSExport]
|
||||||
public partial class GameInterface
|
public partial class GameInterface
|
||||||
{
|
{
|
||||||
private readonly BlitzFileProvider provider;
|
private readonly BlitzFileProvider provider;
|
||||||
|
private readonly BlitzExporter exporter;
|
||||||
|
|
||||||
public HashSet<string> files;
|
public HashSet<string> files;
|
||||||
|
|
||||||
public GameInterface(string directory, string map, string temp)
|
public GameInterface(string directory, string map, string temp)
|
||||||
{
|
|
||||||
provider = new(directory, map, temp);
|
|
||||||
files = [.. provider.Files.Keys];
|
|
||||||
}
|
|
||||||
|
|
||||||
public HashSet<string> Files => files;
|
|
||||||
|
|
||||||
public byte[] TankBigIcon(string tag)
|
|
||||||
{
|
|
||||||
var pda = provider.Discovered<UPrimaryDataAsset>(tag);
|
|
||||||
var icon = pda.Get<FSoftObjectPath>("BigIcon");
|
|
||||||
return provider.Image(icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
[GeneratedRegex(@"T_UI_Flag_(\w+)_S")]
|
|
||||||
private static partial Regex FlagNameRegex();
|
|
||||||
|
|
||||||
public byte[] Flag(string nation)
|
|
||||||
{
|
|
||||||
var flagsBase = "Blitz/Content/UI/Textures/Flag/TankCard/";
|
|
||||||
|
|
||||||
foreach (var path in provider.Files.Keys)
|
|
||||||
{
|
{
|
||||||
if (!path.StartsWith(flagsBase))
|
provider = new(directory, map, temp);
|
||||||
{
|
exporter = new();
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var fileName = Path.GetFileNameWithoutExtension(path);
|
files = [.. provider.Files.Keys];
|
||||||
var match = FlagNameRegex().Match(fileName);
|
}
|
||||||
|
|
||||||
if (
|
public HashSet<string> Files => files;
|
||||||
match.Success
|
|
||||||
&& match.Groups[1].Value.Equals(nation, StringComparison.CurrentCultureIgnoreCase)
|
public byte[] TankBigIcon(string tag)
|
||||||
)
|
{
|
||||||
{
|
var pda = provider.Discovered<UPrimaryDataAsset>(tag);
|
||||||
if (provider.TryLoadPackageObject<UTexture2D>($"{path}.{fileName}", out var uTexture))
|
var icon = pda.Get<FSoftObjectPath>("BigIcon");
|
||||||
|
return provider.Image(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
[GeneratedRegex(@"T_UI_Flag_(\w+)_S")]
|
||||||
|
private static partial Regex FlagNameRegex();
|
||||||
|
|
||||||
|
public byte[] Flag(string nation)
|
||||||
|
{
|
||||||
|
var flagsBase = "Blitz/Content/UI/Textures/Flag/TankCard/";
|
||||||
|
|
||||||
|
foreach (var path in provider.Files.Keys)
|
||||||
{
|
{
|
||||||
CTexture? cTexture = uTexture.Decode(ETexturePlatform.DesktopMobile);
|
if (!path.StartsWith(flagsBase))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (cTexture is null)
|
var fileName = Path.GetFileNameWithoutExtension(path);
|
||||||
{
|
var match = FlagNameRegex().Match(fileName);
|
||||||
Console.WriteLine($"No flag found for {nation}");
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
SKBitmap bitmap = cTexture.ToSkBitmap();
|
if (
|
||||||
SKData data = bitmap.Encode(SKEncodedImageFormat.Webp, 80);
|
match.Success
|
||||||
|
&& match.Groups[1].Value.Equals(nation, StringComparison.CurrentCultureIgnoreCase)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
provider.TryLoadPackageObject<UTexture2D>(
|
||||||
|
$"{path}.{fileName}",
|
||||||
|
out var uTexture
|
||||||
|
)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
CTexture? cTexture = uTexture.Decode(ETexturePlatform.DesktopMobile);
|
||||||
|
|
||||||
return data.ToArray();
|
if (cTexture is null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"No flag found for {nation}");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
SKBitmap bitmap = cTexture.ToSkBitmap();
|
||||||
|
SKData data = bitmap.Encode(SKEncodedImageFormat.Webp, 80);
|
||||||
|
|
||||||
|
return data.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"No flag found for {nation}");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine($"No flag found for {nation}");
|
Console.WriteLine($"No flag found for {nation}");
|
||||||
return [];
|
return [];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine($"No flag found for {nation}");
|
public byte[] ConsumableIcon(string tag)
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] ConsumableIcon(string tag)
|
|
||||||
{
|
|
||||||
var pda = provider.Discovered<UPrimaryDataAsset>(tag);
|
|
||||||
var icon = pda.Get<FSoftObjectPath>("Icon");
|
|
||||||
return provider.Image(icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] EquipmentIcon(string tag)
|
|
||||||
{
|
|
||||||
var pda = provider.Discovered<UPrimaryDataAsset>(tag);
|
|
||||||
var icon = pda.Get<FSoftObjectPath>("Icon");
|
|
||||||
return provider.Image(icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
readonly Dictionary<string, string> tankPartPrefixes = new()
|
|
||||||
{
|
|
||||||
{"chassis", "DT_Chassis"},
|
|
||||||
{"hull", "DT_Hulls"},
|
|
||||||
{"turret", "DT_Turrets"},
|
|
||||||
{"gun", "DT_Guns"},
|
|
||||||
};
|
|
||||||
public byte[] TankPart(string tag, string part)
|
|
||||||
{
|
|
||||||
var pda = provider.Discovered<UPrimaryDataAsset>(tag);
|
|
||||||
UDataTable? dataTable = null;
|
|
||||||
|
|
||||||
foreach (var (prefix, name) in tankPartPrefixes)
|
|
||||||
{
|
{
|
||||||
if (!part.StartsWith(prefix)) { continue; }
|
var pda = provider.Discovered<UPrimaryDataAsset>(tag);
|
||||||
|
var icon = pda.Get<FSoftObjectPath>("Icon");
|
||||||
dataTable = pda.Get<UDataTable>(name);
|
return provider.Image(icon);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dataTable == null)
|
public byte[] EquipmentIcon(string tag)
|
||||||
{
|
{
|
||||||
throw new ArgumentException($"Unknown tank part: {part}");
|
var pda = provider.Discovered<UPrimaryDataAsset>(tag);
|
||||||
|
var icon = pda.Get<FSoftObjectPath>("Icon");
|
||||||
|
return provider.Image(icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
dataTable.TryGetDataTableRow(part, StringComparison.Ordinal, out var row);
|
readonly Dictionary<string, string> tankPartPrefixes = new()
|
||||||
|
|
||||||
if (row == null)
|
|
||||||
{
|
{
|
||||||
throw new ArgumentException($"Unknown tank part: {part}");
|
{ "chassis", "DT_Chassis" },
|
||||||
|
{ "hull", "DT_Hulls" },
|
||||||
|
{ "turret", "DT_Turrets" },
|
||||||
|
{ "gun", "DT_Guns" },
|
||||||
|
};
|
||||||
|
|
||||||
|
public byte[] TankPart(string tag, string part)
|
||||||
|
{
|
||||||
|
var pda = provider.Discovered<UPrimaryDataAsset>(tag);
|
||||||
|
UDataTable? dataTable = null;
|
||||||
|
|
||||||
|
foreach (var (prefix, name) in tankPartPrefixes)
|
||||||
|
{
|
||||||
|
if (!part.StartsWith(prefix))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
dataTable = pda.Get<UDataTable>(name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataTable == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Unknown tank part: {part}");
|
||||||
|
}
|
||||||
|
|
||||||
|
dataTable.TryGetDataTableRow(part, StringComparison.Ordinal, out var row);
|
||||||
|
|
||||||
|
if (row == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Unknown tank part: {part}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var visualData = row.Get<UObject>("VisualData");
|
||||||
|
var meshSettings = visualData.Get<FStructFallback>("MeshSettings");
|
||||||
|
// var collisionMesh = meshSettings.Get<UStaticMesh>("CollisionMesh");
|
||||||
|
var mesh = meshSettings.Get<UStaticMesh>("GunMesh");
|
||||||
|
var gltf = new MonoGltf(mesh);
|
||||||
|
|
||||||
|
exporter.glb(mesh);
|
||||||
|
|
||||||
|
// var options = new ExporterOptions() {
|
||||||
|
// LodFormat = ELodFormat.FirstLod,
|
||||||
|
// MeshFormat = EMeshFormat.Gltf2,
|
||||||
|
// TextureFormat = ETextureFormat.Jpeg,
|
||||||
|
// };
|
||||||
|
// var exporter = new MeshExporter(mesh, options);
|
||||||
|
// var lod0 = exporter.MeshLods[0];
|
||||||
|
|
||||||
|
// exporter.TryWriteToDir(new("../../temp/model"), out var _, out var _);
|
||||||
|
|
||||||
|
// Console.WriteLine(lod0.FileName);
|
||||||
|
// Console.WriteLine(lod0.FileData.Length);
|
||||||
|
|
||||||
|
// foreach (var material in lod0.Materials)
|
||||||
|
// {
|
||||||
|
// material.ma
|
||||||
|
// // material.TryWriteToDir
|
||||||
|
// }
|
||||||
|
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
var visualData = row.Get<UObject>("VisualData");
|
|
||||||
var meshSettings = visualData.Get<FStructFallback>("MeshSettings");
|
|
||||||
// var collisionMesh = meshSettings.Get<UStaticMesh>("CollisionMesh");
|
|
||||||
var mesh = meshSettings.Get<UStaticMesh>("GunMesh");
|
|
||||||
var gltf = new MonoGltf(mesh);
|
|
||||||
|
|
||||||
return [.. gltf.Write()];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+241
-219
@@ -18,247 +18,269 @@ using SharpGLTF.Scenes;
|
|||||||
namespace BlitzKit.Game.Models;
|
namespace BlitzKit.Game.Models;
|
||||||
|
|
||||||
using ConfiguredMeshBuilder = MeshBuilder<
|
using ConfiguredMeshBuilder = MeshBuilder<
|
||||||
VertexPositionNormalTangent,
|
VertexPositionNormalTangent,
|
||||||
VertexColorXTextureX,
|
VertexColorXTextureX,
|
||||||
VertexEmpty
|
VertexEmpty
|
||||||
>;
|
>;
|
||||||
|
|
||||||
public class MonoGltf
|
public class MonoGltf
|
||||||
{
|
{
|
||||||
readonly SceneBuilder Scene;
|
readonly SceneBuilder Scene;
|
||||||
|
|
||||||
public MonoGltf(UStaticMesh staticMesh)
|
public MonoGltf(UStaticMesh staticMesh)
|
||||||
{
|
|
||||||
Scene = new(staticMesh.Name);
|
|
||||||
ExporterOptions options = new() { TextureFormat = ETextureFormat.Jpeg };
|
|
||||||
staticMesh.TryConvert(out var convertedMesh);
|
|
||||||
var lod =
|
|
||||||
convertedMesh.LODs.Find(lod => !lod.SkipLod) ?? throw new Exception("Failed to find lod0"); // first non-skippable lod is the highest quality
|
|
||||||
|
|
||||||
MaterialBuilder emptyMaterial = new("empty_material");
|
|
||||||
Dictionary<string, MaterialBuilder> namedMaterials = [];
|
|
||||||
|
|
||||||
int sectionIndex = 0;
|
|
||||||
foreach (var section in lod.Sections.Value)
|
|
||||||
{
|
{
|
||||||
ConfiguredMeshBuilder meshBuilder = new($"{staticMesh.Name}_section_{sectionIndex}");
|
Scene = new(staticMesh.Name);
|
||||||
MaterialBuilder materialBuilder;
|
ExporterOptions options = new() { TextureFormat = ETextureFormat.Jpeg };
|
||||||
|
staticMesh.TryConvert(out var convertedMesh);
|
||||||
|
var lod =
|
||||||
|
convertedMesh.LODs.Find(lod => !lod.SkipLod)
|
||||||
|
?? throw new Exception("Failed to find lod0");
|
||||||
|
|
||||||
if (section.Material == null || section.MaterialName == null)
|
MaterialBuilder emptyMaterial = new("empty_material");
|
||||||
{
|
Dictionary<string, MaterialBuilder> namedMaterials = [];
|
||||||
materialBuilder = emptyMaterial;
|
|
||||||
}
|
|
||||||
else if (namedMaterials.TryGetValue(section.MaterialName, out MaterialBuilder? value))
|
|
||||||
{
|
|
||||||
materialBuilder = value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var materialInterface = section.Material.Load<UMaterialInterface>()!;
|
|
||||||
MaterialData materialData = new() { Textures = [], Parameters = new() };
|
|
||||||
materialInterface.GetParams(materialData.Parameters, options.MaterialFormat);
|
|
||||||
materialBuilder = new(section.MaterialName);
|
|
||||||
|
|
||||||
foreach (var parameterTexture in materialData.Parameters.Textures)
|
int sectionIndex = 0;
|
||||||
|
foreach (var section in lod.Sections.Value)
|
||||||
{
|
{
|
||||||
var texture = parameterTexture.Value;
|
ConfiguredMeshBuilder meshBuilder = new($"{staticMesh.Name}_section_{sectionIndex}");
|
||||||
|
MaterialBuilder materialBuilder;
|
||||||
|
|
||||||
if (
|
if (section.Material == null || section.MaterialName == null)
|
||||||
texture is not UTexture2D texture2d
|
|
||||||
|| texture2d.Decode(options.Platform) is not { } bitmap
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
switch (parameterTexture.Key)
|
|
||||||
{
|
{
|
||||||
case "BaseColor":
|
materialBuilder = emptyMaterial;
|
||||||
{
|
}
|
||||||
var encoded = bitmap.Encode(options.TextureFormat, 100);
|
else if (namedMaterials.TryGetValue(section.MaterialName, out MaterialBuilder? value))
|
||||||
var bytes = encoded.ToArray();
|
{
|
||||||
materialBuilder.WithBaseColor(bytes);
|
materialBuilder = value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var materialInterface = section.Material.Load<UMaterialInterface>()!;
|
||||||
|
MaterialData materialData = new() { Textures = [], Parameters = new() };
|
||||||
|
materialInterface.GetParams(materialData.Parameters, options.MaterialFormat);
|
||||||
|
materialBuilder = new(section.MaterialName);
|
||||||
|
|
||||||
break;
|
foreach (var parameterTexture in materialData.Parameters.Textures)
|
||||||
}
|
|
||||||
|
|
||||||
case "RM":
|
|
||||||
{
|
|
||||||
var encoded = bitmap.Encode(options.TextureFormat, 100);
|
|
||||||
|
|
||||||
for (int y = 0; y < bitmap.Height; y++)
|
|
||||||
{
|
{
|
||||||
for (int x = 0; x < bitmap.Width; x++)
|
var texture = parameterTexture.Value;
|
||||||
{
|
|
||||||
var index = y * bitmap.Width + x;
|
if (
|
||||||
var color = bitmap.Pixels[index];
|
texture is not UTexture2D texture2d
|
||||||
bitmap.Pixels[index] = new(color.Blue, color.Red, color.Green, color.Alpha);
|
|| texture2d.Decode(options.Platform) is not { } bitmap
|
||||||
}
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
switch (parameterTexture.Key)
|
||||||
|
{
|
||||||
|
case "BaseColor":
|
||||||
|
{
|
||||||
|
var encoded = bitmap.Encode(
|
||||||
|
options.TextureFormat,
|
||||||
|
false,
|
||||||
|
out var _
|
||||||
|
);
|
||||||
|
var bytes = encoded.ToArray();
|
||||||
|
materialBuilder.WithBaseColor(bytes);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "RM":
|
||||||
|
{
|
||||||
|
var encoded = bitmap.Encode(
|
||||||
|
options.TextureFormat,
|
||||||
|
false,
|
||||||
|
out var _
|
||||||
|
);
|
||||||
|
|
||||||
|
// for (int y = 0; y < bitmap.Height; y++)
|
||||||
|
// {
|
||||||
|
// for (int x = 0; x < bitmap.Width; x++)
|
||||||
|
// {
|
||||||
|
// var index = y * bitmap.Width + x;
|
||||||
|
// var color = bitmap.Pixels[index];
|
||||||
|
// bitmap.Pixels[index] = new(
|
||||||
|
// color.Blue,
|
||||||
|
// color.Red,
|
||||||
|
// color.Green,
|
||||||
|
// color.Alpha
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
var bytes = encoded.ToArray();
|
||||||
|
materialBuilder.WithBaseColor(bytes);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "PM_SpecularMasks":
|
||||||
|
// materialBuilder.WithSpecularGlossiness(textureBytes);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "PM_Diffuse":
|
||||||
|
// materialBuilder.WithDiffuse(textureBytes);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "T_R90_IS_4_MISC":
|
||||||
|
case "T_R90_IS_4_MASK":
|
||||||
|
case "Misc":
|
||||||
|
case "Mask":
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Exception(
|
||||||
|
$"Unsupported texture type: {parameterTexture.Key}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Failed to load texture: {parameterTexture.Key}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var primitive = meshBuilder.UsePrimitive(materialBuilder);
|
||||||
|
|
||||||
|
for (int faceIndex = 0; faceIndex < section.NumFaces; faceIndex++)
|
||||||
|
{
|
||||||
|
var wedgeIndex = new int[3];
|
||||||
|
for (var k = 0; k < wedgeIndex.Length; k++)
|
||||||
|
{
|
||||||
|
wedgeIndex[k] = (int)lod.Indices.Value[section.FirstIndex + faceIndex * 3 + k];
|
||||||
}
|
}
|
||||||
|
|
||||||
var bytes = encoded.ToArray();
|
var vertex1 = lod.Verts[wedgeIndex[0]];
|
||||||
materialBuilder.WithBaseColor(bytes);
|
var vertex2 = lod.Verts[wedgeIndex[1]];
|
||||||
|
var vertex3 = lod.Verts[wedgeIndex[2]];
|
||||||
|
|
||||||
break;
|
var (v1, v2, v3) = PrepareTris(vertex1, vertex2, vertex3);
|
||||||
}
|
var (c1, c2, c3) = PrepareUVsAndTexCoords(
|
||||||
|
lod,
|
||||||
|
vertex1,
|
||||||
|
vertex2,
|
||||||
|
vertex3,
|
||||||
|
wedgeIndex
|
||||||
|
);
|
||||||
|
|
||||||
case "PM_SpecularMasks":
|
primitive.AddTriangle((v1, c1), (v2, c2), (v3, c3));
|
||||||
// materialBuilder.WithSpecularGlossiness(textureBytes);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "PM_Diffuse":
|
|
||||||
// materialBuilder.WithDiffuse(textureBytes);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "T_R90_IS_4_MISC":
|
|
||||||
case "T_R90_IS_4_MASK":
|
|
||||||
case "Misc":
|
|
||||||
case "Mask":
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Exception($"Unsupported texture type: {parameterTexture.Key}");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch
|
Scene.AddRigidMesh(meshBuilder, Matrix4x4.Identity);
|
||||||
{
|
|
||||||
PrettyLog.Warn($"Failed to load texture: {parameterTexture.Key}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var primitive = meshBuilder.UsePrimitive(materialBuilder);
|
|
||||||
|
|
||||||
for (int faceIndex = 0; faceIndex < section.NumFaces; faceIndex++)
|
|
||||||
{
|
|
||||||
var wedgeIndex = new int[3];
|
|
||||||
for (var k = 0; k < wedgeIndex.Length; k++)
|
|
||||||
{
|
|
||||||
wedgeIndex[k] = lod.Indices.Value[section.FirstIndex + faceIndex * 3 + k];
|
|
||||||
}
|
|
||||||
|
|
||||||
var vertex1 = lod.Verts[wedgeIndex[0]];
|
|
||||||
var vertex2 = lod.Verts[wedgeIndex[1]];
|
|
||||||
var vertex3 = lod.Verts[wedgeIndex[2]];
|
|
||||||
|
|
||||||
var (v1, v2, v3) = PrepareTris(vertex1, vertex2, vertex3);
|
|
||||||
var (c1, c2, c3) = PrepareUVsAndTexCoords(lod, vertex1, vertex2, vertex3, wedgeIndex);
|
|
||||||
|
|
||||||
primitive.AddTriangle((v1, c1), (v2, c2), (v3, c3));
|
|
||||||
}
|
|
||||||
|
|
||||||
Scene.AddRigidMesh(meshBuilder, Matrix4x4.Identity);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public ArraySegment<byte> Write()
|
public ArraySegment<byte> Write()
|
||||||
{
|
|
||||||
return Scene.ToGltf2().WriteGLB();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (
|
|
||||||
VertexPositionNormalTangent,
|
|
||||||
VertexPositionNormalTangent,
|
|
||||||
VertexPositionNormalTangent
|
|
||||||
) PrepareTris(CMeshVertex vert1, CMeshVertex vert2, CMeshVertex vert3)
|
|
||||||
{
|
|
||||||
VertexPositionNormalTangent v1 = new(
|
|
||||||
SwapYZ(vert1.Position * 0.01f),
|
|
||||||
SwapYZAndNormalize((FVector)vert1.Normal),
|
|
||||||
SwapYZAndNormalize((Vector4)vert1.Tangent)
|
|
||||||
);
|
|
||||||
VertexPositionNormalTangent v2 = new(
|
|
||||||
SwapYZ(vert2.Position * 0.01f),
|
|
||||||
SwapYZAndNormalize((FVector)vert2.Normal),
|
|
||||||
SwapYZAndNormalize((Vector4)vert2.Tangent)
|
|
||||||
);
|
|
||||||
VertexPositionNormalTangent v3 = new(
|
|
||||||
SwapYZ(vert3.Position * 0.01f),
|
|
||||||
SwapYZAndNormalize((FVector)vert3.Normal),
|
|
||||||
SwapYZAndNormalize((Vector4)vert3.Tangent)
|
|
||||||
);
|
|
||||||
|
|
||||||
return (v1, v2, v3);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FVector SwapYZ(FVector vec)
|
|
||||||
{
|
|
||||||
var res = new FVector(vec.X, vec.Z, vec.Y);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FVector SwapYZAndNormalize(FVector vec)
|
|
||||||
{
|
|
||||||
var res = SwapYZ(vec);
|
|
||||||
res.Normalize();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Vector4 SwapYZAndNormalize(Vector4 vec)
|
|
||||||
{
|
|
||||||
return Vector4.Normalize(new Vector4(vec.X, vec.Z, vec.Y, vec.W));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static (
|
|
||||||
VertexColorXTextureX,
|
|
||||||
VertexColorXTextureX,
|
|
||||||
VertexColorXTextureX
|
|
||||||
) PrepareUVsAndTexCoords(
|
|
||||||
CBaseMeshLod lod,
|
|
||||||
CMeshVertex vert1,
|
|
||||||
CMeshVertex vert2,
|
|
||||||
CMeshVertex vert3,
|
|
||||||
int[] indices
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return PrepareUVsAndTexCoords(
|
|
||||||
lod.VertexColors ?? new FColor[lod.NumVerts],
|
|
||||||
vert1,
|
|
||||||
vert2,
|
|
||||||
vert3,
|
|
||||||
lod.ExtraUV.Value,
|
|
||||||
indices
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static (
|
|
||||||
VertexColorXTextureX,
|
|
||||||
VertexColorXTextureX,
|
|
||||||
VertexColorXTextureX
|
|
||||||
) PrepareUVsAndTexCoords(
|
|
||||||
FColor[] colors,
|
|
||||||
CMeshVertex vert1,
|
|
||||||
CMeshVertex vert2,
|
|
||||||
CMeshVertex vert3,
|
|
||||||
FMeshUVFloat[][] uvs,
|
|
||||||
int[] indices
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var (uvs1, uvs2, uvs3) = PrepareUVs(vert1, vert2, vert3, uvs, indices);
|
|
||||||
var c1 = new VertexColorXTextureX((Vector4)colors[indices[0]] / 255, uvs1);
|
|
||||||
var c2 = new VertexColorXTextureX((Vector4)colors[indices[1]] / 255, uvs2);
|
|
||||||
var c3 = new VertexColorXTextureX((Vector4)colors[indices[2]] / 255, uvs3);
|
|
||||||
return (c1, c2, c3);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (List<Vector2>, List<Vector2>, List<Vector2>) PrepareUVs(
|
|
||||||
CMeshVertex vert1,
|
|
||||||
CMeshVertex vert2,
|
|
||||||
CMeshVertex vert3,
|
|
||||||
FMeshUVFloat[][] uvs,
|
|
||||||
int[] indices
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var uvs1 = new List<Vector2>() { (Vector2)vert1.UV };
|
|
||||||
var uvs2 = new List<Vector2>() { (Vector2)vert2.UV };
|
|
||||||
var uvs3 = new List<Vector2>() { (Vector2)vert3.UV };
|
|
||||||
foreach (var uv in uvs)
|
|
||||||
{
|
{
|
||||||
uvs1.Add((Vector2)uv[indices[0]]);
|
return Scene.ToGltf2().WriteGLB();
|
||||||
uvs2.Add((Vector2)uv[indices[1]]);
|
|
||||||
uvs3.Add((Vector2)uv[indices[2]]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (uvs1, uvs2, uvs3);
|
private static (
|
||||||
}
|
VertexPositionNormalTangent,
|
||||||
|
VertexPositionNormalTangent,
|
||||||
|
VertexPositionNormalTangent
|
||||||
|
) PrepareTris(CMeshVertex vert1, CMeshVertex vert2, CMeshVertex vert3)
|
||||||
|
{
|
||||||
|
VertexPositionNormalTangent v1 = new(
|
||||||
|
SwapYZ(vert1.Position * 0.01f),
|
||||||
|
SwapYZAndNormalize((FVector)vert1.Normal),
|
||||||
|
SwapYZAndNormalize((Vector4)vert1.Tangent)
|
||||||
|
);
|
||||||
|
VertexPositionNormalTangent v2 = new(
|
||||||
|
SwapYZ(vert2.Position * 0.01f),
|
||||||
|
SwapYZAndNormalize((FVector)vert2.Normal),
|
||||||
|
SwapYZAndNormalize((Vector4)vert2.Tangent)
|
||||||
|
);
|
||||||
|
VertexPositionNormalTangent v3 = new(
|
||||||
|
SwapYZ(vert3.Position * 0.01f),
|
||||||
|
SwapYZAndNormalize((FVector)vert3.Normal),
|
||||||
|
SwapYZAndNormalize((Vector4)vert3.Tangent)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (v1, v2, v3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FVector SwapYZ(FVector vec)
|
||||||
|
{
|
||||||
|
var res = new FVector(vec.X, vec.Z, vec.Y);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FVector SwapYZAndNormalize(FVector vec)
|
||||||
|
{
|
||||||
|
var res = SwapYZ(vec);
|
||||||
|
res.Normalize();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector4 SwapYZAndNormalize(Vector4 vec)
|
||||||
|
{
|
||||||
|
return Vector4.Normalize(new Vector4(vec.X, vec.Z, vec.Y, vec.W));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static (
|
||||||
|
VertexColorXTextureX,
|
||||||
|
VertexColorXTextureX,
|
||||||
|
VertexColorXTextureX
|
||||||
|
) PrepareUVsAndTexCoords(
|
||||||
|
CBaseMeshLod lod,
|
||||||
|
CMeshVertex vert1,
|
||||||
|
CMeshVertex vert2,
|
||||||
|
CMeshVertex vert3,
|
||||||
|
int[] indices
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return PrepareUVsAndTexCoords(
|
||||||
|
lod.VertexColors ?? new FColor[lod.NumVerts],
|
||||||
|
vert1,
|
||||||
|
vert2,
|
||||||
|
vert3,
|
||||||
|
lod.ExtraUV.Value,
|
||||||
|
indices
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static (
|
||||||
|
VertexColorXTextureX,
|
||||||
|
VertexColorXTextureX,
|
||||||
|
VertexColorXTextureX
|
||||||
|
) PrepareUVsAndTexCoords(
|
||||||
|
FColor[] colors,
|
||||||
|
CMeshVertex vert1,
|
||||||
|
CMeshVertex vert2,
|
||||||
|
CMeshVertex vert3,
|
||||||
|
FMeshUVFloat[][] uvs,
|
||||||
|
int[] indices
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var (uvs1, uvs2, uvs3) = PrepareUVs(vert1, vert2, vert3, uvs, indices);
|
||||||
|
var c1 = new VertexColorXTextureX((Vector4)colors[indices[0]] / 255, uvs1);
|
||||||
|
var c2 = new VertexColorXTextureX((Vector4)colors[indices[1]] / 255, uvs2);
|
||||||
|
var c3 = new VertexColorXTextureX((Vector4)colors[indices[2]] / 255, uvs3);
|
||||||
|
return (c1, c2, c3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (List<Vector2>, List<Vector2>, List<Vector2>) PrepareUVs(
|
||||||
|
CMeshVertex vert1,
|
||||||
|
CMeshVertex vert2,
|
||||||
|
CMeshVertex vert3,
|
||||||
|
FMeshUVFloat[][] uvs,
|
||||||
|
int[] indices
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var uvs1 = new List<Vector2>() { (Vector2)vert1.UV };
|
||||||
|
var uvs2 = new List<Vector2>() { (Vector2)vert2.UV };
|
||||||
|
var uvs3 = new List<Vector2>() { (Vector2)vert3.UV };
|
||||||
|
foreach (var uv in uvs)
|
||||||
|
{
|
||||||
|
uvs1.Add((Vector2)uv[indices[0]]);
|
||||||
|
uvs2.Add((Vector2)uv[indices[1]]);
|
||||||
|
uvs3.Add((Vector2)uv[indices[2]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (uvs1, uvs2, uvs3);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -36,6 +36,7 @@ export async function GET({
|
|||||||
const assetDiscovery = item.BlitzStaticAssetsDiscovery();
|
const assetDiscovery = item.BlitzStaticAssetsDiscovery();
|
||||||
const tag = assertDiscoveryTag(assetDiscovery);
|
const tag = assertDiscoveryTag(assetDiscovery);
|
||||||
const part = game!.tankPart(tag, params.part);
|
const part = game!.tankPart(tag, params.part);
|
||||||
|
const array = new Uint8Array(part);
|
||||||
|
|
||||||
return Response.json(part.length);
|
return new Response(array);
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user