Replace Magick.NET with stb_image; refactor asset pipeline
- Switched image loading/saving from Magick.NET to native stb_image (Ghost.StbI), removing Magick.NET dependency. - Added Ghost.StbI project with native DLL, P/Invoke bindings, and wrapper. - Refactored TextureAssetHandler and TextureProcessor for stb_image, memory-mapped IO, and HDR/16-bit support. - Split IAssetHandler into IImportableAssetHandler and IPackableAssetHandler; updated interfaces to use FileStream. - Added shader and mesh asset handlers (GraphicsShaderAssetHandler, ComputeShaderAssetHandler, FBXAssetHandler). - Improved asset registry/catalog path handling and naming consistency. - Updated asset import pipeline to use new interfaces and trigger engine reimport. - Enhanced UI toolbar button styles and EditPage layout. - Added StbIBindingTest, DisableRuntimeMarshalling, and native wrapper attributes. - Updated wrapper generator for regex derivesFrom; added stbi.json config. - Removed Magick.NET reference; added Ghost.StbI and Ghost.Ufbx references. - Miscellaneous bugfixes and code cleanup.
This commit is contained in:
@@ -18,7 +18,7 @@ public struct DSLShaderError
|
||||
}
|
||||
}
|
||||
|
||||
internal static class DSLShaderCompiler
|
||||
public static class DSLShaderCompiler
|
||||
{
|
||||
private static PipelineState MeragePipeline(PipelineSemantic? semantic, PipelineState parent)
|
||||
{
|
||||
@@ -141,21 +141,27 @@ internal static class DSLShaderCompiler
|
||||
|
||||
var descriptor = new GraphicsShaderDescriptor
|
||||
{
|
||||
name = semantics.name,
|
||||
propertyBufferSize = propertyInfo.size,
|
||||
Name = semantics.name,
|
||||
PropertyBufferSize = propertyInfo.size,
|
||||
|
||||
shaderModel = semantics.shaderModel,
|
||||
passes = passes
|
||||
ShaderModel = semantics.shaderModel,
|
||||
Passes = passes
|
||||
};
|
||||
|
||||
for (var i = 0; i < descriptor.passes.Length; i++)
|
||||
for (var i = 0; i < descriptor.Passes.Length; i++)
|
||||
{
|
||||
descriptor.passes[i].shader = descriptor;
|
||||
descriptor.Passes[i].shader = descriptor;
|
||||
}
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public static Result<GraphicsShaderDescriptor> CompileGraphicsShader(Stream stream)
|
||||
{
|
||||
using var reader = new StreamReader(stream);
|
||||
return CompileGraphicsShader(reader.ReadToEnd());
|
||||
}
|
||||
|
||||
public static Result<GraphicsShaderDescriptor> CompileGraphicsShader(string shaderPath)
|
||||
{
|
||||
try
|
||||
@@ -163,7 +169,8 @@ internal static class DSLShaderCompiler
|
||||
var source = File.ReadAllText(shaderPath);
|
||||
|
||||
// Use ANTLR4 parser
|
||||
var shaderModels = AntlrShaderCompiler.ParseShaders(source, out var parseErrors);
|
||||
var parseErrors = new List<DSLShaderError>();
|
||||
var shaderModels = AntlrShaderCompiler.ParseShaders(source, parseErrors);
|
||||
|
||||
if (parseErrors.Count != 0)
|
||||
{
|
||||
@@ -209,13 +216,20 @@ internal static class DSLShaderCompiler
|
||||
}
|
||||
}
|
||||
|
||||
public static Result<ComputeShaderDescriptor> CompileComputeShader(Stream stream)
|
||||
{
|
||||
using var reader = new StreamReader(stream);
|
||||
return CompileComputeShader(reader.ReadToEnd());
|
||||
}
|
||||
|
||||
public static Result<ComputeShaderDescriptor> CompileComputeShader(string shaderPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
var source = File.ReadAllText(shaderPath);
|
||||
|
||||
var shaderModels = AntlrShaderCompiler.ParseComputeShaders(source, out var parseErrors);
|
||||
var parseErrors = new List<DSLShaderError>();
|
||||
var shaderModels = AntlrShaderCompiler.ParseComputeShaders(source, parseErrors);
|
||||
|
||||
if (parseErrors.Count != 0)
|
||||
{
|
||||
@@ -281,12 +295,12 @@ internal static class DSLShaderCompiler
|
||||
|
||||
return new ComputeShaderDescriptor
|
||||
{
|
||||
name = semantics.name,
|
||||
propertyBufferSize = propertyInfo.size,
|
||||
shaderModel = semantics.shaderModel,
|
||||
shaderCodes = shaderCodes,
|
||||
defines = semantics.defines?.ToArray() ?? Array.Empty<string>(),
|
||||
keywords = semantics.keywords?.ToArray() ?? Array.Empty<KeywordsGroup>()
|
||||
Name = semantics.name,
|
||||
PropertyBufferSize = propertyInfo.size,
|
||||
ShaderModel = semantics.shaderModel,
|
||||
ShaderCodes = shaderCodes,
|
||||
Defines = semantics.defines?.ToArray() ?? Array.Empty<string>(),
|
||||
Keywords = semantics.keywords?.ToArray() ?? Array.Empty<KeywordsGroup>()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,8 @@ namespace Ghost.DSL.ShaderParser;
|
||||
|
||||
public class AntlrShaderCompiler
|
||||
{
|
||||
public static List<GraphicsShaderModel> ParseShaders(string source, out List<DSLShaderError> errors)
|
||||
public static List<GraphicsShaderModel> ParseShaders(string source, List<DSLShaderError> errors)
|
||||
{
|
||||
errors = new List<DSLShaderError>();
|
||||
|
||||
try
|
||||
{
|
||||
var inputStream = new AntlrInputStream(source);
|
||||
@@ -53,7 +51,7 @@ public class AntlrShaderCompiler
|
||||
}
|
||||
}
|
||||
|
||||
public static List<ComputeShaderModel> ParseComputeShaders(string source, out List<DSLShaderError> errors)
|
||||
public static List<ComputeShaderModel> ParseComputeShaders(string source, List<DSLShaderError> errors)
|
||||
{
|
||||
errors = new List<DSLShaderError>();
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Ghost.Editor.Core.AssetHandler;
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class CustomAssetHandlerAttribute : Attribute
|
||||
{
|
||||
public CustomAssetHandlerAttribute(string TypeID, string[] supportedExtensions, int version = 1)
|
||||
public CustomAssetHandlerAttribute(string assetTypeID, string[] supportedExtensions, int version = 1)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -33,14 +33,23 @@ public interface IAssetExportOptions;
|
||||
|
||||
public interface IAssetHandler
|
||||
{
|
||||
bool CanExport => false;
|
||||
AssetType RuntimeAssetType { get; }
|
||||
Guid EditorAssetTypeID { get; }
|
||||
|
||||
IAssetSettings? CreateDefaultSettings();
|
||||
|
||||
ValueTask<Result<IAsset>> LoadAssetAsync(Stream assetStream, Guid id, IAssetSettings? settings, CancellationToken token = default);
|
||||
ValueTask<Result> SaveAssetAsync(Stream targetStream, IAsset asset, CancellationToken token = default);
|
||||
ValueTask<Result> ImportAsync(Stream sourceStream, Stream targetStream, Guid id, IAssetSettings? settings, CancellationToken token = default);
|
||||
ValueTask<Result> ExportAsync(Stream assetStream, Stream targetStream, IAssetExportOptions? options, CancellationToken token = default);
|
||||
ValueTask<Result<IAsset>> LoadAssetAsync(FileStream assetStream, Guid id, IAssetSettings? settings, CancellationToken token = default);
|
||||
ValueTask<Result> SaveAssetAsync(FileStream targetStream, IAsset asset, CancellationToken token = default);
|
||||
}
|
||||
|
||||
public interface IImportableAssetHandler : IAssetHandler
|
||||
{
|
||||
bool CanExport { get; }
|
||||
ValueTask<Result> ImportAsync(FileStream sourceStream, FileStream targetStream, Guid id, IAssetSettings? settings, CancellationToken token = default);
|
||||
ValueTask<Result> ExportAsync(FileStream assetStream, FileStream targetStream, IAssetExportOptions? options, CancellationToken token = default);
|
||||
}
|
||||
|
||||
public interface IPackableAssetHandler : IAssetHandler
|
||||
{
|
||||
ValueTask<Result> PackAsync(FileStream assetStream, Stream targetStream, CancellationToken token = default);
|
||||
}
|
||||
@@ -2,10 +2,6 @@ using Ghost.Engine;
|
||||
|
||||
namespace Ghost.Editor.Core.AssetHandler;
|
||||
|
||||
/// <summary>
|
||||
/// One-time scan at editor startup → two dictionaries.
|
||||
/// All lookups are O(1) after construction.
|
||||
/// </summary>
|
||||
public static class AssetHandlerRegistry
|
||||
{
|
||||
private static readonly Dictionary<string, IAssetHandler> s_byExtension;
|
||||
@@ -21,10 +17,10 @@ public static class AssetHandlerRegistry
|
||||
s_versionByTypeId = new Dictionary<Guid, int>();
|
||||
}
|
||||
|
||||
public static void RegisterHandler(IAssetHandler handler, Guid typeId, ReadOnlySpan<string> extensions, int version)
|
||||
public static void RegisterHandler(IAssetHandler handler, Guid assetTypeId, ReadOnlySpan<string> extensions, int version)
|
||||
{
|
||||
s_byTypeId[typeId] = handler;
|
||||
s_versionByTypeId[typeId] = version;
|
||||
s_byTypeId[assetTypeId] = handler;
|
||||
s_versionByTypeId[assetTypeId] = version;
|
||||
|
||||
foreach (var ext in extensions)
|
||||
{
|
||||
@@ -46,13 +42,13 @@ public static class AssetHandlerRegistry
|
||||
return handler;
|
||||
}
|
||||
|
||||
public static IAssetHandler? GetByTypeId(Guid typeId)
|
||||
public static IAssetHandler? GetByAssetTypeId(Guid typeId)
|
||||
{
|
||||
s_byTypeId.TryGetValue(typeId, out var handler);
|
||||
return handler;
|
||||
}
|
||||
|
||||
public static int GetVersionByTypeId(Guid typeId)
|
||||
public static int GetVersionByAssetTypeId(Guid typeId)
|
||||
{
|
||||
s_versionByTypeId.TryGetValue(typeId, out var version);
|
||||
return version;
|
||||
@@ -63,7 +59,7 @@ public static class AssetHandlerRegistry
|
||||
return s_byExtension.Keys;
|
||||
}
|
||||
|
||||
public static AssetType GetAssetTypeByExtension(string extension)
|
||||
public static AssetType GetRuntimeAssetTypeByExtension(string extension)
|
||||
{
|
||||
if (string.IsNullOrEmpty(extension))
|
||||
{
|
||||
|
||||
380
src/Editor/Ghost.Editor.Core/AssetHandler/MeshAssetHandler.cs
Normal file
380
src/Editor/Ghost.Editor.Core/AssetHandler/MeshAssetHandler.cs
Normal file
@@ -0,0 +1,380 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Engine;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Ghost.Graphics.Utilities;
|
||||
using Ghost.MeshOptimizer;
|
||||
using Ghost.Ufbx;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Ghost.Editor.Core.AssetHandler;
|
||||
|
||||
public abstract class MeshAsset : IAsset
|
||||
{
|
||||
|
||||
private UnsafeList<Vertex> _vertices;
|
||||
private UnsafeList<uint> _indices;
|
||||
|
||||
public Guid ID
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public IAssetSettings Settings
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public Guid TypeID => typeof(MeshAsset).GUID;
|
||||
|
||||
public Span<Vertex> Vertices => _vertices.AsSpan();
|
||||
public Span<uint> Indices => _indices.AsSpan();
|
||||
|
||||
internal MeshAsset(ref UnsafeList<Vertex> vertices, ref UnsafeList<uint> indices, Guid id, MeshAssetSettings settings)
|
||||
{
|
||||
_vertices = vertices;
|
||||
_indices = indices;
|
||||
|
||||
ID = id;
|
||||
Settings = settings;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_vertices.Dispose();
|
||||
_indices.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Guid(GUID)]
|
||||
public partial class FBXAsset : MeshAsset
|
||||
{
|
||||
public const string GUID = "B99CA68E-EE7A-4822-BF1C-AA0A5120C36A";
|
||||
|
||||
internal FBXAsset(ref UnsafeList<Vertex> vertices, ref UnsafeList<uint> indices, Guid id, FbxAssetSettings settings)
|
||||
: base(ref vertices, ref indices, id, settings)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum CoordinateAxis
|
||||
{
|
||||
PositiveX,
|
||||
PositiveY,
|
||||
PositiveZ,
|
||||
NegativeX,
|
||||
NegativeY,
|
||||
NegativeZ
|
||||
}
|
||||
|
||||
public enum VertexDataSource
|
||||
{
|
||||
Imported,
|
||||
Computed,
|
||||
ComputedIfMissing
|
||||
}
|
||||
|
||||
public class MeshAssetSettings : IAssetSettings
|
||||
{
|
||||
public VertexDataSource NormalDataSource
|
||||
{
|
||||
get; set;
|
||||
} = VertexDataSource.ComputedIfMissing;
|
||||
|
||||
public VertexDataSource TangentDataSource
|
||||
{
|
||||
get; set;
|
||||
} = VertexDataSource.ComputedIfMissing;
|
||||
}
|
||||
|
||||
internal class ObjAssetSettings : MeshAssetSettings
|
||||
{
|
||||
public CoordinateAxis ObjectUpAxis
|
||||
{
|
||||
get; set;
|
||||
} = CoordinateAxis.PositiveY;
|
||||
|
||||
public CoordinateAxis ObjectForwardAxis
|
||||
{
|
||||
get; set;
|
||||
} = CoordinateAxis.NegativeZ;
|
||||
|
||||
public CoordinateAxis ObjectRightAxis
|
||||
{
|
||||
get; set;
|
||||
} = CoordinateAxis.PositiveX;
|
||||
|
||||
public float UnitMeterScale
|
||||
{
|
||||
get; set;
|
||||
} = 1.0f;
|
||||
}
|
||||
|
||||
internal class FbxAssetSettings : MeshAssetSettings
|
||||
{
|
||||
}
|
||||
|
||||
internal class MeshParsingWorkItem : IThreadPoolWorkItem
|
||||
{
|
||||
private readonly string _filePath;
|
||||
private readonly AllocationHandle _allocationHandle;
|
||||
private readonly MeshAssetSettings _settings;
|
||||
private readonly TaskCompletionSource<Result> _taskCompletionSource;
|
||||
|
||||
public UnsafeList<Vertex> vertices;
|
||||
public UnsafeList<uint> indices;
|
||||
|
||||
public Task<Result> Task => _taskCompletionSource.Task;
|
||||
|
||||
public MeshParsingWorkItem(string filePath, AllocationHandle allocationHandle, MeshAssetSettings settings)
|
||||
{
|
||||
_filePath = filePath;
|
||||
_allocationHandle = allocationHandle;
|
||||
_settings = settings;
|
||||
_taskCompletionSource = new TaskCompletionSource<Result>();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static float4 ComputeTangent(float3 t, float3 n, float3 b)
|
||||
{
|
||||
var proj = n * math.dot(n, t);
|
||||
t = math.normalize(t - proj);
|
||||
var w = math.dot(math.cross(n, t), b) < 0.0f ? -1.0f : 1.0f;
|
||||
return new float4(t.xyz, w);
|
||||
}
|
||||
|
||||
private static ufbx_coordinate_axis ToUfbxCoordinateAxis(CoordinateAxis axis)
|
||||
{
|
||||
return axis switch
|
||||
{
|
||||
CoordinateAxis.PositiveX => ufbx_coordinate_axis.UFBX_COORDINATE_AXIS_POSITIVE_X,
|
||||
CoordinateAxis.PositiveY => ufbx_coordinate_axis.UFBX_COORDINATE_AXIS_POSITIVE_Y,
|
||||
CoordinateAxis.PositiveZ => ufbx_coordinate_axis.UFBX_COORDINATE_AXIS_POSITIVE_Z,
|
||||
CoordinateAxis.NegativeX => ufbx_coordinate_axis.UFBX_COORDINATE_AXIS_NEGATIVE_X,
|
||||
CoordinateAxis.NegativeY => ufbx_coordinate_axis.UFBX_COORDINATE_AXIS_NEGATIVE_Y,
|
||||
CoordinateAxis.NegativeZ => ufbx_coordinate_axis.UFBX_COORDINATE_AXIS_NEGATIVE_Z,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(axis), axis, null)
|
||||
};
|
||||
}
|
||||
|
||||
public unsafe void Execute()
|
||||
{
|
||||
if (!File.Exists(_filePath))
|
||||
{
|
||||
_taskCompletionSource.SetResult(Result.Failure("Invalid file path."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Path.GetExtension(_filePath).Equals(".obj", StringComparison.OrdinalIgnoreCase)
|
||||
&& !Path.GetExtension(_filePath).Equals(".fbx", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_taskCompletionSource.SetResult(Result.Failure("Unsupported file format. Only .obj and .fbx are supported."));
|
||||
return;
|
||||
}
|
||||
|
||||
var error = new ufbx_error();
|
||||
var load_Opts = new ufbx_load_opts
|
||||
{
|
||||
target_unit_meters = 1.0f,
|
||||
target_axes = ufbx_coordinate_axes.left_handed_y_up,
|
||||
// Force z-axis mirroring to correctly convert handedness to Left-Handed,
|
||||
// while preserving correct left/right orientation when viewed from the front.
|
||||
handedness_conversion_axis = ufbx_mirror_axis.UFBX_MIRROR_AXIS_Z,
|
||||
space_conversion = ufbx_space_conversion.UFBX_SPACE_CONVERSION_MODIFY_GEOMETRY,
|
||||
};
|
||||
|
||||
if (_settings is ObjAssetSettings objSettings)
|
||||
{
|
||||
load_Opts.obj_axes = new ufbx_coordinate_axes
|
||||
{
|
||||
right = ToUfbxCoordinateAxis(objSettings.ObjectRightAxis),
|
||||
up = ToUfbxCoordinateAxis(objSettings.ObjectUpAxis),
|
||||
front = ToUfbxCoordinateAxis(objSettings.ObjectForwardAxis)
|
||||
};
|
||||
|
||||
load_Opts.obj_unit_meters = objSettings.UnitMeterScale;
|
||||
load_Opts.obj_search_mtl_by_filename = true;
|
||||
}
|
||||
|
||||
using var str = new UnsafeArray<byte>(Encoding.UTF8.GetByteCount(_filePath) + 1, AllocationHandle.FreeList);
|
||||
var count = Encoding.UTF8.GetBytes(_filePath, str.AsSpan());
|
||||
str[count] = 0;
|
||||
|
||||
using var scene = new DisposablePtr<ufbx_scene>(ufbx_scene.LoadFile((sbyte*)str.GetUnsafePtr(), &load_Opts, &error));
|
||||
if (scene.Get() == null)
|
||||
{
|
||||
_taskCompletionSource.SetResult(Result.Failure(error.description.ToString()));
|
||||
return;
|
||||
}
|
||||
|
||||
using var flatVertices = new UnsafeList<Vertex>(1024, AllocationHandle.FreeList);
|
||||
|
||||
var missingNormals = false;
|
||||
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 pMesh = node->mesh;
|
||||
if (pMesh->num_faces == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var maxScratchIndices = (int)(pMesh->max_face_triangles * 3u);
|
||||
|
||||
using var triIndicesArray = new UnsafeArray<uint>(maxScratchIndices, AllocationHandle.FreeList);
|
||||
|
||||
for (var j = 0u; j < pMesh->num_faces; j++)
|
||||
{
|
||||
var face = pMesh->faces.data[j];
|
||||
|
||||
var numTris = UfbxApi.TriangulateFace(triIndicesArray.AsSpan(0, maxScratchIndices), pMesh, face);
|
||||
|
||||
var totalIndices = numTris * 3;
|
||||
for (var k = 0; k < totalIndices; k++)
|
||||
{
|
||||
var ufbxTopologyIndex = triIndicesArray[k];
|
||||
|
||||
var posIdx = pMesh->vertex_position.indices.data[ufbxTopologyIndex];
|
||||
var normIdx = pMesh->vertex_normal.exists ? pMesh->vertex_normal.indices.data[ufbxTopologyIndex] : uint.MaxValue;
|
||||
var tanIdx = pMesh->vertex_tangent.exists ? pMesh->vertex_tangent.indices.data[ufbxTopologyIndex] : uint.MaxValue;
|
||||
var uvIdx = pMesh->vertex_uv.exists ? pMesh->vertex_uv.indices.data[ufbxTopologyIndex] : uint.MaxValue;
|
||||
var colIdx = pMesh->vertex_color.exists ? pMesh->vertex_color.indices.data[ufbxTopologyIndex] : uint.MaxValue;
|
||||
var btanIdx = pMesh->vertex_bitangent.exists ? pMesh->vertex_bitangent.indices.data[ufbxTopologyIndex] : uint.MaxValue;
|
||||
|
||||
var position = pMesh->vertex_position.values.data[posIdx];
|
||||
var normal = normIdx != uint.MaxValue ? pMesh->vertex_normal.values.data[normIdx] : default;
|
||||
var uv = uvIdx != uint.MaxValue ? pMesh->vertex_uv.values.data[uvIdx] : default;
|
||||
var color = colIdx != uint.MaxValue ? pMesh->vertex_color.values.data[colIdx] : default;
|
||||
|
||||
var vertex = new Vertex
|
||||
{
|
||||
position = new float3(position.x, position.y, position.z),
|
||||
normal = new float3(normal.x, normal.y, normal.z),
|
||||
uv = new float2(uv.x, uv.y),
|
||||
color = new Color128(color.x, color.y, color.z, color.w)
|
||||
};
|
||||
|
||||
if (tanIdx != uint.MaxValue)
|
||||
{
|
||||
var mt = pMesh->vertex_tangent.values.data[tanIdx];
|
||||
var mb = btanIdx != uint.MaxValue ? pMesh->vertex_bitangent.values.data[btanIdx] : default;
|
||||
|
||||
var t = new float3(mt.x, mt.y, mt.z);
|
||||
var n = vertex.normal;
|
||||
var b = btanIdx != uint.MaxValue ? new float3(mb.x, mb.y, mb.z) : math.cross(n, t);
|
||||
vertex.tangent = ComputeTangent(t, n, b);
|
||||
}
|
||||
|
||||
var newIndex = (uint)flatVertices.Count;
|
||||
|
||||
flatVertices.Add(vertex);
|
||||
|
||||
if (!missingNormals)
|
||||
{
|
||||
missingNormals = normIdx == uint.MaxValue;
|
||||
}
|
||||
|
||||
if (!missingTangents)
|
||||
{
|
||||
missingTangents = tanIdx == uint.MaxValue || btanIdx == uint.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
internal class FBXAssetHandler : IImportableAssetHandler
|
||||
{
|
||||
public AssetType RuntimeAssetType => AssetType.Mesh;
|
||||
|
||||
public Guid EditorAssetTypeID => typeof(FBXAsset).GUID;
|
||||
|
||||
public bool CanExport => false;
|
||||
|
||||
public IAssetSettings? CreateDefaultSettings()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ValueTask<Result<IAsset>> LoadAssetAsync(FileStream assetStream, Guid id, IAssetSettings? settings, CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ValueTask<Result> SaveAssetAsync(FileStream targetStream, IAsset asset, CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ValueTask<Result> ImportAsync(FileStream sourceStream, FileStream targetStream, Guid id, IAssetSettings? settings, CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ValueTask<Result> ExportAsync(FileStream assetStream, FileStream targetStream, IAssetExportOptions? options, CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
158
src/Editor/Ghost.Editor.Core/AssetHandler/ShaderAssetHandler.cs
Normal file
158
src/Editor/Ghost.Editor.Core/AssetHandler/ShaderAssetHandler.cs
Normal file
@@ -0,0 +1,158 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Graphics;
|
||||
using Ghost.DSL.ShaderCompiler;
|
||||
using Ghost.Engine;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.Editor.Core.AssetHandler;
|
||||
|
||||
[Guid(GUID)]
|
||||
public sealed partial class GraphicsShaderAsset : IAsset
|
||||
{
|
||||
public const string GUID = "7BD4591C-B017-4814-AA0B-3F30EB3E727E";
|
||||
|
||||
public Guid ID
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public IAssetSettings Settings
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public Guid TypeID => typeof(GraphicsShaderAsset).GUID;
|
||||
|
||||
public GraphicsShaderDescriptor Descriptor
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
internal GraphicsShaderAsset(GraphicsShaderDescriptor descriptor, Guid id)
|
||||
{
|
||||
ID = id;
|
||||
Descriptor = descriptor;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Guid(GUID)]
|
||||
public sealed partial class ComputeShaderAsset : IAsset
|
||||
{
|
||||
public const string GUID = "EA881979-CD8D-4088-B568-D42645F18C2A";
|
||||
|
||||
public Guid ID
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public IAssetSettings Settings
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public Guid TypeID => typeof(ComputeShaderAsset).GUID;
|
||||
|
||||
public ComputeShaderDescriptor Descriptor
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
internal ComputeShaderAsset(ComputeShaderDescriptor descriptor, Guid id)
|
||||
{
|
||||
ID = id;
|
||||
Descriptor = descriptor;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// Shader does not handle import/export via asset registry, it will handled by the hot reload system.
|
||||
[CustomAssetHandler(GraphicsShaderAsset.GUID, [".gshdr"], 1)]
|
||||
internal class GraphicsShaderAssetHandler : IPackableAssetHandler
|
||||
{
|
||||
public AssetType RuntimeAssetType => AssetType.Shader;
|
||||
public Guid EditorAssetTypeID => typeof(GraphicsShaderAsset).GUID;
|
||||
|
||||
public IAssetSettings? CreateDefaultSettings()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public async ValueTask<Result<IAsset>> LoadAssetAsync(FileStream assetStream, Guid id, IAssetSettings? settings, CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var reader = new StreamReader(assetStream);
|
||||
var shaderCode = await reader.ReadToEndAsync(token);
|
||||
var result = DSLShaderCompiler.CompileGraphicsShader(shaderCode);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
return Result.Failure(result.Message);
|
||||
}
|
||||
|
||||
return new GraphicsShaderAsset(result.Value, id);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Failure($"Failed to load shader asset: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public ValueTask<Result> SaveAssetAsync(FileStream targetStream, IAsset asset, CancellationToken token = default)
|
||||
{
|
||||
return new ValueTask<Result>(Result.Failure("Saving shader assets is not supported yet as it's read-only. Please edit the shader source file directly if you need to modify it."));
|
||||
}
|
||||
|
||||
public ValueTask<Result> PackAsync(FileStream assetStream, Stream targetStream, CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
[CustomAssetHandler(ComputeShaderAsset.GUID, [".gcomp"], 1)]
|
||||
internal class ComputeShaderAssetHandler : IPackableAssetHandler
|
||||
{
|
||||
public AssetType RuntimeAssetType => AssetType.Shader;
|
||||
public Guid EditorAssetTypeID => typeof(ComputeShaderAsset).GUID;
|
||||
|
||||
public IAssetSettings? CreateDefaultSettings()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public async ValueTask<Result<IAsset>> LoadAssetAsync(FileStream assetStream, Guid id, IAssetSettings? settings, CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var reader = new StreamReader(assetStream);
|
||||
var shaderCode = await reader.ReadToEndAsync(token);
|
||||
var result = DSLShaderCompiler.CompileComputeShader(shaderCode);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
return Result.Failure(result.Message);
|
||||
}
|
||||
|
||||
return new ComputeShaderAsset(result.Value, id);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Failure($"Failed to load shader asset: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public ValueTask<Result> SaveAssetAsync(FileStream targetStream, IAsset asset, CancellationToken token = default)
|
||||
{
|
||||
return new ValueTask<Result>(Result.Failure("Saving shader assets is not supported yet as it's read-only. Please edit the shader source file directly if you need to modify it."));
|
||||
}
|
||||
|
||||
public ValueTask<Result> PackAsync(FileStream assetStream, Stream targetStream, CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Engine;
|
||||
using Ghost.Graphics.RHI;
|
||||
using ImageMagick;
|
||||
using Ghost.StbI;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Ghost.Editor.Core.AssetHandler;
|
||||
|
||||
@@ -48,7 +51,7 @@ public enum MipmapFilter : uint
|
||||
}
|
||||
|
||||
[Guid(GUID)]
|
||||
public class TextureAsset : IAsset
|
||||
public unsafe class TextureAsset : IAsset
|
||||
{
|
||||
public const string GUID = "27965FFF-860C-40EF-9123-1874D7DE9CDC";
|
||||
|
||||
@@ -57,7 +60,7 @@ public class TextureAsset : IAsset
|
||||
private readonly Guid _id;
|
||||
private readonly IAssetSettings _settings;
|
||||
|
||||
private readonly MagickImage _textureData;
|
||||
private readonly IntPtr _textureData;
|
||||
private readonly uint _width;
|
||||
private readonly uint _height;
|
||||
private readonly uint _depth;
|
||||
@@ -65,17 +68,17 @@ public class TextureAsset : IAsset
|
||||
private readonly uint _dimension;
|
||||
|
||||
public Guid ID => _id;
|
||||
public Guid TypeID => s_typeID;
|
||||
public Guid TypeID => typeof(TextureAsset).GUID;
|
||||
public IAssetSettings Settings => _settings;
|
||||
|
||||
public MagickImage TextureData => _textureData;
|
||||
public IntPtr TextureData => _textureData;
|
||||
public uint Width => _width;
|
||||
public uint Height => _height;
|
||||
public uint Depth => _depth;
|
||||
public uint Dimension => _dimension;
|
||||
public uint ColorComponents => _colorComponents;
|
||||
|
||||
internal TextureAsset([OwnershipTransfer] MagickImage data, TextureContentHeader header, Guid id, IAssetSettings settings)
|
||||
internal TextureAsset([OwnershipTransfer] IntPtr data, TextureContentHeader header, Guid id, IAssetSettings settings)
|
||||
{
|
||||
_id = id;
|
||||
_settings = settings;
|
||||
@@ -95,7 +98,7 @@ public class TextureAsset : IAsset
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_textureData.Dispose();
|
||||
StbIApi.ImageFree((void*)_textureData);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -253,8 +256,19 @@ public class TextureAssetSettings : IAssetSettings
|
||||
}
|
||||
|
||||
[CustomAssetHandler(TextureAsset.GUID, [".png", ".jpg", ".jpeg", ".tga", ".bmp", ".hdr"], 1)]
|
||||
internal class TextureAssetHandler : IAssetHandler
|
||||
internal class TextureAssetHandler : IImportableAssetHandler, IPackableAssetHandler
|
||||
{
|
||||
internal struct TextureInfo
|
||||
{
|
||||
public IntPtr pixelData;
|
||||
public int width;
|
||||
public int height;
|
||||
public int depth;
|
||||
public int colorComponents;
|
||||
public bool isHDR;
|
||||
}
|
||||
|
||||
public bool CanExport => false;
|
||||
public AssetType RuntimeAssetType => AssetType.Texture;
|
||||
public Guid EditorAssetTypeID => typeof(TextureAsset).GUID;
|
||||
|
||||
@@ -291,23 +305,86 @@ internal class TextureAssetHandler : IAssetHandler
|
||||
return TextureDimension.Texture2D;
|
||||
}
|
||||
|
||||
public ValueTask<Result<IAsset>> LoadAssetAsync(Stream assetStream, Guid id, IAssetSettings? settings, CancellationToken token = default)
|
||||
private static unsafe Result<TextureInfo> GetImageInfo(FileStream sourceStream)
|
||||
{
|
||||
using var mmf = MemoryMappedFile.CreateFromFile(sourceStream, null, 0, MemoryMappedFileAccess.Read, HandleInheritability.None, true);
|
||||
using var accessor = mmf.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read);
|
||||
|
||||
byte* ptr = null;
|
||||
|
||||
try
|
||||
{
|
||||
var ext = Path.GetExtension(sourceStream.Name);
|
||||
var isHDR = ext.Equals(".hdr", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr);
|
||||
|
||||
int imageWidth, imageHeight, bitsPerChannel, colorComponents;
|
||||
|
||||
var bufferSpan = new ReadOnlySpan<byte>(ptr, (int)sourceStream.Length);
|
||||
var code = StbIApi.InfoFromMemory(bufferSpan, &imageWidth, &imageHeight, &colorComponents);
|
||||
if (code == 0)
|
||||
{
|
||||
return Result.Failure("Failed to read image info from memory.");
|
||||
}
|
||||
|
||||
bitsPerChannel = StbIApi.Is16BitFromMemory(bufferSpan) > 0 ? 16 : 8;
|
||||
|
||||
void* pPixels;
|
||||
if (bitsPerChannel > 8)
|
||||
{
|
||||
pPixels = StbIApi.LoadfFromMemory(bufferSpan, &imageWidth, &imageHeight, &colorComponents, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
pPixels = StbIApi.LoadFromMemory(bufferSpan, &imageWidth, &imageHeight, &colorComponents, 4);
|
||||
}
|
||||
|
||||
return new TextureInfo
|
||||
{
|
||||
pixelData = (IntPtr)pPixels,
|
||||
width = imageWidth,
|
||||
height = imageHeight,
|
||||
depth = bitsPerChannel,
|
||||
colorComponents = colorComponents,
|
||||
isHDR = isHDR,
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result<TextureInfo>.Failure($"Failed to get image info: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (ptr != null)
|
||||
{
|
||||
accessor.SafeMemoryMappedViewHandle.ReleasePointer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ValueTask<Result<IAsset>> LoadAssetAsync(FileStream assetStream, Guid id, IAssetSettings? settings, CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var image = new MagickImage(assetStream);
|
||||
var infoResult = GetImageInfo(assetStream);
|
||||
if (infoResult.IsFailure)
|
||||
{
|
||||
return ValueTask.FromResult(Result<IAsset>.Failure(infoResult.Message));
|
||||
}
|
||||
|
||||
var info = infoResult.Value;
|
||||
var textureSettings = settings as TextureAssetSettings ?? new TextureAssetSettings();
|
||||
var contentHeader = new TextureContentHeader
|
||||
{
|
||||
width = image.Width,
|
||||
height = image.Height,
|
||||
depth = image.Depth,
|
||||
colorComponents = image.ChannelCount,
|
||||
width = (uint)info.width,
|
||||
height = (uint)info.height,
|
||||
depth = (uint)info.depth,
|
||||
colorComponents = (uint)info.colorComponents,
|
||||
dimension = (uint)GetTextureDimension(textureSettings)
|
||||
};
|
||||
|
||||
return ValueTask.FromResult(Result.Success<IAsset>(new TextureAsset(image, contentHeader, id, textureSettings)));
|
||||
return ValueTask.FromResult(Result.Success<IAsset>(new TextureAsset(info.pixelData, contentHeader, id, textureSettings)));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -315,54 +392,109 @@ internal class TextureAssetHandler : IAssetHandler
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask<Result> SaveAssetAsync(Stream targetStream, IAsset asset, CancellationToken token = default)
|
||||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
|
||||
private unsafe static void WriteCallback(void* context, void* data, int size)
|
||||
{
|
||||
var stream = (Stream)GCHandle.FromIntPtr((IntPtr)context).Target!;
|
||||
var buffer = new ReadOnlySpan<byte>(data, size);
|
||||
stream.Write(buffer);
|
||||
}
|
||||
|
||||
public async ValueTask<Result> SaveAssetAsync(FileStream targetStream, IAsset asset, CancellationToken token = default)
|
||||
{
|
||||
if (asset is not TextureAsset textureAsset)
|
||||
{
|
||||
return Result.Failure("Asset type is not TextureAsset");
|
||||
}
|
||||
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
var gcHandle = GCHandle.Alloc(targetStream, GCHandleType.Normal);
|
||||
|
||||
try
|
||||
{
|
||||
await textureAsset.TextureData.WriteAsync(targetStream, token);
|
||||
var ext = Path.GetExtension(targetStream.Name);
|
||||
|
||||
unsafe
|
||||
{
|
||||
switch (ext)
|
||||
{
|
||||
case ".png":
|
||||
StbIApi.WritePngToFunc(&WriteCallback, (void*)GCHandle.ToIntPtr(gcHandle), (int)textureAsset.Width, (int)textureAsset.Height, (int)textureAsset.ColorComponents, (void*)textureAsset.TextureData, 0);
|
||||
break;
|
||||
|
||||
case ".jpg":
|
||||
StbIApi.WriteJpgToFunc(&WriteCallback, (void*)GCHandle.ToIntPtr(gcHandle), (int)textureAsset.Width, (int)textureAsset.Height, (int)textureAsset.ColorComponents, (void*)textureAsset.TextureData, 90);
|
||||
break;
|
||||
|
||||
// TODO: Add support for other image formats
|
||||
|
||||
default:
|
||||
return Result.Failure($"Unsupported image format: {ext}");
|
||||
}
|
||||
}
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Failure(ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
gcHandle.Free();
|
||||
}
|
||||
}, token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async ValueTask<Result> ImportAsync(Stream sourceStream, Stream targetStream, Guid id, IAssetSettings? settings, CancellationToken token = default)
|
||||
public async ValueTask<Result> ImportAsync(FileStream sourceStream, FileStream targetStream, Guid id, IAssetSettings? settings, CancellationToken token = default)
|
||||
{
|
||||
if (sourceStream.Length > int.MaxValue)
|
||||
{
|
||||
return Result.Failure("Source stream is too large.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using var image = new MagickImage(sourceStream);
|
||||
var pixels = image.GetPixelsUnsafe().GetAreaPointer(0, 0, image.Width, image.Height);
|
||||
if (pixels == 0)
|
||||
var textureSettings = settings as TextureAssetSettings ?? new TextureAssetSettings();
|
||||
|
||||
using var mmf = MemoryMappedFile.CreateFromFile(sourceStream, null, 0, MemoryMappedFileAccess.Read, HandleInheritability.None, true);
|
||||
using var accessor = mmf.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read);
|
||||
|
||||
var infoResult = GetImageInfo(sourceStream);
|
||||
if (!infoResult.IsSuccess)
|
||||
{
|
||||
return Result.Failure("Failed to retrieve pixel data from the source image.");
|
||||
return Result.Failure(infoResult.Message);
|
||||
}
|
||||
|
||||
var textureSettings = settings as TextureAssetSettings ?? new TextureAssetSettings();
|
||||
var (path, mip) = await TextureProcessor.CompressToCacheAsync(EditorApplication.CacheFolderPath, id, pixels, image.Width, image.Height, image.Depth, textureSettings, token)
|
||||
var info = infoResult.Value;
|
||||
var result = await TextureProcessor.CompressToCacheAsync(EditorApplication.CacheFolderPath, id,
|
||||
info,
|
||||
textureSettings, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (result.IsFailure)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var (cachePath, mip) = result.Value;
|
||||
|
||||
targetStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
var contentHeader = new TextureContentHeader
|
||||
var header = new TextureContentHeader
|
||||
{
|
||||
width = image.Width,
|
||||
height = image.Height,
|
||||
depth = image.Depth,
|
||||
colorComponents = image.ChannelCount,
|
||||
width = (uint)info.width,
|
||||
height = (uint)info.height,
|
||||
depth = (uint)info.depth,
|
||||
colorComponents = (uint)info.colorComponents,
|
||||
mipLevels = (uint)mip,
|
||||
dimension = (uint)GetTextureDimension(textureSettings)
|
||||
};
|
||||
|
||||
targetStream.Write(MemoryMarshal.AsBytes(new Span<TextureContentHeader>(ref contentHeader)));
|
||||
targetStream.Write(MemoryMarshal.AsBytes(new Span<TextureContentHeader>(ref header)));
|
||||
|
||||
await using var ddsStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
await using var ddsStream = new FileStream(cachePath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
await ddsStream.CopyToAsync(targetStream, token).ConfigureAwait(false);
|
||||
await targetStream.FlushAsync(token).ConfigureAwait(false);
|
||||
|
||||
@@ -374,8 +506,13 @@ internal class TextureAssetHandler : IAssetHandler
|
||||
}
|
||||
}
|
||||
|
||||
public ValueTask<Result> ExportAsync(Stream assetStream, Stream targetStream, IAssetExportOptions? options, CancellationToken token = default)
|
||||
public ValueTask<Result> ExportAsync(FileStream assetStream, FileStream targetStream, IAssetExportOptions? options, CancellationToken token = default)
|
||||
{
|
||||
return ValueTask.FromResult(Result.Failure("Exporting texture assets is not supported yet."));
|
||||
}
|
||||
|
||||
public ValueTask<Result> PackAsync(FileStream assetStream, Stream targetStream, CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Nvtt;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using System.IO.Hashing;
|
||||
@@ -24,27 +25,19 @@ internal static class TextureProcessor
|
||||
{
|
||||
private readonly string _outputPath;
|
||||
|
||||
private readonly nint _image;
|
||||
private readonly uint _depth;
|
||||
private readonly uint _width;
|
||||
private readonly uint _height;
|
||||
private readonly TextureAssetHandler.TextureInfo _textureInfo;
|
||||
|
||||
private readonly TextureAssetSettings _settings;
|
||||
private readonly TaskCompletionSource _completionSource;
|
||||
private readonly TaskCompletionSource<Result<int>> _completionSource;
|
||||
|
||||
public int mipmapCount;
|
||||
public Task<Result<int>> Task => _completionSource.Task;
|
||||
|
||||
public Task Task => _completionSource.Task;
|
||||
|
||||
public NvttPipelineTask(string outputPath, nint image, uint width, uint height, uint depth, TextureAssetSettings settings)
|
||||
public NvttPipelineTask(string outputPath, TextureAssetHandler.TextureInfo textureInfo, TextureAssetSettings settings)
|
||||
{
|
||||
_outputPath = outputPath;
|
||||
_image = image;
|
||||
_width = width;
|
||||
_height = height;
|
||||
_depth = depth;
|
||||
_textureInfo = textureInfo;
|
||||
_settings = settings;
|
||||
_completionSource = new TaskCompletionSource();
|
||||
_completionSource = new TaskCompletionSource<Result<int>>();
|
||||
}
|
||||
|
||||
public unsafe void Execute()
|
||||
@@ -54,15 +47,22 @@ internal static class TextureProcessor
|
||||
using var pOutOpts = new DisposablePtr<NvttOutputOptions>(NvttOutputOptions.Create());
|
||||
using var pCtx = new DisposablePtr<NvttContext>(NvttContext.Create());
|
||||
|
||||
var inputFormat = _depth > 8
|
||||
var inputFormat = _textureInfo.colorComponents == 1
|
||||
? NvttInputFormat.NVTT_InputFormat_R_32F
|
||||
: _textureInfo.depth > 8
|
||||
? NvttInputFormat.NVTT_InputFormat_RGBA_32F
|
||||
: NvttInputFormat.NVTT_InputFormat_BGRA_8UB; // we'll swizzle RB below
|
||||
|
||||
pSurface.Get()->SetImageData(inputFormat, (int)_width, (int)_height, 1, (void*)_image, NvttBoolean.NVTT_True, null);
|
||||
var needUnsigned = _settings.Basic.TextureType == TextureType.Normal ? NvttBoolean.NVTT_True : NvttBoolean.NVTT_False;
|
||||
if (pSurface.Get()->SetImageData(inputFormat, _textureInfo.width, _textureInfo.height, _textureInfo.depth, (void*)_textureInfo.pixelData, needUnsigned, null))
|
||||
{
|
||||
_completionSource.SetResult(Result.Failure("Failed to set image data for NVTT compression."));
|
||||
return;
|
||||
}
|
||||
|
||||
// stb gives us RGBA byte order; NVTT BGRA_8UB reads it as BGRA,
|
||||
// so channels R and B are swapped — fix with swizzle(2,1,0,3).
|
||||
if (_depth <= 8)
|
||||
if (_textureInfo.colorComponents > 1 && _textureInfo.depth <= 8)
|
||||
{
|
||||
pSurface.Get()->Swizzle(2, 1, 0, 3, null);
|
||||
}
|
||||
@@ -101,7 +101,7 @@ internal static class TextureProcessor
|
||||
pSurface.Get()->PremultiplyAlpha(null);
|
||||
}
|
||||
|
||||
pCompOpts.Get()->SetFormat(SelectFormat(_settings));
|
||||
pCompOpts.Get()->SetFormat(SelectFormat(_settings, _textureInfo.isHDR));
|
||||
pCompOpts.Get()->SetQuality(SelectQuality(_settings.Advanced.CompressionLevel));
|
||||
|
||||
if (_settings.Advanced.CutoutAlpha)
|
||||
@@ -117,6 +117,7 @@ internal static class TextureProcessor
|
||||
|
||||
var nvttFilter = SelectMipmapFilter(_settings.Advanced.MipmapFilter);
|
||||
|
||||
int mipmapCount;
|
||||
if (!_settings.Advanced.GenerateMipmaps)
|
||||
{
|
||||
mipmapCount = 1;
|
||||
@@ -155,11 +156,13 @@ internal static class TextureProcessor
|
||||
}
|
||||
}
|
||||
|
||||
_completionSource.SetResult();
|
||||
_completionSource.SetResult(Result.Success(mipmapCount));
|
||||
}
|
||||
}
|
||||
|
||||
public static async ValueTask<(string cachePath, int mipmapCount)> CompressToCacheAsync(string cachesFolderPath, Guid assetId, nint image, uint width, uint height, uint depth, TextureAssetSettings settings, CancellationToken cancellationToken)
|
||||
public static async ValueTask<Result<(string cachePath, int mipmapCount)>> CompressToCacheAsync(string cachesFolderPath, Guid assetId,
|
||||
TextureAssetHandler.TextureInfo textureInfo,
|
||||
TextureAssetSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
var settingsHash = ComputeSettingsHash(settings);
|
||||
var cacheFileName = $"texturecache_{assetId:N}_{settingsHash:X16}.dds";
|
||||
@@ -202,15 +205,21 @@ internal static class TextureProcessor
|
||||
}
|
||||
|
||||
ScheduleWork:
|
||||
var workItem = new NvttPipelineTask(cachePath, image, width, height, depth, settings);
|
||||
var workItem = new NvttPipelineTask(cachePath, textureInfo, settings);
|
||||
ThreadPool.UnsafeQueueUserWorkItem(workItem, true);
|
||||
await workItem.Task.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return (cachePath, workItem.mipmapCount);
|
||||
var result = await workItem.Task.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
return Result.Failure(result.Message);
|
||||
}
|
||||
|
||||
private static NvttFormat SelectFormat(TextureAssetSettings settings)
|
||||
=> settings.Basic.TextureType switch
|
||||
return (cachePath, result.Value);
|
||||
}
|
||||
|
||||
private static NvttFormat SelectFormat(TextureAssetSettings settings, bool isHDR)
|
||||
=> isHDR
|
||||
? NvttFormat.NVTT_Format_BC6U
|
||||
: settings.Basic.TextureType switch
|
||||
{
|
||||
TextureType.Normal => NvttFormat.NVTT_Format_BC5, // RG normal map
|
||||
TextureType.SingleChannel => NvttFormat.NVTT_Format_BC4, // single channel
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Editor.Core.AssetHandler;
|
||||
using Ghost.Editor.Core.Services;
|
||||
using Ghost.Engine.AssetLoader;
|
||||
|
||||
namespace Ghost.Editor.Core.Contracts;
|
||||
@@ -40,12 +41,14 @@ public sealed class AssetChangedEventArgs : EventArgs
|
||||
|
||||
public interface IAssetRegistry : IDisposable
|
||||
{
|
||||
event EventHandler<AssetChangedEventArgs>? OnAssetChanged;
|
||||
event EventHandler<Guid>? OnAssetImported;
|
||||
|
||||
AssetCatalog GetAssetCatalog();
|
||||
|
||||
string? GetAssetPath(Guid id);
|
||||
Guid GetAssetGuid(string assetPath);
|
||||
|
||||
event EventHandler<AssetChangedEventArgs>? OnAssetChanged;
|
||||
|
||||
|
||||
ValueTask<Result<Guid>> ImportAssetAsync(string sourceFilePath, string targetAssetPath, CancellationToken token = default);
|
||||
ValueTask<Result> ReimportAssetAsync(Guid assetId, string sourceFilePath, CancellationToken token = default);
|
||||
ValueTask<Result<IAsset>> LoadAssetAsync(Guid id, CancellationToken token = default);
|
||||
|
||||
@@ -55,6 +55,8 @@ public static class EditorApplication
|
||||
|
||||
internal static void Initialize(IServiceProvider serviceProvider, string projectPath, string projectName)
|
||||
{
|
||||
Environment.CurrentDirectory = projectPath;
|
||||
|
||||
s_serviceProvider = serviceProvider;
|
||||
s_currentProjectPath = projectPath;
|
||||
s_currentProjectName = projectName;
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentIcons.WinUI" Version="2.1.324" />
|
||||
<PackageReference Include="Magick.NET-Q16-HDRI-OpenMP-x64" Version="14.12.0" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.6" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.28000.1721" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.260317003" />
|
||||
@@ -29,6 +28,8 @@
|
||||
<ProjectReference Include="..\..\Runtime\Ghost.Generator\Ghost.Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||
<ProjectReference Include="..\..\ThridParty\Ghost.DXC\Ghost.DXC.csproj" />
|
||||
<ProjectReference Include="..\..\ThridParty\Ghost.Nvtt\Ghost.Nvtt.csproj" />
|
||||
<ProjectReference Include="..\..\ThridParty\Ghost.Ufbx\Ghost.Ufbx.csproj" />
|
||||
<ProjectReference Include="..\..\ThridParty\Ghost.StbI\Ghost.StbI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Ghost.Editor.Core.Services;
|
||||
/// Thread-safe SQLite-backed asset catalog.
|
||||
/// Replaces the in-memory dictionary approach with persistent storage.
|
||||
/// </summary>
|
||||
internal sealed class AssetCatalog : IDisposable
|
||||
public sealed partial class AssetCatalog : IDisposable
|
||||
{
|
||||
private readonly SqliteConnection _connection;
|
||||
private readonly Lock _writeLock = new();
|
||||
@@ -101,10 +101,20 @@ internal sealed class AssetCatalog : IDisposable
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
private static string ToUniversalPath(string path)
|
||||
{
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
return Path.GetFullPath(path).Replace('\\', '/');
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public Guid GetGuid(string sourcePath)
|
||||
{
|
||||
_cmdGetGuid.Parameters.Clear();
|
||||
_cmdGetGuid.Parameters.AddWithValue("@path", sourcePath);
|
||||
_cmdGetGuid.Parameters.AddWithValue("@path", ToUniversalPath(sourcePath));
|
||||
var result = _cmdGetGuid.ExecuteScalar();
|
||||
return result is byte[] bytes ? new Guid(bytes) : Guid.Empty;
|
||||
}
|
||||
@@ -122,7 +132,7 @@ internal sealed class AssetCatalog : IDisposable
|
||||
{
|
||||
_cmdUpsert.Parameters.Clear();
|
||||
_cmdUpsert.Parameters.AddWithValue("@guid", meta.Guid.ToByteArray());
|
||||
_cmdUpsert.Parameters.AddWithValue("@path", sourcePath);
|
||||
_cmdUpsert.Parameters.AddWithValue("@path", ToUniversalPath(sourcePath));
|
||||
_cmdUpsert.Parameters.AddWithValue("@handler_id", meta.HandlerTypeId?.ToByteArray() ?? (object)DBNull.Value);
|
||||
_cmdUpsert.Parameters.AddWithValue("@version", meta.HandlerVersion);
|
||||
_cmdUpsert.ExecuteNonQuery();
|
||||
|
||||
@@ -3,6 +3,7 @@ using Ghost.Core.Utilities;
|
||||
using Ghost.Editor.Core.AssetHandler;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
|
||||
namespace Ghost.Editor.Core.Services;
|
||||
|
||||
@@ -22,6 +23,11 @@ internal sealed class AssetRegistry : IAssetRegistry, IDisposable
|
||||
private readonly ConcurrentHashSet<Guid> _dirtyAssets;
|
||||
|
||||
public event EventHandler<AssetChangedEventArgs>? OnAssetChanged;
|
||||
public event EventHandler<Guid>? OnAssetImported
|
||||
{
|
||||
add => _importCoordinator.OnImportCompleted += value;
|
||||
remove => _importCoordinator.OnImportCompleted -= value;
|
||||
}
|
||||
|
||||
public AssetRegistry()
|
||||
{
|
||||
@@ -67,7 +73,7 @@ internal sealed class AssetRegistry : IAssetRegistry, IDisposable
|
||||
if (meta != null)
|
||||
{
|
||||
var sourceRelative = AssetMetaIO.GetSourcePath(Path.GetRelativePath(EditorApplication.AssetsFolderPath, metaPath));
|
||||
_catalog.Upsert(meta, sourceRelative.Replace(Path.DirectorySeparatorChar, '/'));
|
||||
_catalog.Upsert(meta, sourceRelative);
|
||||
foundGuids.Add(meta.Guid);
|
||||
}
|
||||
}
|
||||
@@ -84,7 +90,7 @@ internal sealed class AssetRegistry : IAssetRegistry, IDisposable
|
||||
private async void OnFileSystemEvent(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
var ext = Path.GetExtension(e.FullPath);
|
||||
var relativePath = Path.GetRelativePath(EditorApplication.AssetsFolderPath, e.FullPath).Replace(Path.DirectorySeparatorChar, '/');
|
||||
var relativePath = Path.GetRelativePath(EditorApplication.AssetsFolderPath, e.FullPath);
|
||||
|
||||
if (_ignoreMetaWrites.TryRemove(e.FullPath, out _))
|
||||
{
|
||||
@@ -114,7 +120,7 @@ internal sealed class AssetRegistry : IAssetRegistry, IDisposable
|
||||
var changeType = AssetChangeType.None;
|
||||
if (e.ChangeType == WatcherChangeTypes.Created)
|
||||
{
|
||||
await HandleNewSourceFileAsync(e.FullPath, relativePath);
|
||||
await HandleNewSourceFileAsync(relativePath);
|
||||
changeType = AssetChangeType.Created;
|
||||
}
|
||||
else if (e.ChangeType == WatcherChangeTypes.Changed)
|
||||
@@ -144,14 +150,14 @@ internal sealed class AssetRegistry : IAssetRegistry, IDisposable
|
||||
|
||||
private void OnFileSystemRenameEvent(object sender, RenamedEventArgs e)
|
||||
{
|
||||
var oldRelative = Path.GetRelativePath(EditorApplication.AssetsFolderPath, e.OldFullPath).Replace(Path.DirectorySeparatorChar, '/');
|
||||
var newRelative = Path.GetRelativePath(EditorApplication.AssetsFolderPath, e.FullPath).Replace(Path.DirectorySeparatorChar, '/');
|
||||
var oldRelative = Path.GetRelativePath(EditorApplication.AssetsFolderPath, e.OldFullPath);
|
||||
var newRelative = Path.GetRelativePath(EditorApplication.AssetsFolderPath, e.FullPath);
|
||||
|
||||
var guid = _catalog.GetGuid(oldRelative);
|
||||
if (guid != Guid.Empty)
|
||||
{
|
||||
_catalog.Remove(guid);
|
||||
var metaFile = AssetMetaIO.GetMetaPath(e.FullPath);
|
||||
var metaFile = AssetMetaIO.GetMetaPath(newRelative);
|
||||
if (File.Exists(metaFile))
|
||||
{
|
||||
var meta = AssetMetaIO.ReadAsync(metaFile).AsTask().Result;
|
||||
@@ -165,12 +171,12 @@ internal sealed class AssetRegistry : IAssetRegistry, IDisposable
|
||||
OnAssetChanged?.Invoke(this, new AssetChangedEventArgs(newRelative, oldRelative, AssetChangeType.Renamed));
|
||||
}
|
||||
|
||||
private async Task HandleNewSourceFileAsync(string fullPath, string relativePath)
|
||||
private async Task HandleNewSourceFileAsync(string relativePath)
|
||||
{
|
||||
var ext = Path.GetExtension(relativePath);
|
||||
var handler = AssetHandlerRegistry.GetByExtension(ext);
|
||||
|
||||
var metaPath = AssetMetaIO.GetMetaPath(fullPath);
|
||||
var metaPath = AssetMetaIO.GetMetaPath(relativePath);
|
||||
if (File.Exists(metaPath))
|
||||
{
|
||||
return;
|
||||
@@ -193,6 +199,11 @@ internal sealed class AssetRegistry : IAssetRegistry, IDisposable
|
||||
await _importCoordinator.EnqueueAsync(new ImportJob(meta.Guid, relativePath, metaPath, ImportReason.NewAsset));
|
||||
}
|
||||
|
||||
public AssetCatalog GetAssetCatalog()
|
||||
{
|
||||
return _catalog;
|
||||
}
|
||||
|
||||
public string? GetAssetPath(Guid id)
|
||||
{
|
||||
return _catalog.GetSourcePath(id);
|
||||
@@ -200,7 +211,7 @@ internal sealed class AssetRegistry : IAssetRegistry, IDisposable
|
||||
|
||||
public Guid GetAssetGuid(string path)
|
||||
{
|
||||
return _catalog.GetGuid(path.Replace(Path.DirectorySeparatorChar, '/'));
|
||||
return _catalog.GetGuid(path);
|
||||
}
|
||||
|
||||
public async ValueTask<Result<Guid>> ImportAssetAsync(string sourceFilePath, string targetAssetPath, CancellationToken token = default)
|
||||
@@ -208,17 +219,13 @@ internal sealed class AssetRegistry : IAssetRegistry, IDisposable
|
||||
// Simple copy + wait for FSW or manually trigger?
|
||||
// Current requirement: "returns the new GUID immediately (import happens in background)"
|
||||
|
||||
var ext = Path.GetExtension(sourceFilePath);
|
||||
var relativePath = targetAssetPath.Replace(Path.DirectorySeparatorChar, '/');
|
||||
var fullPath = Path.Combine(EditorApplication.AssetsFolderPath, relativePath);
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(fullPath)!);
|
||||
File.Copy(sourceFilePath, fullPath, true);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetAssetPath)!);
|
||||
File.Copy(sourceFilePath, targetAssetPath, true);
|
||||
|
||||
// FSW will trigger but we can speed it up
|
||||
await HandleNewSourceFileAsync(fullPath, relativePath);
|
||||
await HandleNewSourceFileAsync(targetAssetPath);
|
||||
|
||||
var guid = _catalog.GetGuid(relativePath);
|
||||
var guid = _catalog.GetGuid(targetAssetPath);
|
||||
return Result.Success(guid);
|
||||
}
|
||||
|
||||
@@ -230,8 +237,7 @@ internal sealed class AssetRegistry : IAssetRegistry, IDisposable
|
||||
return Result.Failure("Asset not found");
|
||||
}
|
||||
|
||||
var fullPath = Path.Combine(EditorApplication.AssetsFolderPath, path);
|
||||
var metaPath = AssetMetaIO.GetMetaPath(fullPath);
|
||||
var metaPath = AssetMetaIO.GetMetaPath(path);
|
||||
|
||||
await _importCoordinator.EnqueueAsync(new ImportJob(assetId, path, metaPath, ImportReason.ManualReimport), token);
|
||||
return Result.Success();
|
||||
@@ -293,13 +299,14 @@ internal sealed class AssetRegistry : IAssetRegistry, IDisposable
|
||||
return Result.Failure("Asset does not exist.");
|
||||
}
|
||||
|
||||
var handler = AssetHandlerRegistry.GetByTypeId(asset.TypeID);
|
||||
var handler = AssetHandlerRegistry.GetByAssetTypeId(asset.TypeID);
|
||||
if (handler is null)
|
||||
{
|
||||
return Result.Failure("No Avaliable handler type.");
|
||||
}
|
||||
|
||||
await using var stream = new FileStream(path, FileMode.Open, FileAccess.Write, FileShare.None);
|
||||
// This will trigger the fsw and reimport automatically.
|
||||
return await handler.SaveAssetAsync(stream, asset, token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Editor.Core.AssetHandler;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Engine;
|
||||
|
||||
namespace Ghost.Editor.Core.Services;
|
||||
@@ -8,9 +9,9 @@ internal class EditorContentProvider : IContentProvider
|
||||
{
|
||||
private readonly AssetCatalog _catalog;
|
||||
|
||||
public EditorContentProvider(AssetCatalog catalog)
|
||||
public EditorContentProvider(IAssetRegistry assetRegistry)
|
||||
{
|
||||
_catalog = catalog;
|
||||
_catalog = assetRegistry.GetAssetCatalog();
|
||||
}
|
||||
|
||||
public bool HasAsset(Guid guid)
|
||||
@@ -20,7 +21,7 @@ internal class EditorContentProvider : IContentProvider
|
||||
|
||||
public Result<Stream> OpenRead(Guid guid, CancellationToken token = default)
|
||||
{
|
||||
var importedPath = Path.Combine(EditorApplication.LibraryImportsFolderPath, $"{guid:N}{ImportCoordinator.IMPORTED_EXTENSION}");
|
||||
var importedPath = ImportCoordinator.GetImportedAssetPath(guid);
|
||||
if (!File.Exists(importedPath))
|
||||
{
|
||||
return Result.Failure($"Imported asset not found for GUID: {guid}");
|
||||
@@ -37,7 +38,7 @@ internal class EditorContentProvider : IContentProvider
|
||||
public AssetType GetAssetType(Guid guid)
|
||||
{
|
||||
var handlerID = _catalog.GetHandlerTypeId(guid);
|
||||
var handler = AssetHandlerRegistry.GetByTypeId(handlerID);
|
||||
var handler = AssetHandlerRegistry.GetByAssetTypeId(handlerID);
|
||||
return handler?.RuntimeAssetType ?? AssetType.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ internal readonly record struct ImportJob(
|
||||
ImportReason Reason
|
||||
);
|
||||
|
||||
internal sealed class ImportCoordinator : IDisposable
|
||||
internal sealed partial class ImportCoordinator : IDisposable
|
||||
{
|
||||
public const string IMPORTED_EXTENSION_NAME = "Imported";
|
||||
public const string IMPORTED_EXTENSION = ".imported";
|
||||
@@ -34,9 +34,7 @@ internal sealed class ImportCoordinator : IDisposable
|
||||
private readonly CancellationTokenSource _cts;
|
||||
private readonly Task[] _workers;
|
||||
|
||||
// In a real implementation, this event would be used to notify the UI/Rest of engine
|
||||
// For now we just focus on the core logic
|
||||
// public event EventHandler<AssetChangedEventArgs>? OnAssetChanged;
|
||||
public event EventHandler<Guid>? OnImportCompleted;
|
||||
|
||||
public ImportCoordinator(AssetCatalog catalog, int workerCount = 2)
|
||||
{
|
||||
@@ -76,9 +74,19 @@ internal sealed class ImportCoordinator : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask ProcessImportAsync(ImportJob job, CancellationToken token)
|
||||
public static string GetImportedAssetPath(Guid assetGuid)
|
||||
{
|
||||
var fileName = $"{assetGuid:N}{IMPORTED_EXTENSION}";
|
||||
var folderName = fileName.Substring(0, 2);
|
||||
|
||||
var finalPath = Path.Combine(EditorApplication.LibraryImportsFolderPath, folderName, fileName);
|
||||
Directory.CreateDirectory(finalPath);
|
||||
|
||||
return finalPath;
|
||||
}
|
||||
|
||||
private static async ValueTask ProcessImportAsync(ImportJob job, CancellationToken token)
|
||||
{
|
||||
var fullSourcePath = Path.Combine(EditorApplication.AssetsFolderPath, job.SourcePath);
|
||||
var meta = await AssetMetaIO.ReadAsync(job.MetaPath, token);
|
||||
if (meta is null)
|
||||
{
|
||||
@@ -87,27 +95,27 @@ internal sealed class ImportCoordinator : IDisposable
|
||||
}
|
||||
|
||||
var handler = meta.HandlerTypeId.HasValue
|
||||
? AssetHandlerRegistry.GetByTypeId(meta.HandlerTypeId.Value)
|
||||
? AssetHandlerRegistry.GetByAssetTypeId(meta.HandlerTypeId.Value)
|
||||
: AssetHandlerRegistry.GetByExtension(Path.GetExtension(job.SourcePath));
|
||||
|
||||
var contentHash = await ComputeFileHashAsync(fullSourcePath, token);
|
||||
var contentHash = await ComputeFileHashAsync(job.SourcePath, token);
|
||||
var settingsHash = ComputeSettingsHash(meta.Settings);
|
||||
|
||||
// Check if we can skip (if not a manual reimport)
|
||||
if (job.Reason != ImportReason.ManualReimport &&
|
||||
meta.ContentHash == contentHash &&
|
||||
meta.SettingsHash == settingsHash &&
|
||||
meta.HandlerVersion == AssetHandlerRegistry.GetVersionByTypeId(meta.HandlerTypeId ?? Guid.Empty))
|
||||
meta.HandlerVersion == AssetHandlerRegistry.GetVersionByAssetTypeId(meta.HandlerTypeId ?? Guid.Empty))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var importResult = Result.Success();
|
||||
if (handler is IAssetHandler importable)
|
||||
if (handler is IImportableAssetHandler importable)
|
||||
{
|
||||
var targetPath = Path.Combine(EditorApplication.LibraryImportsFolderPath, $"{job.AssetGuid:N}{IMPORTED_EXTENSION}");
|
||||
var targetPath = GetImportedAssetPath(job.AssetGuid);
|
||||
|
||||
await using var sourceStream = new FileStream(fullSourcePath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
await using var sourceStream = new FileStream(job.SourcePath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
await using var targetStream = new FileStream(targetPath, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
|
||||
importResult = await importable.ImportAsync(sourceStream, targetStream, job.AssetGuid, meta.Settings, token);
|
||||
@@ -117,7 +125,7 @@ internal sealed class ImportCoordinator : IDisposable
|
||||
{
|
||||
meta.ContentHash = contentHash;
|
||||
meta.SettingsHash = settingsHash;
|
||||
meta.HandlerVersion = AssetHandlerRegistry.GetVersionByTypeId(meta.HandlerTypeId ?? Guid.Empty);
|
||||
meta.HandlerVersion = AssetHandlerRegistry.GetVersionByAssetTypeId(meta.HandlerTypeId ?? Guid.Empty);
|
||||
meta.LastImportedUtc = DateTime.UtcNow;
|
||||
|
||||
await AssetMetaIO.WriteAsync(job.MetaPath, meta, token);
|
||||
|
||||
@@ -130,7 +130,7 @@ internal static class ShaderCompilerUtility
|
||||
|
||||
public static Result<UnsafeArray<UnsafeArray<byte>>> CompileComputeShader(this IShaderCompiler shaderCompiler, ComputeShaderDescriptor descriptor, ref readonly ShaderCompilationConfig additionalConfig, AllocationHandle allocationHandle)
|
||||
{
|
||||
var fullDefines = CombineDefines(descriptor.defines, additionalConfig.defines);
|
||||
var fullDefines = CombineDefines(descriptor.Defines, additionalConfig.defines);
|
||||
|
||||
var config = new ShaderCompilationConfig
|
||||
{
|
||||
@@ -141,11 +141,11 @@ internal static class ShaderCompilerUtility
|
||||
stage = ShaderStage.ComputeShader,
|
||||
};
|
||||
|
||||
var compiled = new UnsafeArray<UnsafeArray<byte>>(descriptor.shaderCodes.Length, allocationHandle);
|
||||
for (int i = 0; i < descriptor.shaderCodes.Length; i++)
|
||||
var compiled = new UnsafeArray<UnsafeArray<byte>>(descriptor.ShaderCodes.Length, allocationHandle);
|
||||
for (int i = 0; i < descriptor.ShaderCodes.Length; i++)
|
||||
{
|
||||
config.shaderCode = descriptor.shaderCodes[i].code;
|
||||
config.entryPoint = descriptor.shaderCodes[i].entryPoint;
|
||||
config.shaderCode = descriptor.ShaderCodes[i].code;
|
||||
config.entryPoint = descriptor.ShaderCodes[i].entryPoint;
|
||||
|
||||
var result = shaderCompiler.Compile(ref config, allocationHandle);
|
||||
if (result.IsFailure)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Core.Utilities;
|
||||
using Ghost.Editor.Models;
|
||||
using Ghost.Engine;
|
||||
@@ -65,7 +66,13 @@ internal static class ActivationHandler
|
||||
|
||||
AllocationManager.Initialize(opts);
|
||||
|
||||
App.GetService<EngineCore>();
|
||||
var assetRegistry = App.GetService<IAssetRegistry>();
|
||||
var engineCore = App.GetService<EngineCore>();
|
||||
|
||||
assetRegistry.OnAssetImported += (sender, e) =>
|
||||
{
|
||||
engineCore.AssetManager.ReimportAsset(e);
|
||||
};
|
||||
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -50,10 +50,102 @@
|
||||
</Style>
|
||||
|
||||
<!-- Named Style -->
|
||||
<Style
|
||||
x:Key="ToolbarButton"
|
||||
BasedOn="{StaticResource SubtleButtonStyle}"
|
||||
TargetType="Button" />
|
||||
<Style x:Key="ToolbarButton" TargetType="Button">
|
||||
<Setter Property="Padding" Value="2" />
|
||||
<Setter Property="Background" Value="{ThemeResource SubtleFillColorTransparentBrush}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Grid
|
||||
x:Name="RootGrid"
|
||||
Padding="10,5"
|
||||
Background="{TemplateBinding Background}"
|
||||
CornerRadius="4">
|
||||
<ContentPresenter
|
||||
x:Name="ContentPresenter"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Content="{TemplateBinding Content}" />
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
|
||||
<VisualState x:Name="Normal" />
|
||||
|
||||
<VisualState x:Name="PointerOver">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="RootGrid.Background" Value="{ThemeResource SubtleFillColorSecondaryBrush}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="Pressed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="RootGrid.Background" Value="{ThemeResource SubtleFillColorTertiaryBrush}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="RootGrid.Background" Value="{ThemeResource SubtleFillColorDisabledBrush}" />
|
||||
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ControlStrongFillColorDisabledBrush}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="AccentToolbarButton" TargetType="Button">
|
||||
<Setter Property="Padding" Value="2" />
|
||||
<Setter Property="Background" Value="{ThemeResource SubtleFillColorTransparentBrush}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource AccentFillColorDefaultBrush}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Grid
|
||||
x:Name="RootGrid"
|
||||
Padding="10,5"
|
||||
Background="{TemplateBinding Background}"
|
||||
CornerRadius="4">
|
||||
<ContentPresenter
|
||||
x:Name="ContentPresenter"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Content="{TemplateBinding Content}" />
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
|
||||
<VisualState x:Name="Normal" />
|
||||
|
||||
<VisualState x:Name="PointerOver">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="RootGrid.Background" Value="{ThemeResource SubtleFillColorSecondaryBrush}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="Pressed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="RootGrid.Background" Value="{ThemeResource SubtleFillColorTertiaryBrush}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="RootGrid.Background" Value="{ThemeResource SubtleFillColorDisabledBrush}" />
|
||||
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource AccentFillColorDisabledBrush}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="VerticalDivider" TargetType="Border">
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource DividerStrokeColorDefaultBrush}" />
|
||||
|
||||
@@ -90,7 +90,7 @@ internal partial class ContentBrowserViewModel : ObservableObject
|
||||
if (!isDir)
|
||||
{
|
||||
var ext = Path.GetExtension(fullPath);
|
||||
assetType = AssetHandlerRegistry.GetAssetTypeByExtension(ext);
|
||||
assetType = AssetHandlerRegistry.GetRuntimeAssetTypeByExtension(ext);
|
||||
}
|
||||
Files.Add(new ExplorerItem(Path.GetFileName(fullPath), fullPath, isDir, assetType));
|
||||
}
|
||||
@@ -144,7 +144,7 @@ internal partial class ContentBrowserViewModel : ObservableObject
|
||||
}
|
||||
|
||||
var ext = Path.GetExtension(file);
|
||||
var assetType = AssetHandlerRegistry.GetAssetTypeByExtension(ext);
|
||||
var assetType = AssetHandlerRegistry.GetRuntimeAssetTypeByExtension(ext);
|
||||
|
||||
var fileItem = new ExplorerItem(Path.GetFileName(file), file, false, assetType);
|
||||
Files.Add(fileItem);
|
||||
|
||||
@@ -33,8 +33,6 @@ internal sealed partial class ContentBrowser : UserControl
|
||||
|
||||
Loaded += ProjectBrowser_Loaded;
|
||||
Unloaded += ProjectBrowser_Unloaded;
|
||||
|
||||
GettingFocus += ProjectBrowser_GettingFocus;
|
||||
}
|
||||
|
||||
private void ProjectBrowser_GettingFocus(UIElement sender, GettingFocusEventArgs args)
|
||||
@@ -50,11 +48,13 @@ internal sealed partial class ContentBrowser : UserControl
|
||||
private void ProjectBrowser_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_inspectorService.OnSelectionChanged += _inspectorService_OnSelectionChanged;
|
||||
GettingFocus += ProjectBrowser_GettingFocus;
|
||||
}
|
||||
|
||||
private void ProjectBrowser_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_inspectorService.OnSelectionChanged -= _inspectorService_OnSelectionChanged;
|
||||
GettingFocus -= ProjectBrowser_GettingFocus;
|
||||
|
||||
if (LastFocused == this)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Background="{ThemeResource LayerFillColorDefaultBrush}"
|
||||
NavigationCacheMode="Enabled"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
@@ -15,24 +16,33 @@
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<StackPanel
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
Padding="8,0"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource ControlElevationBorderBrush}"
|
||||
BorderThickness="0,0,0,1"
|
||||
BorderThickness="0,0,0,1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<ComboBox
|
||||
Width="200"
|
||||
VerticalAlignment="Center"
|
||||
SelectedIndex="0">
|
||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||
<FontIcon FontSize="{StaticResource ToolbarFontIconFontSize}" Glyph="" />
|
||||
<TextBlock Text="Selection Mode" />
|
||||
<FontIcon FontSize="{StaticResource ToolbarFontIconFontSize}" Glyph="" />
|
||||
<TextBlock Text="Hierarchy Mode" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||
<FontIcon FontSize="{StaticResource ToolbarFontIconFontSize}" Glyph="" />
|
||||
<TextBlock Text="Placement Mode" />
|
||||
<TextBlock Text="Scatter Mode" />
|
||||
</StackPanel>
|
||||
</ComboBox>
|
||||
|
||||
@@ -59,6 +69,25 @@
|
||||
</MenuBar>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="4">
|
||||
<Button Style="{ThemeResource AccentToolbarButton}">
|
||||
<FontIcon FontSize="{StaticResource ToolbarFontIconFontSize}" Glyph="" />
|
||||
</Button>
|
||||
<Button Style="{ThemeResource AccentToolbarButton}">
|
||||
<FontIcon FontSize="{StaticResource ToolbarFontIconFontSize}" Glyph="" />
|
||||
</Button>
|
||||
<Button IsEnabled="False" Style="{ThemeResource AccentToolbarButton}">
|
||||
<FontIcon FontSize="{StaticResource ToolbarFontIconFontSize}" Glyph="" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="0.25*" MaxWidth="350" />
|
||||
|
||||
@@ -5,17 +5,24 @@ namespace Ghost.Editor.Views.Pages;
|
||||
|
||||
public sealed partial class EditPage : Page
|
||||
{
|
||||
private readonly ContentBrowser _contentBrowser;
|
||||
private readonly LogViewer _logViewer;
|
||||
private ContentBrowser? _contentBrowser;
|
||||
private LogViewer? _logViewer;
|
||||
|
||||
public EditPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_contentBrowser = new ContentBrowser();
|
||||
_logViewer = new LogViewer();
|
||||
ContentBrowserPresenter.Content = GetContentBrowser();
|
||||
}
|
||||
|
||||
ContentBrowserPresenter.Content = _contentBrowser;
|
||||
private ContentBrowser GetContentBrowser()
|
||||
{
|
||||
return _contentBrowser ??= new ContentBrowser();
|
||||
}
|
||||
|
||||
private LogViewer GetLogViewer()
|
||||
{
|
||||
return _logViewer ??= new LogViewer();
|
||||
}
|
||||
|
||||
private void SelectorBar_SelectionChanged(SelectorBar sender, SelectorBarSelectionChangedEventArgs args)
|
||||
@@ -25,10 +32,10 @@ public sealed partial class EditPage : Page
|
||||
switch (currentSelectedIndex)
|
||||
{
|
||||
case 0:
|
||||
ContentBrowserPresenter.Content = _contentBrowser;
|
||||
ContentBrowserPresenter.Content = GetContentBrowser();
|
||||
break;
|
||||
case 2:
|
||||
ContentBrowserPresenter.Content = _logViewer;
|
||||
ContentBrowserPresenter.Content = GetLogViewer();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
Text="Edit" />
|
||||
<SelectorBarItem x:Name="AnalysisSelectorItem" Text="Analysis" />
|
||||
<SelectorBarItem x:Name="BuildSelectorItem" Text="Build" />
|
||||
<SelectorBarItem x:Name="SettingsSelectorItem" Text="Settings" />
|
||||
</SelectorBar>
|
||||
</StackPanel>
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
<Project Path="ThridParty/Ghost.FMOD/Ghost.FMOD.csproj" />
|
||||
<Project Path="ThridParty/Ghost.MeshOptimizer/Ghost.MeshOptimizer.csproj" />
|
||||
<Project Path="ThridParty/Ghost.Nvtt/Ghost.Nvtt.csproj" />
|
||||
<Project Path="ThridParty/Ghost.StbI/Ghost.StbI.csproj" Id="969a1d70-31ee-49e9-9449-ab96a47cb925" />
|
||||
<Project Path="ThridParty/Ghost.Ufbx/Ghost.Ufbx.csproj" Id="c4bd647c-6d77-49d8-ba81-6ed4946474d1" />
|
||||
</Folder>
|
||||
<Folder Name="/Runtime/">
|
||||
|
||||
@@ -60,18 +60,56 @@ public struct PassDescriptor
|
||||
|
||||
public class GraphicsShaderDescriptor
|
||||
{
|
||||
public required string name = string.Empty;
|
||||
public required uint propertyBufferSize;
|
||||
public required ShaderModel shaderModel;
|
||||
public required PassDescriptor[] passes = Array.Empty<PassDescriptor>();
|
||||
public required string Name
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
public required uint PropertyBufferSize
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
public required ShaderModel ShaderModel
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
public required PassDescriptor[] Passes
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
}
|
||||
|
||||
public class ComputeShaderDescriptor
|
||||
{
|
||||
public required string name = string.Empty;
|
||||
public required uint propertyBufferSize;
|
||||
public required ShaderModel shaderModel;
|
||||
public required ShaderCode[] shaderCodes;
|
||||
public required string[] defines;
|
||||
public required KeywordsGroup[] keywords;
|
||||
public required string Name
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
public required uint PropertyBufferSize
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
public required ShaderModel ShaderModel
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
public required ShaderCode[] ShaderCodes
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
public required string[] Defines
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
public required KeywordsGroup[] Keywords
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ internal unsafe partial class AssetEntry
|
||||
|
||||
if (Interlocked.CompareExchange(ref _pendingReimport, false, true))
|
||||
{
|
||||
_assetManager.InvalidateAsset(_assetId); // re-queue
|
||||
_assetManager.ReimportAsset(_assetId); // re-queue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,7 +415,7 @@ internal partial class AssetManager : IDisposable
|
||||
return entry;
|
||||
}
|
||||
|
||||
public void InvalidateAsset(Guid guid)
|
||||
public void ReimportAsset(Guid guid)
|
||||
{
|
||||
if (!_entries.TryGetValue(guid, out var entry))
|
||||
{
|
||||
|
||||
@@ -4,6 +4,12 @@ using Misaki.HighPerformance.Jobs;
|
||||
|
||||
namespace Ghost.Engine;
|
||||
|
||||
public interface IRuntimeInitializeCallback
|
||||
{
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
}
|
||||
|
||||
public sealed partial class EngineCore : IDisposable
|
||||
{
|
||||
private readonly IContentProvider _contentProvider;
|
||||
@@ -13,6 +19,10 @@ public sealed partial class EngineCore : IDisposable
|
||||
private readonly RenderSystem _renderSystem;
|
||||
private readonly AssetManager _assetManager;
|
||||
|
||||
internal JobScheduler JobScheduler => _jobScheduler;
|
||||
internal RenderSystem RenderSystem => _renderSystem;
|
||||
internal AssetManager AssetManager => _assetManager;
|
||||
|
||||
public EngineCore(IContentProvider contentProvider)
|
||||
{
|
||||
_contentProvider = contentProvider;
|
||||
|
||||
13
src/Runtime/Ghost.Engine/TestSetup.cs
Normal file
13
src/Runtime/Ghost.Engine/TestSetup.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ghost.Engine;
|
||||
|
||||
internal class TestSetup : IDisposable
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -188,11 +188,16 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
|
||||
if (isSubAllocation)
|
||||
{
|
||||
hr = CreateResource(&allocationDesc, &resourceDesc, D3D12_BARRIER_LAYOUT_COMMON, options, (uint)additionalDesc.CastableFormat.Length, pCastableFormats, __uuidof(pResource), (void**)&pResource);
|
||||
hr = CreateResource(&allocationDesc, &resourceDesc, D3D12_BARRIER_LAYOUT_COMMON, options,
|
||||
(uint)additionalDesc.CastableFormat.Length, pCastableFormats,
|
||||
__uuidof(pResource), (void**)&pResource);
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = CreateResource(&allocationDesc, &resourceDesc, D3D12_BARRIER_LAYOUT_COMMON, options, (uint)additionalDesc.CastableFormat.Length, pCastableFormats, __uuidof(pAllocation), (void**)&pAllocation);
|
||||
hr = CreateResource(&allocationDesc, &resourceDesc, D3D12_BARRIER_LAYOUT_COMMON, options,
|
||||
(uint)additionalDesc.CastableFormat.Length, pCastableFormats,
|
||||
__uuidof(pAllocation), (void**)&pAllocation);
|
||||
|
||||
if (hr.SUCCEEDED)
|
||||
{
|
||||
pResource = pAllocation->GetResource();
|
||||
@@ -249,11 +254,16 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
|
||||
if (isSubAllocation)
|
||||
{
|
||||
hr = CreateResource(&allocationDesc, &resourceDesc, D3D12_BARRIER_LAYOUT_UNDEFINED, options, 0u, null, __uuidof(pResource), (void**)&pResource);
|
||||
hr = CreateResource(&allocationDesc, &resourceDesc, D3D12_BARRIER_LAYOUT_UNDEFINED, options,
|
||||
0u, null,
|
||||
__uuidof(pResource), (void**)&pResource);
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = CreateResource(&allocationDesc, &resourceDesc, D3D12_BARRIER_LAYOUT_UNDEFINED, options, 0u, null, __uuidof(pAllocation), (void**)&pAllocation);
|
||||
hr = CreateResource(&allocationDesc, &resourceDesc, D3D12_BARRIER_LAYOUT_UNDEFINED, options,
|
||||
0u, null,
|
||||
__uuidof(pAllocation), (void**)&pAllocation);
|
||||
|
||||
if (hr.SUCCEEDED)
|
||||
{
|
||||
pResource = pAllocation->GetResource();
|
||||
|
||||
@@ -75,15 +75,15 @@ public partial struct Shader : IResourceReleasable
|
||||
|
||||
internal Shader(GraphicsShaderDescriptor descriptor)
|
||||
{
|
||||
_nameHash = RHIUtility.GetShaderID(descriptor.name);
|
||||
_propertyBufferSize = descriptor.propertyBufferSize;
|
||||
_shaderPasses = new UnsafeArray<ShaderPass>(descriptor.passes.Length, AllocationHandle.Persistent);
|
||||
_passIDToLocal = new UnsafeHashMap<int, int>(descriptor.passes.Length, AllocationHandle.Persistent);
|
||||
_nameHash = RHIUtility.GetShaderID(descriptor.Name);
|
||||
_propertyBufferSize = descriptor.PropertyBufferSize;
|
||||
_shaderPasses = new UnsafeArray<ShaderPass>(descriptor.Passes.Length, AllocationHandle.Persistent);
|
||||
_passIDToLocal = new UnsafeHashMap<int, int>(descriptor.Passes.Length, AllocationHandle.Persistent);
|
||||
_keywordIDToLocal = new UnsafeHashMap<int, int>(32, AllocationHandle.Persistent);
|
||||
|
||||
for (var i = 0; i < descriptor.passes.Length; i++)
|
||||
for (var i = 0; i < descriptor.Passes.Length; i++)
|
||||
{
|
||||
ref readonly var pass = ref descriptor.passes[i];
|
||||
ref readonly var pass = ref descriptor.Passes[i];
|
||||
|
||||
var keywords = new LocalKeywordSet();
|
||||
|
||||
@@ -189,7 +189,7 @@ public partial struct Shader : IResourceReleasable
|
||||
public unsafe partial struct ComputeShader : IResourceReleasable
|
||||
{
|
||||
private readonly ulong _nameHash;
|
||||
private fixed ulong entryHashes[8]; // Support up to 8 entry points for now, can be extended if needed.
|
||||
private fixed ulong _entryHashes[8]; // Support up to 8 entry points for now, can be extended if needed.
|
||||
private readonly uint _propertyBufferSize;
|
||||
|
||||
private LocalKeywordSet _localKeywordSet;
|
||||
@@ -200,20 +200,20 @@ public unsafe partial struct ComputeShader : IResourceReleasable
|
||||
|
||||
internal ComputeShader(ComputeShaderDescriptor descriptor)
|
||||
{
|
||||
_nameHash = RHIUtility.GetShaderID(descriptor.name);
|
||||
_propertyBufferSize = descriptor.propertyBufferSize;
|
||||
_nameHash = RHIUtility.GetShaderID(descriptor.Name);
|
||||
_propertyBufferSize = descriptor.PropertyBufferSize;
|
||||
|
||||
_keywordIDToLocal = new UnsafeHashMap<int, int>(32, AllocationHandle.Persistent);
|
||||
|
||||
for (var i = 0; i < descriptor.shaderCodes.Length; i++)
|
||||
for (var i = 0; i < descriptor.ShaderCodes.Length; i++)
|
||||
{
|
||||
entryHashes[i] = RHIUtility.GetPassID(_nameHash, i);
|
||||
_entryHashes[i] = RHIUtility.GetPassID(_nameHash, i);
|
||||
}
|
||||
|
||||
var localKeywordIndex = 0;
|
||||
for (var i = 0; i < descriptor.keywords.Length; i++)
|
||||
for (var i = 0; i < descriptor.Keywords.Length; i++)
|
||||
{
|
||||
var group = descriptor.keywords[i];
|
||||
var group = descriptor.Keywords[i];
|
||||
if (group.keywords == null)
|
||||
{
|
||||
continue;
|
||||
@@ -236,7 +236,7 @@ public unsafe partial struct ComputeShader : IResourceReleasable
|
||||
public ulong GetEntryID(int entryIndex)
|
||||
{
|
||||
Logger.DebugAssert(entryIndex >= 0 && entryIndex < 8, "Entry index out of bounds.");
|
||||
return entryHashes[entryIndex];
|
||||
return _entryHashes[entryIndex];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
||||
@@ -251,7 +251,6 @@ public class RenderSystem : IDisposable
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// Wait for either CPU ready signal or shutdown signal
|
||||
waitHandles[0] = frameResource.CpuReadyEvent;
|
||||
var waitResult = WaitHandle.WaitAny(waitHandles);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Ghost.Graphics.Utilities;
|
||||
using Ghost.MeshOptimizer;
|
||||
using Ghost.Ufbx;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
@@ -170,11 +171,11 @@ internal static class MeshUtility
|
||||
MemoryUtility.MemCpy(indices.GetUnsafePtr(), cachedIndices.GetUnsafePtr(), numIndices * sizeof(uint));
|
||||
indices.UnsafeSetCount((int)numIndices);
|
||||
|
||||
//if (needComputeNormals)
|
||||
//{
|
||||
// MeshBuilder.ComputeNormal(vertices, indices);
|
||||
// MeshBuilder.ComputeTangents(vertices, indices);
|
||||
//}
|
||||
if (needComputeNormals)
|
||||
{
|
||||
MeshBuilder.ComputeNormal(vertices, indices);
|
||||
MeshBuilder.ComputeTangents(vertices, indices);
|
||||
}
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Runtime\Ghost.Graphics\Ghost.Graphics.csproj" />
|
||||
<ProjectReference Include="..\..\Test\Ghost.Test.Core\Ghost.Test.Core.csproj" />
|
||||
<ProjectReference Include="..\..\ThridParty\Ghost.Nvtt\Ghost.Nvtt.csproj" />
|
||||
<ProjectReference Include="..\..\ThridParty\Ghost.StbI\Ghost.StbI.csproj" />
|
||||
<ProjectReference Include="..\..\ThridParty\Ghost.Ufbx\Ghost.Ufbx.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Ghost.MicroTest;
|
||||
using Ghost.Test.Core;
|
||||
|
||||
//TestRunner.Run<MeshoptBenchmark>();
|
||||
Console.WriteLine();
|
||||
TestRunner.Run<StbIBindingTest>();
|
||||
72
src/Test/Ghost.MicroTest/StbIBindingTest.cs
Normal file
72
src/Test/Ghost.MicroTest/StbIBindingTest.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using Ghost.StbI;
|
||||
using Ghost.Test.Core;
|
||||
|
||||
namespace Ghost.MicroTest;
|
||||
|
||||
internal class StbIBindingTest : ITest
|
||||
{
|
||||
public void Setup()
|
||||
{
|
||||
}
|
||||
|
||||
public unsafe void Run()
|
||||
{
|
||||
using var stream = File.OpenRead("C:\\Users\\Misaki\\Downloads\\Screenshot 2024-07-20 035047.png");
|
||||
var bytes = new byte[stream.Length];
|
||||
stream.ReadExactly(bytes);
|
||||
|
||||
int width, height, channels;
|
||||
var buff = StbIApi.LoadFromMemory(bytes, &width, &height, &channels, 4);
|
||||
if (buff == null)
|
||||
{
|
||||
Console.WriteLine("Failed to load image");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"Image loaded: {width}x{height}, channels: {channels}");
|
||||
|
||||
var expectedColor = (Span<byte>)stackalloc byte[] { 122, 145, 224, 255 };
|
||||
var firstPixel = new Span<byte>(buff, 4);
|
||||
|
||||
Console.WriteLine("First pixel RGBA: " + string.Join(", ", firstPixel.ToArray()));
|
||||
Console.WriteLine("Expected RGBA: " + string.Join(", ", expectedColor.ToArray()));
|
||||
|
||||
if (!firstPixel.SequenceEqual(expectedColor))
|
||||
{
|
||||
Console.WriteLine("First pixel does not match expected color");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("First pixel matches expected color");
|
||||
}
|
||||
|
||||
firstPixel.Fill(0xFF);
|
||||
|
||||
int result;
|
||||
var newFilePath = "C:\\Users\\Misaki\\Downloads\\ModifiedImage.jpg"u8;
|
||||
fixed (byte* pathPtr = newFilePath)
|
||||
{
|
||||
result = StbIApi.WriteJpg((sbyte*)pathPtr, width, height, 4, buff, 90);
|
||||
}
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
Console.WriteLine("Failed to write image");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Image written successfully");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
StbIApi.ImageFree(buff);
|
||||
}
|
||||
}
|
||||
|
||||
public void Cleanup()
|
||||
{
|
||||
}
|
||||
}
|
||||
25
src/ThridParty/Ghost.Nvtt/Api.cs
Normal file
25
src/ThridParty/Ghost.Nvtt/Api.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.Nvtt;
|
||||
|
||||
public partial class Api
|
||||
{
|
||||
static Api()
|
||||
{
|
||||
NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), (libraryName, assembly, searchPath) =>
|
||||
{
|
||||
var platform = OperatingSystem.IsWindows() ? "win" :
|
||||
OperatingSystem.IsLinux() ? "linux" :
|
||||
OperatingSystem.IsMacOS() ? "osx" : "unknown";
|
||||
var ext = OperatingSystem.IsWindows() ? ".dll" :
|
||||
OperatingSystem.IsLinux() ? ".so" :
|
||||
OperatingSystem.IsMacOS() ? ".dylib" : "";
|
||||
|
||||
var arch = Environment.Is64BitProcess ? "x64" : "x86";
|
||||
var nativeDllDir = Path.Combine("./runtimes", platform + "-" + arch, "native");
|
||||
|
||||
return NativeLibrary.Load(Path.Combine(nativeDllDir, libraryName + ext));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,9 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.Nvtt
|
||||
{
|
||||
public static unsafe partial class Api
|
||||
{
|
||||
static Api()
|
||||
{
|
||||
NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), (libraryName, assembly, searchPath) =>
|
||||
{
|
||||
var platform = OperatingSystem.IsWindows() ? "win" :
|
||||
OperatingSystem.IsLinux() ? "linux" :
|
||||
OperatingSystem.IsMacOS() ? "osx" : "unknown";
|
||||
var ext = OperatingSystem.IsWindows() ? ".dll" :
|
||||
OperatingSystem.IsLinux() ? ".so" :
|
||||
OperatingSystem.IsMacOS() ? ".dylib" : "";
|
||||
|
||||
var arch = Environment.Is64BitProcess ? "x64" : "x86";
|
||||
var nativeDllDir = Path.Combine("./runtimes", platform + "-" + arch, "native");
|
||||
|
||||
return NativeLibrary.Load(Path.Combine(nativeDllDir, libraryName + ext));
|
||||
});
|
||||
}
|
||||
|
||||
[DllImport("nvtt", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern NvttBoolean nvttIsCudaSupported();
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@ namespace Ghost.Nvtt
|
||||
|
||||
private readonly int _value;
|
||||
|
||||
public readonly bool IsTrue => _value != 0;
|
||||
public readonly bool IsFalse => _value == 0;
|
||||
|
||||
public NvttBoolean(int value)
|
||||
{
|
||||
_value = value;
|
||||
|
||||
25
src/ThridParty/Ghost.StbI/Api.cs
Normal file
25
src/ThridParty/Ghost.StbI/Api.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.StbI;
|
||||
|
||||
public partial class Api
|
||||
{
|
||||
static Api()
|
||||
{
|
||||
NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), (libraryName, assembly, searchPath) =>
|
||||
{
|
||||
var platform = OperatingSystem.IsWindows() ? "win" :
|
||||
OperatingSystem.IsLinux() ? "linux" :
|
||||
OperatingSystem.IsMacOS() ? "osx" : "unknown";
|
||||
var ext = OperatingSystem.IsWindows() ? ".dll" :
|
||||
OperatingSystem.IsLinux() ? ".so" :
|
||||
OperatingSystem.IsMacOS() ? ".dylib" : "";
|
||||
|
||||
var arch = Environment.Is64BitProcess ? "x64" : "x86";
|
||||
var nativeDllDir = Path.Combine("./runtimes", platform + "-" + arch, "native");
|
||||
|
||||
return NativeLibrary.Load(Path.Combine(nativeDllDir, libraryName + ext));
|
||||
});
|
||||
}
|
||||
}
|
||||
171
src/ThridParty/Ghost.StbI/Generated/Api.cs
Normal file
171
src/ThridParty/Ghost.StbI/Generated/Api.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.StbI;
|
||||
|
||||
public static unsafe partial class Api
|
||||
{
|
||||
public const int STBI_default = 0;
|
||||
public const int STBI_grey = 1;
|
||||
public const int STBI_grey_alpha = 2;
|
||||
public const int STBI_rgb = 3;
|
||||
public const int STBI_rgb_alpha = 4;
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("stbi_uc *")]
|
||||
public static extern byte* stbi_load_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len, int* x, int* y, int* channels_in_file, int desired_channels);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("stbi_uc *")]
|
||||
public static extern byte* stbi_load_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("stbi_uc *")]
|
||||
public static extern byte* stbi_load([NativeTypeName("const char *")] sbyte* filename, int* x, int* y, int* channels_in_file, int desired_channels);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("stbi_uc *")]
|
||||
public static extern byte* stbi_load_gif_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len, int** delays, int* x, int* y, int* z, int* comp, int req_comp);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("stbi_us *")]
|
||||
public static extern ushort* stbi_load_16_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len, int* x, int* y, int* channels_in_file, int desired_channels);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("stbi_us *")]
|
||||
public static extern ushort* stbi_load_16_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("stbi_us *")]
|
||||
public static extern ushort* stbi_load_16([NativeTypeName("const char *")] sbyte* filename, int* x, int* y, int* channels_in_file, int desired_channels);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern float* stbi_loadf_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len, int* x, int* y, int* channels_in_file, int desired_channels);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern float* stbi_loadf_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern float* stbi_loadf([NativeTypeName("const char *")] sbyte* filename, int* x, int* y, int* channels_in_file, int desired_channels);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void stbi_hdr_to_ldr_gamma(float gamma);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void stbi_hdr_to_ldr_scale(float scale);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void stbi_ldr_to_hdr_gamma(float gamma);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void stbi_ldr_to_hdr_scale(float scale);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int stbi_is_hdr_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int stbi_is_hdr_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int stbi_is_hdr([NativeTypeName("const char *")] sbyte* filename);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("const char *")]
|
||||
public static extern sbyte* stbi_failure_reason();
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void stbi_image_free(void* retval_from_stbi_load);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int stbi_info_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len, int* x, int* y, int* comp);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int stbi_info_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user, int* x, int* y, int* comp);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int stbi_is_16_bit_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int stbi_is_16_bit_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int stbi_info([NativeTypeName("const char *")] sbyte* filename, int* x, int* y, int* comp);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int stbi_is_16_bit([NativeTypeName("const char *")] sbyte* filename);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("char *")]
|
||||
public static extern sbyte* stbi_zlib_decode_malloc_guesssize([NativeTypeName("const char *")] sbyte* buffer, int len, int initial_size, int* outlen);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("char *")]
|
||||
public static extern sbyte* stbi_zlib_decode_malloc_guesssize_headerflag([NativeTypeName("const char *")] sbyte* buffer, int len, int initial_size, int* outlen, int parse_header);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("char *")]
|
||||
public static extern sbyte* stbi_zlib_decode_malloc([NativeTypeName("const char *")] sbyte* buffer, int len, int* outlen);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int stbi_zlib_decode_buffer([NativeTypeName("char *")] sbyte* obuffer, int olen, [NativeTypeName("const char *")] sbyte* ibuffer, int ilen);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("char *")]
|
||||
public static extern sbyte* stbi_zlib_decode_noheader_malloc([NativeTypeName("const char *")] sbyte* buffer, int len, int* outlen);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int stbi_zlib_decode_noheader_buffer([NativeTypeName("char *")] sbyte* obuffer, int olen, [NativeTypeName("const char *")] sbyte* ibuffer, int ilen);
|
||||
|
||||
[NativeTypeName("#define STBI_VERSION 1")]
|
||||
public const int STBI_VERSION = 1;
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int stbi_write_png([NativeTypeName("const char *")] sbyte* filename, int w, int h, int comp, [NativeTypeName("const void *")] void* data, int stride_in_bytes);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int stbi_write_bmp([NativeTypeName("const char *")] sbyte* filename, int w, int h, int comp, [NativeTypeName("const void *")] void* data);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int stbi_write_tga([NativeTypeName("const char *")] sbyte* filename, int w, int h, int comp, [NativeTypeName("const void *")] void* data);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int stbi_write_hdr([NativeTypeName("const char *")] sbyte* filename, int w, int h, int comp, [NativeTypeName("const float *")] float* data);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int stbi_write_jpg([NativeTypeName("const char *")] sbyte* filename, int x, int y, int comp, [NativeTypeName("const void *")] void* data, int quality);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int stbi_write_png_to_func([NativeTypeName("stbi_write_func *")] delegate* unmanaged[Cdecl]<void*, void*, int, void> func, void* context, int w, int h, int comp, [NativeTypeName("const void *")] void* data, int stride_in_bytes);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int stbi_write_bmp_to_func([NativeTypeName("stbi_write_func *")] delegate* unmanaged[Cdecl]<void*, void*, int, void> func, void* context, int w, int h, int comp, [NativeTypeName("const void *")] void* data);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int stbi_write_tga_to_func([NativeTypeName("stbi_write_func *")] delegate* unmanaged[Cdecl]<void*, void*, int, void> func, void* context, int w, int h, int comp, [NativeTypeName("const void *")] void* data);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int stbi_write_hdr_to_func([NativeTypeName("stbi_write_func *")] delegate* unmanaged[Cdecl]<void*, void*, int, void> func, void* context, int w, int h, int comp, [NativeTypeName("const float *")] float* data);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int stbi_write_jpg_to_func([NativeTypeName("stbi_write_func *")] delegate* unmanaged[Cdecl]<void*, void*, int, void> func, void* context, int x, int y, int comp, [NativeTypeName("const void *")] void* data, int quality);
|
||||
|
||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void stbi_flip_vertically_on_write(int flip_boolean);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: DisableRuntimeMarshalling]
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ghost.StbI;
|
||||
|
||||
/// <summary>Defines the annotation found in a native declaration.</summary>
|
||||
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)]
|
||||
[Conditional("DEBUG")]
|
||||
internal sealed partial class NativeAnnotationAttribute : Attribute
|
||||
{
|
||||
private readonly string _annotation;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="NativeAnnotationAttribute" /> class.</summary>
|
||||
/// <param name="annotation">The annotation that was used in the native declaration.</param>
|
||||
public NativeAnnotationAttribute(string annotation)
|
||||
{
|
||||
_annotation = annotation;
|
||||
}
|
||||
|
||||
/// <summary>Gets the annotation that was used in the native declaration.</summary>
|
||||
public string Annotation => _annotation;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ghost.StbI;
|
||||
|
||||
/// <summary>Defines the type of a member as it was used in the native signature.</summary>
|
||||
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = false, Inherited = true)]
|
||||
[Conditional("DEBUG")]
|
||||
internal sealed partial class NativeTypeNameAttribute : Attribute
|
||||
{
|
||||
private readonly string _name;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="NativeTypeNameAttribute" /> class.</summary>
|
||||
/// <param name="name">The name of the type that was used in the native signature.</param>
|
||||
public NativeTypeNameAttribute(string name)
|
||||
{
|
||||
_name = name;
|
||||
}
|
||||
|
||||
/// <summary>Gets the name of the type that was used in the native signature.</summary>
|
||||
public string Name => _name;
|
||||
}
|
||||
13
src/ThridParty/Ghost.StbI/Generated/stbi_io_callbacks.cs
Normal file
13
src/ThridParty/Ghost.StbI/Generated/stbi_io_callbacks.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace Ghost.StbI;
|
||||
|
||||
public unsafe partial struct stbi_io_callbacks
|
||||
{
|
||||
[NativeTypeName("int (*)(void *, char *, int)")]
|
||||
public delegate* unmanaged[Cdecl]<void*, sbyte*, int, int> read;
|
||||
|
||||
[NativeTypeName("void (*)(void *, int)")]
|
||||
public delegate* unmanaged[Cdecl]<void*, int, void> skip;
|
||||
|
||||
[NativeTypeName("int (*)(void *)")]
|
||||
public delegate* unmanaged[Cdecl]<void*, int> eof;
|
||||
}
|
||||
24
src/ThridParty/Ghost.StbI/Ghost.StbI.csproj
Normal file
24
src/ThridParty/Ghost.StbI/Ghost.StbI.csproj
Normal file
@@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="runtimes\win-x64\native\stbi.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
648
src/ThridParty/Ghost.StbI/Wrapper/StbI.nativegen.cs
Normal file
648
src/ThridParty/Ghost.StbI/Wrapper/StbI.nativegen.cs
Normal file
@@ -0,0 +1,648 @@
|
||||
// <auto-generated>
|
||||
// This file is generated by Ghost.NativeWrapperGen. Do not edit manually.
|
||||
// </auto-generated>
|
||||
|
||||
namespace Ghost.StbI;
|
||||
|
||||
public unsafe partial struct StbIApi
|
||||
{
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_load_from_memory(byte*, int, int*, int*, int*, int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static byte* LoadFromMemory(ReadOnlySpan<byte> buffer, int* x, int* y, int* channels_in_file, int desired_channels)
|
||||
{
|
||||
fixed (byte* pbuffer = buffer)
|
||||
{
|
||||
return Api.stbi_load_from_memory(
|
||||
(byte*)pbuffer,
|
||||
buffer.Length,
|
||||
x,
|
||||
y,
|
||||
channels_in_file,
|
||||
desired_channels);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_load_from_callbacks(stbi_io_callbacks*, void*, int*, int*, int*, int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static byte* LoadFromCallbacks(stbi_io_callbacks* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels)
|
||||
{
|
||||
return Api.stbi_load_from_callbacks(
|
||||
clbk,
|
||||
user,
|
||||
x,
|
||||
y,
|
||||
channels_in_file,
|
||||
desired_channels);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_load(sbyte*, int*, int*, int*, int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static byte* Load(sbyte* filename, int* x, int* y, int* channels_in_file, int desired_channels)
|
||||
{
|
||||
return Api.stbi_load(
|
||||
filename,
|
||||
x,
|
||||
y,
|
||||
channels_in_file,
|
||||
desired_channels);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_load_gif_from_memory(byte*, int, int**, int*, int*, int*, int*, int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static byte* LoadGifFromMemory(ReadOnlySpan<byte> buffer, int** delays, int* x, int* y, int* z, int* comp, int req_comp)
|
||||
{
|
||||
fixed (byte* pbuffer = buffer)
|
||||
{
|
||||
return Api.stbi_load_gif_from_memory(
|
||||
(byte*)pbuffer,
|
||||
buffer.Length,
|
||||
delays,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
comp,
|
||||
req_comp);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_load_16_from_memory(byte*, int, int*, int*, int*, int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static ushort* Load16FromMemory(ReadOnlySpan<byte> buffer, int* x, int* y, int* channels_in_file, int desired_channels)
|
||||
{
|
||||
fixed (byte* pbuffer = buffer)
|
||||
{
|
||||
return Api.stbi_load_16_from_memory(
|
||||
(byte*)pbuffer,
|
||||
buffer.Length,
|
||||
x,
|
||||
y,
|
||||
channels_in_file,
|
||||
desired_channels);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_load_16_from_callbacks(stbi_io_callbacks*, void*, int*, int*, int*, int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static ushort* Load16FromCallbacks(stbi_io_callbacks* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels)
|
||||
{
|
||||
return Api.stbi_load_16_from_callbacks(
|
||||
clbk,
|
||||
user,
|
||||
x,
|
||||
y,
|
||||
channels_in_file,
|
||||
desired_channels);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_load_16(sbyte*, int*, int*, int*, int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static ushort* Load16(sbyte* filename, int* x, int* y, int* channels_in_file, int desired_channels)
|
||||
{
|
||||
return Api.stbi_load_16(
|
||||
filename,
|
||||
x,
|
||||
y,
|
||||
channels_in_file,
|
||||
desired_channels);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_loadf_from_memory(byte*, int, int*, int*, int*, int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static float* LoadfFromMemory(ReadOnlySpan<byte> buffer, int* x, int* y, int* channels_in_file, int desired_channels)
|
||||
{
|
||||
fixed (byte* pbuffer = buffer)
|
||||
{
|
||||
return Api.stbi_loadf_from_memory(
|
||||
(byte*)pbuffer,
|
||||
buffer.Length,
|
||||
x,
|
||||
y,
|
||||
channels_in_file,
|
||||
desired_channels);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_loadf_from_callbacks(stbi_io_callbacks*, void*, int*, int*, int*, int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static float* LoadfFromCallbacks(stbi_io_callbacks* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels)
|
||||
{
|
||||
return Api.stbi_loadf_from_callbacks(
|
||||
clbk,
|
||||
user,
|
||||
x,
|
||||
y,
|
||||
channels_in_file,
|
||||
desired_channels);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_loadf(sbyte*, int*, int*, int*, int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static float* Loadf(sbyte* filename, int* x, int* y, int* channels_in_file, int desired_channels)
|
||||
{
|
||||
return Api.stbi_loadf(
|
||||
filename,
|
||||
x,
|
||||
y,
|
||||
channels_in_file,
|
||||
desired_channels);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_hdr_to_ldr_gamma(float)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static void HdrToLdrGamma(float gamma)
|
||||
{
|
||||
Api.stbi_hdr_to_ldr_gamma(gamma);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_hdr_to_ldr_scale(float)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static void HdrToLdrScale(float scale)
|
||||
{
|
||||
Api.stbi_hdr_to_ldr_scale(scale);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_ldr_to_hdr_gamma(float)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static void LdrToHdrGamma(float gamma)
|
||||
{
|
||||
Api.stbi_ldr_to_hdr_gamma(gamma);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_ldr_to_hdr_scale(float)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static void LdrToHdrScale(float scale)
|
||||
{
|
||||
Api.stbi_ldr_to_hdr_scale(scale);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_is_hdr_from_callbacks(stbi_io_callbacks*, void*)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static int IsHdrFromCallbacks(stbi_io_callbacks* clbk, void* user)
|
||||
{
|
||||
return Api.stbi_is_hdr_from_callbacks(
|
||||
clbk,
|
||||
user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_is_hdr_from_memory(byte*, int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static int IsHdrFromMemory(ReadOnlySpan<byte> buffer)
|
||||
{
|
||||
fixed (byte* pbuffer = buffer)
|
||||
{
|
||||
return Api.stbi_is_hdr_from_memory(
|
||||
(byte*)pbuffer,
|
||||
buffer.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_is_hdr(sbyte*)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static int IsHdr(sbyte* filename)
|
||||
{
|
||||
return Api.stbi_is_hdr(filename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_failure_reason()" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static sbyte* FailureReason()
|
||||
{
|
||||
return Api.stbi_failure_reason();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_image_free(void*)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static void ImageFree(void* retval_from_stbi_load)
|
||||
{
|
||||
Api.stbi_image_free(retval_from_stbi_load);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_info_from_memory(byte*, int, int*, int*, int*)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static int InfoFromMemory(ReadOnlySpan<byte> buffer, int* x, int* y, int* comp)
|
||||
{
|
||||
fixed (byte* pbuffer = buffer)
|
||||
{
|
||||
return Api.stbi_info_from_memory(
|
||||
(byte*)pbuffer,
|
||||
buffer.Length,
|
||||
x,
|
||||
y,
|
||||
comp);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_info_from_callbacks(stbi_io_callbacks*, void*, int*, int*, int*)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static int InfoFromCallbacks(stbi_io_callbacks* clbk, void* user, int* x, int* y, int* comp)
|
||||
{
|
||||
return Api.stbi_info_from_callbacks(
|
||||
clbk,
|
||||
user,
|
||||
x,
|
||||
y,
|
||||
comp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_is_16_bit_from_memory(byte*, int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static int Is16BitFromMemory(ReadOnlySpan<byte> buffer)
|
||||
{
|
||||
fixed (byte* pbuffer = buffer)
|
||||
{
|
||||
return Api.stbi_is_16_bit_from_memory(
|
||||
(byte*)pbuffer,
|
||||
buffer.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_is_16_bit_from_callbacks(stbi_io_callbacks*, void*)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static int Is16BitFromCallbacks(stbi_io_callbacks* clbk, void* user)
|
||||
{
|
||||
return Api.stbi_is_16_bit_from_callbacks(
|
||||
clbk,
|
||||
user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_info(sbyte*, int*, int*, int*)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static int Info(sbyte* filename, int* x, int* y, int* comp)
|
||||
{
|
||||
return Api.stbi_info(
|
||||
filename,
|
||||
x,
|
||||
y,
|
||||
comp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_is_16_bit(sbyte*)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static int Is16Bit(sbyte* filename)
|
||||
{
|
||||
return Api.stbi_is_16_bit(filename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_set_unpremultiply_on_load(int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static void SetUnpremultiplyOnLoad(int flag_true_if_should_unpremultiply)
|
||||
{
|
||||
Api.stbi_set_unpremultiply_on_load(flag_true_if_should_unpremultiply);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_convert_iphone_png_to_rgb(int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static void ConvertIphonePngToRgb(int flag_true_if_should_convert)
|
||||
{
|
||||
Api.stbi_convert_iphone_png_to_rgb(flag_true_if_should_convert);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_set_flip_vertically_on_load(int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static void SetFlipVerticallyOnLoad(int flag_true_if_should_flip)
|
||||
{
|
||||
Api.stbi_set_flip_vertically_on_load(flag_true_if_should_flip);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_set_unpremultiply_on_load_thread(int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static void SetUnpremultiplyOnLoadThread(int flag_true_if_should_unpremultiply)
|
||||
{
|
||||
Api.stbi_set_unpremultiply_on_load_thread(flag_true_if_should_unpremultiply);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_convert_iphone_png_to_rgb_thread(int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static void ConvertIphonePngToRgbThread(int flag_true_if_should_convert)
|
||||
{
|
||||
Api.stbi_convert_iphone_png_to_rgb_thread(flag_true_if_should_convert);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_set_flip_vertically_on_load_thread(int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static void SetFlipVerticallyOnLoadThread(int flag_true_if_should_flip)
|
||||
{
|
||||
Api.stbi_set_flip_vertically_on_load_thread(flag_true_if_should_flip);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_zlib_decode_malloc_guesssize(sbyte*, int, int, int*)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static sbyte* ZlibDecodeMallocGuesssize(ReadOnlySpan<sbyte> buffer, int initial_size, int* outlen)
|
||||
{
|
||||
fixed (sbyte* pbuffer = buffer)
|
||||
{
|
||||
return Api.stbi_zlib_decode_malloc_guesssize(
|
||||
(sbyte*)pbuffer,
|
||||
buffer.Length,
|
||||
initial_size,
|
||||
outlen);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_zlib_decode_malloc_guesssize_headerflag(sbyte*, int, int, int*, int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static sbyte* ZlibDecodeMallocGuesssizeHeaderflag(ReadOnlySpan<sbyte> buffer, int initial_size, int* outlen, int parse_header)
|
||||
{
|
||||
fixed (sbyte* pbuffer = buffer)
|
||||
{
|
||||
return Api.stbi_zlib_decode_malloc_guesssize_headerflag(
|
||||
(sbyte*)pbuffer,
|
||||
buffer.Length,
|
||||
initial_size,
|
||||
outlen,
|
||||
parse_header);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_zlib_decode_malloc(sbyte*, int, int*)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static sbyte* ZlibDecodeMalloc(ReadOnlySpan<sbyte> buffer, int* outlen)
|
||||
{
|
||||
fixed (sbyte* pbuffer = buffer)
|
||||
{
|
||||
return Api.stbi_zlib_decode_malloc(
|
||||
(sbyte*)pbuffer,
|
||||
buffer.Length,
|
||||
outlen);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_zlib_decode_buffer(sbyte*, int, sbyte*, int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static int ZlibDecodeBuffer(ReadOnlySpan<sbyte> obuffer, ReadOnlySpan<sbyte> ibuffer)
|
||||
{
|
||||
fixed (sbyte* pobuffer = obuffer)
|
||||
{
|
||||
fixed (sbyte* pibuffer = ibuffer)
|
||||
{
|
||||
return Api.stbi_zlib_decode_buffer(
|
||||
(sbyte*)pobuffer,
|
||||
obuffer.Length,
|
||||
(sbyte*)pibuffer,
|
||||
ibuffer.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_zlib_decode_noheader_malloc(sbyte*, int, int*)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static sbyte* ZlibDecodeNoheaderMalloc(ReadOnlySpan<sbyte> buffer, int* outlen)
|
||||
{
|
||||
fixed (sbyte* pbuffer = buffer)
|
||||
{
|
||||
return Api.stbi_zlib_decode_noheader_malloc(
|
||||
(sbyte*)pbuffer,
|
||||
buffer.Length,
|
||||
outlen);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_zlib_decode_noheader_buffer(sbyte*, int, sbyte*, int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static int ZlibDecodeNoheaderBuffer(ReadOnlySpan<sbyte> obuffer, ReadOnlySpan<sbyte> ibuffer)
|
||||
{
|
||||
fixed (sbyte* pobuffer = obuffer)
|
||||
{
|
||||
fixed (sbyte* pibuffer = ibuffer)
|
||||
{
|
||||
return Api.stbi_zlib_decode_noheader_buffer(
|
||||
(sbyte*)pobuffer,
|
||||
obuffer.Length,
|
||||
(sbyte*)pibuffer,
|
||||
ibuffer.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_write_png(sbyte*, int, int, int, void*, int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static int WritePng(sbyte* filename, int w, int h, int comp, void* data, int stride_in_bytes)
|
||||
{
|
||||
return Api.stbi_write_png(
|
||||
filename,
|
||||
w,
|
||||
h,
|
||||
comp,
|
||||
data,
|
||||
stride_in_bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_write_bmp(sbyte*, int, int, int, void*)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static int WriteBmp(sbyte* filename, int w, int h, int comp, void* data)
|
||||
{
|
||||
return Api.stbi_write_bmp(
|
||||
filename,
|
||||
w,
|
||||
h,
|
||||
comp,
|
||||
data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_write_tga(sbyte*, int, int, int, void*)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static int WriteTga(sbyte* filename, int w, int h, int comp, void* data)
|
||||
{
|
||||
return Api.stbi_write_tga(
|
||||
filename,
|
||||
w,
|
||||
h,
|
||||
comp,
|
||||
data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_write_hdr(sbyte*, int, int, int, float*)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static int WriteHdr(sbyte* filename, int w, int h, int comp, float* data)
|
||||
{
|
||||
return Api.stbi_write_hdr(
|
||||
filename,
|
||||
w,
|
||||
h,
|
||||
comp,
|
||||
data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_write_jpg(sbyte*, int, int, int, void*, int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static int WriteJpg(sbyte* filename, int x, int y, int comp, void* data, int quality)
|
||||
{
|
||||
return Api.stbi_write_jpg(
|
||||
filename,
|
||||
x,
|
||||
y,
|
||||
comp,
|
||||
data,
|
||||
quality);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_write_png_to_func(delegate* unmanaged[Cdecl]<void*, void*, int, void>, void*, int, int, int, void*, int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static int WritePngToFunc(delegate* unmanaged[Cdecl]<void*, void*, int, void> func, void* context, int w, int h, int comp, void* data, int stride_in_bytes)
|
||||
{
|
||||
return Api.stbi_write_png_to_func(
|
||||
func,
|
||||
context,
|
||||
w,
|
||||
h,
|
||||
comp,
|
||||
data,
|
||||
stride_in_bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_write_bmp_to_func(delegate* unmanaged[Cdecl]<void*, void*, int, void>, void*, int, int, int, void*)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static int WriteBmpToFunc(delegate* unmanaged[Cdecl]<void*, void*, int, void> func, void* context, int w, int h, int comp, void* data)
|
||||
{
|
||||
return Api.stbi_write_bmp_to_func(
|
||||
func,
|
||||
context,
|
||||
w,
|
||||
h,
|
||||
comp,
|
||||
data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_write_tga_to_func(delegate* unmanaged[Cdecl]<void*, void*, int, void>, void*, int, int, int, void*)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static int WriteTgaToFunc(delegate* unmanaged[Cdecl]<void*, void*, int, void> func, void* context, int w, int h, int comp, void* data)
|
||||
{
|
||||
return Api.stbi_write_tga_to_func(
|
||||
func,
|
||||
context,
|
||||
w,
|
||||
h,
|
||||
comp,
|
||||
data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_write_hdr_to_func(delegate* unmanaged[Cdecl]<void*, void*, int, void>, void*, int, int, int, float*)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static int WriteHdrToFunc(delegate* unmanaged[Cdecl]<void*, void*, int, void> func, void* context, int w, int h, int comp, float* data)
|
||||
{
|
||||
return Api.stbi_write_hdr_to_func(
|
||||
func,
|
||||
context,
|
||||
w,
|
||||
h,
|
||||
comp,
|
||||
data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_write_jpg_to_func(delegate* unmanaged[Cdecl]<void*, void*, int, void>, void*, int, int, int, void*, int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static int WriteJpgToFunc(delegate* unmanaged[Cdecl]<void*, void*, int, void> func, void* context, int x, int y, int comp, void* data, int quality)
|
||||
{
|
||||
return Api.stbi_write_jpg_to_func(
|
||||
func,
|
||||
context,
|
||||
x,
|
||||
y,
|
||||
comp,
|
||||
data,
|
||||
quality);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: <see cref="Api.stbi_flip_vertically_on_write(int)" />
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public static void FlipVerticallyOnWrite(int flip_boolean)
|
||||
{
|
||||
Api.stbi_flip_vertically_on_write(flip_boolean);
|
||||
}
|
||||
}
|
||||
BIN
src/ThridParty/Ghost.StbI/runtimes/win-x64/native/stbi.dll
LFS
Normal file
BIN
src/ThridParty/Ghost.StbI/runtimes/win-x64/native/stbi.dll
LFS
Normal file
Binary file not shown.
@@ -44,6 +44,7 @@ public sealed class DerivesFromConfig
|
||||
public string ParamPrefix { get; init; } = string.Empty;
|
||||
/// <summary>The suffix of the sibling parameter name to consume (e.g. "_len").</summary>
|
||||
public string ParamSuffix { get; init; } = string.Empty;
|
||||
public string RegexName { get; init; } = string.Empty;
|
||||
/// <summary>Expression to pass in place of the consumed parameter. $arg is replaced with the source param name.</summary>
|
||||
public required string Expr { get; init; }
|
||||
}
|
||||
|
||||
@@ -484,10 +484,20 @@ public sealed class WrapperGeneratorEmitter
|
||||
continue;
|
||||
}
|
||||
|
||||
var expectedSiblingName = remap.DerivesFrom.ParamPrefix + param.Name + remap.DerivesFrom.ParamSuffix;
|
||||
|
||||
var isExactName = !string.IsNullOrWhiteSpace(remap.DerivesFrom.RegexName);
|
||||
var expectedSiblingName = isExactName
|
||||
? remap.DerivesFrom.RegexName
|
||||
: remap.DerivesFrom.ParamPrefix + param.Name + remap.DerivesFrom.ParamSuffix;
|
||||
|
||||
for (var j = i + 1; j < func.Parameters.Count; j++)
|
||||
{
|
||||
if (string.Equals(func.Parameters[j].Name, expectedSiblingName, StringComparison.Ordinal))
|
||||
var match = isExactName
|
||||
|
||||
? Regex.IsMatch(func.Parameters[j].Name, expectedSiblingName)
|
||||
: string.Equals(func.Parameters[j].Name, expectedSiblingName, StringComparison.Ordinal);
|
||||
|
||||
if (match)
|
||||
{
|
||||
consumedByDerivesFrom.Add(j);
|
||||
derivedExprs[j] = remap.DerivesFrom.Expr.Replace("$arg", param.Name, StringComparison.Ordinal);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"profiles": {
|
||||
"Ghost.NativeWrapperGen": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "--config \"F:/csharp/GhostEngine/src/Tools/Ghost.NativeWrapperGen/configs/ufbx.json\" --input \"F:/csharp/GhostEngine/src/ThridParty/Ghost.Ufbx/Generated\" --output \"F:/csharp/GhostEngine/src/ThridParty/Ghost.Ufbx/Wrapper\""
|
||||
"commandLineArgs": "--config \"F:/csharp/GhostEngine/src/Tools/Ghost.NativeWrapperGen/configs/stbi.json\" --input \"F:/csharp/GhostEngine/src/ThridParty/Ghost.StbI/Generated\" --output \"F:/csharp/GhostEngine/src/ThridParty/Ghost.StbI/Wrapper\""
|
||||
}
|
||||
}
|
||||
}
|
||||
48
src/Tools/Ghost.NativeWrapperGen/configs/stbi.json
Normal file
48
src/Tools/Ghost.NativeWrapperGen/configs/stbi.json
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"libraryName": "stbi",
|
||||
"nativeNamespace": "Ghost.StbImage",
|
||||
"outputNamespace": "Ghost.StbImage",
|
||||
"nativeTypePrefix": "stbi_",
|
||||
"skipTypes": [
|
||||
"NativeAnnotationAttribute",
|
||||
"NativeTypeNameAttribute"
|
||||
],
|
||||
"skipFunctions": [],
|
||||
|
||||
"remaps": [
|
||||
{
|
||||
"src": "$TYPE*",
|
||||
"dst": "ReadOnlySpan<$TYPE>",
|
||||
"scope": [ "parameter" ],
|
||||
"filter": [ ".*buffer" ],
|
||||
"derivesFrom": {
|
||||
"regexName": ".*len",
|
||||
"expr": "$arg.Length"
|
||||
},
|
||||
"adapter": {
|
||||
"convertBack": {
|
||||
"wrapCall": "fixed ($TYPE* p$arg = $arg) { $CALL }",
|
||||
"passAs": "($TYPE*)p$arg"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
"actions": [
|
||||
{
|
||||
"filter": "EXTERN_API",
|
||||
"targetType": "StbIApi",
|
||||
"apply": {
|
||||
"type": "STATIC_METHOD",
|
||||
"opts": {
|
||||
"name": {
|
||||
"remove": [
|
||||
"PREFIX"
|
||||
],
|
||||
"style": "PascalCase"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
6062
src/ufbx_temp.h
Normal file
6062
src/ufbx_temp.h
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user