Refactor mesh import, meshlet, and asset handler systems

- Mesh import now builds full node hierarchy and splits geometry by material, with robust normal/tangent handling
- Meshlet generation supports material indices for correct assignment
- Refactored texture cube map compression and mipmap handling
- Updated asset handler registration to new namespace
- Enabled asset reimport on import events
- Improved code quality, resource management, and formatting
This commit is contained in:
2026-04-26 14:49:58 +09:00
parent 1a91811621
commit 5903ddda2b
8 changed files with 218 additions and 169 deletions

View File

@@ -1,4 +1,5 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Engine.Utilities;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Ghost.Graphics.Utilities; using Ghost.Graphics.Utilities;
using Ghost.MeshOptimizer; using Ghost.MeshOptimizer;
@@ -59,23 +60,60 @@ internal unsafe class MeshParsingWorkItem : IJob
}; };
} }
private GeometryMeshNode ParseGeometry(ufbx_mesh* pMesh) [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float4x4 ToFloat4x4(ufbx_vec3 t, ufbx_quat q, ufbx_vec3 s)
{ {
var meshNode = new GeometryMeshNode return float4x4.TRS(
new float3(t.x, t.y, t.z),
new quaternion(q.x, q.y, q.z, q.w),
new float3(s.x, s.y, s.z)
);
}
private MeshNode ParseHierarchy(ufbx_node* node)
{
var children = new List<MeshNode>();
var meshNode = new MeshNode
{ {
Name = pMesh->name.ToString(), Name = node->name.ToString(),
Children = Array.Empty<MeshNode>(), LocalTransform = ToFloat4x4(node->local_transform.translation, node->local_transform.rotation, node->local_transform.scale),
Children = children
}; };
if (node->mesh != null)
{
var geoNodes = ParseGeometry(node->mesh);
children.AddRange(geoNodes);
}
// TODO: Handle lights, cameras, and other node types.
for (var i = 0u; i < node->children.count; i++)
{
children.Add(ParseHierarchy(node->children.data[i]));
}
return meshNode;
}
private IReadOnlyCollection<GeometryMeshNode> ParseGeometry(ufbx_mesh* pMesh)
{
var resultNodes = new List<GeometryMeshNode>();
if (pMesh->num_faces == 0) if (pMesh->num_faces == 0)
{ {
return meshNode; return resultNodes;
} }
var missingNormals = false; var numMaterials = pMesh->materials.count > 0 ? (int)pMesh->materials.count : 1;
var missingTangents = false; var materialBuckets = new UnsafeList<Vertex>[numMaterials];
var missingNormalsBucket = new bool[numMaterials];
var missingTangentsBucket = new bool[numMaterials];
using var flatVertices = new UnsafeList<Vertex>(1024, AllocationHandle.FreeList); for (int i = 0; i < numMaterials; i++)
{
materialBuckets[i] = new UnsafeList<Vertex>(1024, AllocationHandle.FreeList);
}
var maxScratchIndices = (int)(pMesh->max_face_triangles * 3u); var maxScratchIndices = (int)(pMesh->max_face_triangles * 3u);
@@ -84,9 +122,10 @@ internal unsafe class MeshParsingWorkItem : IJob
for (var j = 0u; j < pMesh->num_faces; j++) for (var j = 0u; j < pMesh->num_faces; j++)
{ {
var face = pMesh->faces.data[j]; var face = pMesh->faces.data[j];
var materialIdx = pMesh->face_material.count > j ? pMesh->face_material.data[j] : 0;
var numTris = UfbxApi.TriangulateFace(triIndicesArray.AsSpan(0, maxScratchIndices), pMesh, face); var numTris = UfbxApi.TriangulateFace(triIndicesArray.AsSpan(0, maxScratchIndices), pMesh, face);
var totalIndices = numTris * 3; var totalIndices = numTris * 3;
for (var k = 0; k < totalIndices; k++) for (var k = 0; k < totalIndices; k++)
{ {
@@ -123,62 +162,88 @@ internal unsafe class MeshParsingWorkItem : IJob
vertex.tangent = ComputeTangent(t, n, b); vertex.tangent = ComputeTangent(t, n, b);
} }
var newIndex = (uint)flatVertices.Count; materialBuckets[materialIdx].Add(vertex);
flatVertices.Add(vertex); if (!missingNormalsBucket[materialIdx])
if (!missingNormals)
{ {
missingNormals = normIdx == uint.MaxValue; missingNormalsBucket[materialIdx] = normIdx == uint.MaxValue;
} }
if (!missingTangents) if (!missingTangentsBucket[materialIdx])
{ {
missingTangents = tanIdx == uint.MaxValue || btanIdx == uint.MaxValue; missingTangentsBucket[materialIdx] = tanIdx == uint.MaxValue || btanIdx == uint.MaxValue;
} }
} }
} }
var numIndices = (uint)flatVertices.Count; for (int m = 0; m < numMaterials; m++)
using var weldedIndices = new UnsafeArray<uint>((int)numIndices, AllocationHandle.FreeList);
using var cachedIndices = new UnsafeArray<uint>((int)numIndices, AllocationHandle.FreeList);
var stream = new ufbx_vertex_stream
{ {
data = flatVertices.GetUnsafePtr(), var flatVertices = materialBuckets[m];
vertex_count = numIndices, if (flatVertices.Count == 0)
vertex_size = (nuint)sizeof(Vertex) {
}; flatVertices.Dispose();
continue;
}
var error = new ufbx_error(); var numIndices = (uint)flatVertices.Count;
var numUniqueVertices = UfbxApi.GenerateIndices([stream], weldedIndices, null, &error);
if (numUniqueVertices == 0 && error.type != ufbx_error_type.UFBX_ERROR_NONE) using var weldedIndices = new UnsafeArray<uint>((int)numIndices, AllocationHandle.FreeList);
{ using var cachedIndices = new UnsafeArray<uint>((int)numIndices, AllocationHandle.FreeList);
return;
var stream = new ufbx_vertex_stream
{
data = flatVertices.GetUnsafePtr(),
vertex_count = numIndices,
vertex_size = (nuint)sizeof(Vertex)
};
var error = new ufbx_error();
var numUniqueVertices = UfbxApi.GenerateIndices([stream], weldedIndices, null, &error);
if (numUniqueVertices == 0 && error.type != ufbx_error_type.UFBX_ERROR_NONE)
{
flatVertices.Dispose();
continue;
}
MeshOptApi.OptimizeVertexCache((uint*)cachedIndices.GetUnsafePtr(), (uint*)weldedIndices.GetUnsafePtr(), numIndices, numUniqueVertices);
var nodeVertices = new UnsafeList<Vertex>((int)numUniqueVertices, _allocationHandle);
var nodeIndices = new UnsafeList<uint>((int)numIndices, _allocationHandle);
var finalVertexCount = MeshOptApi.OptimizeVertexFetch(nodeVertices.GetUnsafePtr(), (uint*)cachedIndices.GetUnsafePtr(), numIndices, flatVertices.GetUnsafePtr(), numIndices, (nuint)sizeof(Vertex));
nodeVertices.UnsafeSetCount((int)finalVertexCount);
MemoryUtility.MemCpy(nodeIndices.GetUnsafePtr(), cachedIndices.GetUnsafePtr(), numIndices * sizeof(uint));
nodeIndices.UnsafeSetCount((int)numIndices);
if (_settings.NormalDataSource == VertexDataSource.Computed || (_settings.NormalDataSource == VertexDataSource.ComputedIfMissing && missingNormalsBucket[m]))
{
MeshBuilder.ComputeNormal(nodeVertices, nodeIndices);
}
if (_settings.TangentDataSource == VertexDataSource.Computed || (_settings.TangentDataSource == VertexDataSource.ComputedIfMissing && missingTangentsBucket[m]))
{
MeshBuilder.ComputeTangents(nodeVertices, nodeIndices);
}
var meshNodeName = numMaterials > 1 ? $"{pMesh->name.ToString()}_mat{m}" : pMesh->name.ToString();
var meshNode = new GeometryMeshNode
{
Name = meshNodeName,
LocalTransform = new float4x4(new float4(1, 0, 0, 0), new float4(0, 1, 0, 0), new float4(0, 0, 1, 0), new float4(0, 0, 0, 1)),
Children = Array.Empty<MeshNode>(),
Vertices = nodeVertices,
Indices = nodeIndices,
MaterialIndex = m
};
resultNodes.Add(meshNode);
flatVertices.Dispose();
} }
MeshOptApi.OptimizeVertexCache((uint*)cachedIndices.GetUnsafePtr(), (uint*)weldedIndices.GetUnsafePtr(), numIndices, numUniqueVertices); return resultNodes;
vertices = new UnsafeList<Vertex>((int)numUniqueVertices, _allocationHandle);
indices = new UnsafeList<uint>((int)numIndices, _allocationHandle);
var finalVertexCount = MeshOptApi.OptimizeVertexFetch(vertices.GetUnsafePtr(), (uint*)cachedIndices.GetUnsafePtr(), numIndices, flatVertices.GetUnsafePtr(), numIndices, (nuint)sizeof(Vertex));
vertices.UnsafeSetCount((int)finalVertexCount);
MemoryUtility.MemCpy(indices.GetUnsafePtr(), cachedIndices.GetUnsafePtr(), numIndices * sizeof(uint));
indices.UnsafeSetCount((int)numIndices);
if (_settings.NormalDataSource == VertexDataSource.Computed || (_settings.NormalDataSource == VertexDataSource.ComputedIfMissing && missingNormals))
{
MeshBuilder.ComputeNormal(vertices, indices);
}
if (_settings.TangentDataSource == VertexDataSource.Computed || (_settings.TangentDataSource == VertexDataSource.ComputedIfMissing && missingTangents))
{
MeshBuilder.ComputeTangents(vertices, indices);
}
} }
public void Execute(ref readonly JobExecutionContext context) public void Execute(ref readonly JobExecutionContext context)
@@ -231,68 +296,10 @@ internal unsafe class MeshParsingWorkItem : IJob
return; return;
} }
using var flatVertices = new UnsafeList<Vertex>(1024, AllocationHandle.FreeList); var rootNode = ParseHierarchy(scene.Get()->root_node);
rootNode.Name = Path.GetFileNameWithoutExtension(_filePath);
var missingNormals = false; _taskCompletionSource.SetResult(Result<MeshNode>.Success(rootNode));
var missingTangents = false;
for (var i = 0u; i < scene.Get()->nodes.count; i++)
{
var data = scene.Get()->nodes.data;
var node = scene.Get()->nodes.data[i];
if (node->is_root)
{
continue;
}
if (node->mesh != null)
{
}
}
var numIndices = (uint)flatVertices.Count;
using var weldedIndices = new UnsafeArray<uint>((int)numIndices, AllocationHandle.FreeList);
using var cachedIndices = new UnsafeArray<uint>((int)numIndices, AllocationHandle.FreeList);
var stream = new ufbx_vertex_stream
{
data = flatVertices.GetUnsafePtr(),
vertex_count = numIndices,
vertex_size = (nuint)sizeof(Vertex)
};
var numUniqueVertices = UfbxApi.GenerateIndices([stream], weldedIndices, null, &error);
if (numUniqueVertices == 0 && error.type != ufbx_error_type.UFBX_ERROR_NONE)
{
_taskCompletionSource.SetResult(Result.Failure($"Welding failed: {error.description}"));
return;
}
MeshOptApi.OptimizeVertexCache((uint*)cachedIndices.GetUnsafePtr(), (uint*)weldedIndices.GetUnsafePtr(), numIndices, numUniqueVertices);
vertices = new UnsafeList<Vertex>((int)numUniqueVertices, _allocationHandle);
indices = new UnsafeList<uint>((int)numIndices, _allocationHandle);
var finalVertexCount = MeshOptApi.OptimizeVertexFetch(vertices.GetUnsafePtr(), (uint*)cachedIndices.GetUnsafePtr(), numIndices, flatVertices.GetUnsafePtr(), numIndices, (nuint)sizeof(Vertex));
vertices.UnsafeSetCount((int)finalVertexCount);
MemoryUtility.MemCpy(indices.GetUnsafePtr(), cachedIndices.GetUnsafePtr(), numIndices * sizeof(uint));
indices.UnsafeSetCount((int)numIndices);
if (_settings.NormalDataSource == VertexDataSource.Computed || (_settings.NormalDataSource == VertexDataSource.ComputedIfMissing && missingNormals))
{
MeshBuilder.ComputeNormal(vertices, indices);
}
if (_settings.TangentDataSource == VertexDataSource.Computed || (_settings.TangentDataSource == VertexDataSource.ComputedIfMissing && missingTangents))
{
MeshBuilder.ComputeTangents(vertices, indices);
}
_taskCompletionSource.SetResult(Result.Success());
} }
} }

View File

@@ -695,7 +695,13 @@ public static unsafe partial class MeshProcessor
return finalClusterCount; return finalClusterCount;
} }
public static void BuildMeshlets(MeshletMeshData* pMeshletData, ReadOnlyUnsafeCollection<Vertex> vertices, ReadOnlyUnsafeCollection<uint> indices) private struct MeshletContext
{
public MeshletMeshData* data;
public int materialIndex;
}
public static void BuildMeshlets(MeshletMeshData* pMeshletData, ReadOnlyUnsafeCollection<Vertex> vertices, ReadOnlyUnsafeCollection<uint> indices, int materialIndex = 0)
{ {
Logger.DebugAssert(pMeshletData->meshletCount > 0, "Mesh must have vertices to build meshlets."); Logger.DebugAssert(pMeshletData->meshletCount > 0, "Mesh must have vertices to build meshlets.");
@@ -735,7 +741,13 @@ public static unsafe partial class MeshProcessor
attributeProtectMask = 0, // TODO: We need to protect UVs and other vertex attributes to ensure they are not altered during simplification. attributeProtectMask = 0, // TODO: We need to protect UVs and other vertex attributes to ensure they are not altered during simplification.
}; };
Build(in config, in clodMesh, pMeshletData, MeshletOutputCallback); var context = new MeshletContext
{
data = pMeshletData,
materialIndex = materialIndex
};
Build(in config, in clodMesh, &context, MeshletOutputCallback);
pMeshletData->meshletCount = pMeshletData->meshlets.IsCreated ? pMeshletData->meshlets.Count : 0; pMeshletData->meshletCount = pMeshletData->meshlets.IsCreated ? pMeshletData->meshlets.Count : 0;
@@ -750,12 +762,14 @@ public static unsafe partial class MeshProcessor
pMeshletData->lodLevelCount = (int)maxLodLevel + 1; pMeshletData->lodLevelCount = (int)maxLodLevel + 1;
} }
pMeshletData->materialSlotCount = 1; pMeshletData->materialSlotCount = Math.Max(pMeshletData->materialSlotCount, materialIndex + 1);
} }
private static int MeshletOutputCallback(void* context, ClodGroup group, ReadOnlyUnsafeCollection<ClodCluster> clusters) private static int MeshletOutputCallback(void* contextPtr, ClodGroup group, ReadOnlyUnsafeCollection<ClodCluster> clusters)
{ {
var pMeshletData = (MeshletMeshData*)context; var context = (MeshletContext*)contextPtr;
var pMeshletData = context->data;
var materialIndex = context->materialIndex;
// Ensure lists are initialized // Ensure lists are initialized
if (!pMeshletData->groups.IsCreated) pMeshletData->groups = new UnsafeList<MeshletGroup>(16, AllocationHandle.Persistent); if (!pMeshletData->groups.IsCreated) pMeshletData->groups = new UnsafeList<MeshletGroup>(16, AllocationHandle.Persistent);
@@ -790,7 +804,7 @@ public static unsafe partial class MeshProcessor
groupIndex = (uint)pMeshletData->groups.Count - 1, groupIndex = (uint)pMeshletData->groups.Count - 1,
clusterError = cluster.bounds.error, clusterError = cluster.bounds.error,
parentError = group.simplified.error, parentError = group.simplified.error,
localMaterialIndex = 0, // TODO: support multiple materials localMaterialIndex = (byte)materialIndex,
lodLevel = (byte)group.depth, lodLevel = (byte)group.depth,
}; };
pMeshletData->meshlets.Add(meshlet); pMeshletData->meshlets.Add(meshlet);

View File

@@ -1,11 +1,8 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Engine; using Ghost.Engine;
using Ghost.Nvtt; using Ghost.Nvtt;
using Misaki.HighPerformance.Jobs;
using Misaki.HighPerformance.LowLevel; using Misaki.HighPerformance.LowLevel;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.Mathematics.SPMD;
using System.IO.Hashing; using System.IO.Hashing;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -191,45 +188,63 @@ internal static partial class TextureProcessor
pCtx.Get()->SetCudaAcceleration(NvttApi.IsCudaSupported()); pCtx.Get()->SetCudaAcceleration(NvttApi.IsCudaSupported());
int edgeLength; int maxCubeMips = _mipLevels.Length;
using (var cubeSurface0 = new DisposablePtr<NvttCubeSurface>(NvttCubeSurface.Create())) var cubeSurfaces = new IntPtr[maxCubeMips];
using (var mip0Surf = new DisposablePtr<NvttSurface>(NvttSurface.Create()))
try
{ {
if (!mip0Surf.Get()->SetImageData(NvttInputFormat.NVTT_InputFormat_RGBA_32F, _mipLevels[0].width, _mipLevels[0].height, 1, _mipLevels[0].data.GetUnsafePtr(), false, null)) for (var level = 0; level < maxCubeMips; level++)
{ {
return Result.Failure<int>("Failed to set image data for NVTT compression."); var cubeSurf = NvttCubeSurface.Create();
cubeSurfaces[level] = (IntPtr)cubeSurf;
using var mipSurf = new DisposablePtr<NvttSurface>(NvttSurface.Create());
if (!mipSurf.Get()->SetImageData(NvttInputFormat.NVTT_InputFormat_RGBA_32F, _mipLevels[level].width, _mipLevels[level].height, 1, _mipLevels[level].data.GetUnsafePtr(), false, null))
{
return Result.Failure("Failed to set image data for NVTT compression.");
}
if (_settings.Basic.IsSRGB)
{
mipSurf.Get()->ToSrgb(null);
}
cubeSurf->Fold(mipSurf.Get(), NvttCubeLayout.NVTT_CubeLayout_LatitudeLongitude);
} }
cubeSurface0.Get()->Fold(mip0Surf.Get(), NvttCubeLayout.NVTT_CubeLayout_LatitudeLongitude); var firstCube = (NvttCubeSurface*)cubeSurfaces[0];
edgeLength = cubeSurface0.Get()->EdgeLength(); if (!pCtx.Get()->OutputHeaderCube(firstCube, maxCubeMips, pCompOpts.Get(), pOutOpts.Get()))
} {
return Result.Failure("Failed to output header for cube map.");
pCtx.Get()->OutputHeaderData(NvttTextureType.NVTT_TextureType_Cube, edgeLength, edgeLength, 1, _mipLevels.Length, false, pCompOpts.Get(), pOutOpts.Get()); }
for (var level = 0; level < _mipLevels.Length; level++)
{
using var cubeSurface = new DisposablePtr<NvttCubeSurface>(NvttCubeSurface.Create());
using var mipSurf = new DisposablePtr<NvttSurface>(NvttSurface.Create());
mipSurf.Get()->SetImageData(NvttInputFormat.NVTT_InputFormat_RGBA_32F, _mipLevels[level].width, _mipLevels[level].height, 1, _mipLevels[level].data.GetUnsafePtr(), false, null);
cubeSurface.Get()->Fold(mipSurf.Get(), NvttCubeLayout.NVTT_CubeLayout_LatitudeLongitude);
for (var face = 0; face < 6; face++) for (var face = 0; face < 6; face++)
{ {
var faceSurf = cubeSurface.Get()->Face(face); for (var level = 0; level < maxCubeMips; level++)
if (_settings.Basic.IsSRGB)
{ {
faceSurf->ToSrgb(null); var cubeSurf = (NvttCubeSurface*)cubeSurfaces[level];
using var faceSurf = new DisposablePtr<NvttSurface>(cubeSurf->Face(face));
if (!pCtx.Get()->Compress(faceSurf.Get(), face, level, pCompOpts.Get(), pOutOpts.Get()))
{
return Result.Failure("Failed to compress cube map face.");
}
} }
}
if (!pCtx.Get()->Compress(faceSurf, face, level, pCompOpts.Get(), pOutOpts.Get())) }
finally
{
for (var level = 0; level < maxCubeMips; level++)
{
if (cubeSurfaces[level] != IntPtr.Zero)
{ {
return Result.Failure("Failed to compress mipmap."); var cubeSurf = (NvttCubeSurface*)cubeSurfaces[level];
cubeSurf->Dispose();
} }
} }
} }
return Result.Success(_mipLevels.Length); return Result.Success(maxCubeMips);
} }
public void Execute() public void Execute()
@@ -316,7 +331,17 @@ internal static partial class TextureProcessor
{ {
if (settings.Basic.TextureShape == TextureShape.TextureCube) if (settings.Basic.TextureShape == TextureShape.TextureCube)
{ {
var handle = GenerateMipHDRI(scheduler, textureInfo, out mipLevels); int maxCubeMips;
unsafe
{
using var cubeSurface0 = new DisposablePtr<NvttCubeSurface>(NvttCubeSurface.Create());
using var mip0Surf = new DisposablePtr<NvttSurface>(NvttSurface.Create());
mip0Surf.Get()->SetImageData(NvttInputFormat.NVTT_InputFormat_RGBA_32F, textureInfo.width, textureInfo.height, 1, (void*)textureInfo.pixelData, false, null);
cubeSurface0.Get()->Fold(mip0Surf.Get(), NvttCubeLayout.NVTT_CubeLayout_LatitudeLongitude);
maxCubeMips = (int)Math.Floor(Math.Log2(cubeSurface0.Get()->EdgeLength())) + 1;
}
var handle = GenerateMipHDRI(scheduler, textureInfo, maxCubeMips, out mipLevels);
await scheduler.WaitAsync(handle, cancellationToken); await scheduler.WaitAsync(handle, cancellationToken);
} }

View File

@@ -212,6 +212,10 @@ internal static partial class TextureProcessor
pData[out_idx] = prefilteredColor.x; pData[out_idx] = prefilteredColor.x;
pData[out_idx + 1] = prefilteredColor.y; pData[out_idx + 1] = prefilteredColor.y;
pData[out_idx + 2] = prefilteredColor.z; pData[out_idx + 2] = prefilteredColor.z;
if (channelCount == 4)
{
pData[out_idx + 3] = 1.0f;
}
} }
} }
@@ -251,11 +255,10 @@ internal static partial class TextureProcessor
return bits * 2.3283064365386963e-10f; // bits / 0x100000000 return bits * 2.3283064365386963e-10f; // bits / 0x100000000
} }
private static JobHandle GenerateMipHDRI(JobScheduler scheduler, TextureAssetHandler.TextureInfo textureInfo, out UnsafeArray<MipLevel> mipLevels) private static JobHandle GenerateMipHDRI(JobScheduler scheduler, TextureAssetHandler.TextureInfo textureInfo, int totalMipLevels, out UnsafeArray<MipLevel> mipLevels)
{ {
Logger.DebugAssert(textureInfo.isHDR, "GenerateMipHDRI should only be called for HDR textures."); Logger.DebugAssert(textureInfo.isHDR, "GenerateMipHDRI should only be called for HDR textures.");
Logger.DebugAssert(textureInfo.colorComponents >= 3, "Texture must have at least 3 color components for RGB.");
var totalMipLevels = (int)Math.Floor(Math.Log2(Math.Max(textureInfo.width, textureInfo.height))) + 1;
mipLevels = new UnsafeArray<MipLevel>(totalMipLevels, AllocationHandle.FreeList); mipLevels = new UnsafeArray<MipLevel>(totalMipLevels, AllocationHandle.FreeList);
var radicalInverse_VdCLut = new UnsafeArray<float>(_SAMPLE_COUNT, AllocationHandle.FreeList); var radicalInverse_VdCLut = new UnsafeArray<float>(_SAMPLE_COUNT, AllocationHandle.FreeList);

View File

@@ -159,7 +159,7 @@ internal sealed partial class ImportCoordinator : IDisposable
return string.Empty; return string.Empty;
} }
var hash = XxHash128.HashToUInt128(JsonSerializer.SerializeToUtf8Bytes(settings)); var hash = XxHash128.HashToUInt128(JsonSerializer.SerializeToUtf8Bytes(settings, settings.GetType()));
Span<byte> bytes = stackalloc byte[16]; Span<byte> bytes = stackalloc byte[16];
Unsafe.WriteUnaligned(ref bytes[0], hash); Unsafe.WriteUnaligned(ref bytes[0], hash);

View File

@@ -66,13 +66,13 @@ internal static class ActivationHandler
AllocationManager.Initialize(opts); AllocationManager.Initialize(opts);
//var assetRegistry = App.GetService<IAssetRegistry>(); var assetRegistry = App.GetService<IAssetRegistry>();
//var engineCore = App.GetService<EngineCore>(); var engineCore = App.GetService<EngineCore>();
//assetRegistry.OnAssetImported += (sender, e) => assetRegistry.OnAssetImported += (sender, e) =>
//{ {
// engineCore.AssetManager.ReimportAsset(e); engineCore.AssetManager.ReimportAsset(e);
//}; };
return ValueTask.CompletedTask; return ValueTask.CompletedTask;
} }

View File

@@ -1,13 +1,13 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Ghost.Core.Utilities;
using Ghost.Editor.Core; using Ghost.Editor.Core;
using Ghost.Editor.Core.Assets;
using Ghost.Editor.Core.Contracts; using Ghost.Editor.Core.Contracts;
using Ghost.Editor.Core.Utilities; using Ghost.Editor.Core.Utilities;
using Ghost.Editor.Models; using Ghost.Editor.Models;
using Ghost.Editor.Core.AssetHandler;
using System.Collections.ObjectModel;
using Microsoft.UI.Dispatching;
using Ghost.Engine; using Ghost.Engine;
using Ghost.Core.Utilities; using Microsoft.UI.Dispatching;
using System.Collections.ObjectModel;
namespace Ghost.Editor.ViewModels.Controls; namespace Ghost.Editor.ViewModels.Controls;
@@ -69,7 +69,7 @@ internal partial class ContentBrowserViewModel : ObservableObject
var fullPath = PathUtility.Normalize(e.AssetPath); var fullPath = PathUtility.Normalize(e.AssetPath);
var dirPath = Path.GetDirectoryName(fullPath); var dirPath = Path.GetDirectoryName(fullPath);
if (string.Equals(dirPath, CurrentDirectoryPath, StringComparison.OrdinalIgnoreCase)) if (string.Equals(dirPath, CurrentDirectoryPath, StringComparison.OrdinalIgnoreCase))
{ {
_dispatcherQueue.TryEnqueue(() => _dispatcherQueue.TryEnqueue(() =>
@@ -86,7 +86,7 @@ internal partial class ContentBrowserViewModel : ObservableObject
if (!Files.Any(f => string.Equals(f.Path, fullPath, StringComparison.OrdinalIgnoreCase))) if (!Files.Any(f => string.Equals(f.Path, fullPath, StringComparison.OrdinalIgnoreCase)))
{ {
var isDir = Directory.Exists(fullPath); var isDir = Directory.Exists(fullPath);
AssetType assetType = AssetType.Unknown; var assetType = AssetType.Unknown;
if (!isDir) if (!isDir)
{ {
var ext = Path.GetExtension(fullPath); var ext = Path.GetExtension(fullPath);

View File

@@ -33,7 +33,7 @@ internal class AssetHandlerRegistrationGenerator : IIncrementalGenerator
foreach (var symbol in array) foreach (var symbol in array)
{ {
var attribute = symbol.GetAttributes().FirstOrDefault(a => a.AttributeClass?.ToDisplayString() == "Ghost.Editor.Core.AssetHandler.CustomAssetHandlerAttribute"); var attribute = symbol.GetAttributes().FirstOrDefault(a => a.AttributeClass?.ToDisplayString() == "Ghost.Editor.Core.Assets.CustomAssetHandlerAttribute");
if (attribute == null) if (attribute == null)
{ {
continue; continue;
@@ -44,7 +44,7 @@ internal class AssetHandlerRegistrationGenerator : IIncrementalGenerator
var extensions = $"new string[] {{ {string.Join(", ", extensionsTypesConstants.Select(v => v.ToCSharpString()))} }}"; var extensions = $"new string[] {{ {string.Join(", ", extensionsTypesConstants.Select(v => v.ToCSharpString()))} }}";
var version = (int)attribute.ConstructorArguments[2].Value; var version = (int)attribute.ConstructorArguments[2].Value;
sb.Append($" global::Ghost.Editor.Core.AssetHandler.AssetHandlerRegistry.RegisterHandler(new {symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}(), Guid.Parse(\"{id}\"), {extensions}, {version});"); sb.AppendLine($" global::Ghost.Editor.Core.Assets.AssetHandlerRegistry.RegisterHandler(new {symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}(), Guid.Parse(\"{id}\"), {extensions}, {version});");
} }
var registerTypeName = "g_assethandler_registeration"; var registerTypeName = "g_assethandler_registeration";
@@ -71,7 +71,7 @@ internal static partial class {registerTypeName}
return null; return null;
} }
var iHandlerSymbol = context.SemanticModel.Compilation.GetTypeByMetadataName("Ghost.Editor.Core.AssetHandler.IAssetHandler"); var iHandlerSymbol = context.SemanticModel.Compilation.GetTypeByMetadataName("Ghost.Editor.Core.Assets.IAssetHandler");
if (iHandlerSymbol == null) if (iHandlerSymbol == null)
{ {
return null; return null;
@@ -114,7 +114,7 @@ internal class IAssetSettingsRegistrationGenerator : IIncrementalGenerator
foreach (var iface in array) foreach (var iface in array)
{ {
sb.AppendLine($" global::Ghost.Editor.Core.AssetHandler.AssetHandlerRegistry.RegisterIAssetSettingsType(typeof({iface.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}), \"{iface.Name}\");"); sb.AppendLine($" global::Ghost.Editor.Core.Assets.AssetHandlerRegistry.RegisterIAssetSettingsType(typeof({iface.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}), \"{iface.Name}\");");
} }
var registerTypeName = "g_iassetsettings_registeration"; var registerTypeName = "g_iassetsettings_registeration";
@@ -141,7 +141,7 @@ internal static partial class {registerTypeName}
return null; return null;
} }
var iSettingsSymbol = context.SemanticModel.Compilation.GetTypeByMetadataName("Ghost.Editor.Core.AssetHandler.IAssetSettings"); var iSettingsSymbol = context.SemanticModel.Compilation.GetTypeByMetadataName("Ghost.Editor.Core.Assets.IAssetSettings");
if (iSettingsSymbol == null) if (iSettingsSymbol == null)
{ {
return null; return null;