Start of material editor

This commit is contained in:
Simon Lübeß
2025-03-03 20:29:35 +01:00
parent 10e8b3c282
commit 6d50208700
12 changed files with 304 additions and 370 deletions
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,169 @@
using GlitchyEngine.Renderer;
using ImGui;
using System;
using GlitchyEngine.Math;
using GlitchyEditor.EditWindows;
using GlitchyEngine.Content;
namespace GlitchyEditor.Assets.Editors;
class MaterialEditor
{
public static void ShowEditor(AssetFile assetFile)
{
let material = assetFile.LoadedAsset as Material;
if (material == null)
return;
ImGui.PropertyTableStartNewProperty("Base Material");
if (material.Parent != null)
ImGui.TextUnformatted(material.Parent.Identifier);
else
ImGui.TextUnformatted("None");
ImGui.PropertyTableStartNewProperty("Shader");
ImGui.TextUnformatted(material.Effect.Identifier);
ImGui.PropertyTableStartNewRow();
if (ImGui.CollapsingHeader("Parameters", .DefaultOpen | .AllowOverlap | .Framed | .SpanFullWidth | .SpanAllColumns))
{
for (let (variableName, bufferVariable) in material.Variables)
{
ImGui.PushID(variableName);
ImGui.PropertyTableStartNewRow();
bool readOnly = bufferVariable.Flags.HasFlag(.Readonly);
ImGui.BeginDisabled(readOnly);
if (DrawLockButton(bufferVariable.Flags.HasFlag(.Locked)) && !readOnly)
{
bufferVariable.[Friend]_flags ^= .Locked;
}
ImGui.SameLine();
StringView displayName = variableName;
ImGui.PropertyTableName(displayName);
// TODO: Somehow pass display name and type from imported shader to here!
switch (bufferVariable.ElementType)
{
case .Float:
// TODO: min and max values specified in shader
// TODO: support drag and enter number (specified in shader)
//for (int r < bufferVariable.Rows)
if (bufferVariable.Rows == 1)
{
switch (bufferVariable.Columns)
{
case 1:
material.GetVariable<float>(bufferVariable.Name, var value);
//float[1] minV = hasMin ? min.Get<float[1]>() : .(float.MinValue);
//float[1] maxV = hasMax ? max.Get<float[1]>() : .(float.MaxValue);
if (ImGui.VectorEditor<1>("", ref *(float[1]*)&value, .(), 0.1f /*, minV, maxV*/))
material.SetVariable(bufferVariable.Name, value);
case 2:
material.GetVariable<float2>(bufferVariable.Name, var value);
if (ImGui.Float2Editor("", ref value, .Zero, 0.1f, 100.0f))
material.SetVariable(bufferVariable.Name, value);
case 3:
material.GetVariable<float3>(bufferVariable.Name, var value);
if (ImGui.Float3Editor("", ref value, .Zero, 0.1f, 100.0f))
material.SetVariable(bufferVariable.Name, value);
case 4:
material.GetVariable<float4>(bufferVariable.Name, var value);
if (ImGui.Float4Editor("", ref value, .Zero, 0.1f, 100.0f))
material.SetVariable(bufferVariable.Name, value);
}
}
default:
ImGui.TextUnformatted(scope $"Element Type {_} not supported");
}
ImGui.EndDisabled();
ImGui.PopID();
}
}
ImGui.PropertyTableStartNewRow();
if (ImGui.CollapsingHeader("Textures", .DefaultOpen | .AllowOverlap | .Framed | .SpanFullWidth | .SpanAllColumns))
{
for (var (textureName, texture) in ref material.Textures)
{
ImGui.PushID(textureName);
ImGui.PropertyTableStartNewRow();
bool readOnly = texture.Flags.HasFlag(.Readonly);
ImGui.BeginDisabled(readOnly);
if (DrawLockButton(texture.Flags.HasFlag(.Locked)) && !readOnly)
{
texture.Flags ^= .Locked;
}
ImGui.SameLine();
ImGui.PropertyTableName(textureName);
if (ComponentEditWindow.ShowAssetDropTarget<Texture>(ref texture.TextureHandle))
{
}
ImGui.EndDisabled();
ImGui.PopID();
}
}
}
private static bool DrawLockButton(bool isLocked)
{
ImGui.PushStyleVar(.ItemInnerSpacing, ImGui.Vec2(0, 0));
ImGui.PushStyleColor(.Button, ImGui.Vec4(0, 0, 0, 0));
let colors = ImGui.GetStyle().Colors;
ImGui.Vec4 hoveredColor = colors[(int)ImGui.Col.ButtonHovered];
hoveredColor.w = 0.5f;
ImGui.Vec4 activeColor = colors[(int)ImGui.Col.ButtonActive];
activeColor.w = 0.5f;
ImGui.PushStyleColor(.ButtonHovered, hoveredColor);
ImGui.PushStyleColor(.ButtonActive, activeColor);
float padding = 2.0f;
float size = ImGui.GetTextLineHeight() - 2 * padding;
ImGui.PushID(0);
bool result = ImGui.ImageButton("", isLocked ? EditorIcons.Instance.Icon_Locked : EditorIcons.Instance.Icon_Unlocked, .(size, size), .Zero, .Ones);
ImGui.PopID();
if (isLocked)
ImGui.AttachTooltip("Click to unlock. The property is locked and cannot be changed by children.");
else
ImGui.AttachTooltip("Click to lock. The property is unlocked and can be changed by children.");
ImGui.PopStyleColor(3);
ImGui.PopStyleVar(1);
return result;
}
}
@@ -255,312 +255,3 @@ class MaterialAssetPropertiesEditor : AssetPropertiesEditor
return new Self(assetFile);
}
}
[BonTarget, BonPolyRegister]
class MaterialAssetLoaderConfig : AssetLoaderConfig
{
}
[BonTarget]
public enum VariableValue
{
case bool(bool Value);
case bool2(bool2 Value);
case bool3(bool3 Value);
case bool4(bool4 Value);
case int(int Value);
case int2(int2 Value);
case int3(int3 Value);
case int4(int4 Value);
case uint(uint Value);
case uint2(uint2 Value);
case uint3(uint3 Value);
case uint4(uint4 Value);
case Float(float Value);
case Float2(float2 Value);
case Float3(float3 Value);
case Float4(float4 Value);
case ColorRGB(ColorRGB Value);
case ColorRGBA(ColorRGBA Value);
case None;
/*static this()
{
gBonEnv.typeHandlers.Add(typeof(Self),
((.)new => VariableValueSerialize, (.)new => VariableValueDeserialize));
}
static void VariableValueSerialize(BonWriter writer, ValueView value, BonEnvironment env)
{
Log.EngineLogger.Assert(value.type == typeof(Self));
let variableValue = value.Get<Self>();
writer.Type(variableValue)
using (writer.ObjectBlock())
{
Serialize.Value(writer, nameof(MaterialFile.Effect), materialFile.Effect, env);
Serialize.Value(writer, nameof(MaterialFile.Textures), materialFile.Textures, env);
}
}
static Result<void> VariableValueDeserialize(BonReader reader, ValueView val, BonEnvironment env)
{
return .Ok;
}*/
}
[BonTarget]
class MaterialFile
{
public String Effect ~ delete _;
//public Dictionary<String, String> Textures ~ DeleteDictionaryAndKeysAndValues!(_);
public Dictionary<String, AssetHandle<Texture>> Textures ~ DeleteDictionaryAndKeys!(_);
public Dictionary<String, VariableValue> Variables ~
{
if (_ != null)
{
for (var entry in _)
{
delete entry.key;
//delete entry.value;
/*if (entry.value.HasValue)
entry.value->Dispose();*/
}
delete _;
}
};
/*static this()
{
gBonEnv.typeHandlers.Add(typeof(Self),
((.)new => MaterialSerialize, (.)new => MaterialDeserialize));
}
static void MaterialSerialize(BonWriter writer, ValueView value, BonEnvironment env)
{
Log.EngineLogger.Assert(value.type == typeof(Self));
let materialFile = value.Get<Self>();
using (writer.ObjectBlock())
{
Serialize.Value(writer, nameof(MaterialFile.Effect), materialFile.Effect, env);
Serialize.Value(writer, nameof(MaterialFile.Textures), materialFile.Textures, env);
}
}
private static void SerializeVariablesDictionary(BonWriter writer, MaterialFile materialFile, BonEnvironment env)
{
using (writer.ArrayBlock())
{
for (let (name, value) in materialFile.Variables)
{
let keyVal = ValueView(typeof(String), name);
Serialize.Value(writer, keyVal, env);
writer.Pair();
ValueView valueVal;// = ValueView(, entriesPtr + (currentIndex * entryStride) + entryValueOffset);
switch(value.GetType())
{
case typeof(ColorRGBA):
writer.Identifier("ColorRGBA");
default:
}
Serialize.Value(writer, valueVal, env);
}
}
}
static Result<void> MaterialDeserialize(BonReader reader, ValueView val, BonEnvironment env)
{
return .Ok;
}*/
}
class MaterialAssetLoader : IAssetLoader, IAssetSaver //, IReloadingAssetLoader
{
private static readonly List<StringView> _fileExtensions = new .(){".mat"} ~ delete _;
public static List<StringView> FileExtensions => _fileExtensions;
public AssetLoaderConfig GetDefaultConfig()
{
return new ModelAssetLoaderConfig();
}
public Asset LoadAsset(Stream file, AssetLoaderConfig config, StringView assetIdentifier, StringView? subAsset, IContentManager contentManager)
{
StreamReader reader = scope .(file);
String text = scope .();
reader.ReadToEnd(text);
MaterialFile materialFile = scope .();
var result = Bon.Deserialize<MaterialFile>(ref materialFile, text);
if (result case .Err)
{
Log.EngineLogger.Error("Failed to load material.");
Debug.SafeBreak();
return null;
}
Effect fx = Content.GetAsset<Effect>(contentManager.LoadAsset(materialFile.Effect, true), contentManager);
Material material = new Material(fx);
for (let (slotName, textureHandle) in materialFile.Textures)
{
AssetHandle<Texture> texture = contentManager.LoadAsset(textureHandle);
if (texture.IsInvalid)
{
Log.EngineLogger.Error($"Failed to load texture \"{textureHandle}\".");
}
material.SetTexture(slotName, texture);
}
for (let (slotName, variableValue) in materialFile.Variables)
{
switch (variableValue)
{
case .ColorRGBA(let value):
material.SetVariable(slotName, value);
case .ColorRGB(let value):
material.SetVariable(slotName, value);
case .Float(let value):
material.SetVariable(slotName, value);
case .Float2(let value):
material.SetVariable(slotName, value);
case .Float3(let value):
material.SetVariable(slotName, value);
case .Float4(let value):
material.SetVariable(slotName, value);
case .None:
default:
Log.EngineLogger.Error($"Unknown variable type of variable {slotName}: {variableValue}");
}
}
return material;
}
public Result<void> EditorSaveAsset(Stream file, Asset asset, AssetLoaderConfig config, StringView assetIdentifier, StringView? subAsset, IContentManager contentManager)
{
Material material = asset as Material;
if (material == null)
{
Log.EngineLogger.Error("Asset must be a Material!");
return .Err;
}
MaterialFile materialFile = scope .();
materialFile.Effect = new String(material.Effect?.Identifier ?? "");
materialFile.Textures = new .();
materialFile.Variables = new .();
// TODO: Fix
/*for (let (slotName, texture) in material.[Friend]_textures)
{
materialFile.Textures.Add(new String(slotName), texture.Handle);
}*/
Effect effect = material.Effect;
if (effect != null)
{
for (let (name, arguments) in effect.[Friend]_variableDescriptions)
{
VariableValue variableValue = .None;
let variable = effect.Variables[name];
bool hasPreviewType = MaterialAssetPropertiesEditor.TryGetValue(arguments, "Type", var previewType);
if (hasPreviewType && previewType.Get<String>() == "Color")
{
Log.EngineLogger.AssertDebug(variable.ElementType == .Float && variable.Rows == 1);
if (variable.Columns == 3)
{
material.GetVariable<ColorRGB>(variable.Name, var value);
value = ColorRGB.LinearToSRGB((ColorRGB)value);
//variantValue = new box value;
variableValue = .ColorRGB(value);
}
else if (variable.Columns == 4)
{
material.GetVariable<ColorRGBA>(variable.Name, var value);
value = ColorRGBA.LinearToSRGB((ColorRGBA)value);
variableValue = .ColorRGBA(value);
}
}
else if (variable.ElementType == .Float && variable.Rows == 1)
{
switch (variable.Columns)
{
case 1:
material.GetVariable<float>(variable.Name, let value);
variableValue = .Float(value);
case 2:
material.GetVariable<float2>(variable.Name, let value);
variableValue = .Float2(value);
case 3:
material.GetVariable<float3>(variable.Name, let value);
variableValue = .Float3(value);
case 4:
material.GetVariable<float4>(variable.Name, let value);
variableValue = .Float4(value);
}
}
materialFile.Variables.Add(new String(name), variableValue);
}
}
String text = scope .();
gBonEnv.serializeFlags |= .IncludeDefault | .Verbose;
Bon.Serialize<MaterialFile>(materialFile, text);
StreamWriter writer = scope .(file, .UTF8, 1024);
writer.Write(text);
return .Ok;
}
Material _placeholder;
Material _error;
public Asset GetPlaceholderAsset(Type assetType)
{
return default;
}
public Asset GetErrorAsset(Type assetType)
{
return default;
}
}
@@ -504,7 +504,7 @@ namespace GlitchyEditor.EditWindows
ShowAssetDropTarget<Material>(ref spriteRendererComponent.Material);
}
private static bool ShowAssetDropTarget(ref AssetHandle target)
public static bool ShowAssetDropTarget(ref AssetHandle target)
{
bool changed = false;
@@ -539,8 +539,10 @@ namespace GlitchyEditor.EditWindows
return changed;
}
private static void ShowAssetDropTarget<T>(ref AssetHandle<T> target) where T : Asset
public static bool ShowAssetDropTarget<T>(ref AssetHandle<T> target) where T : Asset
{
bool changed = false;
T currentAsset = target.Get();
StringView identifier = (target.IsValid ? "<Missing Asset>" : "None");
@@ -565,10 +567,13 @@ namespace GlitchyEditor.EditWindows
// TODO: Somehow validate the type, please!
target = (AssetHandle<T>)Content.LoadAsset(path);
changed = true;
}
ImGui.EndDragDropTarget();
}
return changed;
}
private static void ShowCircleRendererComponentEditor(Entity entity, CircleRendererComponent* circleRendererComponent)
@@ -637,7 +637,10 @@ namespace GlitchyEditor.EditWindows
if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(.Left))
{
OpenEntry(entry);
if (ImGui.IsKeyDown(.LeftCtrl))
OpenPropertiesWindow(entry);
else
OpenEntry(entry);
}
if (_assetToRename == entry->Path)
@@ -870,12 +873,17 @@ namespace GlitchyEditor.EditWindows
if (ImGui.MenuItem("Properties..."))
{
AssetHandle? assetHandle = fileOrFolder->AssetFile?.AssetConfig?.AssetHandle;
OpenPropertiesWindow(fileOrFolder);
}
}
if (assetHandle != null)
{
new PropertiesWindow(_editor, .Asset(assetHandle.Value));
}
private void OpenPropertiesWindow(TreeNode<AssetNode> fileOrFolder)
{
AssetHandle? assetHandle = fileOrFolder->AssetFile?.AssetConfig?.AssetHandle;
if (assetHandle != null)
{
new PropertiesWindow(_editor, .Asset(assetHandle.Value));
}
}
@@ -8,6 +8,8 @@ using GlitchyEditor.Assets;
using GlitchyEngine.Core;
using System.Collections;
using GlitchyEngine.World;
using GlitchyEngine.Renderer;
using GlitchyEditor.Assets.Editors;
namespace GlitchyEditor.EditWindows;
@@ -140,25 +142,42 @@ class InspectorWindow : EditorWindow
AssetFile assetFile = assetNode->Value.AssetFile;
// TODO: assetFile.loadedAsset has the asset
// TODO: we need to manually load it
// TODO: if available, show editor for asset
// TODO: if asset changed, show save button?
if (ImGui.BeginPropertyTable("asset_properties", ImGui.GetID("asset_properties")))
{
assetFile.AssetConfig?.ImporterConfig?.ShowEditor(assetFile);
assetFile.AssetConfig?.ProcessorConfig?.ShowEditor(assetFile);
assetFile.AssetConfig?.ExporterConfig?.ShowEditor(assetFile);
if (assetFile.LoadedAsset != null)
{
// TODO: Where do we get the asset editor from?
if (let mat = assetFile.LoadedAsset as Material)
{
MaterialEditor.ShowEditor(assetFile);
//ImGui.TextUnformatted(assetFile.LoadedAsset.Identifier);
}
}
ImGui.EndTable();
ImGui.Separator();
}
bool hasChanges = (assetFile.AssetConfig?.ImporterConfig?.Changed ?? false) || (assetFile.AssetConfig?.ProcessorConfig?.Changed ?? false) || (assetFile.AssetConfig?.ExporterConfig?.Changed ?? false);
bool hasChanges = (assetFile.AssetConfig?.ImporterConfig?.Changed == true) ||
(assetFile.AssetConfig?.ProcessorConfig?.Changed == true) ||
(assetFile.AssetConfig?.ExporterConfig?.Changed == true);
if (!hasChanges)
ImGui.BeginDisabled();
if (ImGui.Button("Apply"))
{
assetFile.SaveAssetConfig();
assetFile.SaveAssetConfigIfChanged();
}
if (!hasChanges)
+4
View File
@@ -34,6 +34,8 @@ namespace GlitchyEditor
public SubTexture2D Icon_Save ~ _.ReleaseRef();
public SubTexture2D Icon_ContextMenu ~ _.ReleaseRef();
public SubTexture2D File_Hlsl ~ _.ReleaseRef();
public SubTexture2D Icon_Locked ~ _.ReleaseRef();
public SubTexture2D Icon_Unlocked ~ _.ReleaseRef();
public static EditorIcons Instance => _editorIcons;
@@ -73,6 +75,8 @@ namespace GlitchyEditor
Icon_Save = GetNextGridTexture(ref pen, iconSize);
Icon_ContextMenu = GetNextGridTexture(ref pen, iconSize);
File_Hlsl = GetNextGridTexture(ref pen, iconSize);
Icon_Locked = GetNextGridTexture(ref pen, iconSize);
Icon_Unlocked = GetNextGridTexture(ref pen, iconSize);
}
public ~this()
+5
View File
@@ -54,6 +54,11 @@ extension ImGui
{
PropertyTableStartNewRow();
PropertyTableName(propertyName);
}
public static void PropertyTableName(StringView propertyName)
{
bool isFirstTableRow = ImGui.TableGetRowIndex() == 0;
if (isFirstTableRow)
+51 -26
View File
@@ -176,7 +176,7 @@ namespace ImGui
(.(55, 55, 230), .(55, 55, 150), .(90, 90, 230)),
(.(230, 25, 45), .(230, 25, 45), .(230, 25, 45)));
public static bool EditVector<NumComponents>(StringView label, ref float[NumComponents] value, float[NumComponents] resetValues = .(), float dragSpeed = 0.1f, float columnWidth = 100f, float[NumComponents] minValue = .(), float[NumComponents] maxValue = .(), bool[NumComponents] componentEnabled = .()) where NumComponents : const int32
public static bool EditVector<NumComponents>(StringView label, ref float[NumComponents] value, float[NumComponents] resetValues = .(), float dragSpeed = 0.1f, float columnWidth = 100f, float[NumComponents] minValue = .(), float[NumComponents] maxValue = .(), bool[NumComponents] componentEnabled = .(true, )) where NumComponents : const int32
{
const String[?] componentNames = .("X", "Y", "Z", "W");
const String[?] componentIds = .("##X", "##Y", "##Z", "##W");
@@ -186,24 +186,33 @@ namespace ImGui
bool changed = false;
bool deactivated = false;
PushID(label);
defer PopID();
if (!label.IsEmpty)
{
PushID(label);
defer:: PopID();
int currentId = ImGui.GetID("");
Columns(2);
defer:: Columns(1);
SetColumnWidth(0, columnWidth);
Columns(2);
defer Columns(1);
SetColumnWidth(0, columnWidth);
//int currentId = ImGui.GetID("");
TextUnformatted(label);
TextUnformatted(label);
NextColumn();
PushMultiItemsWidths(NumComponents, CalcItemWidth());
NextColumn();
}
float lineHeight = GetFont().FontSize + GetStyle().FramePadding.y * 2.0f;
ImGui.Vec2 buttonSize = .(lineHeight + 3.0f, lineHeight);
float itemWidth = CalcItemWidth();
float itemWidthNoButton = itemWidth - buttonSize.y * NumComponents;
bool hideButtons = (itemWidthNoButton / NumComponents) < ImGui.CalcTextSize("0.000").x;
PushMultiItemsWidths(NumComponents, hideButtons ? itemWidth : itemWidthNoButton);
PushStyleVar(.ItemSpacing, Vec2.Zero);
for (int i < NumComponents)
@@ -219,13 +228,16 @@ namespace ImGui
ImGui.BeginDisabled(!componentEnabled[i]);
if (Button(componentNames[i], buttonSize))
if (!hideButtons)
{
value[i] = resetValues[i];
changed = true;
}
if (Button(componentNames[i], buttonSize))
{
value[i] = resetValues[i];
changed = true;
}
SameLine();
SameLine();
}
if (DragFloat(componentIds[i], &value[i], dragSpeed, minValue[i], maxValue[i]))
{
@@ -278,7 +290,7 @@ namespace ImGui
return VectorEditor<4>(label, ref *(float[4]*)&value, (float[4])resetValues, dragSpeed, (float[4])minValue, (float[4])maxValue, (bool[4])componentEnabled, format);
}
public static bool VectorEditor<NumComponents>(StringView label, ref float[NumComponents] value, float[NumComponents] resetValues = .(), float dragSpeed = 0.1f, float[NumComponents] minValue = .(), float[NumComponents] maxValue = .(), bool[NumComponents] componentEnabled = .(), StringView[NumComponents] numberFormat = .()) where NumComponents : const int32
public static bool VectorEditor<NumComponents>(StringView label, ref float[NumComponents] value, float[NumComponents] resetValues = .(), float dragSpeed = 0.1f, float[NumComponents] minValue = .(), float[NumComponents] maxValue = .(), bool[NumComponents] componentEnabled = .(true, ), StringView[NumComponents] numberFormat = .()) where NumComponents : const int32
{
const String[?] componentNames = .("X", "Y", "Z", "W");
const String[?] componentIds = .("##X", "##Y", "##Z", "##W");
@@ -297,7 +309,16 @@ namespace ImGui
float lineHeight = GetFont().FontSize + GetStyle().FramePadding.y * 2.0f;
ImGui.Vec2 buttonSize = .(lineHeight + 3.0f, lineHeight);
float dragFloatWidth = componentWidth - buttonSize.x - GetStyle().FramePadding.x;
float dragFloatWidth = componentWidth - buttonSize.x;
bool showButtons = dragFloatWidth > CalcTextSize("0.000").x;
if (!showButtons)
{
dragFloatWidth = componentWidth;
}
dragFloatWidth -= GetStyle().FramePadding.x * 2;
componentLoop: for (int i < NumComponents)
{
@@ -314,16 +335,19 @@ namespace ImGui
//PushItemWidth(buttonSize.x);
if (Button(componentNames[i], buttonSize))
if (showButtons)
{
value[i] = resetValues[i];
changed = true;
if (Button(componentNames[i], buttonSize))
{
value[i] = resetValues[i];
changed = true;
}
PushStyleVar(.ItemSpacing, Vec2.Zero);
SameLine();
}
PushStyleVar(.ItemSpacing, Vec2.Zero);
SameLine();
StringView format = "0.#####";
if (!numberFormat[i].IsWhiteSpace)
@@ -356,7 +380,8 @@ namespace ImGui
PopItemWidth();
PopStyleVar();
if (showButtons)
PopStyleVar();
EndDisabled();
+10 -2
View File
@@ -27,6 +27,9 @@ public class Material : Asset
private BufferCollection _bufferCollection ~ _?.ReleaseRef();
public Dictionary<StringView, BufferVariable> Variables => _variables;
public TextureCollection Textures => _textureCollection;
public Effect Effect
{
get => _effect;
@@ -313,7 +316,7 @@ public class Material : Asset
}
}
public class TextureCollection
public class TextureCollection : IEnumerable<(String key, TextureEntry value)>
{
public enum TextureFlags
{
@@ -328,7 +331,7 @@ public class TextureCollection
Readonly = 0x8
}
private struct TextureEntry
public struct TextureEntry
{
public AssetHandle<Texture> TextureHandle;
public int32? groupTarget;
@@ -465,4 +468,9 @@ public class TextureCollection
}
protected extern Result<void> SetTexturePlatform();
public Dictionary<String, TextureEntry>.Enumerator GetEnumerator()
{
return _entries.GetEnumerator();
}
}
@@ -150,9 +150,19 @@ public class DeserializationObject
[StructLayout(LayoutKind.Explicit)]
private struct DataHelper
{
[StructLayout(LayoutKind.Sequential)]
public unsafe struct StringView
{
public byte* Utf8Ptr;
public long Length;
}
[FieldOffset(0)]
public EngineObjectReferenceHelper EngineObjectReference;
[FieldOffset(0)]
public StringView String;
[FieldOffset(0)]
public ulong UInt;
@@ -172,35 +182,25 @@ public class DeserializationObject
{
string completeFieldName = $"{_structScopeName}{fieldName}";
// Decimal is the larges primitive we store so we use a decimal as stack allocated memory (because stackalloc doesn't seem to work :(
//decimal backingFieldOnStack = 0.0m;
//byte* rawData = (byte*)&backingFieldOnStack;
//byte* rawData = stackalloc byte[sizeof(DataHelper)];
//ref DataHelper dataHelper = ref Unsafe.AsRef<DataHelper>(rawData);
DataHelper dataHelper = new();
byte* rawData = (byte*)Unsafe.AsPointer(ref dataHelper);
// //byte* rawData = stackalloc byte[sizeof(DataHelper)];
// //ref DataHelper dataHelper = ref Unsafe.AsRef<DataHelper>(rawData);
ScriptGlue.Serialization_DeserializeField(_internalContext, expectedType, completeFieldName, rawData, out SerializationType actualType);
string? GetString()
{
// rawData contains a Pointer and a string length!
byte* utf8Ptr = *(byte**)rawData;
if (utf8Ptr == null)
if (dataHelper.String.Utf8Ptr == null)
return null;
ulong length = *(ulong*)(rawData + 8);
if (length == 0)
if (dataHelper.String.Length == 0)
return string.Empty;
return Encoding.UTF8.GetString(utf8Ptr, (int)length);
if (dataHelper.String.Length is < 0 or > int.MaxValue)
{
throw new InvalidOperationException($"String length is invalid: {dataHelper.String.Length}");
}
return Encoding.UTF8.GetString(dataHelper.String.Utf8Ptr, (int)dataHelper.String.Utf8Ptr);
}
object? value = actualType switch