Compare commits
260 Commits
feature/sc
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
| d4238e3086 | |||
| f552a4e9e1 | |||
| 5b34da6d6c | |||
| 1f1e21905e | |||
| acd8e60ffb | |||
| c6e58b057c | |||
| 34dc6fc8c9 | |||
| d9343a94dc | |||
| b84ee586bf | |||
| 52dfa12e84 | |||
| 326aee2b1c | |||
| 2cbe1ca789 | |||
| 1fe080dd87 | |||
| 211ea2254d | |||
| 914dde2448 | |||
| 5222e801b9 | |||
| a1c5ccf937 | |||
| 6b501efda0 | |||
| a40140cabd | |||
| 7dac1e4437 | |||
| e04c7eb6a7 | |||
| 84c936bb7a | |||
| 18505cdff6 | |||
| f85cf4edde | |||
| 982ce7d8e0 | |||
| cf5af7ee50 | |||
| 60b807abd7 | |||
| cbaa129d9e | |||
| bb07644580 | |||
| 314b0111f0 | |||
| a95ff01366 | |||
| 7e1db7b908 | |||
| 2ea3c509b0 | |||
| e2bc68d359 | |||
| 1cc65e8218 | |||
| d0076c852f | |||
| ba8694ed0c | |||
| 80e820a858 | |||
| b42398bbce | |||
| d052ca848f | |||
| 744b058e6a | |||
| 5de480e231 | |||
| 8d3e1c91d7 | |||
| bffe05f0ef | |||
| 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 |
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.dll filter=lfs diff=lfs merge=lfs -text
|
||||||
9
.gitignore
vendored
@@ -10,6 +10,15 @@
|
|||||||
*.userosscache
|
*.userosscache
|
||||||
*.sln.docstates
|
*.sln.docstates
|
||||||
|
|
||||||
|
AGENTS.md
|
||||||
|
.opencode/
|
||||||
|
.code-review-graph/
|
||||||
|
.antigravitycli/
|
||||||
|
|
||||||
|
ref/
|
||||||
|
docfx/
|
||||||
|
NUL
|
||||||
|
|
||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
*.userprefs
|
*.userprefs
|
||||||
|
|
||||||
|
|||||||
297
AGENTS.md
@@ -1,297 +0,0 @@
|
|||||||
# GhostEngine - Agent Development Guide
|
|
||||||
|
|
||||||
This guide provides essential information for AI coding agents working on the GhostEngine codebase.
|
|
||||||
|
|
||||||
## Project Overview
|
|
||||||
|
|
||||||
- **Type**: Game Engine
|
|
||||||
- **Language**: C#
|
|
||||||
- **Target Framework**: .NET 10.0
|
|
||||||
- **Special Features**: ECS architecture, D3D12 rendering, AOT compilation, WinUI 3 editor
|
|
||||||
- **Platform**: Windows (net10.0-windows10.0.22621.0 for editor projects)
|
|
||||||
|
|
||||||
## Build Commands
|
|
||||||
|
|
||||||
### Build Entire Solution
|
|
||||||
```bash
|
|
||||||
dotnet build GhostEngine.slnx
|
|
||||||
```
|
|
||||||
|
|
||||||
### Build Specific Project
|
|
||||||
```bash
|
|
||||||
dotnet build Ghost.Entities/Ghost.Entities.csproj
|
|
||||||
dotnet build Ghost.Editor/Ghost.Editor.csproj
|
|
||||||
```
|
|
||||||
|
|
||||||
### Build with Configuration
|
|
||||||
```bash
|
|
||||||
dotnet build GhostEngine.slnx -c Release
|
|
||||||
dotnet build GhostEngine.slnx -c Debug
|
|
||||||
```
|
|
||||||
|
|
||||||
### Clean Build
|
|
||||||
```bash
|
|
||||||
dotnet clean GhostEngine.slnx
|
|
||||||
dotnet build GhostEngine.slnx
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test Commands
|
|
||||||
|
|
||||||
### Run All Tests (Custom Framework)
|
|
||||||
Tests use a custom test framework (not xUnit/NUnit/MSTest). Each test project is an executable.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Run entity tests
|
|
||||||
dotnet run --project Ghost.Entities.Test/Ghost.Entities.Test.csproj
|
|
||||||
|
|
||||||
# Run shader tests
|
|
||||||
dotnet run --project Ghost.Shader.Test/Ghost.Shader.Test.csproj
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run Single Test
|
|
||||||
Tests implement `ITest` interface. To run a specific test, modify the test project's `Program.cs`:
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// In Ghost.Entities.Test/Program.cs
|
|
||||||
TestRunner.Run<EntityQueryTest>(); // Run specific test
|
|
||||||
TestRunner.Run<EntityQueryTest>(10); // Run with 10 iterations
|
|
||||||
```
|
|
||||||
|
|
||||||
### Visual Tests (Graphics)
|
|
||||||
Graphics tests use WinUI 3 and require running as packaged apps:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
dotnet run --project Ghost.Graphics.Test/Ghost.Graphics.Test.csproj
|
|
||||||
```
|
|
||||||
|
|
||||||
## Code Style Guidelines
|
|
||||||
|
|
||||||
### Formatting (from .editorconfig)
|
|
||||||
|
|
||||||
- **Braces**: Allman style - all opening braces on new lines
|
|
||||||
- **Line Length**: Max 400 characters (very permissive)
|
|
||||||
- **Single-line statements**: Preserved (allowed)
|
|
||||||
- **Single-line blocks**: Preserved (allowed)
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// Correct brace style
|
|
||||||
public void Method()
|
|
||||||
{
|
|
||||||
if (condition)
|
|
||||||
{
|
|
||||||
DoSomething();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Imports
|
|
||||||
|
|
||||||
- **System directives**: NOT sorted first (dotnet_sort_system_directives_first = false)
|
|
||||||
- **No grouping**: Import directives not separated by blank lines
|
|
||||||
- **Order**: Organize by project convention, not alphabetically
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
using Ghost.Core;
|
|
||||||
using Ghost.Entities;
|
|
||||||
using Misaki.HighPerformance.Collections;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using TerraFX.Interop.DirectX;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Types and Nullability
|
|
||||||
|
|
||||||
- **Nullable**: Enabled for all projects
|
|
||||||
- **Implicit usings**: Enabled
|
|
||||||
- **Unsafe code**: Allowed in most projects (AllowUnsafeBlocks = True)
|
|
||||||
- **Primary constructors**: NOT preferred (csharp_style_prefer_primary_constructors = false)
|
|
||||||
|
|
||||||
### Naming Conventions
|
|
||||||
|
|
||||||
- **Classes/Interfaces**: PascalCase (`EntityManager`, `ICommandBuffer`)
|
|
||||||
- **Methods**: PascalCase (`CreateEntity`, `GetComponent`)
|
|
||||||
- **Properties**: PascalCase (`IsSuccess`, `Value`)
|
|
||||||
- **Fields (private)**: Camel case with underscore prefix (`_entityLocations`, `_world`)
|
|
||||||
- **Fields (public/internal)**: Camel case, no prefix for struct fields (`archetypeID`, `chunkIndex`)
|
|
||||||
- **Type parameters**: Single letter or PascalCase (`T`, `TComponent`)
|
|
||||||
- **Constants**: PascalCase (no SCREAMING_SNAKE_CASE)
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
public class EntityManager
|
|
||||||
{
|
|
||||||
private readonly World _world; // Private field
|
|
||||||
private UnsafeSlotMap<EntityLocation> _entityLocations;
|
|
||||||
|
|
||||||
public World World => _world; // Property
|
|
||||||
|
|
||||||
public Entity CreateEntity() { } // Method
|
|
||||||
}
|
|
||||||
|
|
||||||
internal struct EntityLocation // Struct
|
|
||||||
{
|
|
||||||
public int archetypeID; // Public struct field
|
|
||||||
public int chunkIndex;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Error Handling
|
|
||||||
|
|
||||||
**Use Result Types** - Railway-oriented programming pattern:
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// Custom result types defined in Ghost.Core
|
|
||||||
public ErrorStatus DoOperation()
|
|
||||||
{
|
|
||||||
return ErrorStatus.None; // or ErrorStatus.NotFound, etc.
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result<T> GetValue()
|
|
||||||
{
|
|
||||||
if (success)
|
|
||||||
return Result<T>.Success(value);
|
|
||||||
else
|
|
||||||
return Result<T>.Failure("Error message");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result<T, ErrorStatus> GetValueWithStatus()
|
|
||||||
{
|
|
||||||
if (success)
|
|
||||||
return value; // Implicit conversion
|
|
||||||
else
|
|
||||||
return ErrorStatus.NotFound; // Implicit conversion
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extension methods for checking results
|
|
||||||
result.ThrowIfFailed();
|
|
||||||
var value = result.GetValueOrThrow();
|
|
||||||
var value = result.GetValueOrDefault(defaultValue);
|
|
||||||
if (result.TryGetValue(out var value)) { }
|
|
||||||
```
|
|
||||||
|
|
||||||
**Error Status Values**: None, NotFound, InvalidArgument, InvalidState, InternalError, PermissionDenied, NotSupported, OutOfMemory, Timeout, Cancelled, UnknownError
|
|
||||||
|
|
||||||
### Memory and Performance
|
|
||||||
|
|
||||||
- **Use unsafe code** when needed for performance-critical paths
|
|
||||||
- **Span<T> and stackalloc**: Prefer for temporary allocations
|
|
||||||
- **ref returns**: Use for zero-copy access to internal data
|
|
||||||
- **Allocator patterns**: Use `Allocator.Persistent` for long-lived allocations
|
|
||||||
- **AllocationManager**: Create stack scopes for temporary allocations
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// Stack allocation pattern
|
|
||||||
var entities = (Span<Entity>)stackalloc Entity[1];
|
|
||||||
|
|
||||||
// Using allocation scope
|
|
||||||
using var scope = AllocationManager.CreateStackScope();
|
|
||||||
var batchDestroy = new UnsafeList<EntityLocation>(entities.Length, scope.AllocationHandle);
|
|
||||||
|
|
||||||
// Ref returns for zero-copy access
|
|
||||||
public ref T GetSingleton<T>() where T : unmanaged, IComponent
|
|
||||||
{
|
|
||||||
var ptr = GetSingleton(ComponentTypeID<T>.Value);
|
|
||||||
return ref *(T*)ptr;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Type Safety Patterns
|
|
||||||
|
|
||||||
**Strongly-typed identifiers**:
|
|
||||||
```csharp
|
|
||||||
Identifier<IComponent> componentID;
|
|
||||||
Identifier<Archetype> archetypeID;
|
|
||||||
Handle<T> resourceHandle;
|
|
||||||
```
|
|
||||||
|
|
||||||
**Generic constraints**:
|
|
||||||
```csharp
|
|
||||||
public void Method<T>() where T : unmanaged, IComponent
|
|
||||||
public void Method<T, E>() where E : struct, Enum
|
|
||||||
```
|
|
||||||
|
|
||||||
### Documentation
|
|
||||||
|
|
||||||
- **XML comments**: Required for public APIs
|
|
||||||
- **Summary tags**: Describe what, not how
|
|
||||||
- **Remarks**: Add for complex behavior, thread-safety warnings, structural changes
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
/// <summary>
|
|
||||||
/// Create an entity with specified components.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="set">A set of component space IDs to add to the entities.</param>
|
|
||||||
/// <returns>The created entity.</returns>
|
|
||||||
/// <remarks>
|
|
||||||
/// This method causes structural changes and is not thread-safe.
|
|
||||||
/// Use <see cref="EntityCommandBuffer"/> to defer changes.
|
|
||||||
/// </remarks>
|
|
||||||
public Entity CreateEntity(ComponentSet set) { }
|
|
||||||
```
|
|
||||||
|
|
||||||
### Common Patterns
|
|
||||||
|
|
||||||
**ECS Component Registration**:
|
|
||||||
```csharp
|
|
||||||
// Type-safe component ID
|
|
||||||
ComponentTypeID<Transform>.Value
|
|
||||||
|
|
||||||
// Component sets for archetypes
|
|
||||||
var set = new ComponentSet(ComponentTypeID<Transform>.Value, ComponentTypeID<Velocity>.Value);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Disposal Pattern**:
|
|
||||||
```csharp
|
|
||||||
private bool _disposed;
|
|
||||||
|
|
||||||
~MyClass()
|
|
||||||
{
|
|
||||||
Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_disposed) return;
|
|
||||||
|
|
||||||
// Cleanup code
|
|
||||||
_disposed = true;
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Debug-only validation**:
|
|
||||||
```csharp
|
|
||||||
#if DEBUG || GHOST_EDITOR
|
|
||||||
if (!_isSuccess)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"Error: {_message}");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
```
|
|
||||||
|
|
||||||
## Architecture Notes
|
|
||||||
|
|
||||||
### Entity Component System (ECS)
|
|
||||||
- Archetype-based storage (similar to Unity DOTS)
|
|
||||||
- Component data stored in chunks
|
|
||||||
- Queries use bitset signatures for fast matching
|
|
||||||
- Structural changes move entities between archetypes
|
|
||||||
|
|
||||||
### Graphics (D3D12)
|
|
||||||
- Hardware abstraction via `ICommandBuffer`
|
|
||||||
- Resource lifetime managed via handles
|
|
||||||
- Pipeline state objects (PSO) cached in library
|
|
||||||
- Native interop via TerraFX.Interop
|
|
||||||
|
|
||||||
### Custom Dependencies
|
|
||||||
- `Misaki.HighPerformance.*`: High-performance collections and utilities
|
|
||||||
- `TerraFX.Interop.*`: Native Windows/DirectX interop
|
|
||||||
- Custom source generators in `Ghost.Generator`
|
|
||||||
|
|
||||||
## Important Rules
|
|
||||||
|
|
||||||
1. **Never disable nullable warnings** - fix the root cause
|
|
||||||
2. **Use Result types** instead of throwing exceptions for expected failures
|
|
||||||
3. **Document thread-safety** in XML comments for public APIs
|
|
||||||
4. **AllowUnsafeBlocks** is enabled - use unsafe code when it improves performance
|
|
||||||
5. **Avoid collection expressions/initializers** (disabled in .editorconfig)
|
|
||||||
6. **Prefer explicit over implicit** - clarity over brevity
|
|
||||||
7. **Test changes** by running the appropriate test project executable
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
namespace Ghost.Core.Contracts;
|
|
||||||
|
|
||||||
public interface ICloneable
|
|
||||||
{
|
|
||||||
object Clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ICloneable<T>
|
|
||||||
{
|
|
||||||
T Clone();
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Ghost.Core.Contracts;
|
|
||||||
|
|
||||||
internal interface IReleasable
|
|
||||||
{
|
|
||||||
void InternalRelease();
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
|
||||||
<IsAotCompatible>True</IsAotCompatible>
|
|
||||||
<DefineConstants>$(DefineConstants);PLATEFORME_WIN64</DefineConstants>
|
|
||||||
<IsTrimmable>True</IsTrimmable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
|
||||||
<IsAotCompatible>True</IsAotCompatible>
|
|
||||||
<DefineConstants>$(DefineConstants);PLATEFORME_WIN64</DefineConstants>
|
|
||||||
<IsTrimmable>True</IsTrimmable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Misaki.HighPerformance" Version="1.0.4" />
|
|
||||||
<PackageReference Include="Misaki.HighPerformance.Jobs" Version="1.2.2" />
|
|
||||||
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.3.3" />
|
|
||||||
<PackageReference Include="Misaki.HighPerformance.Mathematics" Version="1.3.1" />
|
|
||||||
<PackageReference Include="System.IO.Hashing" Version="10.0.1" />
|
|
||||||
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.26100.6" />
|
|
||||||
<PackageReference Include="ZLinq" Version="1.5.4" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
namespace Ghost.Core.Graphics;
|
|
||||||
|
|
||||||
public enum KeywordSpace
|
|
||||||
{
|
|
||||||
Local,
|
|
||||||
Global,
|
|
||||||
}
|
|
||||||
|
|
||||||
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 KeywordSpace space;
|
|
||||||
public List<string> keywords;
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct PropertyDescriptor
|
|
||||||
{
|
|
||||||
public ShaderPropertyType type;
|
|
||||||
public string name;
|
|
||||||
public object? defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct PassDescriptor
|
|
||||||
{
|
|
||||||
public string identifier;
|
|
||||||
public string name;
|
|
||||||
|
|
||||||
public ShaderEntryPoint taskShader;
|
|
||||||
public ShaderEntryPoint meshShader;
|
|
||||||
public ShaderEntryPoint pixelShader;
|
|
||||||
public string[] defines;
|
|
||||||
public string[] includes;
|
|
||||||
public KeywordsGroup[] keywords;
|
|
||||||
public PipelineState localPipeline;
|
|
||||||
public string? hlsl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ShaderDescriptor
|
|
||||||
{
|
|
||||||
public string name = string.Empty;
|
|
||||||
public uint cbufferSize;
|
|
||||||
public PropertyDescriptor[] globalProperties = null!;
|
|
||||||
public PropertyDescriptor[] properties = null!;
|
|
||||||
public PassDescriptor[] passes = null!;
|
|
||||||
public string? hlsl;
|
|
||||||
}
|
|
||||||
|
|
||||||
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,196 +0,0 @@
|
|||||||
using System.Collections.ObjectModel;
|
|
||||||
|
|
||||||
namespace Ghost.Core;
|
|
||||||
|
|
||||||
public enum LogLevel
|
|
||||||
{
|
|
||||||
Info,
|
|
||||||
Warning,
|
|
||||||
Error
|
|
||||||
}
|
|
||||||
|
|
||||||
public 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}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ILogger
|
|
||||||
{
|
|
||||||
ReadOnlyObservableCollection<LogMessage> Logs
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Log(string message, LogLevel level);
|
|
||||||
void Log(Exception exception);
|
|
||||||
void Assert(bool condition, string message);
|
|
||||||
void Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Logger
|
|
||||||
{
|
|
||||||
// TODO: Add file logging.
|
|
||||||
private class LoggerImpl : ILogger
|
|
||||||
{
|
|
||||||
private readonly ObservableCollection<LogMessage> _logs = new();
|
|
||||||
private readonly ReadOnlyObservableCollection<LogMessage> _readOnly;
|
|
||||||
private readonly Lock _lock = new();
|
|
||||||
|
|
||||||
public ReadOnlyObservableCollection<LogMessage> Logs => _readOnly;
|
|
||||||
|
|
||||||
public LoggerImpl()
|
|
||||||
{
|
|
||||||
_readOnly = new ReadOnlyObservableCollection<LogMessage>(_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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly ILogger s_logger = new LoggerImpl();
|
|
||||||
|
|
||||||
public 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 Log(LogLevel level, string format, params object?[] args)
|
|
||||||
{
|
|
||||||
s_logger.Log(string.Format(format, args), 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 LogInfo(string format, params object?[] args)
|
|
||||||
{
|
|
||||||
s_logger.Log(string.Format(format, args), 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 LogWarning(string format, params object?[] args)
|
|
||||||
{
|
|
||||||
s_logger.Log(string.Format(format, args), 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(string format, params object?[] args)
|
|
||||||
{
|
|
||||||
s_logger.Log(string.Format(format, args), 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 space handle for the specified space.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type">The space to get the handle for.</param>
|
|
||||||
/// <returns>The space handle as a nint.</returns>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static TypeHandle Get(Type type) => new TypeHandle(type.TypeHandle.Value);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the space handle for the specified space.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The space to get the handle for.</typeparam>
|
|
||||||
/// <returns>The space 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,9 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Ghost.Core.Utilities;
|
|
||||||
|
|
||||||
internal class EnumUtility
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -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,115 +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
|
|
||||||
{
|
|
||||||
var ptr = uPtr.Detach();
|
|
||||||
if (ptr != null)
|
|
||||||
{
|
|
||||||
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,323 +0,0 @@
|
|||||||
using Ghost.Core;
|
|
||||||
using Ghost.Core.Graphics;
|
|
||||||
using Ghost.DSL.ShaderParser;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Ghost.DSL.ShaderCompiler;
|
|
||||||
|
|
||||||
public struct DSLShaderError
|
|
||||||
{
|
|
||||||
public string message;
|
|
||||||
public int line;
|
|
||||||
public int column;
|
|
||||||
|
|
||||||
public override readonly string ToString()
|
|
||||||
{
|
|
||||||
return $"Error at {line}:{column} - {message}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static class DSLShaderCompiler
|
|
||||||
{
|
|
||||||
private const string _GLOBAL_PROPERTY_FILE_NAME = "GlobalData.g.hlsl";
|
|
||||||
private const string _GENERATED_FILE_HEADER = "// Auto-generated shader file. Please do not edit this file directly.";
|
|
||||||
|
|
||||||
private static string GetPassUniqueId(DSLShaderSemantics shader, PassSemantic pass)
|
|
||||||
{
|
|
||||||
return $"{shader.name}_{pass.name}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static PipelineState MeragePipeline(PipelineSemantic? semantic, PipelineState parent)
|
|
||||||
{
|
|
||||||
if (semantic == null)
|
|
||||||
{
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new PipelineState
|
|
||||||
{
|
|
||||||
ZTest = semantic.zTest ?? parent.ZTest,
|
|
||||||
ZWrite = semantic.zWrite ?? parent.ZWrite,
|
|
||||||
Cull = semantic.cull ?? parent.Cull,
|
|
||||||
Blend = semantic.blend ?? parent.Blend,
|
|
||||||
ColorMask = semantic.colorMask ?? parent.ColorMask
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static uint CalculateCBufferSize(ReadOnlySpan<PropertyDescriptor> properties)
|
|
||||||
{
|
|
||||||
if (properties.IsEmpty)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var currentOffset = 0u;
|
|
||||||
|
|
||||||
foreach (var prop in properties)
|
|
||||||
{
|
|
||||||
var size = prop.type.GetSize();
|
|
||||||
|
|
||||||
if ((currentOffset % 16) + size > 16)
|
|
||||||
{
|
|
||||||
currentOffset = (currentOffset + 15u) & ~15u;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentOffset += size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (currentOffset + 15u) & ~15u;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Implement shader inheritance resolution, including property and pass merging.
|
|
||||||
// Currently, we just ignore inheritance.
|
|
||||||
public static ShaderDescriptor ResolveShader(DSLShaderSemantics semantics)
|
|
||||||
{
|
|
||||||
var descriptor = new ShaderDescriptor
|
|
||||||
{
|
|
||||||
name = semantics.name,
|
|
||||||
hlsl = semantics.hlsl
|
|
||||||
};
|
|
||||||
|
|
||||||
var shaderGlobalProperties = semantics.properties?
|
|
||||||
.Where(p => p.scope == PropertyScope.Global)
|
|
||||||
.Select(p => new PropertyDescriptor
|
|
||||||
{
|
|
||||||
name = p.name,
|
|
||||||
type = p.type,
|
|
||||||
defaultValue = p.defaultValue
|
|
||||||
}).ToArray();
|
|
||||||
|
|
||||||
var shaderLocalProperties = semantics.properties?
|
|
||||||
.Where(p => p.scope == PropertyScope.Local)
|
|
||||||
.Select(p => new PropertyDescriptor
|
|
||||||
{
|
|
||||||
name = p.name,
|
|
||||||
type = p.type,
|
|
||||||
defaultValue = p.defaultValue
|
|
||||||
}).ToArray();
|
|
||||||
|
|
||||||
descriptor.globalProperties = shaderGlobalProperties ?? Array.Empty<PropertyDescriptor>();
|
|
||||||
descriptor.properties = shaderLocalProperties ?? Array.Empty<PropertyDescriptor>();
|
|
||||||
descriptor.cbufferSize = CalculateCBufferSize(descriptor.properties);
|
|
||||||
|
|
||||||
if (semantics.passes != null)
|
|
||||||
{
|
|
||||||
descriptor.passes = new PassDescriptor[semantics.passes.Count];
|
|
||||||
for (int i = 0; i < semantics.passes.Count; i++)
|
|
||||||
{
|
|
||||||
var pass = semantics.passes[i];
|
|
||||||
var localPipeline = MeragePipeline(pass.localPipeline, PipelineState.Default);
|
|
||||||
descriptor.passes[i] = new PassDescriptor
|
|
||||||
{
|
|
||||||
identifier = GetPassUniqueId(semantics, pass),
|
|
||||||
name = pass.name,
|
|
||||||
taskShader = pass.taskShader,
|
|
||||||
meshShader = pass.meshShader,
|
|
||||||
pixelShader = pass.pixelShader,
|
|
||||||
localPipeline = localPipeline,
|
|
||||||
defines = pass.defines?.ToArray() ?? Array.Empty<string>(),
|
|
||||||
includes = pass.includes?.ToArray() ?? Array.Empty<string>(),
|
|
||||||
keywords = pass.keywords?.ToArray() ?? Array.Empty<KeywordsGroup>(),
|
|
||||||
hlsl = pass.hlsl
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
descriptor.passes = Array.Empty<PassDescriptor>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return descriptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Result<ShaderDescriptor> CompileShader(string shaderPath, string generatedOutputDirectory)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var source = File.ReadAllText(shaderPath);
|
|
||||||
|
|
||||||
// Use ANTLR4 parser
|
|
||||||
var shaderModels = AntlrShaderCompiler.ParseShaders(source, out var parseErrors);
|
|
||||||
|
|
||||||
if (parseErrors.Count != 0)
|
|
||||||
{
|
|
||||||
var errorMessages = new StringBuilder();
|
|
||||||
foreach (var error in parseErrors)
|
|
||||||
{
|
|
||||||
errorMessages.AppendLine(error.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.Failure("Failed to parse shader due to errors:\n" + errorMessages.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shaderModels.Count == 0)
|
|
||||||
{
|
|
||||||
return Result.Failure("No shader found in the provided file.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to semantics
|
|
||||||
var model = AntlrShaderCompiler.ConvertToSemantics(shaderModels[0], out var errors);
|
|
||||||
|
|
||||||
if (errors.Count != 0 || model == null)
|
|
||||||
{
|
|
||||||
var errorMessages = new StringBuilder();
|
|
||||||
foreach (var error in errors)
|
|
||||||
{
|
|
||||||
errorMessages.AppendLine(error.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.Failure("Failed to compile shader due to errors:\n" + errorMessages.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
var desc = ResolveShader(model);
|
|
||||||
var globalPropResult = GenerateGlobalProperties(desc.globalProperties, generatedOutputDirectory);
|
|
||||||
if (globalPropResult.IsFailure)
|
|
||||||
{
|
|
||||||
return Result.Failure("Failed to generate global properties: " + globalPropResult.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
var generatedResult = GenerateShaderCode(desc, generatedOutputDirectory);
|
|
||||||
if (generatedResult.IsFailure)
|
|
||||||
{
|
|
||||||
return Result.Failure("Failed to generate pass files: " + generatedResult.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (ref var pass in desc.passes.AsSpan())
|
|
||||||
{
|
|
||||||
if (pass.includes == null)
|
|
||||||
{
|
|
||||||
pass.includes = new string[2];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Array.Resize(ref pass.includes, pass.includes.Length + 2);
|
|
||||||
// Shift existing includes to make room for the two new includes at the front.
|
|
||||||
pass.includes.AsSpan(0, pass.includes.Length - 2).CopyTo(pass.includes.AsSpan(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
pass.includes[0] = globalPropResult.Value;
|
|
||||||
pass.includes[1] = generatedResult.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return desc;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
return Result.Failure("Failed to compile shader: " + ex.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ShaderPropertyTypeToHLSLType(ShaderPropertyType type)
|
|
||||||
{
|
|
||||||
return type switch
|
|
||||||
{
|
|
||||||
ShaderPropertyType.Float => "float",
|
|
||||||
ShaderPropertyType.Float2 => "float2",
|
|
||||||
ShaderPropertyType.Float3 => "float3",
|
|
||||||
ShaderPropertyType.Float4 => "float4",
|
|
||||||
ShaderPropertyType.Int => "int",
|
|
||||||
ShaderPropertyType.Int2 => "int2",
|
|
||||||
ShaderPropertyType.Int3 => "int3",
|
|
||||||
ShaderPropertyType.Int4 => "int4",
|
|
||||||
ShaderPropertyType.UInt => "uint",
|
|
||||||
ShaderPropertyType.UInt2 => "uint2",
|
|
||||||
ShaderPropertyType.UInt3 => "uint3",
|
|
||||||
ShaderPropertyType.UInt4 => "uint4",
|
|
||||||
ShaderPropertyType.Bool => "bool",
|
|
||||||
ShaderPropertyType.Bool2 => "bool2",
|
|
||||||
ShaderPropertyType.Bool3 => "bool3",
|
|
||||||
ShaderPropertyType.Bool4 => "bool4",
|
|
||||||
// NOTE: Textures here are bindless, represented as uint (descriptor index).
|
|
||||||
ShaderPropertyType.Texture2D => "TEXTURE2D",
|
|
||||||
ShaderPropertyType.Texture3D => "TEXTURE3D",
|
|
||||||
ShaderPropertyType.TextureCube => "TEXTURECUBE",
|
|
||||||
ShaderPropertyType.Texture2DArray => "TEXTURE2D_ARRAY",
|
|
||||||
ShaderPropertyType.TextureCubeArray => "TEXTURECUBE_ARRAY",
|
|
||||||
ShaderPropertyType.Sampler => "SAMPLER",
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unsupported shader property type: {type}")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Result<string> GenerateShaderCode(ShaderDescriptor descriptor, string targetDirectory)
|
|
||||||
{
|
|
||||||
if (!Directory.Exists(targetDirectory))
|
|
||||||
{
|
|
||||||
return Result.Failure("Target directory does not exist.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var outputFileName = descriptor.name.Replace('/', '_');
|
|
||||||
var outputFilePath = Path.Combine(targetDirectory, outputFileName + ".g.hlsl");
|
|
||||||
var outputDirectory = Path.GetDirectoryName(outputFilePath);
|
|
||||||
|
|
||||||
if (!Directory.Exists(outputDirectory))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(outputDirectory!);
|
|
||||||
}
|
|
||||||
|
|
||||||
using var fileStream = File.CreateText(outputFilePath);
|
|
||||||
var fileDefine = outputFileName.Replace('/', '_').ToUpperInvariant() + "_G_HLSL";
|
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
|
|
||||||
sb.AppendLine(_GENERATED_FILE_HEADER);
|
|
||||||
sb.AppendLine(@$"
|
|
||||||
#ifndef {fileDefine}
|
|
||||||
#define {fileDefine}
|
|
||||||
|
|
||||||
#include ""F:/csharp/GhostEngine/Ghost.Graphics/Shaders/Includes/Common.hlsl""");
|
|
||||||
|
|
||||||
sb.Append(@"
|
|
||||||
struct PerMaterialData
|
|
||||||
{");
|
|
||||||
foreach (var prop in descriptor.properties)
|
|
||||||
{
|
|
||||||
sb.Append($@"
|
|
||||||
{ShaderPropertyTypeToHLSLType(prop.type)} {prop.name};");
|
|
||||||
}
|
|
||||||
sb.Append(@"
|
|
||||||
};");
|
|
||||||
|
|
||||||
sb.AppendLine();
|
|
||||||
sb.AppendLine(@$"
|
|
||||||
#endif // {fileDefine}");
|
|
||||||
|
|
||||||
fileStream.Write(sb.ToString());
|
|
||||||
|
|
||||||
return outputFilePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Result<string> GenerateGlobalProperties(ReadOnlySpan<PropertyDescriptor> globalProperties, string targetDirectory)
|
|
||||||
{
|
|
||||||
if (!Directory.Exists(targetDirectory))
|
|
||||||
{
|
|
||||||
return Result.Failure("Target directory does not exist.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var globalFilePath = Path.Combine(targetDirectory, _GLOBAL_PROPERTY_FILE_NAME);
|
|
||||||
using var globalFileStream = File.CreateText(globalFilePath);
|
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
|
|
||||||
sb.AppendLine(_GENERATED_FILE_HEADER);
|
|
||||||
sb.Append(@"
|
|
||||||
#ifndef GLOBALDATA_G_HLSL
|
|
||||||
#define GLOBALDATA_G_HLSL
|
|
||||||
|
|
||||||
#include ""F:/csharp/GhostEngine/Ghost.Graphics/Shaders/Includes/Common.hlsl""
|
|
||||||
|
|
||||||
struct GlobalData
|
|
||||||
{");
|
|
||||||
foreach (var prop in globalProperties)
|
|
||||||
{
|
|
||||||
sb.Append($@"
|
|
||||||
{ShaderPropertyTypeToHLSLType(prop.type)} {prop.name};");
|
|
||||||
}
|
|
||||||
sb.AppendLine(@"
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // GLOBALDATA_G_HLSL");
|
|
||||||
globalFileStream.Write(sb.ToString());
|
|
||||||
|
|
||||||
return globalFilePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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,22 +0,0 @@
|
|||||||
namespace Ghost.Editor.Core.AssetHandle;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The base class for all asset types in the Ghost Editor.
|
|
||||||
/// </summary>
|
|
||||||
public abstract class Asset
|
|
||||||
{
|
|
||||||
public abstract string Name
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Guid ID
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Asset(Guid id)
|
|
||||||
{
|
|
||||||
ID = id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,170 +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 readonly Dictionary<Guid, string> s_assetPathLookup = new();
|
|
||||||
private static readonly Dictionary<string, Guid> s_pathAssetLookup = new();
|
|
||||||
|
|
||||||
private static void InitializeMetaData()
|
|
||||||
{
|
|
||||||
if (s_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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s_watcher.Created += OnAssetCreated;
|
|
||||||
s_watcher.Deleted += OnAssetDeleted;
|
|
||||||
s_watcher.Renamed += OnAssetRenamed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Result<string, Error> GetMetaFilePath(string assetPath)
|
|
||||||
{
|
|
||||||
if (Directory.Exists(assetPath))
|
|
||||||
{
|
|
||||||
return Error.NotFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Path.GetExtension(assetPath).Equals(FileExtensions.META_FILE_EXTENSION, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return Error.InvalidState;
|
|
||||||
}
|
|
||||||
|
|
||||||
return assetPath + FileExtensions.META_FILE_EXTENSION;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 async Task<Result> WriteMetaFileAsync(string metaFilePath, AssetMeta metaData)
|
|
||||||
{
|
|
||||||
using var fileStream = File.Create(metaFilePath);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await JsonSerializer.SerializeAsync(fileStream, metaData);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
return Result.Failure(ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static async Task<Result> GenerateMetaFileAsync(string assetPath)
|
|
||||||
{
|
|
||||||
Result r;
|
|
||||||
|
|
||||||
var metaFileResult = GetMetaFilePath(assetPath);
|
|
||||||
if (metaFileResult.IsFailure)
|
|
||||||
{
|
|
||||||
return Result.Failure(metaFileResult.Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (File.Exists(metaFileResult.Value))
|
|
||||||
{
|
|
||||||
using var fileStream = File.OpenRead(metaFileResult.Value);
|
|
||||||
var existingMeta = await JsonSerializer.DeserializeAsync<AssetMeta>(fileStream);
|
|
||||||
if (existingMeta != null && s_assetPathLookup.TryGetValue(existingMeta.Guid, out var path))
|
|
||||||
{
|
|
||||||
if (assetPath != path)
|
|
||||||
{
|
|
||||||
existingMeta.Guid = Guid.NewGuid();
|
|
||||||
r = await WriteMetaFileAsync(metaFileResult.Value, existingMeta);
|
|
||||||
if (r.IsFailure)
|
|
||||||
{
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
var defaultSettings = GetDefaultSettingsForAsset(assetPath);
|
|
||||||
var metaData = new AssetMeta
|
|
||||||
{
|
|
||||||
Guid = Guid.NewGuid(),
|
|
||||||
Settings = defaultSettings
|
|
||||||
};
|
|
||||||
|
|
||||||
r = await WriteMetaFileAsync(metaFileResult.Value, metaData);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async void OnAssetCreated(object sender, FileSystemEventArgs e)
|
|
||||||
{
|
|
||||||
await GenerateMetaFileAsync(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 async void OnAssetRenamed(object sender, RenamedEventArgs e)
|
|
||||||
{
|
|
||||||
var oldMetaPath = e.OldFullPath + FileExtensions.META_FILE_EXTENSION;
|
|
||||||
var newMetaPath = e.FullPath + FileExtensions.META_FILE_EXTENSION;
|
|
||||||
|
|
||||||
if (File.Exists(oldMetaPath))
|
|
||||||
{
|
|
||||||
File.Move(oldMetaPath, newMetaPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await GenerateMetaFileAsync(e.FullPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
using Ghost.Core;
|
|
||||||
using Ghost.Editor.Core.Utilities;
|
|
||||||
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 = TypeCache.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))
|
|
||||||
{
|
|
||||||
Logger.LogError($"Duplicate asset open handler for extension '{ext}' found in method '{method.Name}'. Existing handler will be overwritten.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_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,33 +0,0 @@
|
|||||||
using Ghost.Data.Services;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.AssetHandle;
|
|
||||||
|
|
||||||
public static partial class AssetDatabase
|
|
||||||
{
|
|
||||||
private static FileSystemWatcher? s_watcher;
|
|
||||||
|
|
||||||
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));
|
|
||||||
s_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;
|
|
||||||
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,27 +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.Core.Controls.Internal">
|
|
||||||
|
|
||||||
<Style TargetType="local:ComponentView">
|
|
||||||
<Setter Property="Template">
|
|
||||||
<Setter.Value>
|
|
||||||
<ControlTemplate TargetType="local:ComponentView">
|
|
||||||
<StackPanel Margin="0,0,0,16">
|
|
||||||
<Border
|
|
||||||
Padding="8"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
Background="{ThemeResource SolidBackgroundFillColorSecondaryBrush}">
|
|
||||||
<TextBlock Style="{StaticResource BodyStrongTextBlockStyle}" Text="{TemplateBinding HeaderText}" />
|
|
||||||
</Border>
|
|
||||||
<StackPanel
|
|
||||||
x:Name="ContentContainer"
|
|
||||||
Margin="8,2,2,0"
|
|
||||||
Spacing="2" />
|
|
||||||
</StackPanel>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
</ResourceDictionary>
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
using Ghost.Editor.Core.Inspector;
|
|
||||||
using Ghost.Editor.Core.Resources;
|
|
||||||
using Ghost.Editor.Core.Utilities;
|
|
||||||
using Ghost.Entities;
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.Controls.Internal;
|
|
||||||
|
|
||||||
internal sealed unsafe partial class ComponentView : Control
|
|
||||||
{
|
|
||||||
private delegate void EditorUpdate();
|
|
||||||
|
|
||||||
private StackPanel? _contentContainer;
|
|
||||||
|
|
||||||
private readonly World? _world;
|
|
||||||
private readonly Entity _entity = Entity.Invalid;
|
|
||||||
private readonly Type? _componentType;
|
|
||||||
private readonly ComponentInfo _componentInfo;
|
|
||||||
|
|
||||||
private object? _managedInstance;
|
|
||||||
private void* _pComponentData;
|
|
||||||
|
|
||||||
private ComponentEditor? _customEditor;
|
|
||||||
private PropertyField[]? _propertyFields;
|
|
||||||
private EditorUpdate? _editorUpdate;
|
|
||||||
|
|
||||||
public string HeaderText
|
|
||||||
{
|
|
||||||
get => (string)GetValue(HeaderTextProperty);
|
|
||||||
set => SetValue(HeaderTextProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly DependencyProperty HeaderTextProperty =
|
|
||||||
DependencyProperty.Register(nameof(HeaderText), typeof(string), typeof(ComponentView), new PropertyMetadata(string.Empty));
|
|
||||||
|
|
||||||
internal ComponentView()
|
|
||||||
{
|
|
||||||
DefaultStyleKey = typeof(ComponentView);
|
|
||||||
|
|
||||||
Unloaded += (s, e) =>
|
|
||||||
{
|
|
||||||
_customEditor?.Destroy();
|
|
||||||
|
|
||||||
_contentContainer = null;
|
|
||||||
_customEditor = null;
|
|
||||||
_propertyFields = null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public ComponentView(string header, World world, Entity entity, Type componentType) : this()
|
|
||||||
{
|
|
||||||
HeaderText = header;
|
|
||||||
|
|
||||||
_world = world;
|
|
||||||
_entity = entity;
|
|
||||||
_componentType = componentType;
|
|
||||||
_componentInfo = ComponentRegistry.GetComponentInfo(componentType);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnApplyTemplate()
|
|
||||||
{
|
|
||||||
_contentContainer = (StackPanel)GetTemplateChild("ContentContainer");
|
|
||||||
|
|
||||||
base.OnApplyTemplate();
|
|
||||||
ReBuild();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReflectionUpdate()
|
|
||||||
{
|
|
||||||
if (_propertyFields == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var propertyField in _propertyFields)
|
|
||||||
{
|
|
||||||
propertyField.UpdateValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CustomEditorUpdate()
|
|
||||||
{
|
|
||||||
_customEditor?.Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ReBuild()
|
|
||||||
{
|
|
||||||
if (_contentContainer == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_contentContainer.Children.Clear();
|
|
||||||
if (_world == null || _componentType == null || _entity == Entity.Invalid)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_propertyFields != null)
|
|
||||||
{
|
|
||||||
foreach (var propertyField in _propertyFields)
|
|
||||||
{
|
|
||||||
propertyField.OnValueChanged -= OnPropertyValueChanged;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var componentObject = new ComponentObject(_world, _entity);
|
|
||||||
var editorType = TypeCache.GetTypes().FirstOrDefault(t =>
|
|
||||||
typeof(ComponentEditor).IsAssignableFrom(t) &&
|
|
||||||
t.GetCustomAttribute<CustomEditorAttribute>()?.TargetType.IsAssignableFrom(_componentType) == true);
|
|
||||||
|
|
||||||
if (editorType != null)
|
|
||||||
{
|
|
||||||
_customEditor = (ComponentEditor)Activator.CreateInstance(editorType)!;
|
|
||||||
_customEditor.Initialize(componentObject);
|
|
||||||
_customEditor.Create(_contentContainer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var fields = _componentType.GetFields(StaticResource.ComponentPropertyBindingFlags);
|
|
||||||
_propertyFields = new PropertyField[fields.Length];
|
|
||||||
|
|
||||||
_pComponentData = _world.EntityManager.GetComponent(_entity, _componentInfo.id);
|
|
||||||
_managedInstance = Marshal.PtrToStructure((nint)_pComponentData, _componentType);
|
|
||||||
if (_managedInstance == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < fields.Length; i++)
|
|
||||||
{
|
|
||||||
var field = fields[i];
|
|
||||||
var propertyField = PropertyField.Create(field.Name, field, _managedInstance);
|
|
||||||
propertyField.OnValueChanged += OnPropertyValueChanged;
|
|
||||||
|
|
||||||
_propertyFields[i] = propertyField;
|
|
||||||
_contentContainer.Children.Add(propertyField);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_editorUpdate = _customEditor == null ? ReflectionUpdate : CustomEditorUpdate;
|
|
||||||
_editorUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPropertyValueChanged(PropertyField field)
|
|
||||||
{
|
|
||||||
if (_managedInstance == null || _pComponentData == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Marshal.StructureToPtr(_managedInstance, (nint)_pComponentData, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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,45 +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>
|
|
||||||
<!-- in .net 10, field keyword is not preview anymore, but we are still waiting roslyn team to update their code analyzer packages -->
|
|
||||||
<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>
|
|
||||||
</Project>
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
using Microsoft.UI.Xaml.Controls;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.Inspector;
|
|
||||||
|
|
||||||
public abstract class ComponentEditor
|
|
||||||
{
|
|
||||||
private ComponentObject _componentObject;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the underlying component object used by this class to manage its functionality.
|
|
||||||
/// </summary>
|
|
||||||
protected ComponentObject ComponentObject => _componentObject;
|
|
||||||
|
|
||||||
internal void Initialize(ComponentObject componentObject)
|
|
||||||
{
|
|
||||||
_componentObject = componentObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called when the component editor is created.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="container">The container to add the editor controls to.</param>
|
|
||||||
public virtual void Create(StackPanel container)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called when the component editor needs to update its UI based on the current state of the component data.
|
|
||||||
/// </summary>
|
|
||||||
public virtual void Update()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called when the component editor is destroyed.
|
|
||||||
/// </summary>
|
|
||||||
public virtual void Destroy()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
using Ghost.Entities;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.Inspector;
|
|
||||||
|
|
||||||
public readonly struct ComponentObject
|
|
||||||
{
|
|
||||||
private readonly World _world;
|
|
||||||
private readonly Entity _entity;
|
|
||||||
|
|
||||||
internal ComponentObject(World world, Entity entity)
|
|
||||||
{
|
|
||||||
_world = world;
|
|
||||||
_entity = entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ref T GetData<T>()
|
|
||||||
where T : unmanaged, IComponent
|
|
||||||
{
|
|
||||||
return ref _world.EntityManager.GetComponent<T>(_entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetData<T>(in T data)
|
|
||||||
where T : unmanaged, IComponent
|
|
||||||
{
|
|
||||||
_world.EntityManager.SetComponent(_entity, 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,18 +0,0 @@
|
|||||||
using Microsoft.UI.Xaml.Controls;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.Resources;
|
|
||||||
|
|
||||||
public static class EditorIconSource
|
|
||||||
{
|
|
||||||
public static readonly IconSource scene_24 = new FontIconSource
|
|
||||||
{
|
|
||||||
Glyph = "\uF159",
|
|
||||||
FontSize = 24
|
|
||||||
};
|
|
||||||
|
|
||||||
public static readonly IconSource entity_24 = new FontIconSource
|
|
||||||
{
|
|
||||||
Glyph = "\uF158",
|
|
||||||
FontSize = 24
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
using Ghost.Entities;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.SceneGraph;
|
|
||||||
|
|
||||||
public sealed partial class EntityNode : SceneGraphNode
|
|
||||||
{
|
|
||||||
private readonly Entity _entity;
|
|
||||||
|
|
||||||
public Entity Entity => _entity;
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
# Architecture Plan: Scene Graph and Scene Representation
|
|
||||||
|
|
||||||
The Scene Graph is a hierarchical structure that represents all the objects and entities within a 3D scene in the Ghost Editor.
|
|
||||||
|
|
||||||
## Scene Graph (Editor representation of runtime data)
|
|
||||||
|
|
||||||
There should be three main types of nodes in the Scene Graph for now:
|
|
||||||
|
|
||||||
1. **Scene Graph Node**: The base class for all nodes in the Scene Graph.
|
|
||||||
2. **Entity Node**: Represents an individual entity within a scene. Name stored here, not runtime component.
|
|
||||||
3. **Scene Node**: Represents a Scene object, which can contain multiple entities. Name stored here not runtime data.
|
|
||||||
|
|
||||||
### Editor World
|
|
||||||
|
|
||||||
Editor contains a different world compares to the runtime world. When user click the Play button, we will create a runtime world and load the scene data from the editor world to the runtime world.
|
|
||||||
This allows us to
|
|
||||||
|
|
||||||
1. Unload the runtime only systems like physics, rendering, etc when user stop playing.
|
|
||||||
2. Load editor only systems like gizmos, debug, etc when user stop playing.
|
|
||||||
3. Allow editor only entities like editor camera, editor lights, etc to exist in the editor world without affecting the runtime world.
|
|
||||||
|
|
||||||
### Editor Hierarchy
|
|
||||||
|
|
||||||
The Scene Graph should be represented as a tree structure in the editor (TreeView in WinUI 3), where:
|
|
||||||
|
|
||||||
- The top level nodes represents the loaded Scenes in the editor world.
|
|
||||||
- Levels below the Scene nodes represents the Entity nodes that belong to that scene.
|
|
||||||
- Each Entity node can have child Entity nodes representing parent-child relationships between entities.
|
|
||||||
|
|
||||||
An example hierarchy could look like this:
|
|
||||||
|
|
||||||
```
|
|
||||||
- Scene 1
|
|
||||||
- Entity A
|
|
||||||
- Entity B
|
|
||||||
- Entity C
|
|
||||||
- Scene 2
|
|
||||||
- Entity D
|
|
||||||
```
|
|
||||||
|
|
||||||
## Scene (The runtime representation)
|
|
||||||
|
|
||||||
A Scene is a collection of entities with SceneID component from a world that are grouped together. There can be multiple scenes in a world.
|
|
||||||
|
|
||||||
### Save a Scene
|
|
||||||
|
|
||||||
When save a scene, all entities with the SceneID component matching the scene's ID should be included in the saved data.
|
|
||||||
When an Entity references another Entity in the same scene, we should store the file local id instead of the global entity id.
|
|
||||||
For example, if Entity A (id: 10, 5th in scene) references Entity B (id: 20, 50th in scene) in the same scene, in the saved data for Entity A,
|
|
||||||
we should store 50 (the file local id) as the reference to Entity B instead of 20 (the global entity id).
|
|
||||||
|
|
||||||
> We does not allow cross-scene references for now because ideally it's not a good practice to have cross-scene references.
|
|
||||||
> We can use query or singleton pattern to access entities from other scenes if needed because they are in the same world.
|
|
||||||
|
|
||||||
### Load a Scene
|
|
||||||
|
|
||||||
When loading a scene, we need to reconstruct the entities and their relationships based on the saved data.
|
|
||||||
|
|
||||||
1. We allocate the entities in the world and assign them new global entity IDs.
|
|
||||||
2. We remap the file local IDs to the new global entity IDs and change the references accordingly.
|
|
||||||
For example if Entity A (file local id: 5) references Entity B (file local id: 50) in the saved data,
|
|
||||||
we need to find the new global entity IDs for both entities after loading and update the reference in Entity A to point to the new global entity ID of Entity B.
|
|
||||||
|
|
||||||
### Data format
|
|
||||||
|
|
||||||
The scene data should be stored in a structured format (e.g., JSON or binary) that includes:
|
|
||||||
|
|
||||||
- List of entities with their components and properties (Entities must in the order that file local id directly maps to the index in the list)
|
|
||||||
- References between entities using file local IDs
|
|
||||||
|
|
||||||
> The name of the saved scene file should match the name of the scene node in the editor.
|
|
||||||
|
|
||||||
JSON should only be used in the editor and JSON serialization/deserialization logic should also only exist in the editor codebase (Ghost.Editor.Core). Reflection is allowed here.
|
|
||||||
Binary format should be used in the runtime for better performance. The runtime codebase (Ghost.Engine) must be aot compatible.
|
|
||||||
|
|
||||||
Currently we strict the IComponent to must be unmanaged and blittable types.
|
|
||||||
However, we also support ManagedEntity and ManagedEntityRef with ScriptComponent to allow OOP like logic for common gameplay logic that DOD pattern is not suitable for.
|
|
||||||
Serializing/deserializing with those components will be tricky. We can use MemoryPack for binary serialization/deserialization because it supports both unmanaged and managed types.
|
|
||||||
|
|
||||||
## What need to implement
|
|
||||||
|
|
||||||
- [ ] Scene type for the runtime representation if needed
|
|
||||||
- [ ] Scene Graph data structures (SceneNode, EntityNode)
|
|
||||||
- [ ] Editor World management (loading/unloading scenes, managing entities)
|
|
||||||
- [ ] Scene saving/loading logic with file local ID remapping
|
|
||||||
- [ ] Serialization/deserialization logic for scene data (JSON for editor, binary for runtime)
|
|
||||||
- [ ] UI integration for displaying and managing the Scene Graph in the editor with WinUI 3 TreeView
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.SceneGraph;
|
|
||||||
|
|
||||||
public abstract partial class SceneGraphNode : ObservableObject
|
|
||||||
{
|
|
||||||
[ObservableProperty]
|
|
||||||
public partial string Name
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObservableCollection<SceneGraphNode> Children
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
} = new();
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
namespace Ghost.Editor.Core.SceneGraph;
|
|
||||||
|
|
||||||
public sealed partial class SceneNode : SceneGraphNode
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -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[] s_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!);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s_types = loadableTypes.Select(t => t.GetTypeInfo()).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Type[] GetTypes()
|
|
||||||
{
|
|
||||||
return s_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,20 +0,0 @@
|
|||||||
using Ghost.Editor.Core.Inspector;
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.Components;
|
|
||||||
|
|
||||||
//[CustomEditor(typeof(Hierarchy))]
|
|
||||||
internal class HierarchyEditor : ComponentEditor
|
|
||||||
{
|
|
||||||
public void Create(ComponentObject componentObject, StackPanel container)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update(ComponentObject componentObject)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Destroy(ComponentObject componentObject)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +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;
|
|
||||||
using Misaki.HighPerformance.Mathematics;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.Components;
|
|
||||||
|
|
||||||
[CustomEditor(typeof(LocalToWorld))]
|
|
||||||
internal class LocalToWorldEditor : ComponentEditor
|
|
||||||
{
|
|
||||||
private Float3Field _translationField = null!;
|
|
||||||
private Float3Field _rotationField = null!;
|
|
||||||
private Float3Field _scaleField = null!;
|
|
||||||
|
|
||||||
public override void Create(StackPanel container)
|
|
||||||
{
|
|
||||||
_translationField = new Float3Field();
|
|
||||||
_rotationField = new Float3Field();
|
|
||||||
_scaleField = new Float3Field();
|
|
||||||
|
|
||||||
_translationField.OnValueChanged += (s, e) =>
|
|
||||||
{
|
|
||||||
ref var data = ref ComponentObject.GetData<LocalToWorld>();
|
|
||||||
data.matrix.c3.xyz = e.NewValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
_rotationField.OnValueChanged += (s, e) =>
|
|
||||||
{
|
|
||||||
ref var data = ref ComponentObject.GetData<LocalToWorld>();
|
|
||||||
var newRotation = quaternion.EulerXYZ(e.NewValue * math.TORADIANS);
|
|
||||||
|
|
||||||
data.matrix.GetTRS(out var oldTranslation, out var _, out var oldScale);
|
|
||||||
data.matrix = float4x4.TRS(oldTranslation, newRotation, oldScale);
|
|
||||||
};
|
|
||||||
|
|
||||||
_scaleField.OnValueChanged += (s, e) =>
|
|
||||||
{
|
|
||||||
ref var data = ref ComponentObject.GetData<LocalToWorld>();
|
|
||||||
var newScale = e.NewValue;
|
|
||||||
|
|
||||||
data.matrix.GetTRS(out var oldTranslation, out var oldRotation, out var _);
|
|
||||||
data.matrix = float4x4.TRS(oldTranslation, oldRotation, newScale);
|
|
||||||
};
|
|
||||||
|
|
||||||
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>();
|
|
||||||
data.matrix.GetTRS(out var position, out var rotation, out var scale);
|
|
||||||
|
|
||||||
_translationField.Value = position;
|
|
||||||
_rotationField.Value = math.degrees(math.EulerXYZ(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.Init(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,122 +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>
|
|
||||||
<!-- in .net 10, field keyword is not preview anymore, but we are still waiting roslyn team to update their code analyzer packages -->
|
|
||||||
<langversion>preview</langversion>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<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.1" />
|
|
||||||
<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" />
|
|
||||||
<ProjectReference Include="..\Ghost.Entities\Ghost.Entities.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<SceneNode> SceneList
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
private set;
|
|
||||||
} = new(EditorSceneManager.LoadedWorlds);
|
|
||||||
|
|
||||||
private void OnWorldLoaded(SceneNode node)
|
|
||||||
{
|
|
||||||
SceneList.Add(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnWorldUnloaded(SceneNode node)
|
|
||||||
{
|
|
||||||
SceneList.Remove(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnNavigatedTo(object? parameter)
|
|
||||||
{
|
|
||||||
EditorSceneManager.OnWorldLoaded += OnWorldLoaded;
|
|
||||||
EditorSceneManager.OnWorldUnloaded += OnWorldUnloaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnNavigatedFrom()
|
|
||||||
{
|
|
||||||
EditorSceneManager.OnWorldLoaded -= OnWorldLoaded;
|
|
||||||
EditorSceneManager.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||