Compare commits
245 Commits
feature/ar
...
220db828a0
| Author | SHA1 | Date | |
|---|---|---|---|
| 220db828a0 | |||
| d2bf2f12a2 | |||
| e7fedfd35a | |||
| e384a2f38c | |||
| 0eaf7cd51d | |||
| 631638f3fb | |||
| e3a02437c3 | |||
| 5903ddda2b | |||
| 1a91811621 | |||
| 4757c0c91a | |||
| 3533d3367f | |||
| 884611181a | |||
| cb4092179f | |||
| c249a389e3 | |||
| ed00f205b0 | |||
| 4f5556ee1b | |||
| abd5ad74d5 | |||
| 13bf1501e4 | |||
| 6615fe794e | |||
| d9bfa43663 | |||
| 817b32b8d9 | |||
| c66fda5332 | |||
| f9a6e9cbbe | |||
| 4ed5572ce7 | |||
| 68fda03aa9 | |||
| 0fc449bc78 | |||
| a5c10cfe5a | |||
| 6c96d4cf50 | |||
| c6bdbe0710 | |||
| effd33b285 | |||
| 92970f85ef | |||
| 2dc97f3149 | |||
| ba9e24c46c | |||
| 6321b36ef5 | |||
| d03eb659fa | |||
| e32a24739d | |||
| eb41f23582 | |||
| 3157596b5d | |||
| a00cb27529 | |||
| 0b6e5b8501 | |||
| 89e6c68f2a | |||
| b28b32f502 | |||
| fa617accc3 | |||
| ff22b89ba3 | |||
| 2e6e705558 | |||
| e6e38f5eea | |||
| d15bd22743 | |||
| 15870ffe89 | |||
| 70b7e56eb7 | |||
| 257838b33e | |||
| 8ff98c56be | |||
| 2c84696994 | |||
| a33a150d06 | |||
| 60ef684d80 | |||
| 90ac5e6d4b | |||
| bd13e7faa0 | |||
| b5d8009bec | |||
| 3aef53cad9 | |||
| 99adf8fc3b | |||
| 1c553a55fa | |||
| e9f822409d | |||
| 0d8bc6f868 | |||
| c8f24edfd8 | |||
| 2946b905c6 | |||
| 666528263b | |||
| c52daf3914 | |||
| 9738971369 | |||
| af56338347 | |||
| 45711e7770 | |||
| 0f0b36a932 | |||
| e6d0529ef1 | |||
| d367cff79f | |||
| 35731d4ebe | |||
| 8d3c5ecb1f | |||
| 1d48784a1c | |||
| e5aa328576 | |||
| 55eb240de6 | |||
| 45d810e01c | |||
| 1ec8496b8b | |||
| 45375ac2ff | |||
| 4188152f49 | |||
| 5521a8cce2 | |||
| baca976c6f | |||
| b87e01f6b3 | |||
| 2fa9976658 | |||
| e92e365a3a | |||
| 09576bb6e1 | |||
| 332a940993 | |||
| acbf315e8f | |||
| 11101f8352 | |||
| bf40eabcac | |||
| ea4d1084e9 | |||
| 47ffc01524 | |||
| 5f0eea49cf | |||
| 51398f29d2 | |||
| 17588439fa | |||
| 668e66937b | |||
| 5845e7e9fb | |||
| de71043be3 | |||
| 3f6de84387 | |||
| 975c359bf4 | |||
| 71abd60a75 | |||
| 777c4ef31d | |||
| 3c9c95ad73 | |||
| 4713bfe7da | |||
| 9a1b8dcab0 | |||
| ea7d3fad26 | |||
| c77592d479 | |||
| 287b3b303f | |||
| 7ac9a66110 | |||
| 0a0359ec06 | |||
| cda3b292b5 | |||
| 65a335fc1a | |||
| c1f7f3e14e | |||
| a409a93a10 | |||
| 5ceb7c11ed | |||
| e80266f2bc | |||
| 04a3b924ab | |||
| 10bc76a654 | |||
| c4c0b5cd87 | |||
| 08e4d3311a | |||
| 299bcf520c | |||
| 304df0a381 | |||
| 8c136709ff | |||
| e83555498a | |||
| 07274b6699 | |||
| 095fcc87a7 | |||
| 419552439d | |||
| 5efd0c8aee | |||
| b3d753fd08 | |||
| e69e071ce2 | |||
| 231756006e | |||
| 98405cb8ec | |||
| 4aeaecfe81 | |||
| c6a71e599b | |||
| b194b57e4e | |||
| 1cd0971b4d | |||
| c2cfd18273 | |||
| 7d759c8797 | |||
| a2c2198715 | |||
| 8d789af888 | |||
| dee33958b9 | |||
| bb0f9be600 | |||
| 49e6bbe8b0 | |||
| ad90bf1d34 | |||
| c0116d5409 | |||
| 8d49dba2f1 | |||
| ad928feea2 | |||
| 17090eaa0d | |||
| 56b84effb6 | |||
| 944687848e | |||
| 038a13bbe0 | |||
| efc9e8862d | |||
| 979f1d64a7 | |||
| 87217337b7 | |||
| 3ea4260405 | |||
| 4052ffb854 | |||
| 8ba976b0ba | |||
| f38ad04c4f | |||
| dd41cafd64 | |||
| d8a7b07624 | |||
| 0a2eb619eb | |||
| 447a4e6904 | |||
| b729ca86f5 | |||
| 7860e5e341 | |||
| 92e3d33361 | |||
| d44ec0be31 | |||
| 2b3bf21a74 | |||
| 37f4795b4f | |||
| 793df1af4f | |||
| a35321df89 | |||
| db0be367ef | |||
| 4a98e44630 | |||
| 9cf03e0b6f | |||
| bc78c8fbee | |||
| fe49e57330 | |||
| 2376fc9414 | |||
| 0a3502b858 | |||
| 22fdae1061 | |||
| 92c503b253 | |||
| f7fb7da496 | |||
| 2ba60c4bae | |||
| f2b68955b1 | |||
| 85a000e5c4 | |||
| 301a6d1c45 | |||
| e831b71a79 | |||
| 9bae3e647e | |||
| 6cadd8edeb | |||
| 3e4084c42a | |||
| cce1cf7256 | |||
| 254b08bc81 | |||
| 912b320d8f | |||
| 8a3b40b4f8 | |||
| 619720feee | |||
| bfe8588d76 | |||
| b8af6e8c3a | |||
| 5e42d699c3 | |||
| 6f802ac12b | |||
| 162b71f309 | |||
| 30090f84ab | |||
| 93c58fa7fb | |||
| 78e3b4ef31 | |||
| db8ca971a8 | |||
| 638417d4f0 | |||
| 426786397c | |||
| 9bbccfc8f8 | |||
| eadd13931f | |||
| 59991f47d5 | |||
| 9fcf06dbe4 | |||
| 6505099667 | |||
| d263f0c7e1 | |||
| 9f05944d81 | |||
| e71851550b | |||
| 8a5795069f | |||
| b505c7c1c0 | |||
| 8d82c0a750 | |||
| 8df0b46960 | |||
| 06a150b899 | |||
| 49f54c6b43 | |||
| fdf831630b | |||
| ba5dc2159e | |||
| 0201f0fc33 | |||
| 364fbf9208 | |||
| e11a9ebb52 | |||
| 4173ff2432 | |||
| 139312d73b | |||
| 92b966fe0d | |||
| 1c155f962c | |||
| ac36bbf8c7 | |||
| 02df8d7732 | |||
| 954e3756aa | |||
| 1fc9df1812 | |||
| 87e315a588 | |||
| d71bdb3fc9 | |||
| 6a041f75ba | |||
| c9be05fc60 | |||
| f988c34b3d | |||
| a89719bfc9 | |||
| b8ce824292 | |||
| aa3d9c749b | |||
| d23e701f0a | |||
| 2881fda112 | |||
| 840cf7dd5a | |||
| 00b4e82ded | |||
| 3118021272 |
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.dll filter=lfs diff=lfs merge=lfs -text
|
||||
9
.gitignore
vendored
@@ -10,6 +10,15 @@
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
AGENTS.md
|
||||
.opencode/
|
||||
.code-review-graph/
|
||||
.github/instructions/
|
||||
|
||||
ref/
|
||||
docfx/
|
||||
NUL
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Ghost.Core.Contracts;
|
||||
|
||||
internal interface IReleasable
|
||||
{
|
||||
void InternalRelease();
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
namespace Ghost.Core.Graphics;
|
||||
|
||||
public enum ZTestOptions
|
||||
{
|
||||
Disabled,
|
||||
Less,
|
||||
LessEqual,
|
||||
Equal,
|
||||
GreaterEqual,
|
||||
Greater,
|
||||
NotEqual,
|
||||
Always
|
||||
}
|
||||
|
||||
public enum ZWriteOptions
|
||||
{
|
||||
Off,
|
||||
On
|
||||
}
|
||||
|
||||
public enum CullOptions
|
||||
{
|
||||
Off,
|
||||
Front,
|
||||
Back
|
||||
}
|
||||
|
||||
public enum BlendOptions
|
||||
{
|
||||
Opaque,
|
||||
Alpha,
|
||||
Additive,
|
||||
Multiply,
|
||||
PremultipliedAlpha
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
namespace Ghost.Core.Graphics;
|
||||
|
||||
public enum KeywordType
|
||||
{
|
||||
Static,
|
||||
Dynamic,
|
||||
}
|
||||
|
||||
public enum ShaderPropertyType
|
||||
{
|
||||
None,
|
||||
Float, Float2, Float3, Float4,
|
||||
Float4x4,
|
||||
Int, Int2, Int3, Int4,
|
||||
UInt, UInt2, UInt3, UInt4,
|
||||
Bool, Bool2, Bool3, Bool4,
|
||||
Texture2D, Texture3D, TextureCube,
|
||||
Texture2DArray, TextureCubeArray,
|
||||
Sampler
|
||||
}
|
||||
|
||||
public struct ShaderEntryPoint
|
||||
{
|
||||
public string entry;
|
||||
public string shader;
|
||||
|
||||
public readonly bool IsCreated => !string.IsNullOrEmpty(entry) && !string.IsNullOrEmpty(shader);
|
||||
}
|
||||
|
||||
public struct KeywordsGroup
|
||||
{
|
||||
public KeywordType type;
|
||||
public List<string>? keywords;
|
||||
}
|
||||
|
||||
public struct PipelineDescriptor
|
||||
{
|
||||
public ZTestOptions zTest;
|
||||
public ZWriteOptions zWrite;
|
||||
public CullOptions cull;
|
||||
public BlendOptions blend;
|
||||
public uint colorMask;
|
||||
|
||||
public static PipelineDescriptor Default = new PipelineDescriptor
|
||||
{
|
||||
zTest = ZTestOptions.LessEqual,
|
||||
zWrite = ZWriteOptions.On,
|
||||
cull = CullOptions.Back,
|
||||
blend = BlendOptions.Opaque,
|
||||
colorMask = 0
|
||||
};
|
||||
}
|
||||
|
||||
public interface IPassDescriptor
|
||||
{
|
||||
public string Identifier
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
|
||||
public struct PropertyDescriptor
|
||||
{
|
||||
public ShaderPropertyType type;
|
||||
public string name;
|
||||
public object? defaultValue;
|
||||
}
|
||||
|
||||
public class FullPassDescriptor : IPassDescriptor
|
||||
{
|
||||
public string uniqueIdentifier = string.Empty;
|
||||
public string name = string.Empty;
|
||||
|
||||
public ShaderEntryPoint taskShader;
|
||||
public ShaderEntryPoint meshShader;
|
||||
public ShaderEntryPoint pixelShader;
|
||||
public List<string>? defines;
|
||||
public List<KeywordsGroup>? keywords;
|
||||
public PipelineDescriptor localPipeline;
|
||||
|
||||
public string Identifier => uniqueIdentifier;
|
||||
public string Name => name;
|
||||
}
|
||||
|
||||
public class FallbackPassDescriptor : IPassDescriptor
|
||||
{
|
||||
public string fallbackPassIdentifier = string.Empty;
|
||||
public string name = string.Empty;
|
||||
|
||||
public string Identifier => fallbackPassIdentifier;
|
||||
public string Name => name;
|
||||
}
|
||||
|
||||
public class ShaderDescriptor
|
||||
{
|
||||
public string name = string.Empty;
|
||||
public string? generatedCodePath;
|
||||
public uint cbufferSize;
|
||||
public List<PropertyDescriptor> globalProperties = new();
|
||||
public List<PropertyDescriptor> properties = new();
|
||||
public List<IPassDescriptor> passes = new();
|
||||
}
|
||||
|
||||
public static class ShaderDescriptorExtensions
|
||||
{
|
||||
public static uint GetSize(this ShaderPropertyType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
ShaderPropertyType.Float => 4,
|
||||
ShaderPropertyType.Float2 => 8,
|
||||
ShaderPropertyType.Float3 => 12,
|
||||
ShaderPropertyType.Float4 => 16,
|
||||
ShaderPropertyType.Float4x4 => 64,
|
||||
ShaderPropertyType.Int => 4,
|
||||
ShaderPropertyType.Int2 => 8,
|
||||
ShaderPropertyType.Int3 => 12,
|
||||
ShaderPropertyType.Int4 => 16,
|
||||
ShaderPropertyType.UInt => 4,
|
||||
ShaderPropertyType.UInt2 => 8,
|
||||
ShaderPropertyType.UInt3 => 12,
|
||||
ShaderPropertyType.UInt4 => 16,
|
||||
ShaderPropertyType.Bool => 4,
|
||||
ShaderPropertyType.Bool2 => 8,
|
||||
ShaderPropertyType.Bool3 => 12,
|
||||
ShaderPropertyType.Bool4 => 16,
|
||||
ShaderPropertyType.Texture2D => 4, // Bindless resource use uint32
|
||||
ShaderPropertyType.Texture3D => 4,
|
||||
ShaderPropertyType.TextureCube => 4,
|
||||
ShaderPropertyType.Texture2DArray => 4,
|
||||
ShaderPropertyType.TextureCubeArray => 4,
|
||||
ShaderPropertyType.Sampler => 4,
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
namespace Ghost.Core;
|
||||
|
||||
public interface IHandleType;
|
||||
public interface IIdentifierType;
|
||||
public interface IKeyType;
|
||||
|
||||
public readonly struct Handle<T> : IEquatable<Handle<T>>
|
||||
{
|
||||
public readonly int id;
|
||||
public readonly int generation;
|
||||
|
||||
public Handle(int id, int generation)
|
||||
{
|
||||
this.id = id;
|
||||
this.generation = generation;
|
||||
}
|
||||
|
||||
public static Handle<T> Invalid => new(-1, -1);
|
||||
|
||||
public readonly bool IsValid => this != Invalid;
|
||||
public readonly bool IsNotValid => this == Invalid;
|
||||
|
||||
public readonly override int GetHashCode()
|
||||
{
|
||||
return id + (generation << 16);
|
||||
}
|
||||
|
||||
public readonly override bool Equals(object? obj)
|
||||
{
|
||||
return obj is Handle<T> id && Equals(id);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Handle<{typeof(T).Name}>({id}, {generation})";
|
||||
}
|
||||
|
||||
public readonly bool Equals(Handle<T> other)
|
||||
{
|
||||
return id == other.id;
|
||||
}
|
||||
|
||||
public readonly int CompareTo(Handle<T> other)
|
||||
{
|
||||
return id.CompareTo(other.id);
|
||||
}
|
||||
|
||||
public static bool operator ==(Handle<T> a, Handle<T> b)
|
||||
{
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool operator !=(Handle<T> a, Handle<T> b)
|
||||
{
|
||||
return !a.Equals(b);
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct Identifier<T> : IEquatable<Identifier<T>>
|
||||
{
|
||||
public readonly int value;
|
||||
|
||||
public Identifier(int value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static Identifier<T> Invalid => new(-1);
|
||||
|
||||
public readonly bool IsValid => this != Invalid;
|
||||
public readonly bool IsNotValid => this == Invalid;
|
||||
|
||||
public readonly override int GetHashCode()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
public readonly override bool Equals(object? obj)
|
||||
{
|
||||
return obj is Identifier<T> id && Equals(id);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Identifier<{typeof(T).Name}>({value})";
|
||||
}
|
||||
|
||||
public readonly bool Equals(Identifier<T> other)
|
||||
{
|
||||
return value == other.value;
|
||||
}
|
||||
|
||||
public readonly int CompareTo(Identifier<T> other)
|
||||
{
|
||||
return value.CompareTo(other.value);
|
||||
}
|
||||
|
||||
public static bool operator ==(Identifier<T> a, Identifier<T> b)
|
||||
{
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool operator !=(Identifier<T> a, Identifier<T> b)
|
||||
{
|
||||
return !a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool operator <(Identifier<T> a, Identifier<T> b)
|
||||
{
|
||||
return a.value < b.value;
|
||||
}
|
||||
|
||||
public static bool operator >(Identifier<T> a, Identifier<T> b)
|
||||
{
|
||||
return a.value > b.value;
|
||||
}
|
||||
|
||||
public static bool operator <=(Identifier<T> a, Identifier<T> b)
|
||||
{
|
||||
return a.value <= b.value;
|
||||
}
|
||||
|
||||
public static bool operator >=(Identifier<T> a, Identifier<T> b)
|
||||
{
|
||||
return a.value >= b.value;
|
||||
}
|
||||
|
||||
public static implicit operator int(Identifier<T> id) => id.value;
|
||||
public static implicit operator Identifier<T>(int value) => new Identifier<T>(value);
|
||||
}
|
||||
|
||||
public readonly struct Key<T>
|
||||
{
|
||||
public readonly ulong value;
|
||||
|
||||
public Key(ulong value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static Key<T> Invalid => new(0);
|
||||
|
||||
public bool IsValid => this != Invalid;
|
||||
|
||||
public readonly override int GetHashCode()
|
||||
{
|
||||
return value.GetHashCode();
|
||||
}
|
||||
|
||||
public readonly override bool Equals(object? obj)
|
||||
{
|
||||
return obj is Key<T> id && Equals(id);
|
||||
}
|
||||
|
||||
public readonly bool Equals(Key<T> other)
|
||||
{
|
||||
return value == other.value;
|
||||
}
|
||||
|
||||
public readonly int CompareTo(Key<T> other)
|
||||
{
|
||||
return value.CompareTo(other.value);
|
||||
}
|
||||
|
||||
public static bool operator ==(Key<T> a, Key<T> b)
|
||||
{
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool operator !=(Key<T> a, Key<T> b)
|
||||
{
|
||||
return !a.Equals(b);
|
||||
}
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ghost.Core;
|
||||
|
||||
public enum LogLevel
|
||||
{
|
||||
Info,
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
|
||||
internal readonly struct LogMessage
|
||||
{
|
||||
public LogLevel Level
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public string Message
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public string? StackTrace
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public DateTime Timestamp
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public LogMessage(LogLevel level, string message, string? stackTrace = null)
|
||||
{
|
||||
Level = level;
|
||||
Message = message;
|
||||
StackTrace = stackTrace;
|
||||
Timestamp = DateTime.Now;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (StackTrace != null)
|
||||
{
|
||||
return $"{Timestamp:HH:mm:ss} [{Level}] {Message}\n{StackTrace}";
|
||||
}
|
||||
|
||||
return $"{Timestamp:HH:mm:ss} [{Level}] {Message}";
|
||||
}
|
||||
}
|
||||
|
||||
internal interface ILogger
|
||||
{
|
||||
public ReadOnlyObservableCollection<LogMessage> Logs
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public void Log(string message, LogLevel level);
|
||||
public void Log(Exception exception);
|
||||
public void Assert(bool condition, string message);
|
||||
public void Clear();
|
||||
}
|
||||
|
||||
// TODO: Add file logging.
|
||||
internal class LoggerImplementation : ILogger
|
||||
{
|
||||
private readonly ObservableCollection<LogMessage> _logs = new();
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
public ReadOnlyObservableCollection<LogMessage> Logs => new(_logs);
|
||||
|
||||
public void Log(string message, LogLevel level)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_logs.Add(new LogMessage(level, message));
|
||||
}
|
||||
}
|
||||
|
||||
public void Log(Exception exception)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_logs.Add(new LogMessage(LogLevel.Error, exception.Message, exception.StackTrace));
|
||||
}
|
||||
}
|
||||
|
||||
public void Assert(bool condition, string message)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Log(message, LogLevel.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_logs.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Logger
|
||||
{
|
||||
private static readonly ILogger s_logger = new LoggerImplementation();
|
||||
internal static ReadOnlyObservableCollection<LogMessage> Logs => s_logger.Logs;
|
||||
|
||||
public static void Log(LogLevel level, object? message)
|
||||
{
|
||||
s_logger.Log(message?.ToString() ?? "null", level);
|
||||
}
|
||||
|
||||
public static void Log(LogLevel level, string message)
|
||||
{
|
||||
s_logger.Log(message, level);
|
||||
}
|
||||
|
||||
public static void LogInfo(object? message)
|
||||
{
|
||||
s_logger.Log(message?.ToString() ?? "null", LogLevel.Info);
|
||||
}
|
||||
|
||||
public static void LogInfo(string message)
|
||||
{
|
||||
s_logger.Log(message, LogLevel.Info);
|
||||
}
|
||||
|
||||
public static void LogWarning(object? message)
|
||||
{
|
||||
s_logger.Log(message?.ToString() ?? "null", LogLevel.Warning);
|
||||
}
|
||||
|
||||
public static void LogWarning(string message)
|
||||
{
|
||||
s_logger.Log(message, LogLevel.Warning);
|
||||
}
|
||||
|
||||
public static void LogError(object? message)
|
||||
{
|
||||
s_logger.Log(message?.ToString() ?? "null", LogLevel.Error);
|
||||
}
|
||||
|
||||
public static void LogError(string message)
|
||||
{
|
||||
s_logger.Log(message, LogLevel.Error);
|
||||
}
|
||||
|
||||
public static void LogError(Exception ex)
|
||||
{
|
||||
s_logger.Log(ex);
|
||||
}
|
||||
|
||||
public static void Assert(bool condition, string message)
|
||||
{
|
||||
s_logger.Assert(condition, message);
|
||||
}
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
s_logger.Clear();
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Core;
|
||||
|
||||
public readonly struct TypeHandle
|
||||
{
|
||||
public readonly IntPtr Value
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
private TypeHandle(IntPtr value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type handle for the specified type.
|
||||
/// </summary>
|
||||
/// <param name="type">The type to get the handle for.</param>
|
||||
/// <returns>The type handle as a nint.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static TypeHandle Get(Type type) => new TypeHandle(type.TypeHandle.Value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type handle for the specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to get the handle for.</typeparam>
|
||||
/// <returns>The type handle as a nint.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static TypeHandle Get<T>() => Get(typeof(T));
|
||||
|
||||
/// <summary>
|
||||
/// Converts a TypeHandle to a Type.
|
||||
/// </summary>
|
||||
/// <param name="handle">The TypeHandle to convert.</param>
|
||||
/// <returns>The corresponding Type.</returns>
|
||||
public Type? ToType()
|
||||
{
|
||||
return Type.GetTypeFromHandle(RuntimeTypeHandle.FromIntPtr(Value));
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
|
||||
public static implicit operator TypeHandle(IntPtr value)
|
||||
{
|
||||
return new TypeHandle(value);
|
||||
}
|
||||
|
||||
public static implicit operator IntPtr(TypeHandle handle)
|
||||
{
|
||||
return handle.Value;
|
||||
}
|
||||
|
||||
public static implicit operator TypeHandle(Type type)
|
||||
{
|
||||
return Get(type);
|
||||
}
|
||||
|
||||
public static implicit operator Type?(TypeHandle handle)
|
||||
{
|
||||
return handle.ToType();
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using Ghost.Core.Contracts;
|
||||
|
||||
namespace Ghost.Core.Utilities;
|
||||
|
||||
internal static class InternalResource
|
||||
{
|
||||
public static void Release<T>(ref T? resource)
|
||||
where T : IReleasable
|
||||
{
|
||||
resource?.InternalRelease();
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Versioning;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Ghost.Core.Utilities;
|
||||
|
||||
[SupportedOSPlatform("windows10.0.19041.0")]
|
||||
internal static unsafe partial class Win32Utility
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public readonly ref struct IID_PPV
|
||||
{
|
||||
public readonly Guid* iid;
|
||||
public readonly void** ppv;
|
||||
|
||||
public IID_PPV(Guid* iid, void** ppv)
|
||||
{
|
||||
this.iid = iid;
|
||||
this.ppv = ppv;
|
||||
}
|
||||
|
||||
public void Deconstruct(out Guid* iid, out void** ppv)
|
||||
{
|
||||
iid = this.iid;
|
||||
ppv = this.ppv;
|
||||
}
|
||||
}
|
||||
|
||||
public static Guid* IID_NULL
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in IID.IID_NULL));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static IID_PPV IID_PPV_ARGS<T>(ComPtr<T>* comPtr)
|
||||
where T : unmanaged, IUnknown.Interface
|
||||
{
|
||||
return new IID_PPV(Windows.__uuidof<T>(), (void**)comPtr);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Attach<T>(ref this UniquePtr<T> uPtr, T* other)
|
||||
where T : unmanaged, IUnknown.Interface
|
||||
{
|
||||
var ptr = uPtr.Get();
|
||||
if (ptr != null)
|
||||
{
|
||||
var refCount = ptr->Release();
|
||||
Debug.Assert((refCount != 0) || (ptr != other));
|
||||
}
|
||||
|
||||
uPtr = new UniquePtr<T>(other);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Dispose<T>(ref this UniquePtr<T> uPtr)
|
||||
where T : unmanaged, IUnknown.Interface
|
||||
{
|
||||
T* ptr = uPtr.Get();
|
||||
if (ptr != null)
|
||||
{
|
||||
uPtr = default;
|
||||
ptr->Release();
|
||||
//MemoryLeakException.ThrowIfRefCountNonZero(ptr->Release());
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Result ToResult(this HRESULT hr, [CallerArgumentExpression(nameof(hr))] string? op = null)
|
||||
{
|
||||
if (hr.SUCCEEDED)
|
||||
{
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
return Result.Failure($"{op} failed with code {hr}");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void** ReleaseAndGetVoidAddressOf<T>(ref this ComPtr<T> comPtr)
|
||||
where T : unmanaged, IUnknown.Interface
|
||||
{
|
||||
return (void**)comPtr.ReleaseAndGetAddressOf();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ComPtr<T> Move<T>(ref this ComPtr<T> comPtr)
|
||||
where T : unmanaged, IUnknown.Interface
|
||||
{
|
||||
var copy = default(ComPtr<T>);
|
||||
comPtr.Swap(ref copy);
|
||||
return copy;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool HasFlag<T>(this uint flags, T flag)
|
||||
where T : Enum
|
||||
{
|
||||
return (flags & Unsafe.As<T, uint>(ref flag)) != 0;
|
||||
}
|
||||
|
||||
extension(MemoryLeakException)
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ThrowIfRefCountNonZero(uint count)
|
||||
{
|
||||
if (count != 0)
|
||||
{
|
||||
throw new MemoryLeakException($"Reference count is not zero: {count}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Ghost.Data.Repository;
|
||||
|
||||
internal class AssetsRepository
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
using Ghost.Core;
|
||||
|
||||
namespace Ghost.Editor.Core.AppState;
|
||||
|
||||
internal partial class AppStateMachine : IDisposable, IAsyncDisposable
|
||||
{
|
||||
private Dictionary<StateKey, Lazy<IAppState>> _states = new();
|
||||
private IAppState? _current;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public void RegisterState(StateKey key, Func<IAppState> stateFactory)
|
||||
{
|
||||
_states[key] = new(stateFactory);
|
||||
}
|
||||
|
||||
public async Task<Result> TransitionToAsync(StateKey stateKey, object? parameter = null)
|
||||
{
|
||||
var previous = _current;
|
||||
if (!_states.TryGetValue(stateKey, out var next))
|
||||
{
|
||||
return Result.Failure($"State '{stateKey}' not found.");
|
||||
}
|
||||
|
||||
Result result;
|
||||
if (previous != null)
|
||||
{
|
||||
result = await previous.OnExitingAsync();
|
||||
if (result.IsFailure)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
result = await next.Value.OnEnteringAsync(parameter);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
if (previous != null)
|
||||
{
|
||||
await previous.OnEnteredAsync(parameter);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (previous != null)
|
||||
{
|
||||
result = await previous.OnExitedAsync();
|
||||
if (result.IsFailure)
|
||||
{
|
||||
await next.Value.OnExitedAsync();
|
||||
await previous.OnEnteredAsync(parameter);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
result = await next.Value.OnEnteredAsync(parameter);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
await next.Value.OnExitedAsync();
|
||||
|
||||
if (previous != null)
|
||||
{
|
||||
await previous.OnEnteredAsync(parameter);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
_current = next.Value;
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DisposeAsync().AsTask().Wait();
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_states.Clear();
|
||||
if (_current != null)
|
||||
{
|
||||
await _current.OnExitingAsync();
|
||||
await _current.OnExitedAsync();
|
||||
}
|
||||
|
||||
_current = null;
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using Ghost.Core;
|
||||
|
||||
namespace Ghost.Editor.Core.AppState;
|
||||
|
||||
internal interface IAppState
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when exiting the state.
|
||||
/// </summary>
|
||||
public Task<Result> OnExitingAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Called when entering the state, right after OnEnteringAsync.
|
||||
/// <paramref name="parameter">can be used to pass data into the state, such as a project to load.</summary>
|
||||
/// </summary>
|
||||
public Task<Result> OnEnteringAsync(object? parameter);
|
||||
|
||||
/// <summary>
|
||||
/// Called when exiting the state, specifically for pose transitions.
|
||||
/// </summary>
|
||||
public Task<Result> OnExitedAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Called when entered the state, specifically after the state has been fully initialized and is ready for interaction.
|
||||
/// </summary>
|
||||
/// <param name="parameter">can be used to pass data into the state, such as a project to load.</param>
|
||||
public Task<Result> OnEnteredAsync(object? parameter);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace Ghost.Editor.Core.AppState;
|
||||
|
||||
internal enum StateKey
|
||||
{
|
||||
None,
|
||||
Landing,
|
||||
EngineEditor,
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Editor.Core.Utilities;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Ghost.Editor.Core.AssetHandle;
|
||||
|
||||
public static partial class AssetDatabase
|
||||
{
|
||||
private static readonly Dictionary<string, Type> s_importerTypeLookup = new();
|
||||
|
||||
private static void InitializeMetaData()
|
||||
{
|
||||
if (_watcher == null)
|
||||
{
|
||||
throw new InvalidOperationException("AssetDatabase is not initialized. Ensure that Initialize() is called before registering asset importers.");
|
||||
}
|
||||
|
||||
var importerTypes = TypeCache.GetTypes().Where(t => t.GetCustomAttribute<AssetImporterAttribute>() != null);
|
||||
foreach (var type in importerTypes)
|
||||
{
|
||||
var attribute = type.GetCustomAttribute<AssetImporterAttribute>()!;
|
||||
foreach (var extension in attribute.SupportedExtensions)
|
||||
{
|
||||
s_importerTypeLookup[extension] = type;
|
||||
}
|
||||
}
|
||||
|
||||
_watcher.Created += OnAssetCreated;
|
||||
_watcher.Deleted += OnAssetDeleted;
|
||||
_watcher.Renamed += OnAssetRenamed;
|
||||
}
|
||||
|
||||
private static Result<string> GetMetaFilePath(string assetPath)
|
||||
{
|
||||
if (Directory.Exists(assetPath))
|
||||
{
|
||||
return Result<string>.Failure("Folder does not have meta data");
|
||||
}
|
||||
|
||||
if (Path.GetExtension(assetPath).Equals(".meta", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return Result<string>.Failure("Asset path cannot be a meta file");
|
||||
}
|
||||
|
||||
return Result<string>.Success(assetPath + ".meta");
|
||||
}
|
||||
|
||||
private static ImporterSettings? GetDefaultSettingsForAsset(string assetPath)
|
||||
{
|
||||
var extension = Path.GetExtension(assetPath);
|
||||
|
||||
if (s_importerTypeLookup.TryGetValue(extension, out var importerType))
|
||||
{
|
||||
var settingsType = importerType.BaseType?.GetGenericArguments()[0];
|
||||
if (settingsType == null || !typeof(ImporterSettings).IsAssignableFrom(settingsType))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return (ImporterSettings?)Activator.CreateInstance(settingsType);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void WriteMetaFile(string metaFilePath, AssetMeta metaData)
|
||||
{
|
||||
using var fileStream = File.Create(metaFilePath);
|
||||
|
||||
try
|
||||
{
|
||||
JsonSerializer.Serialize(fileStream, metaData);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
internal static Result GenerateMetaFile(string assetPath)
|
||||
{
|
||||
var metaFileResult = GetMetaFilePath(assetPath);
|
||||
if (!metaFileResult.IsSuccess)
|
||||
{
|
||||
return Result.Failure(metaFileResult.Message);
|
||||
}
|
||||
|
||||
if (File.Exists(metaFileResult.Value))
|
||||
{
|
||||
var existingMeta = JsonSerializer.Deserialize<AssetMeta>(File.ReadAllText(metaFileResult.Value));
|
||||
if (existingMeta != null && s_assetPathLookup.TryGetValue(existingMeta.Guid, out var path))
|
||||
{
|
||||
if (assetPath != path)
|
||||
{
|
||||
existingMeta.Guid = Guid.NewGuid();
|
||||
WriteMetaFile(metaFileResult.Value, existingMeta);
|
||||
}
|
||||
}
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
var defaultSettings = GetDefaultSettingsForAsset(assetPath);
|
||||
var metaData = new AssetMeta
|
||||
{
|
||||
Guid = Guid.NewGuid(),
|
||||
Settings = defaultSettings
|
||||
};
|
||||
|
||||
WriteMetaFile(metaFileResult.Value, metaData);
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
private static void OnAssetCreated(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
GenerateMetaFile(e.FullPath);
|
||||
}
|
||||
|
||||
private static void OnAssetDeleted(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
var metaFileResult = GetMetaFilePath(e.FullPath);
|
||||
if (metaFileResult.IsSuccess && File.Exists(metaFileResult.Value))
|
||||
{
|
||||
try
|
||||
{
|
||||
var meta = JsonSerializer.Deserialize<AssetMeta>(File.ReadAllText(metaFileResult.Value));
|
||||
if (meta != null
|
||||
&& s_assetPathLookup.TryGetValue(meta.Guid, out var path)
|
||||
&& path == e.FullPath)
|
||||
{
|
||||
s_assetPathLookup.Remove(meta.Guid);
|
||||
}
|
||||
|
||||
File.Delete(metaFileResult.Value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnAssetRenamed(object sender, RenamedEventArgs e)
|
||||
{
|
||||
var oldMetaPath = e.OldFullPath + ".meta";
|
||||
var newMetaPath = e.FullPath + ".meta";
|
||||
|
||||
if (File.Exists(oldMetaPath))
|
||||
{
|
||||
File.Move(oldMetaPath, newMetaPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
GenerateMetaFile(e.FullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Ghost.Editor.Core.AssetHandle;
|
||||
|
||||
public static partial class AssetDatabase
|
||||
{
|
||||
private static readonly Dictionary<string, Action<string>> _assetOpenHandlers = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
private static void InitializeAssetHandle()
|
||||
{
|
||||
var methods = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(a => a.GetTypes())
|
||||
.SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
|
||||
.Where(m => m.GetCustomAttribute<AssetOpenHandlerAttribute>() != null &&
|
||||
m.GetParameters().Length == 1 &&
|
||||
m.GetParameters()[0].ParameterType == typeof(string));
|
||||
|
||||
foreach (var method in methods)
|
||||
{
|
||||
var attr = method.GetCustomAttribute<AssetOpenHandlerAttribute>()!;
|
||||
var del = (Action<string>)Delegate.CreateDelegate(typeof(Action<string>), method);
|
||||
foreach (var ext in attr.Extensions)
|
||||
{
|
||||
if (_assetOpenHandlers.ContainsKey(ext))
|
||||
{
|
||||
throw new InvalidOperationException($"Duplicate handler for extension '{ext}'");
|
||||
}
|
||||
|
||||
_assetOpenHandlers[ext] = del;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void OpenAsset(string path)
|
||||
{
|
||||
var extension = Path.GetExtension(path);
|
||||
if (_assetOpenHandlers.TryGetValue(extension, out var handler))
|
||||
{
|
||||
handler(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(path)
|
||||
{
|
||||
UseShellExecute = true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
using Ghost.Data.Services;
|
||||
|
||||
namespace Ghost.Editor.Core.AssetHandle;
|
||||
|
||||
public static partial class AssetDatabase
|
||||
{
|
||||
private static FileSystemWatcher? _watcher;
|
||||
|
||||
private static readonly Dictionary<Guid, string> s_assetPathLookup = new();
|
||||
|
||||
public static DirectoryInfo? AssetsDirectory
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
internal static void Initialize()
|
||||
{
|
||||
if (ProjectService.CurrentProject.Metadata == null)
|
||||
{
|
||||
throw new InvalidOperationException("Project metadata is not initialized. Ensure that the project is loaded before accessing the AssetDatabase.");
|
||||
}
|
||||
|
||||
AssetsDirectory = new DirectoryInfo(Path.Combine(Path.GetDirectoryName(ProjectService.CurrentProject.Path)!, ProjectService.ASSETS_FOLDER));
|
||||
_watcher = new FileSystemWatcher
|
||||
{
|
||||
Path = AssetsDirectory.FullName,
|
||||
IncludeSubdirectories = true,
|
||||
EnableRaisingEvents = true
|
||||
};
|
||||
|
||||
InitializeAssetHandle();
|
||||
InitializeMetaData();
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
namespace Ghost.Editor.Core.AssetHandle;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
public class AssetImporterAttribute : Attribute
|
||||
{
|
||||
public string[] SupportedExtensions
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public AssetImporterAttribute(params string[] supportedExtensions)
|
||||
{
|
||||
SupportedExtensions = supportedExtensions;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
namespace Ghost.Editor.Core.AssetHandle;
|
||||
|
||||
internal class AssetMeta
|
||||
{
|
||||
public Guid Guid
|
||||
{
|
||||
get;
|
||||
internal set;
|
||||
}
|
||||
|
||||
public ImporterSettings? Settings
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
namespace Ghost.Editor.Core.AssetHandle;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class AssetOpenHandlerAttribute : Attribute
|
||||
{
|
||||
public string[] Extensions
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public AssetOpenHandlerAttribute(params string[] extensions)
|
||||
{
|
||||
Extensions = extensions.Select(e => e.StartsWith('.') ? e.ToLowerInvariant() : '.' + e.ToLowerInvariant()).ToArray();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
namespace Ghost.Editor.Core.AssetHandle;
|
||||
|
||||
public abstract class ImporterSettings
|
||||
{
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Ghost.Editor.Core.Contracts;
|
||||
|
||||
public interface INavigationAware
|
||||
{
|
||||
public void OnNavigatedTo(object? parameter);
|
||||
public void OnNavigatedFrom();
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Ghost.Editor.Controls.Internal" />
|
||||
@@ -1,48 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0-windows10.0.22621.0</TargetFramework>
|
||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
||||
<RootNamespace>Ghost.Editor.Core</RootNamespace>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<SupportedOSPlatformVersion>10.0.20348.0</SupportedOSPlatformVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
<LangVersion>preview</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7175" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Behaviors" Version="8.2.250402" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ghost.Data\Ghost.Data.csproj" />
|
||||
<ProjectReference Include="..\Ghost.Core\Ghost.Core.csproj" />
|
||||
<ProjectReference Include="..\Ghost.Engine\Ghost.Engine.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\BasicInput\PropertyField.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Update="Controls\BasicInput\Vector3Field.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Update="Controls\ControlsDictionary.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Update="Controls\Internal\ComponentDataView.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Update="Controls\Internal\NavigationTabView.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Data\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,29 +0,0 @@
|
||||
using Ghost.SparseEntities;
|
||||
using Ghost.SparseEntities.Components;
|
||||
using Ghost.SparseEntities.Query;
|
||||
|
||||
namespace Ghost.Editor.Core.Inspector;
|
||||
|
||||
public unsafe readonly struct ComponentObject
|
||||
{
|
||||
private readonly World _world;
|
||||
private readonly Entity _entity;
|
||||
|
||||
internal ComponentObject(World world, Entity entity)
|
||||
{
|
||||
_world = world;
|
||||
_entity = entity;
|
||||
}
|
||||
|
||||
public CompRef<T> GetData<T>()
|
||||
where T : unmanaged, IComponentData
|
||||
{
|
||||
return _world.EntityManager.GetComponent<T>(_entity);
|
||||
}
|
||||
|
||||
public void SetData<T>(in T data)
|
||||
where T : unmanaged, IComponentData
|
||||
{
|
||||
_world.EntityManager.SetComponent(_entity, in data);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace Ghost.Editor.Core.Inspector;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class CustomEditorAttribute(Type targetType) : Attribute
|
||||
{
|
||||
internal Type TargetType
|
||||
{
|
||||
get;
|
||||
} = targetType;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Ghost.Editor.Core.Inspector;
|
||||
|
||||
public interface IInspectable
|
||||
{
|
||||
public IconSource? Icon
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public UIElement? HeaderContent
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public UIElement? InspectorContent
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
namespace Ghost.Editor.Core.Inspector;
|
||||
|
||||
internal interface IInspectorService
|
||||
{
|
||||
public IInspectable? SelectedInspectable
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public event Action? OnSelectionChanged;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
namespace Ghost.Editor.Core.Inspector;
|
||||
|
||||
public class InspectorService : IInspectorService
|
||||
{
|
||||
public IInspectable? SelectedInspectable
|
||||
{
|
||||
get => field;
|
||||
set
|
||||
{
|
||||
if (field != value)
|
||||
{
|
||||
field = value;
|
||||
OnSelectionChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event Action? OnSelectionChanged;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using CommunityToolkit.WinUI.Behaviors;
|
||||
|
||||
namespace Ghost.Editor.Core.Notifications;
|
||||
|
||||
public interface INotificationService
|
||||
{
|
||||
public void ShowNotification(string? message, MessageType type, int duration = 5, string? title = null);
|
||||
public void ShowNotification(Notification notification);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Ghost.Editor.Core.Progress;
|
||||
|
||||
public interface IProgressService
|
||||
{
|
||||
public void ShowProgress(string message, double progress = 0.0);
|
||||
public void ShowIndeterminateProgress(string message);
|
||||
public void SetProgress(double progress);
|
||||
public void HideProgress();
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
namespace Ghost.Editor.Core.Resources;
|
||||
|
||||
internal static class FileExtensions
|
||||
{
|
||||
public const string PROJECT_FILE_EXTENSION = ".ghostproj";
|
||||
public const string TEMPLATE_FILE_EXTENSION = ".ghosttemplate";
|
||||
public const string SCENE_FILE_EXTENSION = ".ghostscene";
|
||||
public const string ASSET_FILE_EXTENSION = ".ghostasset";
|
||||
public const string SHADER_FILE_EXTENSION = ".ghostshader";
|
||||
public const string MATERIAL_FILE_EXTENSION = ".ghostmaterial";
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
using Ghost.Editor.Core.Progress;
|
||||
using Ghost.Editor.Core.Resources;
|
||||
using Ghost.Editor.Core.Utilities;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Ghost.Editor.Core.SceneGraph;
|
||||
|
||||
public enum OpenWorldMode
|
||||
{
|
||||
Single,
|
||||
Additive,
|
||||
AdditiveWithoutLoading
|
||||
}
|
||||
|
||||
public static class EditorWorldManager
|
||||
{
|
||||
// TODO: Use guid keys instead of string paths for better performance and uniqueness
|
||||
private static readonly Dictionary<string, WorldNode> _loadedWorlds = new();
|
||||
public static IEnumerable<WorldNode> LoadedWorlds => _loadedWorlds.Values;
|
||||
|
||||
public static event Action<WorldNode>? OnWorldLoaded;
|
||||
public static event Action<WorldNode>? OnWorldUnloaded;
|
||||
|
||||
public static async Task LoadWorld(string worldPath)
|
||||
{
|
||||
if (_loadedWorlds.ContainsKey(worldPath)
|
||||
|| !File.Exists(worldPath)
|
||||
|| Path.GetExtension(worldPath) != FileExtensions.SCENE_FILE_EXTENSION)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var progressService = EditorApplication.GetService<IProgressService>();
|
||||
progressService.ShowIndeterminateProgress("Loading world...");
|
||||
|
||||
foreach (var world in _loadedWorlds)
|
||||
{
|
||||
world.Value.Unload();
|
||||
OnWorldUnloaded?.Invoke(world.Value);
|
||||
}
|
||||
|
||||
await using var readStream = new FileStream(worldPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
var deserializedScene = await JsonSerializer.DeserializeAsync<WorldNode>(readStream, Engine.Resources.StaticResource.defaultSerializerOptions) ?? throw new Exception("Deserialization failed.");
|
||||
|
||||
_loadedWorlds.Clear();
|
||||
|
||||
_loadedWorlds[worldPath] = deserializedScene;
|
||||
await deserializedScene.LoadAsync();
|
||||
|
||||
progressService.HideProgress();
|
||||
OnWorldLoaded?.Invoke(deserializedScene);
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
using Ghost.Editor.Core.Controls.Internal;
|
||||
using Ghost.Editor.Core.Inspector;
|
||||
using Ghost.Editor.Core.Resources;
|
||||
using Ghost.Engine.Editor;
|
||||
using Ghost.SparseEntities;
|
||||
using Microsoft.UI.Text;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Ghost.Editor.Core.SceneGraph;
|
||||
|
||||
public partial class EntityNode : SceneGraphNode
|
||||
{
|
||||
public WorldNode Owner
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Entity Entity
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public override SceneGraphNodeType NodeType => SceneGraphNodeType.Entity;
|
||||
|
||||
public EntityNode(WorldNode owner, Entity entity, string name)
|
||||
{
|
||||
Owner = owner;
|
||||
Entity = entity;
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class EntityNode : IInspectable
|
||||
{
|
||||
public IconSource? Icon => EditorIconSource.entity_24;
|
||||
|
||||
public UIElement? HeaderContent
|
||||
{
|
||||
get
|
||||
{
|
||||
var root = new StackPanel()
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
|
||||
var nameText = new TextBox
|
||||
{
|
||||
Text = Name,
|
||||
FontWeight = FontWeights.Bold,
|
||||
};
|
||||
var idText = new TextBlock
|
||||
{
|
||||
Text = $"ID: {Entity.ID} Generation: {Entity.Generation}",
|
||||
Margin = new Thickness(5, 7, 0, 0),
|
||||
Opacity = 0.75,
|
||||
Style = Application.Current.Resources["CaptionTextBlockStyle"] as Style
|
||||
};
|
||||
|
||||
nameText.SetBinding(TextBox.TextProperty, new Binding
|
||||
{
|
||||
Source = this,
|
||||
Path = new PropertyPath(nameof(Name)),
|
||||
Mode = BindingMode.TwoWay,
|
||||
UpdateSourceTrigger = UpdateSourceTrigger.LostFocus,
|
||||
});
|
||||
|
||||
root.Children.Add(nameText);
|
||||
root.Children.Add(idText);
|
||||
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
||||
public UIElement? InspectorContent
|
||||
{
|
||||
get
|
||||
{
|
||||
var root = new StackPanel()
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||
VerticalAlignment = VerticalAlignment.Top
|
||||
};
|
||||
|
||||
foreach (var (typeHandle, componentPtr) in Owner.World.EntityManager.GetComponentsUnsafe(Entity))
|
||||
{
|
||||
if (componentPtr == IntPtr.Zero)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var type = typeHandle.ToType();
|
||||
if (type == null || type.GetCustomAttribute<HideEditorAttribute>() != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var dataView = new ComponentDataView(type.Name, Owner.World, Entity, type);
|
||||
root.Children.Add(dataView);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
using Ghost.Engine.Components;
|
||||
using Ghost.SparseEntities;
|
||||
|
||||
namespace Ghost.Editor.Core.SceneGraph;
|
||||
|
||||
public class SceneGraphHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="EntityNode"/> entity with default components.
|
||||
/// </summary>
|
||||
/// <param name="world">The world context where the entity will be created.</param>
|
||||
/// <param name="entity">The entity to be wrapped in the <see cref="EntityNode"/>.</param>
|
||||
public static EntityNode CreateEntityNode(WorldNode owner, Entity entity, string name)
|
||||
{
|
||||
owner.World.EntityManager.AddComponent(entity, LocalToWorld.Identity);
|
||||
owner.World.EntityManager.AddComponent(entity, Hierarchy.Root);
|
||||
return new EntityNode(owner, entity, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="Entity"/> and <see cref="EntityNode"/> entity with default components.
|
||||
/// </summary>
|
||||
/// <param name="owner">The world context where the entity will be created.</param>
|
||||
public static EntityNode CreateEntityNode(WorldNode owner, string name)
|
||||
{
|
||||
var entity = owner.World.EntityManager.CreateEntity();
|
||||
return CreateEntityNode(owner, entity, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches childEntity to parentEntity in the scene graph.
|
||||
/// </summary>
|
||||
/// <param name="world">The world context where the entities exist.</param>
|
||||
/// <param name="parentNode">The parent entity to which the child will be attached.</param>
|
||||
/// <param name="childNode">The child entity to be attached.</param>
|
||||
public static void AttachChild(WorldNode scene, EntityNode parentNode, EntityNode childNode)
|
||||
{
|
||||
// 1) If the child already has a parent, detach it first
|
||||
var childHierarchy = scene.World.EntityManager.GetComponent<Hierarchy>(childNode.Entity);
|
||||
if (childHierarchy.ValueRO.parent != Entity.Invalid)
|
||||
{
|
||||
DetachFromParent(scene, childNode);
|
||||
}
|
||||
|
||||
// 2) Link child to new parent
|
||||
childHierarchy.ValueRW.parent = parentNode.Entity;
|
||||
|
||||
// 3) Insert child at the head of parent's child list
|
||||
var parentHierarchy = scene.World.EntityManager.GetComponent<Hierarchy>(parentNode.Entity);
|
||||
|
||||
childHierarchy.ValueRW.nextSibling = parentHierarchy.ValueRO.firstChild;
|
||||
parentHierarchy.ValueRW.firstChild = childNode.Entity;
|
||||
|
||||
// 4) Write back
|
||||
scene.World.EntityManager.SetComponent(parentNode.Entity, in parentHierarchy.ValueRO);
|
||||
scene.World.EntityManager.SetComponent(childNode.Entity, in childHierarchy.ValueRO);
|
||||
|
||||
// 5) Update children list in parent node
|
||||
parentNode.AddChild(childNode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detaches the specified entity from its parent in the scene graph.
|
||||
/// </summary>
|
||||
/// <param name="world">The world context where the entities exist.</param>
|
||||
/// <param name="node">The entity to detach from its parent.</param>
|
||||
public static void DetachFromParent(WorldNode scene, EntityNode node)
|
||||
{
|
||||
var hierarchy = scene.World.EntityManager.GetComponent<Hierarchy>(node.Entity);
|
||||
var parent = hierarchy.ValueRO.parent;
|
||||
if (parent == Entity.Invalid)
|
||||
{
|
||||
return; // already root
|
||||
}
|
||||
|
||||
var parentHierarchy = scene.World.EntityManager.GetComponent<Hierarchy>(parent);
|
||||
|
||||
// If entity is the first child, simply move head
|
||||
if (parentHierarchy.ValueRO.firstChild == node.Entity)
|
||||
{
|
||||
parentHierarchy.ValueRW.firstChild = hierarchy.ValueRO.nextSibling;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, find the previous sibling in the linked list
|
||||
var prevSibling = parentHierarchy.ValueRO.firstChild;
|
||||
while (prevSibling != Entity.Invalid)
|
||||
{
|
||||
var prevHierarchy = scene.World.EntityManager.GetComponent<Hierarchy>(prevSibling);
|
||||
if (prevHierarchy.ValueRW.nextSibling == node.Entity)
|
||||
{
|
||||
prevHierarchy.ValueRW.nextSibling = hierarchy.ValueRO.nextSibling;
|
||||
scene.World.EntityManager.SetComponent(prevSibling, in prevHierarchy.ValueRO);
|
||||
break;
|
||||
}
|
||||
|
||||
prevSibling = prevHierarchy.ValueRO.nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear child's references
|
||||
hierarchy.ValueRW.parent = Entity.Invalid;
|
||||
hierarchy.ValueRW.nextSibling = Entity.Invalid;
|
||||
|
||||
// Write back
|
||||
scene.World.EntityManager.SetComponent(parent, in parentHierarchy.ValueRO);
|
||||
scene.World.EntityManager.SetComponent(node.Entity, in hierarchy.ValueRO);
|
||||
|
||||
// Remove from parent's children list
|
||||
scene.EntityNodeLookup[parent].RemoveChild(node);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ghost.Editor.Core.SceneGraph;
|
||||
|
||||
public enum SceneGraphNodeType
|
||||
{
|
||||
Scene,
|
||||
Entity,
|
||||
}
|
||||
|
||||
public abstract partial class SceneGraphNode : ObservableObject
|
||||
{
|
||||
public ObservableCollection<SceneGraphNode>? Children
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string Name
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public abstract SceneGraphNodeType NodeType
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public int ChildCount => Children?.Count ?? 0;
|
||||
|
||||
public virtual void AddChild(SceneGraphNode child)
|
||||
{
|
||||
Children ??= new();
|
||||
Children.Add(child);
|
||||
}
|
||||
|
||||
public virtual bool RemoveChild(SceneGraphNode child)
|
||||
{
|
||||
return Children?.Remove(child) ?? false;
|
||||
}
|
||||
|
||||
public SceneGraphNode GetChild(int index)
|
||||
{
|
||||
if (Children == null || index < 0 || index >= Children.Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range.");
|
||||
}
|
||||
|
||||
return Children[index];
|
||||
}
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
using Ghost.Editor.Core.AssetHandle;
|
||||
using Ghost.Editor.Core.Inspector;
|
||||
using Ghost.Editor.Core.Resources;
|
||||
using Ghost.Editor.Core.Serializer;
|
||||
using Ghost.Engine.Components;
|
||||
using Ghost.SparseEntities;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ghost.Editor.Core.SceneGraph;
|
||||
|
||||
[JsonConverter(typeof(WorldNodeSerializer))]
|
||||
public partial class WorldNode : SceneGraphNode, IEquatable<WorldNode>
|
||||
{
|
||||
private World _world;
|
||||
private Dictionary<Entity, EntityNode> _entityNodeLookup = new();
|
||||
|
||||
public World World => _world;
|
||||
public Dictionary<Entity, EntityNode> EntityNodeLookup => _entityNodeLookup;
|
||||
|
||||
public override SceneGraphNodeType NodeType => SceneGraphNodeType.Scene;
|
||||
|
||||
public WorldNode(World world, string name)
|
||||
{
|
||||
_world = world;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
internal WorldNode()
|
||||
{
|
||||
_world = World.Create();
|
||||
}
|
||||
|
||||
private void UpdateLookup(Entity key, EntityNode value)
|
||||
{
|
||||
_entityNodeLookup[key] = value;
|
||||
if (value.Children == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var child in value.Children)
|
||||
{
|
||||
if (child is EntityNode entityChild)
|
||||
{
|
||||
UpdateLookup(entityChild.Entity, entityChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void AddChild(SceneGraphNode child)
|
||||
{
|
||||
if (child is not EntityNode entityNode)
|
||||
{
|
||||
throw new ArgumentException("Child must be of type EntityNode.", nameof(child));
|
||||
}
|
||||
|
||||
base.AddChild(entityNode);
|
||||
UpdateLookup(entityNode.Entity, entityNode);
|
||||
}
|
||||
|
||||
public override bool RemoveChild(SceneGraphNode child)
|
||||
{
|
||||
if (child is not EntityNode entityNode)
|
||||
{
|
||||
throw new ArgumentException("Child must be of type EntityNode.", nameof(child));
|
||||
}
|
||||
|
||||
var result = base.RemoveChild(child);
|
||||
if (result)
|
||||
{
|
||||
_entityNodeLookup.Remove(entityNode.Entity);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private EntityNode BuildNodeRecursive(Entity entity)
|
||||
{
|
||||
if (!_entityNodeLookup.TryGetValue(entity, out var node))
|
||||
{
|
||||
node = new EntityNode(this, entity, "New Entity");
|
||||
_entityNodeLookup[entity] = node;
|
||||
}
|
||||
|
||||
var hc = _world.EntityManager.GetComponent<Hierarchy>(entity);
|
||||
var child = hc.ValueRO.firstChild;
|
||||
|
||||
while (child != Entity.Invalid)
|
||||
{
|
||||
node.AddChild(BuildNodeRecursive(child));
|
||||
var childHC = _world.EntityManager.GetComponent<Hierarchy>(child);
|
||||
child = childHC.ValueRO.nextSibling;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private void BuildGraph()
|
||||
{
|
||||
foreach (var (entity, hierarchy) in _world.Query<Hierarchy>())
|
||||
{
|
||||
if (hierarchy.ValueRO.parent == Entity.Invalid)
|
||||
{
|
||||
var node = BuildNodeRecursive(entity);
|
||||
AddChild(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Task LoadAsync()
|
||||
{
|
||||
return Task.Run(BuildGraph);
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
_world.Dispose();
|
||||
_world = null!;
|
||||
|
||||
Children?.Clear();
|
||||
_entityNodeLookup.Clear();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"WorldNode: {Name} (World ID: {_world.ID})";
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(_world, Name);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is WorldNode other && Equals(other);
|
||||
}
|
||||
|
||||
public bool Equals(WorldNode? other)
|
||||
{
|
||||
if (other is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(this, other))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return _world.Equals(other._world) && Name == other.Name;
|
||||
}
|
||||
|
||||
public static bool operator ==(WorldNode? left, WorldNode? right)
|
||||
{
|
||||
if (left is null)
|
||||
{
|
||||
return right is null;
|
||||
}
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(WorldNode? left, WorldNode? right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class WorldNode : IInspectable
|
||||
{
|
||||
public IconSource? Icon => EditorIconSource.scene_24;
|
||||
|
||||
[AssetOpenHandler(FileExtensions.SCENE_FILE_EXTENSION)]
|
||||
public static async void Open(string path)
|
||||
{
|
||||
await EditorWorldManager.LoadWorld(path);
|
||||
}
|
||||
|
||||
public UIElement? HeaderContent => null;
|
||||
|
||||
public UIElement? InspectorContent => null;
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
using Ghost.Editor.Core.SceneGraph;
|
||||
using Ghost.Engine.Utilities;
|
||||
using Ghost.SparseEntities;
|
||||
using Ghost.SparseEntities.Components;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ghost.Editor.Core.Serializer;
|
||||
|
||||
internal class WorldNodeSerializer : JsonConverter<WorldNode>
|
||||
{
|
||||
private static class Property
|
||||
{
|
||||
public const string NAME = "Name";
|
||||
public const string ENTITIES = "Entities";
|
||||
public const string ID = "ID";
|
||||
public const string ENTITY_ID = "EntityID";
|
||||
public const string COMPONENTS = "Components";
|
||||
public const string DATA = "Data";
|
||||
public const string SYSTEMS = "Systems";
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type typeToConvert)
|
||||
{
|
||||
return typeToConvert == typeof(WorldNode) || typeToConvert.IsSubclassOf(typeof(WorldNode));
|
||||
}
|
||||
|
||||
public override WorldNode? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
var element = JsonDocument.ParseValue(ref reader).RootElement;
|
||||
var name = element.GetProperty(Property.NAME).GetString() ?? "New World";
|
||||
|
||||
var world = World.Create();
|
||||
var result = new WorldNode(world, name);
|
||||
|
||||
foreach (var entityElement in element.GetProperty(Property.ENTITIES).EnumerateArray())
|
||||
{
|
||||
var entityName = entityElement.GetProperty(Property.NAME).GetString() ?? "New Entity";
|
||||
var entityID = entityElement.GetProperty(Property.ID).GetInt32();
|
||||
var entity = new Entity(entityID, 0);
|
||||
var node = new EntityNode(result, entity, entityName);
|
||||
|
||||
world.EntityManager.AddEntityInternal(entity);
|
||||
result.EntityNodeLookup[entity] = node;
|
||||
}
|
||||
|
||||
foreach (var componentElement in element.GetProperty(Property.COMPONENTS).EnumerateObject())
|
||||
{
|
||||
var typeName = componentElement.Name;
|
||||
var type = Type.GetType(typeName) ?? throw new Exception($"Type {typeName} not found.");
|
||||
|
||||
foreach (var dataElement in componentElement.Value.EnumerateArray())
|
||||
{
|
||||
var entityID = dataElement.GetProperty(Property.ENTITY_ID).GetInt32();
|
||||
var entity = new Entity(entityID, 0);
|
||||
|
||||
var dataProperty = dataElement.GetProperty(Property.DATA);
|
||||
var component = JsonSerializer.Deserialize(dataProperty.GetRawText(), type, options);
|
||||
if (component is IComponentData data)
|
||||
{
|
||||
world.EntityManager.AddComponent(entity, data, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var systemElement in element.GetProperty(Property.SYSTEMS).EnumerateArray())
|
||||
{
|
||||
var typeString = systemElement.GetString();
|
||||
if (string.IsNullOrEmpty(typeString))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var systemType = Type.GetType(typeString);
|
||||
if (systemType == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
world.SystemStorage.AddSystem(systemType);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, WorldNode value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteObject(() =>
|
||||
{
|
||||
writer.WriteString(Property.NAME, value.Name);
|
||||
writer.WriteArray(Property.ENTITIES, value.World.EntityManager.Entities, entity =>
|
||||
{
|
||||
if (!entity.IsValid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
writer.WriteObject(() =>
|
||||
{
|
||||
writer.WriteString(Property.NAME, value.EntityNodeLookup[entity].Name);
|
||||
writer.WriteNumber(Property.ID, entity.ID);
|
||||
});
|
||||
});
|
||||
|
||||
writer.WriteObject(Property.COMPONENTS, () =>
|
||||
{
|
||||
for (var i = 0; i < value.World.ComponentStorage.ComponentPools.Count; i++)
|
||||
{
|
||||
var pool = value.World.ComponentStorage.ComponentPools[i];
|
||||
if (pool == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var type = value.World.ComponentStorage.GetComponentPoolType(i).GetType();
|
||||
var typeName = type.AssemblyQualifiedName ?? type.Name;
|
||||
|
||||
writer.WriteArray(typeName, pool.Enumerate(), data =>
|
||||
{
|
||||
writer.WriteObject(() =>
|
||||
{
|
||||
writer.WriteNumber(Property.ENTITY_ID, data.entity.ID);
|
||||
writer.WritePropertyName(Property.DATA);
|
||||
JsonSerializer.Serialize(writer, data.component, type, options);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
writer.WriteArray(Property.SYSTEMS, value.World.SystemStorage.Systems, systemType =>
|
||||
{
|
||||
writer.WriteStringValue(systemType.AssemblyQualifiedName ?? systemType.Name);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace Ghost.Editor.Core.Utilities;
|
||||
|
||||
public static class EditorApplication
|
||||
{
|
||||
private static IServiceProvider? _serviceProvider;
|
||||
|
||||
public static Application Current => Application.Current;
|
||||
|
||||
internal static void Initialize(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public static T GetService<T>()
|
||||
where T : class
|
||||
{
|
||||
if (_serviceProvider?.GetService(typeof(T)) is not T service)
|
||||
{
|
||||
throw new ArgumentException($"{typeof(T)} needs to be registered in ConfigureServices within App.xaml.cs.");
|
||||
}
|
||||
|
||||
return service;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using Ghost.Core.Attributes;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Ghost.Editor.Core.Utilities;
|
||||
|
||||
public static class TypeCache
|
||||
{
|
||||
private static readonly TypeInfo[] _types;
|
||||
|
||||
static TypeCache()
|
||||
{
|
||||
var loadableTypes = new List<Type>();
|
||||
var assembliesToScan = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.Where(a => a.GetCustomAttribute<EngineAssemblyAttribute>() != null);
|
||||
|
||||
foreach (var assembly in assembliesToScan)
|
||||
{
|
||||
try
|
||||
{
|
||||
loadableTypes.AddRange(assembly.GetTypes());
|
||||
}
|
||||
catch (ReflectionTypeLoadException ex)
|
||||
{
|
||||
var types = ex.Types.Where(t => t != null);
|
||||
loadableTypes.AddRange(types!);
|
||||
}
|
||||
}
|
||||
|
||||
_types = loadableTypes.Select(t => t.GetTypeInfo()).ToArray();
|
||||
}
|
||||
|
||||
public static Type[] GetTypes()
|
||||
{
|
||||
return _types;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using Ghost.Data.Resources;
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Editor.Core.Utilities;
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace Ghost.Editor;
|
||||
|
||||
internal static class ActivationHandler
|
||||
{
|
||||
private static void FolderInitialization()
|
||||
{
|
||||
if (!Directory.Exists(DataPath.s_applicationDataFolder))
|
||||
{
|
||||
Directory.CreateDirectory(DataPath.s_applicationDataFolder);
|
||||
}
|
||||
|
||||
if (!Directory.Exists(DataPath.s_projectTemplateFolder))
|
||||
{
|
||||
Directory.CreateDirectory(DataPath.s_projectTemplateFolder);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Handle(LaunchActivatedEventArgs args)
|
||||
{
|
||||
FolderInitialization();
|
||||
ProjectService.EnsureDefaultTemplate();
|
||||
|
||||
EditorApplication.Initialize(((App)(Application.Current)).Host.Services);
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Editor.Core.AppState;
|
||||
using Ghost.Editor.Core.Inspector;
|
||||
using Ghost.Editor.Core.Notifications;
|
||||
using Ghost.Editor.Core.Progress;
|
||||
using Ghost.Editor.Utilities;
|
||||
using Ghost.Engine.Services;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
|
||||
namespace Ghost.Editor;
|
||||
|
||||
/// <summary>
|
||||
/// Provides application-specific behavior to supplement the default Application class.
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
private Window? _window;
|
||||
|
||||
internal static Window? Window
|
||||
{
|
||||
get => (Current as App)!._window;
|
||||
set
|
||||
{
|
||||
if (Current is App app)
|
||||
{
|
||||
// HACK: As far as I can tell, there is no proper application shutdown event in WinUI 3.
|
||||
app._window?.Closed -= app.OnClosed;
|
||||
app._window = value;
|
||||
app._window?.Closed += app.OnClosed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal IHost Host
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the singleton application object. This is the first line of authored code
|
||||
/// executed, and as such is the logical equivalent of main() or WinMain().
|
||||
/// </summary>
|
||||
internal App()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Host = Microsoft.Extensions.Hosting.Host.
|
||||
CreateDefaultBuilder().
|
||||
UseContentRoot(AppContext.BaseDirectory).
|
||||
ConfigureServices((context, services) =>
|
||||
{
|
||||
HostHelper.AddLandingScope(context, services);
|
||||
HostHelper.AddEngineScope(context, services);
|
||||
|
||||
services.AddSingleton<AppStateMachine>();
|
||||
services.AddSingleton<INotificationService, NotificationService>();
|
||||
services.AddSingleton<IProgressService, ProgressService>();
|
||||
services.AddSingleton<IInspectorService, InspectorService>();
|
||||
})
|
||||
.Build();
|
||||
|
||||
UnhandledException += App_UnhandledException;
|
||||
}
|
||||
|
||||
internal static IServiceScope CreateScope()
|
||||
{
|
||||
return (Current as App)!.Host.Services.CreateScope();
|
||||
}
|
||||
|
||||
public static T GetService<T>() where T : class
|
||||
{
|
||||
if ((Current as App)!.Host.Services.GetService(typeof(T)) is not T service)
|
||||
{
|
||||
throw new ArgumentException($"{typeof(T)} needs to be registered in ConfigureServices within App.xaml.cs.");
|
||||
}
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the application is launched.
|
||||
/// </summary>
|
||||
/// <param name="args">Details about the launch request and process.</param>
|
||||
protected override async void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
base.OnLaunched(args);
|
||||
|
||||
await Host.StartAsync();
|
||||
ActivationHandler.Handle(args);
|
||||
|
||||
var stateMachine = GetService<AppStateMachine>();
|
||||
stateMachine.RegisterState(StateKey.Landing, () => new LandingState());
|
||||
stateMachine.RegisterState(StateKey.EngineEditor, () => new EditorState());
|
||||
|
||||
await stateMachine.TransitionToAsync(StateKey.Landing);
|
||||
}
|
||||
|
||||
private void OnClosed(object? sender, WindowEventArgs args)
|
||||
{
|
||||
Host.StopAsync().GetAwaiter().GetResult();
|
||||
Host.Dispose();
|
||||
}
|
||||
|
||||
private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
|
||||
{
|
||||
Logger.LogError(e.Exception);
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 599 B |
|
Before Width: | Height: | Size: 831 B |
|
Before Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 433 B |
|
Before Width: | Height: | Size: 599 B |
|
Before Width: | Height: | Size: 583 B |
|
Before Width: | Height: | Size: 831 B |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 852 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 163 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
@@ -1,61 +0,0 @@
|
||||
using Ghost.Editor.Core.Controls;
|
||||
using Ghost.Editor.Core.Inspector;
|
||||
using Ghost.Engine.Components;
|
||||
using Ghost.Engine.Utilities;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Ghost.Editor.Components;
|
||||
|
||||
[CustomEditor(typeof(LocalToWorld))]
|
||||
internal class LocalToWorldEditor : ComponentEditor
|
||||
{
|
||||
private Vector3Field _translationField = null!;
|
||||
private Vector3Field _rotationField = null!;
|
||||
private Vector3Field _scaleField = null!;
|
||||
|
||||
public override void Create(StackPanel container)
|
||||
{
|
||||
_translationField = new Vector3Field();
|
||||
_rotationField = new Vector3Field();
|
||||
_scaleField = new Vector3Field();
|
||||
|
||||
_translationField.OnValueChanged += (s, e) =>
|
||||
{
|
||||
var data = ComponentObject.GetData<LocalToWorld>();
|
||||
MatrixUtility.GetTRS(data.ValueRO.matrix, out var _, out var oldRotation, out var oldScale);
|
||||
data.ValueRW.matrix = MatrixUtility.CreateTRS(e.NewValue, oldRotation, oldScale);
|
||||
};
|
||||
|
||||
_rotationField.OnValueChanged += (s, e) =>
|
||||
{
|
||||
var data = ComponentObject.GetData<LocalToWorld>();
|
||||
MatrixUtility.GetTRS(data.ValueRO.matrix, out var oldTranslation, out var _, out var oldScale);
|
||||
data.ValueRW.matrix = MatrixUtility.CreateTRS(oldTranslation, e.NewValue.ToQuaternion(), oldScale);
|
||||
};
|
||||
|
||||
_scaleField.OnValueChanged += (s, e) =>
|
||||
{
|
||||
var data = ComponentObject.GetData<LocalToWorld>();
|
||||
MatrixUtility.GetTRS(data.ValueRO.matrix, out var oldTranslation, out var oldRotation, out var _);
|
||||
data.ValueRW.matrix = MatrixUtility.CreateTRS(oldTranslation, oldRotation, e.NewValue);
|
||||
};
|
||||
|
||||
container.Children.Add(new PropertyField() { Label = "Position", Content = _translationField });
|
||||
container.Children.Add(new PropertyField() { Label = "Rotation", Content = _rotationField });
|
||||
container.Children.Add(new PropertyField() { Label = "Scale", Content = _scaleField });
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
var data = ComponentObject.GetData<LocalToWorld>();
|
||||
MatrixUtility.GetTRS(data.ValueRO.matrix, out var translation, out var rotation, out var scale);
|
||||
|
||||
_translationField.Value = translation;
|
||||
_rotationField.Value = VectorUtility.CreateFromQuaternion(rotation);
|
||||
_scaleField.Value = scale;
|
||||
}
|
||||
|
||||
public override void Destroy()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
|
||||
namespace Ghost.Editor.Controls;
|
||||
|
||||
public abstract partial class ViewModelPage<VM> : Page
|
||||
where VM : ObservableObject
|
||||
{
|
||||
public VM ViewModel
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
protected ViewModelPage(VM viewModel)
|
||||
{
|
||||
ViewModel = viewModel;
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
if (ViewModel is INavigationAware navigationAware)
|
||||
{
|
||||
navigationAware.OnNavigatedTo(e.Parameter);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedFrom(e);
|
||||
if (ViewModel is INavigationAware navigationAware)
|
||||
{
|
||||
navigationAware.OnNavigatedFrom();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
using Ghost.Data.Models;
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Editor.Core.AssetHandle;
|
||||
using Ghost.Editor.View.Windows;
|
||||
using Ghost.Engine;
|
||||
using Ghost.Engine.Services;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
|
||||
namespace Ghost.Editor.Core.AppState;
|
||||
|
||||
internal class EditorState : IAppState
|
||||
{
|
||||
private EngineEditorWindow? _window;
|
||||
private EngineCore? _engineCore;
|
||||
|
||||
public Task OnExitingAsync()
|
||||
{
|
||||
if (App.Window == _window)
|
||||
{
|
||||
App.Window = null;
|
||||
}
|
||||
|
||||
_engineCore?.ShutDown();
|
||||
CompositionTarget.Rendering -= OnRendering;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task OnEnteringAsync(object? parameter)
|
||||
{
|
||||
if (parameter is not ProjectMetadataInfo metadataInfo)
|
||||
{
|
||||
throw new ArgumentException("Parameter must be of type ProjectMetadata.", nameof(parameter));
|
||||
}
|
||||
|
||||
ProjectService.CurrentProject = metadataInfo;
|
||||
|
||||
_engineCore = App.GetService<EngineCore>();
|
||||
_engineCore.Start(new Engine.Models.LaunchArgument());
|
||||
CompositionTarget.Rendering += OnRendering;
|
||||
|
||||
_window = App.GetService<EngineEditorWindow>();
|
||||
_window.Activate();
|
||||
|
||||
App.Window = _window;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task OnExitedAsync()
|
||||
{
|
||||
_window?.Close();
|
||||
_window = null;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task OnEnteredAsync(object? parameter)
|
||||
{
|
||||
AssetDatabase.Initialize();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void OnRendering(object? sender, object e)
|
||||
{
|
||||
if (GraphicsPipeline.WaitForGPUReady(0))
|
||||
{
|
||||
_window?.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.High, () =>
|
||||
{
|
||||
PlayerLoopService.Update();
|
||||
GraphicsPipeline.SignalCPUReady();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using Ghost.Editor.View.Windows;
|
||||
|
||||
namespace Ghost.Editor.Core.AppState;
|
||||
|
||||
internal class LandingState : IAppState
|
||||
{
|
||||
private LandingWindow? _window;
|
||||
|
||||
public Task OnExitingAsync()
|
||||
{
|
||||
if (App.Window == _window)
|
||||
{
|
||||
App.Window = null;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task OnEnteringAsync(object? parameter)
|
||||
{
|
||||
_window = App.GetService<LandingWindow>();
|
||||
_window.Activate();
|
||||
|
||||
App.Window = _window;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task OnExitedAsync()
|
||||
{
|
||||
_window?.Close();
|
||||
_window = null;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task OnEnteredAsync(object? parameter)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net10.0-windows10.0.22621.0</TargetFramework>
|
||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
||||
<Platforms>x86;x64;ARM64</Platforms>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
|
||||
<PublishProfile>win-$(Platform).pubxml</PublishProfile>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<EnableMsixTooling>true</EnableMsixTooling>
|
||||
<LangVersion>preview</LangVersion>
|
||||
</PropertyGroup>
|
||||
<!--<ItemGroup>
|
||||
<Content Remove="Assets\icon-256.png" />
|
||||
<Content Remove="Assets\Icon.altform-lightunplated_targetsize-16.png" />
|
||||
<Content Remove="Assets\Icon.altform-lightunplated_targetsize-24.png" />
|
||||
<Content Remove="Assets\Icon.altform-lightunplated_targetsize-256.png" />
|
||||
<Content Remove="Assets\Icon.altform-lightunplated_targetsize-32.png" />
|
||||
<Content Remove="Assets\Icon.altform-lightunplated_targetsize-48.png" />
|
||||
<Content Remove="Assets\Icon.altform-unplated_targetsize-16.png" />
|
||||
<Content Remove="Assets\Icon.altform-unplated_targetsize-24.png" />
|
||||
<Content Remove="Assets\Icon.altform-unplated_targetsize-256.png" />
|
||||
<Content Remove="Assets\Icon.altform-unplated_targetsize-32.png" />
|
||||
<Content Remove="Assets\Icon.altform-unplated_targetsize-48.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\Icon.scale-100.png" />
|
||||
<None Remove="Assets\Icon.scale-125.png" />
|
||||
<None Remove="Assets\Icon.scale-150.png" />
|
||||
<None Remove="Assets\Icon.scale-200.png" />
|
||||
<None Remove="Assets\Icon.scale-400.png" />
|
||||
<None Remove="Assets\Icon.targetsize-16.png" />
|
||||
<None Remove="Assets\Icon.targetsize-16_altform-unplated.png" />
|
||||
<None Remove="Assets\Icon.targetsize-24.png" />
|
||||
<None Remove="Assets\Icon.targetsize-24_altform-lightunplated.png" />
|
||||
<None Remove="Assets\Icon.targetsize-256.png" />
|
||||
<None Remove="Assets\Icon.targetsize-256_altform-unplated.png" />
|
||||
<None Remove="Assets\Icon.targetsize-32.png" />
|
||||
<None Remove="Assets\Icon.targetsize-32_altform-lightunplated.png" />
|
||||
<None Remove="Assets\Icon.targetsize-48.png" />
|
||||
<None Remove="Assets\Icon.targetsize-48_altform-unplated.png" />
|
||||
<None Remove="View\Pages\EngineEditor\ConsolePage.xaml" />
|
||||
<None Remove="View\Pages\EngineEditor\HierarchyPage.xaml" />
|
||||
<None Remove="View\Pages\EngineEditor\InspectorPage.xaml" />
|
||||
<None Remove="View\Pages\EngineEditor\ProjectPage.xaml" />
|
||||
<None Remove="View\Pages\EngineEditor\ScenePage.xaml" />
|
||||
<None Remove="View\Pages\Landing\CreateProjectPage.xaml" />
|
||||
<None Remove="View\Pages\Landing\OpenProjectPage.xaml" />
|
||||
<None Remove="View\Windows\EngineEditorWindow.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Remove="App.xaml" />
|
||||
</ItemGroup>-->
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Assets\SplashScreen.scale-200.png" />
|
||||
<Content Include="Assets\LockScreenLogo.scale-200.png" />
|
||||
<Content Include="Assets\Square150x150Logo.scale-200.png" />
|
||||
<Content Include="Assets\StoreLogo.png" />
|
||||
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging
|
||||
Tools extension to be activated for this project even if the Windows App SDK Nuget
|
||||
package has not yet been restored.
|
||||
-->
|
||||
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
|
||||
<ProjectCapability Include="Msix" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.TabbedCommandBar" Version="8.2.250402" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7175" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
|
||||
<PackageReference Include="WinUIEx" Version="2.9.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ghost.Editor.Core\Ghost.Editor.Core.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Pages\Landing\CreateProjectPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Window\Landing.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Pages\Landing\OpenProjectPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Pages\EngineEditor\InspectorPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Pages\EngineEditor\HierarchyPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Pages\EngineEditor\ProjectPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Pages\EngineEditor\ConsolePage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Themes\Override.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Windows\EngineEditorWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Pages\EngineEditor\ScenePage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals" />
|
||||
|
||||
<!--
|
||||
Defining the "HasPackageAndPublishMenuAddedByProject" property here allows the Solution
|
||||
Explorer "Package and Publish" context menu entry to be enabled for this project even if
|
||||
the Windows App SDK Nuget package has not yet been restored.
|
||||
-->
|
||||
<PropertyGroup Condition="'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
|
||||
<HasPackageAndPublishMenu>true</HasPackageAndPublishMenu>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Publish Properties -->
|
||||
<PropertyGroup>
|
||||
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
|
||||
<PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun>
|
||||
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
|
||||
<Nullable>enable</Nullable>
|
||||
<SupportedOSPlatformVersion>10.0.20348.0</SupportedOSPlatformVersion>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<PublishAot>False</PublishAot>
|
||||
<PublishTrimmed>False</PublishTrimmed>
|
||||
<RootNamespace>Ghost.Editor</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -1,27 +0,0 @@
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ghost.Editor.Models;
|
||||
|
||||
internal class ExplorerItem(string name, string path, bool isDirectory)
|
||||
{
|
||||
public string Name
|
||||
{
|
||||
get;
|
||||
} = name;
|
||||
|
||||
public string FullName
|
||||
{
|
||||
get;
|
||||
} = path;
|
||||
|
||||
public bool IsDirectory
|
||||
{
|
||||
get;
|
||||
} = isDirectory;
|
||||
|
||||
public ObservableCollection<ExplorerItem>? Children
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:internal="using:Ghost.Editor.Controls.Internal">
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<StaticResource x:Key="TabViewItemHeaderBackgroundSelected" ResourceKey="ControlFillColorSecondaryBrush" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<StaticResource x:Key="TabViewItemHeaderBackgroundSelected" ResourceKey="ControlFillColorSecondaryBrush" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<Style TargetType="internal:NavigationTabView">
|
||||
<Setter Property="TabWidthMode" Value="Compact" />
|
||||
</Style>
|
||||
<Style TargetType="NumberBox" />
|
||||
</ResourceDictionary>
|
||||
@@ -1,27 +0,0 @@
|
||||
using Ghost.Engine.Utilities;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ghost.Editor.Utilities.Converters;
|
||||
|
||||
public partial class Vector3ToQuaternionConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is Vector3 vector)
|
||||
{
|
||||
return Quaternion.CreateFromYawPitchRoll(vector.Y, vector.X, vector.Z);
|
||||
}
|
||||
|
||||
throw new ArgumentException("Value must be of type System.Numerics.Vector3.", nameof(value));
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is Quaternion quaternion)
|
||||
{
|
||||
return VectorUtility.CreateFromQuaternion(quaternion);
|
||||
}
|
||||
throw new ArgumentException("Value must be of type System.Numerics.Quaternion.", nameof(value));
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Editor.View.Pages.EngineEditor;
|
||||
using Ghost.Editor.View.Pages.Landing;
|
||||
using Ghost.Editor.View.Windows;
|
||||
using Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
using Ghost.Editor.ViewModels.Pages.Landing;
|
||||
using Ghost.Editor.ViewModels.Windows;
|
||||
using Ghost.Engine;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Ghost.Editor.Utilities;
|
||||
|
||||
internal static partial class HostHelper
|
||||
{
|
||||
public static void AddLandingScope(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<LandingWindow>();
|
||||
|
||||
services.AddTransient<CreateProjectPage>();
|
||||
services.AddTransient<CreateProjectViewModel>();
|
||||
|
||||
services.AddTransient<OpenProjectPage>();
|
||||
services.AddTransient<OpenProjectViewModel>();
|
||||
|
||||
services.AddTransient<ProjectService>();
|
||||
}
|
||||
|
||||
public static void AddEngineScope(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<EngineCore>();
|
||||
|
||||
services.AddSingleton<EngineEditorWindow>();
|
||||
services.AddSingleton<EngineEditorViewModel>();
|
||||
|
||||
services.AddTransient<ScenePage>();
|
||||
|
||||
services.AddTransient<HierarchyPage>();
|
||||
services.AddTransient<HierarchyViewModel>();
|
||||
|
||||
services.AddTransient<ProjectPage>();
|
||||
services.AddTransient<ProjectViewModel>();
|
||||
|
||||
services.AddTransient<ConsolePage>();
|
||||
services.AddTransient<ConsoleViewModel>();
|
||||
|
||||
services.AddTransient<InspectorPage>();
|
||||
services.AddTransient<InspectorViewModel>();
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="Ghost.Editor.View.Pages.EngineEditor.ConsolePage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Ghost.Editor.View.Pages.EngineEditor"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Background="{ThemeResource LayerFillColorDefaultBrush}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultSolid}"
|
||||
BorderThickness="0,0,0,1">
|
||||
<CommandBar DefaultLabelPosition="Collapsed">
|
||||
<CommandBar.PrimaryCommands>
|
||||
<AppBarButton Command="{x:Bind ViewModel.ClearLogsCommand}" Content="Clear" />
|
||||
<AppBarSeparator />
|
||||
<AppBarToggleButton Width="45" IsChecked="{x:Bind ViewModel.ShowInfo, Mode=TwoWay}">
|
||||
<AppBarToggleButton.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</AppBarToggleButton.Icon>
|
||||
</AppBarToggleButton>
|
||||
<AppBarToggleButton Width="45" IsChecked="{x:Bind ViewModel.ShowWarning, Mode=TwoWay}">
|
||||
<AppBarToggleButton.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</AppBarToggleButton.Icon>
|
||||
</AppBarToggleButton>
|
||||
<AppBarToggleButton Width="45" IsChecked="{x:Bind ViewModel.ShowError, Mode=TwoWay}">
|
||||
<AppBarToggleButton.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</AppBarToggleButton.Icon>
|
||||
</AppBarToggleButton>
|
||||
</CommandBar.PrimaryCommands>
|
||||
|
||||
<CommandBar.SecondaryCommands>
|
||||
<AppBarToggleButton BorderThickness="0" Label="Clear On Play" />
|
||||
<AppBarToggleButton
|
||||
BorderThickness="0"
|
||||
IsChecked="{x:Bind ViewModel.ShowStackTrace, Mode=TwoWay}"
|
||||
Label="Show Stack Trace" />
|
||||
</CommandBar.SecondaryCommands>
|
||||
</CommandBar>
|
||||
</Grid>
|
||||
|
||||
<!-- Log Content -->
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="100" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ListView
|
||||
x:Name="LogListView"
|
||||
Grid.Row="0"
|
||||
ItemsSource="{x:Bind ViewModel.Logs, Mode=OneWay}"
|
||||
SelectedItem="{x:Bind ViewModel.SelectedLog, Mode=TwoWay}" />
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Padding="4"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultSolid}"
|
||||
BorderThickness="0,1,0,0">
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
|
||||
<TextBlock
|
||||
IsTextSelectionEnabled="True"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind ViewModel.SelectedLog.ToStringWithStackTrace(), Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -1,19 +0,0 @@
|
||||
using Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Ghost.Editor.View.Pages.EngineEditor;
|
||||
|
||||
internal sealed partial class ConsolePage : Page
|
||||
{
|
||||
public ConsoleViewModel ViewModel
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public ConsolePage()
|
||||
{
|
||||
ViewModel = App.GetService<ConsoleViewModel>();
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<internal:NavigationTabPage
|
||||
x:Class="Ghost.Editor.View.Pages.EngineEditor.HierarchyPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:internal="using:Ghost.Editor.Controls.Internal"
|
||||
xmlns:local="using:Ghost.Editor.View.Pages.EngineEditor"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:sg="using:Ghost.Editor.Core.SceneGraph"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<internal:NavigationTabPage.Resources>
|
||||
<DataTemplate x:Key="SceneTemplate" x:DataType="sg:SceneGraphNode">
|
||||
<TreeViewItem
|
||||
AutomationProperties.Name="{x:Bind Name, Mode=OneWay}"
|
||||
Background="{ThemeResource ControlSolidFillColorDefaultBrush}"
|
||||
IsExpanded="True"
|
||||
ItemsSource="{x:Bind Children, Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<FontIcon FontSize="14" Glyph="" />
|
||||
<TextBlock Margin="10,0" Text="{x:Bind Name, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</TreeViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="EntityTemplate" x:DataType="sg:SceneGraphNode">
|
||||
<TreeViewItem AutomationProperties.Name="{x:Bind Name, Mode=OneWay}" ItemsSource="{x:Bind Children, Mode=OneWay}">
|
||||
<StackPanel Margin="10,0" Orientation="Horizontal">
|
||||
<FontIcon FontSize="14" Glyph="" />
|
||||
<TextBlock Margin="5,0,0,0" Text="{x:Bind Name, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</TreeViewItem>
|
||||
</DataTemplate>
|
||||
</internal:NavigationTabPage.Resources>
|
||||
|
||||
<Grid Padding="4,6" Background="{ThemeResource LayerFillColorDefaultBrush}">
|
||||
<TreeView ItemsSource="{x:Bind ViewModel.SceneList}" SelectionChanged="TreeView_SelectionChanged">
|
||||
<TreeView.ItemTemplateSelector>
|
||||
<local:HierarchyTemplateSector EntityTemplate="{StaticResource EntityTemplate}" WorldTemplate="{StaticResource SceneTemplate}" />
|
||||
</TreeView.ItemTemplateSelector>
|
||||
</TreeView>
|
||||
</Grid>
|
||||
</internal:NavigationTabPage>
|
||||
@@ -1,79 +0,0 @@
|
||||
using Ghost.Editor.Controls.Internal;
|
||||
using Ghost.Editor.Core.Inspector;
|
||||
using Ghost.Editor.Core.SceneGraph;
|
||||
using Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Ghost.Editor.View.Pages.EngineEditor;
|
||||
|
||||
internal sealed partial class HierarchyPage : NavigationTabPage
|
||||
{
|
||||
private readonly IInspectorService _inspectorService;
|
||||
|
||||
public HierarchyViewModel ViewModel
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public HierarchyPage()
|
||||
{
|
||||
_inspectorService = App.GetService<IInspectorService>();
|
||||
ViewModel = App.GetService<HierarchyViewModel>();
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public override void OnNavigatedTo(object? parameter)
|
||||
{
|
||||
ViewModel.OnNavigatedTo(parameter);
|
||||
}
|
||||
|
||||
public override void OnNavigatedFrom()
|
||||
{
|
||||
ViewModel.OnNavigatedFrom();
|
||||
}
|
||||
|
||||
private void TreeView_SelectionChanged(TreeView sender, TreeViewSelectionChangedEventArgs args)
|
||||
{
|
||||
if (args.AddedItems.Count > 0 && args.AddedItems[0] is IInspectable inspectable)
|
||||
{
|
||||
_inspectorService.SelectedInspectable = inspectable;
|
||||
}
|
||||
else
|
||||
{
|
||||
_inspectorService.SelectedInspectable = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal partial class HierarchyTemplateSector : DataTemplateSelector
|
||||
{
|
||||
public DataTemplate? WorldTemplate
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public DataTemplate? EntityTemplate
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item)
|
||||
{
|
||||
if (WorldTemplate == null || EntityTemplate == null)
|
||||
{
|
||||
return base.SelectTemplateCore(item);
|
||||
}
|
||||
|
||||
var node = (SceneGraphNode)item;
|
||||
return node.NodeType switch
|
||||
{
|
||||
SceneGraphNodeType.Scene => WorldTemplate,
|
||||
SceneGraphNodeType.Entity => EntityTemplate,
|
||||
_ => base.SelectTemplateCore(item)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<internal:NavigationTabPage
|
||||
x:Class="Ghost.Editor.View.Pages.EngineEditor.InspectorPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:internal="using:Ghost.Editor.Controls.Internal"
|
||||
xmlns:local="using:Ghost.Editor.View.Pages.EngineEditor"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Background="{ThemeResource LayerFillColorDefaultBrush}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="75" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Header -->
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
Padding="15,0,10,0"
|
||||
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultSolid}"
|
||||
BorderThickness="0,0,0,1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<IconSourceElement
|
||||
Grid.Column="0"
|
||||
Margin="0,0,15,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
IconSource="{x:Bind ViewModel.Inspectable.Icon, Mode=OneWay}" />
|
||||
<ContentPresenter Grid.Column="1" Content="{x:Bind ViewModel.Inspectable.HeaderContent, Mode=OneWay}" />
|
||||
</Grid>
|
||||
|
||||
<!-- Content -->
|
||||
<Grid Grid.Row="1" Padding="0,0,0,0">
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
||||
<ContentPresenter Content="{x:Bind ViewModel.Inspectable.InspectorContent, Mode=OneWay}" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</internal:NavigationTabPage>
|
||||
@@ -1,29 +0,0 @@
|
||||
using Ghost.Editor.Controls.Internal;
|
||||
using Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
|
||||
namespace Ghost.Editor.View.Pages.EngineEditor;
|
||||
|
||||
internal sealed partial class InspectorPage : NavigationTabPage
|
||||
{
|
||||
public InspectorViewModel ViewModel
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public InspectorPage()
|
||||
{
|
||||
ViewModel = App.GetService<InspectorViewModel>();
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public override void OnNavigatedTo(object? parameter)
|
||||
{
|
||||
ViewModel.OnNavigatedTo(parameter);
|
||||
}
|
||||
|
||||
public override void OnNavigatedFrom()
|
||||
{
|
||||
ViewModel.OnNavigatedFrom();
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="Ghost.Editor.View.Pages.EngineEditor.ProjectPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converter="using:Ghost.Editor.Utilities.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Ghost.Editor.View.Pages.EngineEditor"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:model="using:Ghost.Editor.Models"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<converter:AssetPathToGlyphConverter x:Key="AssetPathToGlyphConverter" />
|
||||
</Page.Resources>
|
||||
|
||||
<Grid Background="{ThemeResource LayerFillColorDefaultBrush}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="250" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Folder Tree View -->
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
Padding="4"
|
||||
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultSolid}"
|
||||
BorderThickness="0,0,1,0">
|
||||
<TreeView
|
||||
x:Name="DirectoryTreeView"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
ItemsSource="{x:Bind ViewModel.SubDirectories}"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
SelectedItem="{x:Bind ViewModel.SelectedDirectory, Mode=TwoWay}">
|
||||
<TreeView.ItemTemplate>
|
||||
<DataTemplate x:DataType="model:ExplorerItem">
|
||||
<TreeViewItem ItemsSource="{x:Bind Children}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<FontIcon
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Glyph="" />
|
||||
<TextBlock
|
||||
Margin="8,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind Name}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
</StackPanel>
|
||||
</TreeViewItem>
|
||||
</DataTemplate>
|
||||
</TreeView.ItemTemplate>
|
||||
</TreeView>
|
||||
</Grid>
|
||||
|
||||
<!-- Files -->
|
||||
<Grid Grid.Column="1">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
Padding="4"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultSolid}"
|
||||
BorderThickness="0,0,0,1">
|
||||
<BreadcrumbBar Height="15" />
|
||||
</Grid>
|
||||
|
||||
<ScrollViewer
|
||||
Grid.Row="1"
|
||||
Padding="8"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<GridView
|
||||
x:Name="AssetsGridView"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
ItemsSource="{x:Bind ViewModel.DirectoryAssets, Mode=OneWay}"
|
||||
SelectedItem="{x:Bind ViewModel.SelectedAsset, Mode=TwoWay}">
|
||||
<GridView.ItemContainerStyle>
|
||||
<Style BasedOn="{StaticResource DefaultGridViewItemStyle}" TargetType="GridViewItem">
|
||||
<Setter Property="Margin" Value="2" />
|
||||
</Style>
|
||||
</GridView.ItemContainerStyle>
|
||||
|
||||
<GridView.ItemTemplate>
|
||||
<DataTemplate x:DataType="model:ExplorerItem">
|
||||
<Grid
|
||||
Width="100"
|
||||
Height="100"
|
||||
Padding="8"
|
||||
DoubleTapped="GridViewItem_DoubleTapped"
|
||||
IsDoubleTapEnabled="True">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="0.25*" />
|
||||
</Grid.RowDefinitions>
|
||||
<FontIcon FontSize="42" Glyph="{x:Bind FullName, Converter={StaticResource AssetPathToGlyphConverter}}" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="8,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind Name}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</GridView.ItemTemplate>
|
||||
</GridView>
|
||||
</ScrollViewer>
|
||||
|
||||
<Grid
|
||||
Grid.Row="2"
|
||||
Padding="4"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultSolid}"
|
||||
BorderThickness="0,1,0,0">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
HorizontalTextAlignment="Left"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind ViewModel.SelectedAsset.FullName, Mode=OneWay}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -1,25 +0,0 @@
|
||||
using Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
|
||||
namespace Ghost.Editor.View.Pages.EngineEditor;
|
||||
|
||||
internal sealed partial class ProjectPage : Page
|
||||
{
|
||||
public ProjectViewModel ViewModel
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public ProjectPage()
|
||||
{
|
||||
ViewModel = App.GetService<ProjectViewModel>();
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void GridViewItem_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
|
||||
{
|
||||
ViewModel.OpenSelected();
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<internal:NavigationTabPage
|
||||
x:Class="Ghost.Editor.View.Pages.EngineEditor.ScenePage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:internal="using:Ghost.Editor.Controls.Internal"
|
||||
xmlns:local="using:Ghost.Editor.View.Pages.EngineEditor"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<SwapChainPanel
|
||||
x:Name="SwapChainPanel"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch" />
|
||||
</Grid>
|
||||
</internal:NavigationTabPage>
|
||||
@@ -1,45 +0,0 @@
|
||||
using Ghost.Editor.Controls.Internal;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using WinRT;
|
||||
|
||||
namespace Ghost.Editor.View.Pages.EngineEditor;
|
||||
|
||||
internal sealed partial class ScenePage : NavigationTabPage
|
||||
{
|
||||
private Renderer? _renderView;
|
||||
private ISwapChainPanelNative _swapChainPanelNative;
|
||||
|
||||
public ScenePage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
SwapChainPanel.Loaded += SwapChainPanel_Loaded;
|
||||
SwapChainPanel.Unloaded += SwapChainPanel_Unloaded;
|
||||
SwapChainPanel.SizeChanged += SwapChainPanel_SizeChanged;
|
||||
}
|
||||
|
||||
private void SwapChainPanel_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var guid = typeof(ISwapChainPanelNative.Interface).GUID;
|
||||
((IWinRTObject)SwapChainPanel).NativeObject.TryAs(guid, out var swapChainPanelNativeHandle);
|
||||
_swapChainPanelNative = new ISwapChainPanelNative(swapChainPanelNativeHandle);
|
||||
|
||||
_renderView = GraphicsPipeline.GraphicsDevice.CreateRenderer(new(_swapChainPanelNative, (uint)SwapChainPanel.ActualWidth, (uint)SwapChainPanel.ActualHeight));
|
||||
}
|
||||
|
||||
private void SwapChainPanel_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_swapChainPanelNative.Dispose();
|
||||
_renderView?.Dispose();
|
||||
}
|
||||
|
||||
private void SwapChainPanel_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
if (e.NewSize.Width > 8.0 && e.NewSize.Height > 8.0)
|
||||
{
|
||||
_renderView?.RequestResize((uint)e.NewSize.Width, (uint)e.NewSize.Height);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="Ghost.Editor.View.Pages.Landing.CreateProjectPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:data="using:Ghost.Data.Models"
|
||||
xmlns:editor="using:Ghost.Editor.Core.Controls"
|
||||
xmlns:local="using:Ghost.Editor.View.Pages.Landing"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
NavigationCacheMode="Enabled"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Template Info -->
|
||||
<Grid Grid.Column="0" Width="300">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Margin="0,0,0,24"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="Template" />
|
||||
|
||||
<ListView
|
||||
Grid.Row="1"
|
||||
ItemsSource="{x:Bind ViewModel.templates}"
|
||||
SelectedItem="{x:Bind ViewModel.SelectedTemplate, Mode=TwoWay}">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="data:TemplateData">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ImageIcon
|
||||
Grid.Column="0"
|
||||
Width="24"
|
||||
Height="24">
|
||||
<ImageIcon.Source>
|
||||
<BitmapImage UriSource="{x:Bind GetIconURI()}" />
|
||||
</ImageIcon.Source>
|
||||
</ImageIcon>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="8,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind Info.Name}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
|
||||
<!-- Project Info -->
|
||||
<Grid
|
||||
Grid.Column="1"
|
||||
Margin="16,0,0,0"
|
||||
Padding="16"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
CornerRadius="{StaticResource OverlayCornerRadius}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="300" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0" CornerRadius="4">
|
||||
<Image VerticalAlignment="Center" Stretch="UniformToFill">
|
||||
<Image.Source>
|
||||
<BitmapImage UriSource="{x:Bind ViewModel.SelectedTemplate.Value.GetPreviewURI(), Mode=OneWay}" />
|
||||
</Image.Source>
|
||||
</Image>
|
||||
<Grid
|
||||
MaxHeight="100"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="{ThemeResource ControlOnImageFillColorDefaultBrush}">
|
||||
<TextBlock
|
||||
Margin="16"
|
||||
VerticalAlignment="Bottom"
|
||||
Foreground="{ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="{x:Bind ViewModel.SelectedTemplate.Value.Info.Description, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<StackPanel Grid.Row="1" Margin="8,0">
|
||||
<TextBlock
|
||||
Margin="0,16,0,8"
|
||||
Style="{StaticResource TitleTextBlockStyle}"
|
||||
Text="{x:Bind ViewModel.SelectedTemplate.Value.Info.Name, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Margin="0,8,0,16"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="Project Settings" />
|
||||
|
||||
<editor:PropertyField Label="Name">
|
||||
<TextBox Text="{x:Bind ViewModel.ProjectName, Mode=TwoWay}" />
|
||||
</editor:PropertyField>
|
||||
<editor:PropertyField Label="Location">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox
|
||||
Grid.Column="0"
|
||||
IsReadOnly="True"
|
||||
Text="{x:Bind ViewModel.ProjectLocation, Mode=TwoWay}" />
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
Margin="4,0,0,0"
|
||||
VerticalAlignment="Stretch"
|
||||
Command="{x:Bind ViewModel.SelectionProjectLocationCommand}">
|
||||
<FontIcon FontSize="16" Glyph="" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</editor:PropertyField>
|
||||
</StackPanel>
|
||||
|
||||
<Grid Grid.Row="2">
|
||||
<Button
|
||||
Width="150"
|
||||
HorizontalAlignment="Right"
|
||||
Command="{x:Bind ViewModel.CreateProjectCommand}"
|
||||
Content="Create"
|
||||
Style="{ThemeResource AccentButtonStyle}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -1,32 +0,0 @@
|
||||
using Ghost.Editor.ViewModels.Pages.Landing;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
|
||||
namespace Ghost.Editor.View.Pages.Landing;
|
||||
|
||||
internal sealed partial class CreateProjectPage : Page
|
||||
{
|
||||
public CreateProjectViewModel ViewModel
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public CreateProjectPage()
|
||||
{
|
||||
ViewModel = App.GetService<CreateProjectViewModel>();
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
ViewModel.OnNavigatedTo(e.Parameter);
|
||||
}
|
||||
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedFrom(e);
|
||||
ViewModel.OnNavigatedFrom();
|
||||
}
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="Ghost.Editor.View.Pages.Landing.OpenProjectPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="using:Ghost.Editor.Utilities.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:data="using:Ghost.Data.Models"
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:local="using:Ghost.Editor.View.Pages.Landing"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
NavigationCacheMode="Enabled"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<converters:GetDirectoryNameConverter x:Key="DirNameConverter" />
|
||||
</Page.Resources>
|
||||
|
||||
<Grid x:Name="MainContainer">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0" Margin="16,4">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="Projects" />
|
||||
<AutoSuggestBox
|
||||
Width="300"
|
||||
HorizontalAlignment="Right"
|
||||
PlaceholderText="Search project by name"
|
||||
QueryIcon="Find" />
|
||||
</Grid>
|
||||
|
||||
<!-- Header for the ListView -->
|
||||
<Grid Grid.Row="1" Margin="28,16,45,8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="200" />
|
||||
<ColumnDefinition Width="165" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="NAME" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="LAST OPEN" />
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="ENGINE VERSION" />
|
||||
</Grid>
|
||||
|
||||
<!-- Project ListView -->
|
||||
<Grid
|
||||
Grid.Row="2"
|
||||
Padding="8"
|
||||
AllowDrop="True"
|
||||
DragEnter="ProjectContainer_DragEnter"
|
||||
DragLeave="ProjectContainer_DragLeave"
|
||||
DragOver="ProjectContainer_DragOver"
|
||||
Drop="ProjectContainer_Drop">
|
||||
<ListView
|
||||
Padding="4,8"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
CornerRadius="{StaticResource OverlayCornerRadius}"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="ListView_ItemClick"
|
||||
ItemsSource="{x:Bind ViewModel.projects}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="data:ProjectMetadataInfo">
|
||||
<Grid Height="64" Padding="4,8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="200" />
|
||||
<ColumnDefinition Width="100" />
|
||||
<ColumnDefinition Width="65" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Column="0" VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="{x:Bind Metadata.Name}" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="0,4,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind Path, Converter={StaticResource DirNameConverter}}" />
|
||||
</Grid>
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="16,4"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind Metadata.LastOpened}" />
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
Margin="16,4"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind Metadata.EngineVersion}" />
|
||||
<Button
|
||||
Grid.Column="3"
|
||||
HorizontalAlignment="Right"
|
||||
Background="Transparent"
|
||||
BorderThickness="0">
|
||||
<FontIcon Glyph="" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
|
||||
<!-- Drag Visual -->
|
||||
<Grid
|
||||
x:Name="DragVisual"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource ControlStrongStrokeColorDefaultBrush}"
|
||||
BorderThickness="2"
|
||||
CornerRadius="{StaticResource OverlayCornerRadius}"
|
||||
Visibility="{x:Bind ViewModel.DragVisibility, Mode=OneWay}">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource TitleTextBlockStyle}"
|
||||
Text="Drage Project Folder Here" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<!-- Empty Place Holder -->
|
||||
<Grid
|
||||
x:Name="EmptyPlaceHolder"
|
||||
Grid.Row="2"
|
||||
Visibility="{x:Bind ViewModel.EmptyVisibility, Mode=OneWay}">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource TitleTextBlockStyle}"
|
||||
Text="No projects found" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -1,72 +0,0 @@
|
||||
using Ghost.Data.Models;
|
||||
using Ghost.Editor.ViewModels.Pages.Landing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
|
||||
namespace Ghost.Editor.View.Pages.Landing;
|
||||
|
||||
internal sealed partial class OpenProjectPage : Page
|
||||
{
|
||||
public OpenProjectViewModel ViewModel
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public OpenProjectPage()
|
||||
{
|
||||
ViewModel = App.GetService<OpenProjectViewModel>();
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
ViewModel.OnNavigatedTo(e.Parameter);
|
||||
}
|
||||
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedFrom(e);
|
||||
ViewModel.OnNavigatedFrom();
|
||||
}
|
||||
|
||||
private void ProjectContainer_DragEnter(object sender, DragEventArgs e)
|
||||
{
|
||||
ViewModel.DragVisibility = Visibility.Visible;
|
||||
ViewModel.EmptyVisibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void ProjectContainer_DragLeave(object sender, DragEventArgs e)
|
||||
{
|
||||
ViewModel.DragVisibility = Visibility.Collapsed;
|
||||
ViewModel.UpdateEmptyPlaceHolderVisibility();
|
||||
}
|
||||
|
||||
private void ProjectContainer_DragOver(object sender, DragEventArgs e)
|
||||
{
|
||||
if (e.DataView.Contains(StandardDataFormats.StorageItems))
|
||||
{
|
||||
e.AcceptedOperation = DataPackageOperation.Link;
|
||||
}
|
||||
else
|
||||
{
|
||||
e.AcceptedOperation = DataPackageOperation.None;
|
||||
}
|
||||
}
|
||||
|
||||
private async void ProjectContainer_Drop(object sender, DragEventArgs e)
|
||||
{
|
||||
await ViewModel.ContentDrop(e.DataView);
|
||||
}
|
||||
|
||||
private async void ListView_ItemClick(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
if (e.ClickedItem is ProjectMetadataInfo project)
|
||||
{
|
||||
await Task.Yield();
|
||||
await ViewModel.OpenProjectAsync(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,249 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<winex:WindowEx
|
||||
x:Class="Ghost.Editor.View.Windows.EngineEditorWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:behaviors="using:CommunityToolkit.WinUI.Behaviors"
|
||||
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:ee="using:Ghost.Editor.View.Pages.EngineEditor"
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:internal="using:Ghost.Editor.Controls.Internal"
|
||||
xmlns:local="using:Ghost.Editor.View.Windows"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:winex="using:WinUIEx"
|
||||
Activated="WindowEx_Activated"
|
||||
Closed="WindowEx_Closed"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Window.SystemBackdrop>
|
||||
<MicaBackdrop />
|
||||
</Window.SystemBackdrop>
|
||||
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Titlebar -->
|
||||
<StackPanel
|
||||
Grid.Row="0"
|
||||
Padding="8"
|
||||
Orientation="Horizontal">
|
||||
<ImageIcon
|
||||
Width="24"
|
||||
Height="24"
|
||||
VerticalAlignment="Center"
|
||||
Source="ms-appx:///Assets/Icon.targetsize-32.png" />
|
||||
<TextBlock
|
||||
Margin="8,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind ViewModel.engineVersionDescriptor}" />
|
||||
<TextBlock
|
||||
Margin="8,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind ViewModel.CurrentProject.Metadata.Name, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<Grid Grid.Row="1" Margin="4,4">
|
||||
<controls:TabbedCommandBar>
|
||||
<controls:TabbedCommandBar.MenuItems>
|
||||
<controls:TabbedCommandBarItem Header="Home">
|
||||
<AppBarButton Label="Undo" />
|
||||
<AppBarButton Label="Redo" />
|
||||
<AppBarButton Label="Paste" />
|
||||
</controls:TabbedCommandBarItem>
|
||||
<controls:TabbedCommandBarItem Header="Home">
|
||||
<AppBarButton Label="Undo" />
|
||||
<AppBarButton Label="Redo" />
|
||||
<AppBarButton Label="Paste" />
|
||||
</controls:TabbedCommandBarItem>
|
||||
<controls:TabbedCommandBarItem Header="Home">
|
||||
<AppBarButton Label="Undo" />
|
||||
<AppBarButton Label="Redo" />
|
||||
<AppBarButton Label="Paste" />
|
||||
</controls:TabbedCommandBarItem>
|
||||
</controls:TabbedCommandBar.MenuItems>
|
||||
</controls:TabbedCommandBar>
|
||||
</Grid>
|
||||
|
||||
<!-- Editor -->
|
||||
<Grid Grid.Row="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<internal:NavigationTabView
|
||||
Grid.Column="0"
|
||||
Width="350"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<internal:NavigationTabView.TabItems>
|
||||
<ee:HierarchyPage Header="Hierarchy">
|
||||
<ee:HierarchyPage.IconSource>
|
||||
<FontIconSource Glyph="" />
|
||||
</ee:HierarchyPage.IconSource>
|
||||
</ee:HierarchyPage>
|
||||
</internal:NavigationTabView.TabItems>
|
||||
</internal:NavigationTabView>
|
||||
|
||||
<internal:NavigationTabView Grid.Column="1">
|
||||
<internal:NavigationTabView.TabItems>
|
||||
<ee:ScenePage Header="Scene">
|
||||
<ee:ScenePage.IconSource>
|
||||
<FontIconSource Glyph="" />
|
||||
</ee:ScenePage.IconSource>
|
||||
</ee:ScenePage>
|
||||
</internal:NavigationTabView.TabItems>
|
||||
</internal:NavigationTabView>
|
||||
|
||||
<internal:NavigationTabView
|
||||
Grid.Column="2"
|
||||
Width="350"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<internal:NavigationTabView.TabItems>
|
||||
<ee:InspectorPage Header="Inspector">
|
||||
<ee:InspectorPage.IconSource>
|
||||
<FontIconSource Glyph="" />
|
||||
</ee:InspectorPage.IconSource>
|
||||
</ee:InspectorPage>
|
||||
</internal:NavigationTabView.TabItems>
|
||||
</internal:NavigationTabView>
|
||||
</Grid>
|
||||
|
||||
<internal:NavigationTabView Grid.Row="1" Height="350">
|
||||
<internal:NavigationTabView.TabItems>
|
||||
<TabViewItem Header="Project">
|
||||
<TabViewItem.IconSource>
|
||||
<FontIconSource Glyph="" />
|
||||
</TabViewItem.IconSource>
|
||||
<ee:ProjectPage />
|
||||
</TabViewItem>
|
||||
<TabViewItem Header="Console">
|
||||
<TabViewItem.IconSource>
|
||||
<FontIconSource Glyph="" />
|
||||
</TabViewItem.IconSource>
|
||||
<ee:ConsolePage />
|
||||
</TabViewItem>
|
||||
</internal:NavigationTabView.TabItems>
|
||||
</internal:NavigationTabView>
|
||||
</Grid>
|
||||
|
||||
<!-- Status Bar -->
|
||||
<Grid
|
||||
Grid.Row="3"
|
||||
Height="25"
|
||||
Background="{ThemeResource SolidBackgroundFillColorBaseAltBrush}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Column="0">
|
||||
<FontIcon
|
||||
Margin="8,0,0,0"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource SystemFillColorSuccessBrush}"
|
||||
Glyph=""
|
||||
Visibility="Visible" />
|
||||
|
||||
<StackPanel Orientation="Horizontal" Visibility="Collapsed">
|
||||
<FontIcon
|
||||
Margin="8,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource SystemFillColorAttentionBrush}"
|
||||
Glyph="" />
|
||||
<TextBlock
|
||||
Margin="4,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="0" />
|
||||
<FontIcon
|
||||
Margin="8,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource SystemFillColorCautionBrush}"
|
||||
Glyph="" />
|
||||
<TextBlock
|
||||
Margin="4,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="0" />
|
||||
<FontIcon
|
||||
Margin="8,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource SystemFillColorCriticalBrush}"
|
||||
Glyph="" />
|
||||
<TextBlock
|
||||
Margin="4,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="0" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="4">
|
||||
<InfoBar
|
||||
x:Name="InfoBar"
|
||||
Margin="16"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<behaviors:StackedNotificationsBehavior x:Name="NotificationQueue" />
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</InfoBar>
|
||||
|
||||
<Grid
|
||||
x:Name="ProgressBarContainer"
|
||||
Background="{ThemeResource SmokeFillColorDefaultBrush}"
|
||||
Visibility="Collapsed">
|
||||
<Grid
|
||||
Height="100"
|
||||
Padding="36,24"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Background="{ThemeResource SolidBackgroundFillColorBaseBrush}"
|
||||
CornerRadius="{StaticResource OverlayCornerRadius}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock
|
||||
x:Name="ProgressMessage"
|
||||
Grid.Row="0"
|
||||
Margin="0,0,0,12"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource TitleTextBlockStyle}"
|
||||
Text="Loading..." />
|
||||
<ProgressBar
|
||||
x:Name="ProgressBar"
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
IsIndeterminate="True" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</winex:WindowEx>
|
||||
@@ -1,54 +0,0 @@
|
||||
using Ghost.Data.Resources;
|
||||
using Ghost.Editor.Core.Notifications;
|
||||
using Ghost.Editor.Core.Progress;
|
||||
using Ghost.Editor.ViewModels.Windows;
|
||||
using Ghost.Engine.Resources;
|
||||
using WinUIEx;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
|
||||
namespace Ghost.Editor.View.Windows;
|
||||
/// <summary>
|
||||
/// An empty window that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
internal sealed partial class EngineEditorWindow : WindowEx
|
||||
{
|
||||
private readonly NotificationService _notificationService;
|
||||
private readonly ProgressService _progressService;
|
||||
|
||||
public EngineEditorViewModel ViewModel
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public EngineEditorWindow()
|
||||
{
|
||||
ViewModel = App.GetService<EngineEditorViewModel>();
|
||||
|
||||
_notificationService = (NotificationService)App.GetService<INotificationService>();
|
||||
_progressService = (ProgressService)App.GetService<IProgressService>();
|
||||
|
||||
AppWindow.SetIcon(AssetsPath.s_appIconPath);
|
||||
Title = EngineData.ENGINE_NAME;
|
||||
ExtendsContentIntoTitleBar = true;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
this.CenterOnScreen();
|
||||
}
|
||||
|
||||
private void WindowEx_Activated(object sender, Microsoft.UI.Xaml.WindowActivatedEventArgs args)
|
||||
{
|
||||
Bindings.Update();
|
||||
|
||||
_notificationService.SetReference(InfoBar, NotificationQueue);
|
||||
_progressService.SetReference(ProgressBarContainer);
|
||||
}
|
||||
|
||||
private void WindowEx_Closed(object sender, Microsoft.UI.Xaml.WindowEventArgs args)
|
||||
{
|
||||
_notificationService.ClearReference();
|
||||
_progressService.ClearReference();
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<winex:WindowEx
|
||||
x:Class="Ghost.Editor.View.Windows.LandingWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:behaviors="using:CommunityToolkit.WinUI.Behaviors"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:local="using:Ghost.Editor.View.Windows"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:winex="using:WinUIEx"
|
||||
Activated="WindowEx_Activated"
|
||||
Closed="WindowEx_Closed"
|
||||
IsResizable="False"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Window.SystemBackdrop>
|
||||
<MicaBackdrop />
|
||||
</Window.SystemBackdrop>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0">
|
||||
<TextBlock
|
||||
Margin="24,0,0,0"
|
||||
VerticalAlignment="Bottom"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="Ghost Engine" />
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="1" Padding="24,0,24,18">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<SelectorBar
|
||||
Grid.Row="0"
|
||||
HorizontalAlignment="Right"
|
||||
SelectionChanged="SelectorBar_SelectionChanged">
|
||||
<SelectorBarItem IsSelected="True" Text="Open">
|
||||
<SelectorBarItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</SelectorBarItem.Icon>
|
||||
</SelectorBarItem>
|
||||
<SelectorBarItem Text="Create">
|
||||
<SelectorBarItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</SelectorBarItem.Icon>
|
||||
</SelectorBarItem>
|
||||
</SelectorBar>
|
||||
|
||||
<Frame
|
||||
x:Name="ContentFrame"
|
||||
Grid.Row="1"
|
||||
Padding="8"
|
||||
CacheMode="BitmapCache"
|
||||
CacheSize="10" />
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="1" Padding="16">
|
||||
<InfoBar
|
||||
x:Name="InfoBar"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<behaviors:StackedNotificationsBehavior x:Name="NotificationQueue" />
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</InfoBar>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</winex:WindowEx>
|
||||
@@ -1,59 +0,0 @@
|
||||
using Ghost.Data.Resources;
|
||||
using Ghost.Editor.Core.Notifications;
|
||||
using Ghost.Editor.View.Pages.Landing;
|
||||
using Ghost.Engine.Resources;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
using WinUIEx;
|
||||
|
||||
namespace Ghost.Editor.View.Windows;
|
||||
|
||||
internal sealed partial class LandingWindow : WindowEx
|
||||
{
|
||||
private readonly NotificationService _notificationService;
|
||||
|
||||
private int _previousSelectedIndex;
|
||||
|
||||
public LandingWindow()
|
||||
{
|
||||
_notificationService = (NotificationService)App.GetService<INotificationService>();
|
||||
|
||||
AppWindow.SetIcon(AssetsPath.s_appIconPath);
|
||||
Title = EngineData.ENGINE_NAME;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
this.SetWindowSize(1000, 750);
|
||||
this.CenterOnScreen();
|
||||
|
||||
ExtendsContentIntoTitleBar = true;
|
||||
}
|
||||
|
||||
private void WindowEx_Activated(object sender, Microsoft.UI.Xaml.WindowActivatedEventArgs args)
|
||||
{
|
||||
_notificationService.SetReference(InfoBar, NotificationQueue);
|
||||
}
|
||||
|
||||
private void WindowEx_Closed(object sender, Microsoft.UI.Xaml.WindowEventArgs args)
|
||||
{
|
||||
_notificationService.ClearReference();
|
||||
}
|
||||
|
||||
private void SelectorBar_SelectionChanged(SelectorBar sender, SelectorBarSelectionChangedEventArgs e)
|
||||
{
|
||||
var selectedItem = sender.SelectedItem;
|
||||
var currentSelectedIndex = sender.Items.IndexOf(selectedItem);
|
||||
var pageType = currentSelectedIndex switch
|
||||
{
|
||||
1 => typeof(CreateProjectPage),
|
||||
_ => typeof(OpenProjectPage),
|
||||
};
|
||||
|
||||
var slideNavigationTransitionEffect = currentSelectedIndex - _previousSelectedIndex > 0 ?
|
||||
SlideNavigationTransitionEffect.FromRight : SlideNavigationTransitionEffect.FromLeft;
|
||||
|
||||
ContentFrame.Navigate(pageType, null, new SlideNavigationTransitionInfo() { Effect = slideNavigationTransitionEffect });
|
||||
|
||||
_previousSelectedIndex = currentSelectedIndex;
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Ghost.Engine.Models;
|
||||
using Ghost.Engine.Services;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
|
||||
internal partial class ConsoleViewModel : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<LogMessage> Logs
|
||||
{
|
||||
get; set;
|
||||
} = new();
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool ShowInfo
|
||||
{
|
||||
get; set;
|
||||
} = true;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool ShowWarning
|
||||
{
|
||||
get; set;
|
||||
} = true;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool ShowError
|
||||
{
|
||||
get; set;
|
||||
} = true;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool ShowStackTrace
|
||||
{
|
||||
get; set;
|
||||
} = false;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial LogMessage? SelectedLog
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public ConsoleViewModel()
|
||||
{
|
||||
foreach (var log in Logger.Logs)
|
||||
{
|
||||
Logs.Add(log);
|
||||
}
|
||||
|
||||
Logger.OnLogsUpdate += UpdateLogs;
|
||||
}
|
||||
|
||||
~ConsoleViewModel()
|
||||
{
|
||||
Logger.OnLogsUpdate -= UpdateLogs;
|
||||
}
|
||||
|
||||
private void UpdateLogs(LogChangeContext ctx)
|
||||
{
|
||||
switch (ctx.changeType)
|
||||
{
|
||||
case LogChangeType.LogAdded:
|
||||
Logs.Add(Logger.Logs[ctx.index]);
|
||||
break;
|
||||
case LogChangeType.LogRemoved:
|
||||
if (Logs.Count > 0)
|
||||
{
|
||||
Logs.RemoveAt(ctx.index);
|
||||
}
|
||||
break;
|
||||
case LogChangeType.LogsCleared:
|
||||
Logs.Clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnShowStackTraceChanged(bool value)
|
||||
{
|
||||
Logger.HasStackTrace = value;
|
||||
Logger.LogInfo($"Stack trace visibility set to {value}.");
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ClearLogs()
|
||||
{
|
||||
Logger.Clear();
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Core.SceneGraph;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
|
||||
internal partial class HierarchyViewModel : ObservableObject, INavigationAware
|
||||
{
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<WorldNode> SceneList
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
} = new(EditorWorldManager.LoadedWorlds);
|
||||
|
||||
private void OnWorldLoaded(WorldNode node)
|
||||
{
|
||||
SceneList.Add(node);
|
||||
}
|
||||
|
||||
private void OnWorldUnloaded(WorldNode node)
|
||||
{
|
||||
SceneList.Remove(node);
|
||||
}
|
||||
|
||||
public void OnNavigatedTo(object? parameter)
|
||||
{
|
||||
EditorWorldManager.OnWorldLoaded += OnWorldLoaded;
|
||||
EditorWorldManager.OnWorldUnloaded += OnWorldUnloaded;
|
||||
}
|
||||
|
||||
public void OnNavigatedFrom()
|
||||
{
|
||||
EditorWorldManager.OnWorldLoaded -= OnWorldLoaded;
|
||||
EditorWorldManager.OnWorldUnloaded -= OnWorldUnloaded;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Core.Inspector;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
|
||||
internal partial class InspectorViewModel(IInspectorService inspectorService) : ObservableObject, INavigationAware
|
||||
{
|
||||
[ObservableProperty]
|
||||
public partial IInspectable? Inspectable
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public void OnNavigatedTo(object? parameter)
|
||||
{
|
||||
inspectorService.OnSelectionChanged += OnSelectionChanged;
|
||||
Inspectable = inspectorService.SelectedInspectable;
|
||||
}
|
||||
|
||||
public void OnNavigatedFrom()
|
||||
{
|
||||
inspectorService.OnSelectionChanged -= OnSelectionChanged;
|
||||
Inspectable = null;
|
||||
}
|
||||
|
||||
private void OnSelectionChanged()
|
||||
{
|
||||
Inspectable = inspectorService.SelectedInspectable;
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Editor.Core.AssetHandle;
|
||||
using Ghost.Editor.Models;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
|
||||
internal partial class ProjectViewModel : ObservableObject
|
||||
{
|
||||
public ObservableCollection<ExplorerItem> SubDirectories
|
||||
{
|
||||
get;
|
||||
} = new();
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<ExplorerItem> DirectoryAssets
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = new();
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ExplorerItem? SelectedDirectory
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ExplorerItem? SelectedAsset
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public ProjectViewModel()
|
||||
{
|
||||
if (ProjectService.CurrentProject.Metadata == null)
|
||||
{
|
||||
throw new InvalidOperationException("Current project is not set.");
|
||||
}
|
||||
|
||||
var assetsRootItem = new ExplorerItem("Assets", Path.Combine(Path.GetDirectoryName(ProjectService.CurrentProject.Path)!, ProjectService.ASSETS_FOLDER), true);
|
||||
LoadSubFolderRecursive(ref assetsRootItem);
|
||||
|
||||
SubDirectories.Add(assetsRootItem);
|
||||
}
|
||||
|
||||
private static void LoadSubFolderRecursive(ref ExplorerItem parentItem)
|
||||
{
|
||||
foreach (var directory in Directory.EnumerateDirectories(parentItem.FullName))
|
||||
{
|
||||
var item = new ExplorerItem(Path.GetFileName(directory), directory, true);
|
||||
LoadSubFolderRecursive(ref item);
|
||||
|
||||
parentItem.Children ??= new();
|
||||
parentItem.Children.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public static Task<ExplorerItem?> FindNodeIterative(ExplorerItem root, Func<ExplorerItem, bool> predicate)
|
||||
{
|
||||
var stack = new Stack<ExplorerItem>();
|
||||
stack.Push(root);
|
||||
|
||||
return Task.Run(() =>
|
||||
{
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
var node = stack.Pop();
|
||||
if (predicate(node))
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
if (node.Children == null || node.Children.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var i = node.Children.Count - 1; i >= 0; i--)
|
||||
{
|
||||
stack.Push(node.Children[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private void NavigateToDirectory(string? path)
|
||||
{
|
||||
App.Window?.DispatcherQueue.TryEnqueue(async () =>
|
||||
{
|
||||
DirectoryAssets.Clear();
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var directory in Directory.EnumerateDirectories(path))
|
||||
{
|
||||
var directoryItem = new ExplorerItem(Path.GetFileName(directory), directory, true);
|
||||
DirectoryAssets.Add(directoryItem);
|
||||
}
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(path))
|
||||
{
|
||||
var fileItem = new ExplorerItem(Path.GetFileName(file), file, false);
|
||||
DirectoryAssets.Add(fileItem);
|
||||
}
|
||||
|
||||
SelectedDirectory = await FindNodeIterative(SubDirectories[0], x => x.FullName == path);
|
||||
});
|
||||
}
|
||||
|
||||
public void OpenSelected()
|
||||
{
|
||||
if (SelectedAsset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (SelectedAsset.IsDirectory)
|
||||
{
|
||||
NavigateToDirectory(SelectedAsset.FullName);
|
||||
}
|
||||
else
|
||||
{
|
||||
AssetDatabase.OpenAsset(SelectedAsset.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnSelectedDirectoryChanged(ExplorerItem? value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DirectoryAssets.Clear();
|
||||
NavigateToDirectory(value.FullName);
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Ghost.Data.Models;
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Editor.Core.AppState;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Core.Notifications;
|
||||
using Ghost.Editor.Utilities;
|
||||
using Ghost.Engine.Resources;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.Landing;
|
||||
|
||||
internal partial class CreateProjectViewModel(INotificationService notificationService, ProjectService projectService, AppStateMachine stateService) : ObservableObject, INavigationAware
|
||||
{
|
||||
public ObservableCollection<TemplateData> templates = new();
|
||||
|
||||
[ObservableProperty]
|
||||
public partial TemplateData? SelectedTemplate
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string? ProjectName
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string? ProjectLocation
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public async void OnNavigatedTo(object? parameter)
|
||||
{
|
||||
templates.Clear();
|
||||
await foreach (var (path, info) in ProjectService.GetProjectTemplatesAsync())
|
||||
{
|
||||
templates.Add(new(path, info));
|
||||
}
|
||||
|
||||
SelectedTemplate = templates.FirstOrDefault();
|
||||
}
|
||||
|
||||
public void OnNavigatedFrom()
|
||||
{
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SelectionProjectLocation()
|
||||
{
|
||||
var folder = await SystemUtilities.OpenFolderPickerAsync();
|
||||
if (folder != null)
|
||||
{
|
||||
ProjectLocation = folder.Path;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task CreateProject()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ProjectName)
|
||||
|| !Directory.Exists(ProjectLocation)
|
||||
|| !SelectedTemplate.HasValue)
|
||||
{
|
||||
notificationService.ShowNotification("Incorrect project info", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await projectService.CreateProjectAsync(ProjectName, ProjectLocation, EngineData.s_engineVersion, SelectedTemplate.Value.directory);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
notificationService.ShowNotification(result.Message, MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await stateService.TransitionToAsync(StateKey.EngineEditor, result.Value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
notificationService.ShowNotification($"Failed to load project: {e.Message}", MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.Data.Models;
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Editor.Core.AppState;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Core.Notifications;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System.Collections.ObjectModel;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.Landing;
|
||||
|
||||
internal partial class OpenProjectViewModel(ProjectService projectService, INotificationService _notificationService, AppStateMachine _stateService) : ObservableObject, INavigationAware
|
||||
{
|
||||
public readonly ObservableCollection<ProjectMetadataInfo> projects = new();
|
||||
|
||||
[ObservableProperty]
|
||||
public partial Visibility EmptyVisibility
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial Visibility DragVisibility
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public void UpdateEmptyPlaceHolderVisibility()
|
||||
{
|
||||
EmptyVisibility = projects.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public async void OnNavigatedTo(object? parameter)
|
||||
{
|
||||
await foreach (var projectInfo in projectService.GetAllProjectAsync())
|
||||
{
|
||||
var metadata = await ProjectService.LoadMetadataAsync(projectInfo.MetadataPath);
|
||||
if (metadata == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
projects.Add(new(projectInfo.MetadataPath, metadata));
|
||||
}
|
||||
|
||||
UpdateEmptyPlaceHolderVisibility();
|
||||
DragVisibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public void OnNavigatedFrom()
|
||||
{
|
||||
projects.Clear();
|
||||
}
|
||||
|
||||
public async Task ContentDrop(DataPackageView dataView)
|
||||
{
|
||||
var errorMessage = string.Empty;
|
||||
if (dataView.Contains(StandardDataFormats.StorageItems))
|
||||
{
|
||||
var items = await dataView.GetStorageItemsAsync();
|
||||
var rootFolder = items.OfType<StorageFolder>().FirstOrDefault();
|
||||
if (rootFolder != null)
|
||||
{
|
||||
var result = await projectService.AddProjectFromDirectoryAsync(rootFolder.Path);
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
projects.Add(result.Value);
|
||||
goto CloseDropPanel;
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = result.Message;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = "Unsupported data format. Please drop a folder containing a project.";
|
||||
}
|
||||
|
||||
_notificationService.ShowNotification(errorMessage, MessageType.Error);
|
||||
|
||||
CloseDropPanel:
|
||||
DragVisibility = Visibility.Collapsed;
|
||||
UpdateEmptyPlaceHolderVisibility();
|
||||
}
|
||||
|
||||
public async Task OpenProjectAsync(ProjectMetadataInfo project)
|
||||
{
|
||||
try
|
||||
{
|
||||
project.Metadata.LastOpened = DateTime.Now;
|
||||
await ProjectService.CreateMetadataFileAsync(project.Path, project.Metadata);
|
||||
|
||||
await _stateService.TransitionToAsync(StateKey.EngineEditor, project);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_notificationService.ShowNotification($"Failed to load project: {e.Message}", MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.Data.Models;
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Engine.Resources;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Windows;
|
||||
|
||||
internal partial class EngineEditorViewModel : ObservableRecipient
|
||||
{
|
||||
public string engineVersionDescriptor = $"{EngineData.ENGINE_NAME} - {EngineData.s_engineVersion}";
|
||||
|
||||
public ProjectMetadataInfo CurrentProject => ProjectService.CurrentProject;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
using Ghost.Core.Attributes;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Ghost.Editor")]
|
||||
|
||||
[assembly: EngineAssembly]
|
||||
@@ -1,21 +0,0 @@
|
||||
using Ghost.Engine.Utilities;
|
||||
using Ghost.SparseEntities.Components;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Engine.Components;
|
||||
|
||||
[SkipLocalsInit]
|
||||
public struct LocalToWorld : IComponentData
|
||||
{
|
||||
public Matrix4x4 matrix;
|
||||
|
||||
public static LocalToWorld Identity
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => new()
|
||||
{
|
||||
matrix = MatrixUtility.CreateTRS(Vector3.Zero, Quaternion.Identity, Vector3.One)
|
||||
};
|
||||
}
|
||||
}
|
||||