mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
Start of material editor
This commit is contained in:
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);
|
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);
|
ShowAssetDropTarget<Material>(ref spriteRendererComponent.Material);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool ShowAssetDropTarget(ref AssetHandle target)
|
public static bool ShowAssetDropTarget(ref AssetHandle target)
|
||||||
{
|
{
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
|
||||||
@@ -539,8 +539,10 @@ namespace GlitchyEditor.EditWindows
|
|||||||
return changed;
|
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();
|
T currentAsset = target.Get();
|
||||||
|
|
||||||
StringView identifier = (target.IsValid ? "<Missing Asset>" : "None");
|
StringView identifier = (target.IsValid ? "<Missing Asset>" : "None");
|
||||||
@@ -565,10 +567,13 @@ namespace GlitchyEditor.EditWindows
|
|||||||
|
|
||||||
// TODO: Somehow validate the type, please!
|
// TODO: Somehow validate the type, please!
|
||||||
target = (AssetHandle<T>)Content.LoadAsset(path);
|
target = (AssetHandle<T>)Content.LoadAsset(path);
|
||||||
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndDragDropTarget();
|
ImGui.EndDragDropTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ShowCircleRendererComponentEditor(Entity entity, CircleRendererComponent* circleRendererComponent)
|
private static void ShowCircleRendererComponentEditor(Entity entity, CircleRendererComponent* circleRendererComponent)
|
||||||
|
|||||||
@@ -637,7 +637,10 @@ namespace GlitchyEditor.EditWindows
|
|||||||
|
|
||||||
if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(.Left))
|
if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(.Left))
|
||||||
{
|
{
|
||||||
OpenEntry(entry);
|
if (ImGui.IsKeyDown(.LeftCtrl))
|
||||||
|
OpenPropertiesWindow(entry);
|
||||||
|
else
|
||||||
|
OpenEntry(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_assetToRename == entry->Path)
|
if (_assetToRename == entry->Path)
|
||||||
@@ -870,12 +873,17 @@ namespace GlitchyEditor.EditWindows
|
|||||||
|
|
||||||
if (ImGui.MenuItem("Properties..."))
|
if (ImGui.MenuItem("Properties..."))
|
||||||
{
|
{
|
||||||
AssetHandle? assetHandle = fileOrFolder->AssetFile?.AssetConfig?.AssetHandle;
|
OpenPropertiesWindow(fileOrFolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (assetHandle != null)
|
private void OpenPropertiesWindow(TreeNode<AssetNode> fileOrFolder)
|
||||||
{
|
{
|
||||||
new PropertiesWindow(_editor, .Asset(assetHandle.Value));
|
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 GlitchyEngine.Core;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using GlitchyEngine.World;
|
using GlitchyEngine.World;
|
||||||
|
using GlitchyEngine.Renderer;
|
||||||
|
using GlitchyEditor.Assets.Editors;
|
||||||
|
|
||||||
namespace GlitchyEditor.EditWindows;
|
namespace GlitchyEditor.EditWindows;
|
||||||
|
|
||||||
@@ -140,25 +142,42 @@ class InspectorWindow : EditorWindow
|
|||||||
|
|
||||||
AssetFile assetFile = assetNode->Value.AssetFile;
|
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")))
|
if (ImGui.BeginPropertyTable("asset_properties", ImGui.GetID("asset_properties")))
|
||||||
{
|
{
|
||||||
assetFile.AssetConfig?.ImporterConfig?.ShowEditor(assetFile);
|
assetFile.AssetConfig?.ImporterConfig?.ShowEditor(assetFile);
|
||||||
assetFile.AssetConfig?.ProcessorConfig?.ShowEditor(assetFile);
|
assetFile.AssetConfig?.ProcessorConfig?.ShowEditor(assetFile);
|
||||||
assetFile.AssetConfig?.ExporterConfig?.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.EndTable();
|
||||||
|
|
||||||
ImGui.Separator();
|
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)
|
if (!hasChanges)
|
||||||
ImGui.BeginDisabled();
|
ImGui.BeginDisabled();
|
||||||
|
|
||||||
if (ImGui.Button("Apply"))
|
if (ImGui.Button("Apply"))
|
||||||
{
|
{
|
||||||
assetFile.SaveAssetConfig();
|
assetFile.SaveAssetConfigIfChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasChanges)
|
if (!hasChanges)
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ namespace GlitchyEditor
|
|||||||
public SubTexture2D Icon_Save ~ _.ReleaseRef();
|
public SubTexture2D Icon_Save ~ _.ReleaseRef();
|
||||||
public SubTexture2D Icon_ContextMenu ~ _.ReleaseRef();
|
public SubTexture2D Icon_ContextMenu ~ _.ReleaseRef();
|
||||||
public SubTexture2D File_Hlsl ~ _.ReleaseRef();
|
public SubTexture2D File_Hlsl ~ _.ReleaseRef();
|
||||||
|
public SubTexture2D Icon_Locked ~ _.ReleaseRef();
|
||||||
|
public SubTexture2D Icon_Unlocked ~ _.ReleaseRef();
|
||||||
|
|
||||||
public static EditorIcons Instance => _editorIcons;
|
public static EditorIcons Instance => _editorIcons;
|
||||||
|
|
||||||
@@ -73,6 +75,8 @@ namespace GlitchyEditor
|
|||||||
Icon_Save = GetNextGridTexture(ref pen, iconSize);
|
Icon_Save = GetNextGridTexture(ref pen, iconSize);
|
||||||
Icon_ContextMenu = GetNextGridTexture(ref pen, iconSize);
|
Icon_ContextMenu = GetNextGridTexture(ref pen, iconSize);
|
||||||
File_Hlsl = GetNextGridTexture(ref pen, iconSize);
|
File_Hlsl = GetNextGridTexture(ref pen, iconSize);
|
||||||
|
Icon_Locked = GetNextGridTexture(ref pen, iconSize);
|
||||||
|
Icon_Unlocked = GetNextGridTexture(ref pen, iconSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ~this()
|
public ~this()
|
||||||
|
|||||||
@@ -54,6 +54,11 @@ extension ImGui
|
|||||||
{
|
{
|
||||||
PropertyTableStartNewRow();
|
PropertyTableStartNewRow();
|
||||||
|
|
||||||
|
PropertyTableName(propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void PropertyTableName(StringView propertyName)
|
||||||
|
{
|
||||||
bool isFirstTableRow = ImGui.TableGetRowIndex() == 0;
|
bool isFirstTableRow = ImGui.TableGetRowIndex() == 0;
|
||||||
|
|
||||||
if (isFirstTableRow)
|
if (isFirstTableRow)
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ namespace ImGui
|
|||||||
(.(55, 55, 230), .(55, 55, 150), .(90, 90, 230)),
|
(.(55, 55, 230), .(55, 55, 150), .(90, 90, 230)),
|
||||||
(.(230, 25, 45), .(230, 25, 45), .(230, 25, 45)));
|
(.(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[?] componentNames = .("X", "Y", "Z", "W");
|
||||||
const String[?] componentIds = .("##X", "##Y", "##Z", "##W");
|
const String[?] componentIds = .("##X", "##Y", "##Z", "##W");
|
||||||
@@ -186,24 +186,33 @@ namespace ImGui
|
|||||||
bool changed = false;
|
bool changed = false;
|
||||||
bool deactivated = false;
|
bool deactivated = false;
|
||||||
|
|
||||||
PushID(label);
|
if (!label.IsEmpty)
|
||||||
defer PopID();
|
{
|
||||||
|
PushID(label);
|
||||||
|
defer:: PopID();
|
||||||
|
|
||||||
int currentId = ImGui.GetID("");
|
Columns(2);
|
||||||
|
defer:: Columns(1);
|
||||||
|
SetColumnWidth(0, columnWidth);
|
||||||
|
|
||||||
Columns(2);
|
//int currentId = ImGui.GetID("");
|
||||||
defer Columns(1);
|
|
||||||
SetColumnWidth(0, columnWidth);
|
|
||||||
|
|
||||||
TextUnformatted(label);
|
TextUnformatted(label);
|
||||||
|
|
||||||
NextColumn();
|
NextColumn();
|
||||||
|
}
|
||||||
PushMultiItemsWidths(NumComponents, CalcItemWidth());
|
|
||||||
|
|
||||||
float lineHeight = GetFont().FontSize + GetStyle().FramePadding.y * 2.0f;
|
float lineHeight = GetFont().FontSize + GetStyle().FramePadding.y * 2.0f;
|
||||||
ImGui.Vec2 buttonSize = .(lineHeight + 3.0f, lineHeight);
|
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);
|
PushStyleVar(.ItemSpacing, Vec2.Zero);
|
||||||
|
|
||||||
for (int i < NumComponents)
|
for (int i < NumComponents)
|
||||||
@@ -219,13 +228,16 @@ namespace ImGui
|
|||||||
|
|
||||||
ImGui.BeginDisabled(!componentEnabled[i]);
|
ImGui.BeginDisabled(!componentEnabled[i]);
|
||||||
|
|
||||||
if (Button(componentNames[i], buttonSize))
|
if (!hideButtons)
|
||||||
{
|
{
|
||||||
value[i] = resetValues[i];
|
if (Button(componentNames[i], buttonSize))
|
||||||
changed = true;
|
{
|
||||||
}
|
value[i] = resetValues[i];
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
SameLine();
|
SameLine();
|
||||||
|
}
|
||||||
|
|
||||||
if (DragFloat(componentIds[i], &value[i], dragSpeed, minValue[i], maxValue[i]))
|
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);
|
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[?] componentNames = .("X", "Y", "Z", "W");
|
||||||
const String[?] componentIds = .("##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;
|
float lineHeight = GetFont().FontSize + GetStyle().FramePadding.y * 2.0f;
|
||||||
ImGui.Vec2 buttonSize = .(lineHeight + 3.0f, lineHeight);
|
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)
|
componentLoop: for (int i < NumComponents)
|
||||||
{
|
{
|
||||||
@@ -314,16 +335,19 @@ namespace ImGui
|
|||||||
|
|
||||||
//PushItemWidth(buttonSize.x);
|
//PushItemWidth(buttonSize.x);
|
||||||
|
|
||||||
if (Button(componentNames[i], buttonSize))
|
if (showButtons)
|
||||||
{
|
{
|
||||||
value[i] = resetValues[i];
|
if (Button(componentNames[i], buttonSize))
|
||||||
changed = true;
|
{
|
||||||
|
value[i] = resetValues[i];
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
PushStyleVar(.ItemSpacing, Vec2.Zero);
|
||||||
|
|
||||||
|
SameLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
PushStyleVar(.ItemSpacing, Vec2.Zero);
|
|
||||||
|
|
||||||
SameLine();
|
|
||||||
|
|
||||||
StringView format = "0.#####";
|
StringView format = "0.#####";
|
||||||
|
|
||||||
if (!numberFormat[i].IsWhiteSpace)
|
if (!numberFormat[i].IsWhiteSpace)
|
||||||
@@ -356,7 +380,8 @@ namespace ImGui
|
|||||||
|
|
||||||
PopItemWidth();
|
PopItemWidth();
|
||||||
|
|
||||||
PopStyleVar();
|
if (showButtons)
|
||||||
|
PopStyleVar();
|
||||||
|
|
||||||
EndDisabled();
|
EndDisabled();
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ public class Material : Asset
|
|||||||
|
|
||||||
private BufferCollection _bufferCollection ~ _?.ReleaseRef();
|
private BufferCollection _bufferCollection ~ _?.ReleaseRef();
|
||||||
|
|
||||||
|
public Dictionary<StringView, BufferVariable> Variables => _variables;
|
||||||
|
public TextureCollection Textures => _textureCollection;
|
||||||
|
|
||||||
public Effect Effect
|
public Effect Effect
|
||||||
{
|
{
|
||||||
get => _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
|
public enum TextureFlags
|
||||||
{
|
{
|
||||||
@@ -328,7 +331,7 @@ public class TextureCollection
|
|||||||
Readonly = 0x8
|
Readonly = 0x8
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct TextureEntry
|
public struct TextureEntry
|
||||||
{
|
{
|
||||||
public AssetHandle<Texture> TextureHandle;
|
public AssetHandle<Texture> TextureHandle;
|
||||||
public int32? groupTarget;
|
public int32? groupTarget;
|
||||||
@@ -465,4 +468,9 @@ public class TextureCollection
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected extern Result<void> SetTexturePlatform();
|
protected extern Result<void> SetTexturePlatform();
|
||||||
|
|
||||||
|
public Dictionary<String, TextureEntry>.Enumerator GetEnumerator()
|
||||||
|
{
|
||||||
|
return _entries.GetEnumerator();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -150,9 +150,19 @@ public class DeserializationObject
|
|||||||
[StructLayout(LayoutKind.Explicit)]
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
private struct DataHelper
|
private struct DataHelper
|
||||||
{
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public unsafe struct StringView
|
||||||
|
{
|
||||||
|
public byte* Utf8Ptr;
|
||||||
|
public long Length;
|
||||||
|
}
|
||||||
|
|
||||||
[FieldOffset(0)]
|
[FieldOffset(0)]
|
||||||
public EngineObjectReferenceHelper EngineObjectReference;
|
public EngineObjectReferenceHelper EngineObjectReference;
|
||||||
|
|
||||||
|
[FieldOffset(0)]
|
||||||
|
public StringView String;
|
||||||
|
|
||||||
[FieldOffset(0)]
|
[FieldOffset(0)]
|
||||||
public ulong UInt;
|
public ulong UInt;
|
||||||
|
|
||||||
@@ -172,35 +182,25 @@ public class DeserializationObject
|
|||||||
{
|
{
|
||||||
string completeFieldName = $"{_structScopeName}{fieldName}";
|
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();
|
DataHelper dataHelper = new();
|
||||||
byte* rawData = (byte*)Unsafe.AsPointer(ref dataHelper);
|
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);
|
ScriptGlue.Serialization_DeserializeField(_internalContext, expectedType, completeFieldName, rawData, out SerializationType actualType);
|
||||||
|
|
||||||
string? GetString()
|
string? GetString()
|
||||||
{
|
{
|
||||||
// rawData contains a Pointer and a string length!
|
if (dataHelper.String.Utf8Ptr == null)
|
||||||
byte* utf8Ptr = *(byte**)rawData;
|
|
||||||
|
|
||||||
if (utf8Ptr == null)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
ulong length = *(ulong*)(rawData + 8);
|
if (dataHelper.String.Length == 0)
|
||||||
|
|
||||||
if (length == 0)
|
|
||||||
return string.Empty;
|
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
|
object? value = actualType switch
|
||||||
|
|||||||
Reference in New Issue
Block a user