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:
@@ -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,6 +122,7 @@ 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);
|
||||||
|
|
||||||
@@ -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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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, face, level, pCompOpts.Get(), pOutOpts.Get()))
|
if (!pCtx.Get()->Compress(faceSurf.Get(), face, level, pCompOpts.Get(), pOutOpts.Get()))
|
||||||
|
{
|
||||||
|
return Result.Failure("Failed to compress cube map face.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user