Compare commits
40 Commits
feature/sc
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
| 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
|
||||
1
.gitignore
vendored
@@ -9,6 +9,7 @@
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
AGENTS.md
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
212
AGENT.md
Normal file
@@ -0,0 +1,212 @@
|
||||
# GhostEngine — Agent Guidelines
|
||||
|
||||
## Repository Overview
|
||||
|
||||
GhostEngine is a C# game engine targeting .NET 10 / Windows, built around:
|
||||
- **ECS runtime** (`Ghost.Entities`, `Ghost.Core`) — high-performance, AOT-compatible
|
||||
- **Graphics** (`Ghost.Graphics`, `Ghost.Graphics.RHI`, `Ghost.Graphics.D3D12`) — D3D12 RHI
|
||||
- **Editor** (`Ghost.Editor`, `Ghost.Editor.Core`, `Ghost.DSL`, `Ghost.Data`) — WinUI 3 (WindowsAppSDK)
|
||||
- **Third-party bindings** (`Ghost.FMOD`, `Ghost.MeshOptimizer`, `Ghost.Nvtt`, `Ghost.Ufbx`)
|
||||
- **Tools** (`Ghost.NativeWrapperGen`)
|
||||
|
||||
Solution file: `src/GhostEngine.slnx`
|
||||
All commands below should be run from the `src/` directory unless noted.
|
||||
|
||||
---
|
||||
|
||||
## Build Commands
|
||||
|
||||
```shell
|
||||
# Build entire solution (x64, Debug)
|
||||
dotnet build GhostEngine.slnx -c Debug -p:Platform=x64
|
||||
|
||||
# Build entire solution (Release)
|
||||
dotnet build GhostEngine.slnx -c Release -p:Platform=x64
|
||||
|
||||
# Build a single project
|
||||
dotnet build Runtime/Ghost.Entities/Ghost.Entities.csproj
|
||||
|
||||
# Clean
|
||||
dotnet clean GhostEngine.slnx
|
||||
```
|
||||
|
||||
> **Note:** Editor projects (`Ghost.Editor`, `Ghost.Editor.Core`) require
|
||||
> `net10.0-windows10.0.22621.0` and the Windows App SDK. They will only build
|
||||
> on a Windows machine with the correct SDK installed. Platform-agnostic runtime
|
||||
> and test projects target plain `net10.0`.
|
||||
|
||||
---
|
||||
|
||||
## Test Commands
|
||||
|
||||
There are two test frameworks in use:
|
||||
|
||||
### MSTest — `Ghost.UnitTest`
|
||||
Standard `dotnet test` runner. Tests are parallelized at method level by default.
|
||||
Currently the integration test file is `#if false`-guarded until the asset
|
||||
service is fully wired.
|
||||
|
||||
```shell
|
||||
# Run all MSTest tests
|
||||
dotnet test Test/Ghost.UnitTest/Ghost.UnitTest.csproj -c Debug -p:Platform=x64
|
||||
|
||||
# Run a single test method by name
|
||||
dotnet test Test/Ghost.UnitTest/Ghost.UnitTest.csproj \
|
||||
--filter "FullyQualifiedName~TestAutoMetaGeneration_WhenFileCreated"
|
||||
|
||||
# Run a single test class
|
||||
dotnet test Test/Ghost.UnitTest/Ghost.UnitTest.csproj \
|
||||
--filter "ClassName~AssetDatabaseIntegrationTest"
|
||||
```
|
||||
|
||||
### Custom TestRunner — `Ghost.MicroTest` / `Ghost.Entities.Test`
|
||||
These are console executables driven by `Ghost.Test.Core.TestRunner`. There is
|
||||
no `dotnet test` integration; run them directly:
|
||||
|
||||
```shell
|
||||
# Micro tests (native binding smoke tests)
|
||||
dotnet run --project Test/Ghost.MicroTest/Ghost.MicroTest.csproj
|
||||
|
||||
# ECS benchmarks / manual tests
|
||||
dotnet run --project Test/Ghost.Entities.Test/Ghost.Entities.Test.csproj -c Release
|
||||
```
|
||||
|
||||
To run a specific `ITest` implementation, edit `Program.cs` in the respective
|
||||
project and call `TestRunner.Run<YourTestClass>()`.
|
||||
|
||||
---
|
||||
|
||||
## Code Style
|
||||
|
||||
### EditorConfig (enforced — `src/.editorconfig`)
|
||||
- Max line length: **200**
|
||||
- Opening braces always on a **new line** for all C# constructs
|
||||
- Single-line statements and blocks are **preserved** (not force-expanded)
|
||||
- **No** primary constructors (`csharp_style_prefer_primary_constructors = false`)
|
||||
- `System.*` using directives are **not** sorted first
|
||||
- Import directive groups are **not** separated by blank lines
|
||||
- Collection expressions and collection initializer syntax are **disabled**
|
||||
(`dotnet_style_prefer_collection_expression = false`)
|
||||
|
||||
### Language
|
||||
- C# `latest` (runtime/test projects) or `preview` (editor projects, for `field`
|
||||
keyword support in .NET 10)
|
||||
- Nullable reference types: **enabled** everywhere (`<Nullable>enable</Nullable>`)
|
||||
- Implicit usings: **enabled** (`<ImplicitUsings>enable</ImplicitUsings>`)
|
||||
- Unsafe blocks: **enabled** where needed (ECS, graphics, native bindings)
|
||||
|
||||
### Namespaces & File Layout
|
||||
- One type per file; file name matches type name exactly
|
||||
- Namespace matches folder structure: `Ghost.<Module>[.<SubFolder>]`
|
||||
- `partial` classes are split across files named `TypeName.Purpose.cs`
|
||||
(e.g. `EntityManager.cs`, `EntityManager.Managed.cs`)
|
||||
- `AssemblyInfo.cs` holds `[assembly: InternalsVisibleTo(...)]` and assembly
|
||||
attributes; do not scatter these across regular source files
|
||||
|
||||
### Naming Conventions
|
||||
| Symbol | Convention | Example |
|
||||
|--------|-----------|---------|
|
||||
| Private fields | `_camelCase` | `_jobScheduler` |
|
||||
| Private static fields | `s_camelCase` | `s_worlds`, `s_logger` |
|
||||
| Constants (public/private) | `UPPER_SNAKE_CASE` | `ASSET_EXTENSION`, `ASSETS_FOLDER_NAME` |
|
||||
| Properties & public members | `PascalCase` | `EntityManager`, `IsSuccess` |
|
||||
| Local variables / params | `camelCase` | `entityCapacity`, `signatureHash` |
|
||||
| Interfaces | `I` prefix | `IComponent`, `ISystem`, `ITest` |
|
||||
| Generic type parameters | `T`, `TKey`, `TValue`, `E` | |
|
||||
| Type-tagged structs (handles) | Generic param encodes context | `Handle<T>`, `Identifier<T>`, `Key64<T>` |
|
||||
|
||||
### Types & Structs
|
||||
- Prefer `readonly struct` for value types that are logically immutable.
|
||||
- Prefer `ref struct` / `readonly ref struct` for stack-only types
|
||||
(`RefResult<T,E>`, `SystemAPI`, `ChunkView`).
|
||||
- Use `partial class` to split large classes by concern.
|
||||
- Avoid primary constructors (disabled by editorconfig).
|
||||
- Use the `field` keyword (preview feature) for auto-property backing fields
|
||||
where it simplifies code — only in editor projects that opt in via
|
||||
`<langversion>preview</langversion>`.
|
||||
|
||||
### Imports
|
||||
- `using` directives at the top of each file, before the `namespace` declaration.
|
||||
- No blank line between `using` groups (enforced by editorconfig).
|
||||
- `System.*` namespaces may appear in any order alongside project namespaces.
|
||||
- Prefer specific `using` imports over global usings for clarity in low-level
|
||||
performance-critical files.
|
||||
|
||||
### Error Handling
|
||||
- **Return `Result` / `Result<T>` instead of throwing** for expected failures
|
||||
(file-not-found, invalid args, etc.).
|
||||
`Result.Success()` / `Result.Failure(message)` or `Result.Failure(Error.XXX)`.
|
||||
- Use the typed `Error` enum (`Ghost.Core.Error`) for structured error codes.
|
||||
- Use `result.ThrowIfFailed()` / `result.GetValueOrThrow()` extension methods at
|
||||
call sites that want throw-on-failure semantics.
|
||||
- **Throw exceptions** only for programming errors / invariant violations
|
||||
(corrupt state, null-ref on internal APIs).
|
||||
- In performance-critical paths, guard validation behind `#if DEBUG || GHOST_EDITOR`
|
||||
to eliminate overhead in release builds.
|
||||
- `Logger.LogError(...)` / `Logger.LogWarning(...)` for non-fatal operational
|
||||
issues; do not use `Console.WriteLine` in production library code.
|
||||
|
||||
### Performance Patterns
|
||||
- Annotate hot paths with `[MethodImpl(MethodImplOptions.AggressiveInlining)]`.
|
||||
- Annotate log/assert helpers with `[StackTraceHidden]`.
|
||||
- Prefer `stackalloc` + `Span<T>` over heap allocation for small temporary arrays.
|
||||
- Use the `Misaki.HighPerformance.*` allocation APIs (`AllocationManager`,
|
||||
`UnsafeList<T>`, `UnsafeHashMap<T,V>`, etc.) for long-lived unmanaged buffers.
|
||||
- All runtime/ECS types must be AOT-compatible and trimmable (set
|
||||
`<IsAotCompatible>True</IsAotCompatible>` and `<IsTrimmable>True</IsTrimmable>`
|
||||
in Release config).
|
||||
- Avoid LINQ in hot paths; use `for` loops or `foreach` over `Span<T>`.
|
||||
|
||||
### Attributes & Extensibility
|
||||
- Custom attributes for editor extension points inherit from
|
||||
`DiscoverableAttributeBase` (discovered at startup via `TypeCache`).
|
||||
- Use `[UpdateAfter(typeof(X))]` / `[UpdateBefore(typeof(X))]` to declare
|
||||
`ISystem` ordering dependencies; `SystemGroup.SortSystems()` topologically sorts
|
||||
them at startup.
|
||||
- Use `[EditorInjection(ServiceLifetime.Singleton)]` to register editor services
|
||||
via DI without manual wiring.
|
||||
|
||||
### XML Documentation
|
||||
- All public API surface should have `<summary>` doc-comments.
|
||||
- Use `<remarks>` for non-obvious behavior or threading constraints.
|
||||
- Document thread-safety expectations explicitly (see `EntityCommandBuffer` /
|
||||
`AssetRegistry` as reference).
|
||||
|
||||
### Preprocessor Defines
|
||||
| Define | Meaning |
|
||||
|--------|---------|
|
||||
| `DEBUG` | Standard debug build |
|
||||
| `GHOST_EDITOR` | Editor build (extra validation, reflection helpers) |
|
||||
| `PLATEFORME_WIN64` | Windows 64-bit platform target |
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
src/
|
||||
GhostEngine.slnx # Solution
|
||||
.editorconfig # Formatting rules
|
||||
Runtime/
|
||||
Ghost.Core/ # Core types: Result, Handle, Logger, math helpers
|
||||
Ghost.Engine/ # Engine entry point & loop
|
||||
Ghost.Entities/ # ECS: World, Entity, Component, System
|
||||
Ghost.Generator/ # Source generators
|
||||
Ghost.Graphics/ # High-level graphics API
|
||||
Ghost.Graphics.RHI/ # Render hardware interface abstractions
|
||||
Ghost.Graphics.D3D12/ # D3D12 backend
|
||||
Editor/
|
||||
Ghost.Editor/ # WinUI 3 shell
|
||||
Ghost.Editor.Core/ # Editor services, asset registry, inspector
|
||||
Ghost.DSL/ # Shader DSL compiler
|
||||
Ghost.Data/ # Serialization / project data models
|
||||
ThridParty/ # Native binding wrappers (FMOD, MeshOptimizer, Nvtt, Ufbx)
|
||||
Test/
|
||||
Ghost.Test.Core/ # Shared ITest / TestRunner infrastructure
|
||||
Ghost.UnitTest/ # MSTest integration tests
|
||||
Ghost.MicroTest/ # Native binding smoke tests (console app)
|
||||
Ghost.Entities.Test/ # ECS benchmarks (BenchmarkDotNet, console app)
|
||||
Ghost.Shader.Test/ # Shader DSL manual tests (console app)
|
||||
Tools/
|
||||
Ghost.NativeWrapperGen/ # Code-gen tool for native wrappers
|
||||
```
|
||||
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,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,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,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,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,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,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,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,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,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,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,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,91 +0,0 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Ghost.Data.Models;
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Editor.Core.AppState;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Core.Notifications;
|
||||
using Ghost.Editor.Utilities;
|
||||
using Ghost.Engine.Resources;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.Landing;
|
||||
|
||||
internal partial class CreateProjectViewModel(INotificationService notificationService, ProjectService projectService, AppStateMachine stateService) : ObservableObject, INavigationAware
|
||||
{
|
||||
public ObservableCollection<TemplateData> templates = new();
|
||||
|
||||
[ObservableProperty]
|
||||
public partial TemplateData? SelectedTemplate
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string? ProjectName
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string? ProjectLocation
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public async void OnNavigatedTo(object? parameter)
|
||||
{
|
||||
templates.Clear();
|
||||
await foreach (var (path, info) in ProjectService.GetProjectTemplatesAsync())
|
||||
{
|
||||
templates.Add(new(path, info));
|
||||
}
|
||||
|
||||
SelectedTemplate = templates.FirstOrDefault();
|
||||
}
|
||||
|
||||
public void OnNavigatedFrom()
|
||||
{
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SelectionProjectLocation()
|
||||
{
|
||||
var folder = await SystemUtilities.OpenFolderPickerAsync();
|
||||
if (folder != null)
|
||||
{
|
||||
ProjectLocation = folder.Path;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task CreateProject()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ProjectName)
|
||||
|| !Directory.Exists(ProjectLocation)
|
||||
|| !SelectedTemplate.HasValue)
|
||||
{
|
||||
notificationService.ShowNotification("Incorrect project info", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await projectService.CreateProjectAsync(ProjectName, ProjectLocation, EngineData.EngineVersion, SelectedTemplate.Value.directory);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
notificationService.ShowNotification(result.Message, MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await stateService.TransitionToAsync(StateKey.EngineEditor, result.Value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
notificationService.ShowNotification($"Failed to load project: {e.Message}", MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.Data.Models;
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Editor.Core.AppState;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Core.Notifications;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System.Collections.ObjectModel;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.Landing;
|
||||
|
||||
internal partial class OpenProjectViewModel(ProjectService projectService, INotificationService _notificationService, AppStateMachine _stateService) : ObservableObject, INavigationAware
|
||||
{
|
||||
public readonly ObservableCollection<ProjectMetadataInfo> projects = new();
|
||||
|
||||
[ObservableProperty]
|
||||
public partial Visibility EmptyVisibility
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial Visibility DragVisibility
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public void UpdateEmptyPlaceHolderVisibility()
|
||||
{
|
||||
EmptyVisibility = projects.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public async void OnNavigatedTo(object? parameter)
|
||||
{
|
||||
await foreach (var projectInfo in projectService.GetAllProjectAsync())
|
||||
{
|
||||
var metadata = await ProjectService.LoadMetadataAsync(projectInfo.MetadataPath);
|
||||
if (metadata == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
projects.Add(new(projectInfo.MetadataPath, metadata));
|
||||
}
|
||||
|
||||
UpdateEmptyPlaceHolderVisibility();
|
||||
DragVisibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public void OnNavigatedFrom()
|
||||
{
|
||||
projects.Clear();
|
||||
}
|
||||
|
||||
public async Task ContentDrop(DataPackageView dataView)
|
||||
{
|
||||
var errorMessage = string.Empty;
|
||||
if (dataView.Contains(StandardDataFormats.StorageItems))
|
||||
{
|
||||
var items = await dataView.GetStorageItemsAsync();
|
||||
var rootFolder = items.OfType<StorageFolder>().FirstOrDefault();
|
||||
if (rootFolder != null)
|
||||
{
|
||||
var result = await projectService.AddProjectFromDirectoryAsync(rootFolder.Path);
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
projects.Add(result.Value);
|
||||
goto CloseDropPanel;
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = result.Message;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = "Unsupported data format. Please drop a folder containing a project.";
|
||||
}
|
||||
|
||||
_notificationService.ShowNotification(errorMessage, MessageType.Error);
|
||||
|
||||
CloseDropPanel:
|
||||
DragVisibility = Visibility.Collapsed;
|
||||
UpdateEmptyPlaceHolderVisibility();
|
||||
}
|
||||
|
||||
public async Task OpenProjectAsync(ProjectMetadataInfo project)
|
||||
{
|
||||
try
|
||||
{
|
||||
project.Metadata.LastOpened = DateTime.Now;
|
||||
await ProjectService.CreateMetadataFileAsync(project.Path, project.Metadata);
|
||||
|
||||
await _stateService.TransitionToAsync(StateKey.EngineEditor, project);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_notificationService.ShowNotification($"Failed to load project: {e.Message}", MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.Data.Models;
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Engine.Resources;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Windows;
|
||||
|
||||
internal partial class EngineEditorViewModel : ObservableRecipient
|
||||
{
|
||||
public string engineVersionDescriptor = $"{EngineData.ENGINE_NAME} - {EngineData.EngineVersion}";
|
||||
|
||||
public ProjectMetadataInfo CurrentProject => ProjectService.CurrentProject;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
using Ghost.Entities;
|
||||
using Misaki.HighPerformance.Jobs;
|
||||
|
||||
namespace Ghost.Engine;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
internal class EngineEntryAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[EngineEntry]
|
||||
public partial class EngineCore
|
||||
{
|
||||
private readonly JobScheduler _jobScheduler;
|
||||
|
||||
public JobScheduler JobScheduler => _jobScheduler;
|
||||
|
||||
internal EngineCore()
|
||||
{
|
||||
_jobScheduler = new JobScheduler(Environment.ProcessorCount - 2); // We -2 here, one for main thread, one for render thread
|
||||
|
||||
ComponentRegistry.GetOrRegisterComponentID<ManagedEntityRef>();
|
||||
}
|
||||
|
||||
internal void Init()
|
||||
{
|
||||
}
|
||||
|
||||
internal void Dispose()
|
||||
{
|
||||
_jobScheduler.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
namespace Ghost.Engine.Models;
|
||||
|
||||
public enum LogLevel
|
||||
{
|
||||
Info,
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
|
||||
internal class LogMessage
|
||||
{
|
||||
public LogLevel Level
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string? Message
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string? StackTrace
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public DateTime Timestamp
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public LogMessage(LogLevel level, string? message, string? stackTrace = null)
|
||||
{
|
||||
Level = level;
|
||||
Message = message;
|
||||
StackTrace = stackTrace;
|
||||
Timestamp = DateTime.Now;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Timestamp:HH:mm:ss} [{Level}] {Message}";
|
||||
}
|
||||
|
||||
public string ToStringWithStackTrace()
|
||||
{
|
||||
if (StackTrace == null)
|
||||
{
|
||||
return ToString();
|
||||
}
|
||||
|
||||
return $"{ToString()}\n{StackTrace}";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
using Ghost.Entities.Test;
|
||||
using Ghost.Test.Core;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
|
||||
AllocationManager.EnableDebugLayer();
|
||||
TestRunner.Run<SerializationTest>();
|
||||
AllocationManager.Dispose();
|
||||
@@ -1,176 +0,0 @@
|
||||
#if false
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
|
||||
namespace Ghost.Entities;
|
||||
|
||||
public interface ISharedComponent
|
||||
{
|
||||
}
|
||||
|
||||
internal unsafe sealed class SharedComponentStore : IDisposable
|
||||
{
|
||||
private struct EntryInfo
|
||||
{
|
||||
public int RefCount;
|
||||
public int HashCode;
|
||||
public int Version;
|
||||
public int NextFree; // free-list linkage (index)
|
||||
}
|
||||
|
||||
private struct TypeStore : IDisposable
|
||||
{
|
||||
public int TypeSize;
|
||||
public UnsafeList<byte> Data; // raw bytes, stride = TypeSize
|
||||
public UnsafeList<EntryInfo> Infos; // parallel to Data entries (Entry 0 reserved)
|
||||
public UnsafeHashMap<long, int> HashLookup; // (hashKey) -> entryIndex
|
||||
public int FreeListHead; // head index, 0 means none (we'll use Infos[0].NextFree too if you prefer)
|
||||
public int VersionCounter;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Data.Dispose();
|
||||
Infos.Dispose();
|
||||
HashLookup.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly UnsafeHashMap<int, TypeStore> _perType; // componentTypeId -> TypeStore
|
||||
|
||||
public SharedComponentStore(int initialCapacity = 16)
|
||||
{
|
||||
_perType = new UnsafeHashMap<int, TypeStore>(initialCapacity, Allocator.Persistent);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var kvp in _perType)
|
||||
{
|
||||
kvp.Value.Dispose();
|
||||
}
|
||||
|
||||
_perType.Dispose();
|
||||
}
|
||||
|
||||
public int InsertOrGet(int componentTypeId, int typeSize, void* data, int hashCode)
|
||||
{
|
||||
// Reserve index 0 for "default"
|
||||
if (data == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ref var store = ref GetOrCreateTypeStore(componentTypeId, typeSize);
|
||||
|
||||
// Combine (typeId, hash) into a single key; collisions handled by memcmp below.
|
||||
var key = ((long)componentTypeId << 32) ^ (uint)hashCode;
|
||||
|
||||
if (store.HashLookup.TryGetValue(key, out var existingIndex))
|
||||
{
|
||||
var existingPtr = (byte*)store.Data.GetUnsafePtr() + (existingIndex * store.TypeSize);
|
||||
if (new Span<byte>(existingPtr, store.TypeSize).SequenceEqual(new Span<byte>(data, store.TypeSize)))
|
||||
{
|
||||
((EntryInfo*)store.Infos.GetUnsafePtr())[existingIndex].RefCount++;
|
||||
return existingIndex;
|
||||
}
|
||||
// If collision: fall through to insert (you may want a secondary structure).
|
||||
}
|
||||
|
||||
int index = AllocateEntry(ref store);
|
||||
|
||||
var dst = (byte*)store.Data.GetUnsafePtr() + (index * store.TypeSize);
|
||||
MemoryUtility.MemCpy(dst, data, (nuint)store.TypeSize);
|
||||
|
||||
store.Infos[index] = new EntryInfo
|
||||
{
|
||||
RefCount = 1,
|
||||
HashCode = hashCode,
|
||||
Version = ++store.VersionCounter,
|
||||
NextFree = -1
|
||||
};
|
||||
|
||||
store.HashLookup[key] = index;
|
||||
return index;
|
||||
}
|
||||
|
||||
public void AddRef(int componentTypeId, int index)
|
||||
{
|
||||
if (index == 0) return;
|
||||
ref var store = ref _perType[componentTypeId];
|
||||
store.Infos[index].RefCount++;
|
||||
}
|
||||
|
||||
public void Release(int componentTypeId, int index)
|
||||
{
|
||||
if (index == 0) return;
|
||||
ref var store = ref _perType.GetValueByKey(componentTypeId);
|
||||
|
||||
ref var info = ref store.Infos.Ptr[index];
|
||||
info.RefCount--;
|
||||
if (info.RefCount > 0) return;
|
||||
|
||||
// Remove from hash lookup (best-effort; collisions require more robust handling)
|
||||
long key = ((long)componentTypeId << 32) ^ (uint)info.HashCode;
|
||||
store.HashLookup.Remove(key);
|
||||
|
||||
// Push to free-list
|
||||
info.NextFree = store.FreeListHead;
|
||||
store.FreeListHead = index;
|
||||
}
|
||||
|
||||
public void* GetDataPtr(int componentTypeId, int index)
|
||||
{
|
||||
if (index == 0) return null;
|
||||
ref var store = ref _perType.GetValueByKey(componentTypeId);
|
||||
return (byte*)store.Data.Ptr + (index * store.TypeSize);
|
||||
}
|
||||
|
||||
private ref TypeStore GetOrCreateTypeStore(int componentTypeId, int typeSize)
|
||||
{
|
||||
if (_perType.TryGetValue(componentTypeId, out var existing))
|
||||
{
|
||||
// UnsafeHashMap returns by value in some implementations; you may need a different pattern here.
|
||||
// Adjust to your container API (e.g., TryGetValueRef).
|
||||
}
|
||||
|
||||
var store = new TypeStore
|
||||
{
|
||||
TypeSize = typeSize,
|
||||
Data = new UnsafeList<byte>(typeSize * 16, Allocator.Persistent),
|
||||
Infos = new UnsafeList<EntryInfo>(16, Allocator.Persistent),
|
||||
HashLookup = new UnsafeHashMap<long, int>(16, Allocator.Persistent),
|
||||
FreeListHead = 0,
|
||||
VersionCounter = 0
|
||||
};
|
||||
|
||||
// Create reserved default entry at index 0
|
||||
store.Data.Resize(typeSize); // one element worth of bytes
|
||||
store.Infos.Add(new EntryInfo { RefCount = int.MaxValue, HashCode = 0, Version = 0, NextFree = -1 });
|
||||
|
||||
_perType.Add(componentTypeId, store);
|
||||
// NOTE: returning a ref requires a "get ref" API; adjust to your UnsafeHashMap capabilities.
|
||||
return ref _perType.GetValueByKey(componentTypeId);
|
||||
}
|
||||
|
||||
private static int AllocateEntry(ref TypeStore store)
|
||||
{
|
||||
if (store.FreeListHead != 0)
|
||||
{
|
||||
int idx = store.FreeListHead;
|
||||
store.FreeListHead = store.Infos[idx].NextFree;
|
||||
store.Infos[idx].NextFree = -1;
|
||||
return idx;
|
||||
}
|
||||
|
||||
int newIndex = store.Infos.Count;
|
||||
store.Infos.Add(default);
|
||||
|
||||
int newByteCount = (newIndex + 1) * store.TypeSize;
|
||||
store.Data.Resize(newByteCount);
|
||||
|
||||
return newIndex;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,5 +0,0 @@
|
||||
namespace Ghost.Graphics.Core;
|
||||
|
||||
public class Camera
|
||||
{
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using System.Drawing;
|
||||
using System.Runtime.InteropServices;
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
namespace Ghost.Graphics.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a color with 4 bytes components.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Size = 4)]
|
||||
public struct Color32 : IEquatable<Color32>
|
||||
{
|
||||
public byte r;
|
||||
public byte g;
|
||||
public byte b;
|
||||
public byte a;
|
||||
|
||||
public Color32(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public Color32(Color color)
|
||||
: this(color.R, color.G, color.B, color.A)
|
||||
{
|
||||
}
|
||||
|
||||
public Color32(Color128 color128)
|
||||
: this((byte)(color128.r * 255.0f), (byte)(color128.g * 255.0f), (byte)(color128.b * 255.0f), (byte)(color128.a * 255.0f))
|
||||
{
|
||||
}
|
||||
|
||||
public readonly bool Equals(Color32 other)
|
||||
{
|
||||
return r == other.r && g == other.g && b == other.b && a == other.a;
|
||||
}
|
||||
|
||||
public override readonly bool Equals(object? obj)
|
||||
{
|
||||
return obj is Color32 color && Equals(color);
|
||||
}
|
||||
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(r, g, b, a);
|
||||
}
|
||||
|
||||
public static bool operator ==(Color32 left, Color32 right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(Color32 left, Color32 right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a color with 16 bytes components.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Size = 16)]
|
||||
public struct Color128 : IEquatable<Color128>
|
||||
{
|
||||
public float r;
|
||||
public float g;
|
||||
public float b;
|
||||
public float a;
|
||||
|
||||
public Color128(float r, float g, float b, float a)
|
||||
{
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public Color128(Color color)
|
||||
: this(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f, color.A / 255.0f)
|
||||
{
|
||||
}
|
||||
|
||||
public Color128(Color32 color32)
|
||||
: this(color32.r / 255.0f, color32.g / 255.0f, color32.b / 255.0f, color32.a / 255.0f)
|
||||
{
|
||||
}
|
||||
|
||||
public Color128(float4 v)
|
||||
: this(v.x, v.y, v.z, v.w)
|
||||
{
|
||||
}
|
||||
|
||||
public readonly bool Equals(Color128 other)
|
||||
{
|
||||
return r.Equals(other.r) && g.Equals(other.g) && b.Equals(other.b) && a.Equals(other.a);
|
||||
}
|
||||
|
||||
public override readonly bool Equals(object? obj)
|
||||
{
|
||||
return obj is Color128 color && Equals(color);
|
||||
}
|
||||
|
||||
public readonly override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(r, g, b, a);
|
||||
}
|
||||
|
||||
public static bool operator ==(Color128 left, Color128 right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(Color128 left, Color128 right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Vertex
|
||||
{
|
||||
public static class Semantic
|
||||
{
|
||||
public const DXGI_FORMAT ALIGNED_FORMAT = DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT;
|
||||
public const int COUNT = 5;
|
||||
|
||||
public static readonly FixedText32 Position = new("POSITION");
|
||||
public static readonly FixedText32 Normal = new("NORMAL");
|
||||
public static readonly FixedText32 Tangent = new("TANGENT");
|
||||
public static readonly FixedText32 Uv = new("TEXCOORD");
|
||||
public static readonly FixedText32 Color = new("COLOR");
|
||||
}
|
||||
|
||||
public float4 position;
|
||||
public float4 normal;
|
||||
public float4 tangent;
|
||||
public float4 uv;
|
||||
public Color128 color;
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RenderPasses;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.RenderGraphModule;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <summary>
|
||||
/// D3D12 implementation of the renderer interface using RHI abstractions
|
||||
/// </summary>
|
||||
internal class D3D12Renderer : IRenderer
|
||||
{
|
||||
private readonly D3D12GraphicsEngine _graphicsEngine;
|
||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||
|
||||
private readonly ICommandBuffer _commandBuffer;
|
||||
private readonly RenderGraph _renderGraph;
|
||||
|
||||
private uint _frameIndex;
|
||||
private bool _disposed;
|
||||
|
||||
// NOTE: Testing only.
|
||||
private readonly MeshRenderPass _pass;
|
||||
|
||||
public IRenderOutput? RenderOutput
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
// TODO: Add render graph support
|
||||
|
||||
public D3D12Renderer(D3D12GraphicsEngine graphicsEngine, D3D12ResourceDatabase resourceDatabase)
|
||||
{
|
||||
_graphicsEngine = graphicsEngine;
|
||||
_resourceDatabase = resourceDatabase;
|
||||
|
||||
_commandBuffer = _graphicsEngine.CreateCommandBuffer(CommandBufferType.Graphics);
|
||||
_renderGraph = new RenderGraph(_graphicsEngine);
|
||||
|
||||
// NOTE: Testing only.
|
||||
_pass = new();
|
||||
}
|
||||
|
||||
~D3D12Renderer()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public Result Render(ICommandAllocator commandAllocator)
|
||||
{
|
||||
if (RenderOutput is null)
|
||||
{
|
||||
return Result.Failure("Render target strategy is not set.");
|
||||
}
|
||||
|
||||
var target = RenderOutput.GetRenderTarget();
|
||||
if (target.IsInvalid)
|
||||
{
|
||||
return Result.Failure("Render target is invalid.");
|
||||
}
|
||||
|
||||
_commandBuffer.Begin(commandAllocator);
|
||||
RenderOutput.BeginRender(_commandBuffer);
|
||||
|
||||
// NOTE: Temporary solution: render directly to the swap chain back buffer if available.
|
||||
// HACK: This is hard coded for testing purposes only.
|
||||
|
||||
var error = RenderScene(target, RenderOutput.Viewport, RenderOutput.Scissor);
|
||||
if (error != Error.None)
|
||||
{
|
||||
_commandBuffer.End();
|
||||
return Result.Failure(error);
|
||||
}
|
||||
|
||||
RenderOutput.EndRender(_commandBuffer);
|
||||
var r = _commandBuffer.End();
|
||||
if (r.IsFailure)
|
||||
{
|
||||
return r;
|
||||
}
|
||||
|
||||
_graphicsEngine.Device.GraphicsQueue.Submit(_commandBuffer);
|
||||
RenderOutput.Present();
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
// TODO: A proper render graph integration.
|
||||
private Error RenderScene(Handle<Texture> target, ViewportDesc viewport, RectDesc rect)
|
||||
{
|
||||
// NOTE: Testing only.
|
||||
var ctx = new RenderingContext(_graphicsEngine, _commandBuffer);
|
||||
if (_frameIndex == 0)
|
||||
{
|
||||
_pass.Initialize(ref ctx);
|
||||
}
|
||||
|
||||
//_commandBuffer.BeginRenderPass(rtDesc, depthDesc, false);
|
||||
_commandBuffer.SetViewport(viewport);
|
||||
_commandBuffer.SetScissorRect(rect);
|
||||
|
||||
_renderGraph.Reset();
|
||||
|
||||
var backBuffer = _renderGraph.ImportTexture(target, "Back Buffer");
|
||||
_pass.Build(_renderGraph, backBuffer);
|
||||
|
||||
// Create view state from viewport
|
||||
var viewState = new ViewState((uint)viewport.Width, (uint)viewport.Height);
|
||||
|
||||
// Compile with view state
|
||||
_renderGraph.Compile(in viewState);
|
||||
_renderGraph.Execute(_commandBuffer);
|
||||
|
||||
//_commandBuffer.EndRenderPass();
|
||||
_frameIndex++;
|
||||
|
||||
return Error.None;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: Testing only.
|
||||
_pass.Cleanup(_resourceDatabase);
|
||||
_renderGraph.Dispose();
|
||||
|
||||
_commandBuffer.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using Ghost.Graphics.Core;
|
||||
|
||||
namespace Ghost.Graphics.D3D12.Utilities;
|
||||
|
||||
internal unsafe static class D3D12PipelineResource
|
||||
{
|
||||
private readonly static D3D12_INPUT_ELEMENT_DESC[] s_inputElementDescs = [
|
||||
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.Position.GetUnsafePointer(), SemanticIndex = 0u, Format = Vertex.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 0u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
|
||||
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.Normal.GetUnsafePointer(), SemanticIndex = 0u, Format = Vertex.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 16u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
|
||||
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.Tangent.GetUnsafePointer(), SemanticIndex = 0u, Format = Vertex.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 32u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
|
||||
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.Uv.GetUnsafePointer(), SemanticIndex = 0u, Format = Vertex.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 48u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
|
||||
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.Color.GetUnsafePointer(), SemanticIndex = 0u, Format = Vertex.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 64u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
|
||||
];
|
||||
|
||||
public const DXGI_FORMAT SWAP_CHAIN_BACK_BUFFER_FORMAT = DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
|
||||
public static D3D12_INPUT_LAYOUT_DESC InputLayoutDescription => new()
|
||||
{
|
||||
pInputElementDescs = (D3D12_INPUT_ELEMENT_DESC*)Unsafe.AsPointer(ref s_inputElementDescs[0]),
|
||||
NumElements = (uint)s_inputElementDescs.Length
|
||||
};
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Contracts;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
/// <summary>
|
||||
/// High-level renderer interface that uses RHI abstractions
|
||||
/// </summary>
|
||||
public interface IRenderer : IDisposable
|
||||
{
|
||||
IRenderOutput? RenderOutput
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders a frame
|
||||
/// </summary>
|
||||
/// <param name="commandAllocator">Command allocator to use for rendering</param>
|
||||
/// <returns>Result of the rendering operation</returns>
|
||||
Result Render(ICommandAllocator commandAllocator);
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.IO.Hashing;
|
||||
|
||||
namespace Ghost.Graphics.RenderGraphModule;
|
||||
|
||||
/// <summary>
|
||||
/// Computes structural hashes of render graphs for compilation caching.
|
||||
/// Hashes are based on graph topology and resource configurations, not runtime values.
|
||||
/// </summary>
|
||||
internal static class RenderGraphHasher
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes a hash of the entire render graph structure.
|
||||
/// Used for cache invalidation - same hash means same compilation result.
|
||||
/// </summary>
|
||||
public static unsafe ulong ComputeGraphHash(List<RenderGraphPassBase> passes, RenderGraphResourceRegistry resources)
|
||||
{
|
||||
using var scope = AllocationManager.CreateStackScope();
|
||||
var bufferPool = new UnsafeList<byte>(2048, scope.AllocationHandle);
|
||||
var pData = (byte*)bufferPool.GetUnsafePtr();
|
||||
var offset = 0;
|
||||
|
||||
// Hash pass count
|
||||
*(int*)(pData + offset) = passes.Count;
|
||||
offset += sizeof(int);
|
||||
|
||||
// Hash each pass structure (excluding names)
|
||||
for (var i = 0; i < passes.Count; i++)
|
||||
{
|
||||
var pass = passes[i];
|
||||
|
||||
*(RenderPassType*)(pData + offset) = pass.type;
|
||||
offset += sizeof(RenderPassType);
|
||||
|
||||
*(bool*)(pData + offset) = pass.allowCulling;
|
||||
offset += sizeof(bool);
|
||||
|
||||
*(bool*)(pData + offset) = pass.asyncCompute;
|
||||
offset += sizeof(bool);
|
||||
|
||||
// Hash depth attachment
|
||||
offset = ComputeTextureHash(pData, offset, pass.depthAccess.id, resources);
|
||||
|
||||
pData[offset] = (byte)pass.depthAccess.accessFlags;
|
||||
offset += sizeof(AccessFlags);
|
||||
|
||||
*(int*)(pData + offset) = pass.maxColorIndex;
|
||||
offset += sizeof(int);
|
||||
for (var j = 0; j <= pass.maxColorIndex; j++)
|
||||
{
|
||||
offset = ComputeTextureHash(pData, offset, pass.colorAccess[j].id, resources);
|
||||
|
||||
pData[offset] = (byte)pass.colorAccess[j].accessFlags;
|
||||
offset += sizeof(AccessFlags);
|
||||
}
|
||||
|
||||
for (var j = 0; j < (int)RenderGraphResourceType.Count; j++)
|
||||
{
|
||||
var readList = pass.resourceReads[j];
|
||||
var writeList = pass.resourceWrites[j];
|
||||
var createList = pass.resourceCreates[j];
|
||||
|
||||
*(int*)(pData + offset) = readList.Count;
|
||||
offset += sizeof(int);
|
||||
for (var k = 0; k < readList.Count; k++)
|
||||
{
|
||||
*(int*)(pData + offset) = readList[k].Value;
|
||||
offset += sizeof(int);
|
||||
}
|
||||
|
||||
*(int*)(pData + offset) = writeList.Count;
|
||||
offset += sizeof(int);
|
||||
for (var k = 0; k < writeList.Count; k++)
|
||||
{
|
||||
*(int*)(pData + offset) = writeList[k].Value;
|
||||
offset += sizeof(int);
|
||||
}
|
||||
|
||||
*(int*)(pData + offset) = createList.Count;
|
||||
offset += sizeof(int);
|
||||
for (var k = 0; k < createList.Count; k++)
|
||||
{
|
||||
*(int*)(pData + offset) = createList[k].Value;
|
||||
offset += sizeof(int);
|
||||
}
|
||||
|
||||
*(int*)(pData + offset) = pass.randomAccess.Count;
|
||||
offset += sizeof(int);
|
||||
for (var k = 0; k < pass.randomAccess.Count; k++)
|
||||
{
|
||||
*(int*)(pData + offset) = pass.randomAccess[k].Value;
|
||||
offset += sizeof(int);
|
||||
}
|
||||
}
|
||||
|
||||
*(int*)(pData + offset) = pass.GetRenderFuncHashCode();
|
||||
offset += sizeof(int);
|
||||
}
|
||||
|
||||
var span = new Span<byte>(pData, offset);
|
||||
return XxHash64.HashToUInt64(span);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes a hash of a texture resource's structural properties.
|
||||
/// For imported textures, hashes the backing handle.
|
||||
/// For transient textures, hashes the descriptor (respecting size mode).
|
||||
/// </summary>
|
||||
private static unsafe int ComputeTextureHash(byte* pData, int offset, Identifier<RGTexture> texture, RenderGraphResourceRegistry resources)
|
||||
{
|
||||
if (texture.IsInvalid)
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
|
||||
var resource = resources.GetResource(texture.AsResource());
|
||||
|
||||
// Hash imported flag
|
||||
*(pData + offset) = resource.isImported ? (byte)1 : (byte)0;
|
||||
offset += sizeof(byte);
|
||||
|
||||
// For imported textures, hash the backing resource handle
|
||||
if (resource.isImported)
|
||||
{
|
||||
*(int*)(pData + offset) = resource.backingResource.GetHashCode();
|
||||
offset += sizeof(int);
|
||||
return offset;
|
||||
}
|
||||
|
||||
var desc = resource.rgTextureDesc;
|
||||
|
||||
// Hash format (structural)
|
||||
*(TextureFormat*)(pData + offset) = desc.format;
|
||||
offset += sizeof(TextureFormat);
|
||||
|
||||
// Hash size mode (structural)
|
||||
*(RGTextureSizeMode*)(pData + offset) = desc.sizeMode;
|
||||
offset += sizeof(RGTextureSizeMode);
|
||||
|
||||
// Hash size specification based on mode
|
||||
if (desc.sizeMode == RGTextureSizeMode.Absolute)
|
||||
{
|
||||
// Absolute mode: hash actual dimensions
|
||||
*(uint*)(pData + offset) = desc.width;
|
||||
offset += sizeof(uint);
|
||||
*(uint*)(pData + offset) = desc.height;
|
||||
offset += sizeof(uint);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Relative mode: hash scale factors (NOT resolved dimensions)
|
||||
*(float*)(pData + offset) = desc.scaleX;
|
||||
offset += sizeof(float);
|
||||
*(float*)(pData + offset) = desc.scaleY;
|
||||
offset += sizeof(float);
|
||||
}
|
||||
|
||||
// Hash other structural properties
|
||||
*(TextureDimension*)(pData + offset) = desc.dimension;
|
||||
offset += sizeof(TextureDimension);
|
||||
*(uint*)(pData + offset) = desc.mipLevels;
|
||||
offset += sizeof(uint);
|
||||
*(TextureUsage*)(pData + offset) = desc.usage;
|
||||
offset += sizeof(TextureUsage);
|
||||
|
||||
*(bool*)(pData + offset) = desc.clearAtFirstUse;
|
||||
offset += sizeof(bool);
|
||||
*(bool*)(pData + offset) = desc.discardAtLastUse;
|
||||
offset += sizeof(bool);
|
||||
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
namespace Ghost.Graphics.RenderPasses;
|
||||
|
||||
internal class SimpleRenderPipeline
|
||||
{
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace Ghost.Test.Core;
|
||||
|
||||
public interface ITest
|
||||
{
|
||||
public void Setup();
|
||||
|
||||
public void Run();
|
||||
|
||||
public void Cleanup();
|
||||
}
|
||||
@@ -1,283 +0,0 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.Zeux.MeshOptimizer;
|
||||
|
||||
public static unsafe partial class Api
|
||||
{
|
||||
private const string _DLL_NAME = "meshoptimizer";
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_generateVertexRemap([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("const void *")] void* vertices, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_size);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_generateVertexRemapMulti([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("const struct meshopt_Stream *")] meshopt_Stream* streams, [NativeTypeName("size_t")] nuint stream_count);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_generateVertexRemapCustom([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride, [NativeTypeName("int (*)(void *, unsigned int, unsigned int)")] delegate* unmanaged[Cdecl]<void*, uint, uint, int> callback, void* context);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_remapVertexBuffer(void* destination, [NativeTypeName("const void *")] void* vertices, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_size, [NativeTypeName("const unsigned int *")] uint* remap);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_remapIndexBuffer([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("const unsigned int *")] uint* remap);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_generateShadowIndexBuffer([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("const void *")] void* vertices, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_size, [NativeTypeName("size_t")] nuint vertex_stride);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_generateShadowIndexBufferMulti([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("const struct meshopt_Stream *")] meshopt_Stream* streams, [NativeTypeName("size_t")] nuint stream_count);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_generatePositionRemap([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_generateAdjacencyIndexBuffer([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_generateTessellationIndexBuffer([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_generateProvokingIndexBuffer([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("unsigned int *")] uint* reorder, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("size_t")] nuint vertex_count);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_optimizeVertexCache([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("size_t")] nuint vertex_count);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_optimizeVertexCacheStrip([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("size_t")] nuint vertex_count);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_optimizeVertexCacheFifo([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("unsigned int")] uint cache_size);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_optimizeOverdraw([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride, float threshold);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_optimizeVertexFetch(void* destination, [NativeTypeName("unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("const void *")] void* vertices, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_size);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_optimizeVertexFetchRemap([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("size_t")] nuint vertex_count);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_encodeIndexBuffer([NativeTypeName("unsigned char *")] byte* buffer, [NativeTypeName("size_t")] nuint buffer_size, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_encodeIndexBufferBound([NativeTypeName("size_t")] nuint index_count, [NativeTypeName("size_t")] nuint vertex_count);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_encodeIndexVersion(int version);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int meshopt_decodeIndexBuffer(void* destination, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("size_t")] nuint index_size, [NativeTypeName("const unsigned char *")] byte* buffer, [NativeTypeName("size_t")] nuint buffer_size);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int meshopt_decodeIndexVersion([NativeTypeName("const unsigned char *")] byte* buffer, [NativeTypeName("size_t")] nuint buffer_size);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_encodeIndexSequence([NativeTypeName("unsigned char *")] byte* buffer, [NativeTypeName("size_t")] nuint buffer_size, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_encodeIndexSequenceBound([NativeTypeName("size_t")] nuint index_count, [NativeTypeName("size_t")] nuint vertex_count);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int meshopt_decodeIndexSequence(void* destination, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("size_t")] nuint index_size, [NativeTypeName("const unsigned char *")] byte* buffer, [NativeTypeName("size_t")] nuint buffer_size);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_encodeVertexBuffer([NativeTypeName("unsigned char *")] byte* buffer, [NativeTypeName("size_t")] nuint buffer_size, [NativeTypeName("const void *")] void* vertices, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_size);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_encodeVertexBufferBound([NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_size);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_encodeVertexBufferLevel([NativeTypeName("unsigned char *")] byte* buffer, [NativeTypeName("size_t")] nuint buffer_size, [NativeTypeName("const void *")] void* vertices, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_size, int level, int version);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_encodeVertexVersion(int version);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int meshopt_decodeVertexBuffer(void* destination, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_size, [NativeTypeName("const unsigned char *")] byte* buffer, [NativeTypeName("size_t")] nuint buffer_size);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern int meshopt_decodeVertexVersion([NativeTypeName("const unsigned char *")] byte* buffer, [NativeTypeName("size_t")] nuint buffer_size);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_decodeFilterOct(void* buffer, [NativeTypeName("size_t")] nuint count, [NativeTypeName("size_t")] nuint stride);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_decodeFilterQuat(void* buffer, [NativeTypeName("size_t")] nuint count, [NativeTypeName("size_t")] nuint stride);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_decodeFilterExp(void* buffer, [NativeTypeName("size_t")] nuint count, [NativeTypeName("size_t")] nuint stride);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_decodeFilterColor(void* buffer, [NativeTypeName("size_t")] nuint count, [NativeTypeName("size_t")] nuint stride);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_encodeFilterOct(void* destination, [NativeTypeName("size_t")] nuint count, [NativeTypeName("size_t")] nuint stride, int bits, [NativeTypeName("const float *")] float* data);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_encodeFilterQuat(void* destination, [NativeTypeName("size_t")] nuint count, [NativeTypeName("size_t")] nuint stride, int bits, [NativeTypeName("const float *")] float* data);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_encodeFilterExp(void* destination, [NativeTypeName("size_t")] nuint count, [NativeTypeName("size_t")] nuint stride, int bits, [NativeTypeName("const float *")] float* data, [NativeTypeName("enum meshopt_EncodeExpMode")] meshopt_EncodeExpMode mode);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_encodeFilterColor(void* destination, [NativeTypeName("size_t")] nuint count, [NativeTypeName("size_t")] nuint stride, int bits, [NativeTypeName("const float *")] float* data);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_simplify([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride, [NativeTypeName("size_t")] nuint target_index_count, float target_error, [NativeTypeName("unsigned int")] uint options, float* result_error);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_simplifyWithAttributes([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride, [NativeTypeName("const float *")] float* vertex_attributes, [NativeTypeName("size_t")] nuint vertex_attributes_stride, [NativeTypeName("const float *")] float* attribute_weights, [NativeTypeName("size_t")] nuint attribute_count, [NativeTypeName("const unsigned char *")] byte* vertex_lock, [NativeTypeName("size_t")] nuint target_index_count, float target_error, [NativeTypeName("unsigned int")] uint options, float* result_error);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_simplifyWithUpdate([NativeTypeName("unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride, float* vertex_attributes, [NativeTypeName("size_t")] nuint vertex_attributes_stride, [NativeTypeName("const float *")] float* attribute_weights, [NativeTypeName("size_t")] nuint attribute_count, [NativeTypeName("const unsigned char *")] byte* vertex_lock, [NativeTypeName("size_t")] nuint target_index_count, float target_error, [NativeTypeName("unsigned int")] uint options, float* result_error);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_simplifySloppy([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride, [NativeTypeName("const unsigned char *")] byte* vertex_lock, [NativeTypeName("size_t")] nuint target_index_count, float target_error, float* result_error);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_simplifyPrune([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride, float target_error);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_simplifyPoints([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride, [NativeTypeName("const float *")] float* vertex_colors, [NativeTypeName("size_t")] nuint vertex_colors_stride, float color_weight, [NativeTypeName("size_t")] nuint target_vertex_count);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern float meshopt_simplifyScale([NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_stripify([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("unsigned int")] uint restart_index);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_stripifyBound([NativeTypeName("size_t")] nuint index_count);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_unstripify([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("unsigned int")] uint restart_index);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_unstripifyBound([NativeTypeName("size_t")] nuint index_count);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("struct meshopt_VertexCacheStatistics")]
|
||||
public static extern meshopt_VertexCacheStatistics meshopt_analyzeVertexCache([NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("unsigned int")] uint cache_size, [NativeTypeName("unsigned int")] uint warp_size, [NativeTypeName("unsigned int")] uint primgroup_size);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("struct meshopt_VertexFetchStatistics")]
|
||||
public static extern meshopt_VertexFetchStatistics meshopt_analyzeVertexFetch([NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_size);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("struct meshopt_OverdrawStatistics")]
|
||||
public static extern meshopt_OverdrawStatistics meshopt_analyzeOverdraw([NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("struct meshopt_CoverageStatistics")]
|
||||
public static extern meshopt_CoverageStatistics meshopt_analyzeCoverage([NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_buildMeshlets([NativeTypeName("struct meshopt_Meshlet *")] meshopt_Meshlet* meshlets, [NativeTypeName("unsigned int *")] uint* meshlet_vertices, [NativeTypeName("unsigned char *")] byte* meshlet_triangles, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride, [NativeTypeName("size_t")] nuint max_vertices, [NativeTypeName("size_t")] nuint max_triangles, float cone_weight);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_buildMeshletsScan([NativeTypeName("struct meshopt_Meshlet *")] meshopt_Meshlet* meshlets, [NativeTypeName("unsigned int *")] uint* meshlet_vertices, [NativeTypeName("unsigned char *")] byte* meshlet_triangles, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint max_vertices, [NativeTypeName("size_t")] nuint max_triangles);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_buildMeshletsBound([NativeTypeName("size_t")] nuint index_count, [NativeTypeName("size_t")] nuint max_vertices, [NativeTypeName("size_t")] nuint max_triangles);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_buildMeshletsFlex([NativeTypeName("struct meshopt_Meshlet *")] meshopt_Meshlet* meshlets, [NativeTypeName("unsigned int *")] uint* meshlet_vertices, [NativeTypeName("unsigned char *")] byte* meshlet_triangles, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride, [NativeTypeName("size_t")] nuint max_vertices, [NativeTypeName("size_t")] nuint min_triangles, [NativeTypeName("size_t")] nuint max_triangles, float cone_weight, float split_factor);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_buildMeshletsSpatial([NativeTypeName("struct meshopt_Meshlet *")] meshopt_Meshlet* meshlets, [NativeTypeName("unsigned int *")] uint* meshlet_vertices, [NativeTypeName("unsigned char *")] byte* meshlet_triangles, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride, [NativeTypeName("size_t")] nuint max_vertices, [NativeTypeName("size_t")] nuint min_triangles, [NativeTypeName("size_t")] nuint max_triangles, float fill_weight);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_optimizeMeshlet([NativeTypeName("unsigned int *")] uint* meshlet_vertices, [NativeTypeName("unsigned char *")] byte* meshlet_triangles, [NativeTypeName("size_t")] nuint triangle_count, [NativeTypeName("size_t")] nuint vertex_count);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("struct meshopt_Bounds")]
|
||||
public static extern meshopt_Bounds meshopt_computeClusterBounds([NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("struct meshopt_Bounds")]
|
||||
public static extern meshopt_Bounds meshopt_computeMeshletBounds([NativeTypeName("const unsigned int *")] uint* meshlet_vertices, [NativeTypeName("const unsigned char *")] byte* meshlet_triangles, [NativeTypeName("size_t")] nuint triangle_count, [NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("struct meshopt_Bounds")]
|
||||
public static extern meshopt_Bounds meshopt_computeSphereBounds([NativeTypeName("const float *")] float* positions, [NativeTypeName("size_t")] nuint count, [NativeTypeName("size_t")] nuint positions_stride, [NativeTypeName("const float *")] float* radii, [NativeTypeName("size_t")] nuint radii_stride);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static extern nuint meshopt_partitionClusters([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const unsigned int *")] uint* cluster_indices, [NativeTypeName("size_t")] nuint total_index_count, [NativeTypeName("const unsigned int *")] uint* cluster_index_counts, [NativeTypeName("size_t")] nuint cluster_count, [NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride, [NativeTypeName("size_t")] nuint target_partition_size);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_spatialSortRemap([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_spatialSortTriangles([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const unsigned int *")] uint* indices, [NativeTypeName("size_t")] nuint index_count, [NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_spatialClusterPoints([NativeTypeName("unsigned int *")] uint* destination, [NativeTypeName("const float *")] float* vertex_positions, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_positions_stride, [NativeTypeName("size_t")] nuint cluster_size);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: NativeTypeName("unsigned short")]
|
||||
public static extern ushort meshopt_quantizeHalf(float v);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern float meshopt_quantizeFloat(float v, int N);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern float meshopt_dequantizeHalf([NativeTypeName("unsigned short")] ushort h);
|
||||
|
||||
[DllImport(_DLL_NAME, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void meshopt_setAllocator([NativeTypeName("void *(*)(size_t) __attribute__((cdecl))")] delegate* unmanaged[Cdecl]<nuint, void*> allocate, [NativeTypeName("void (*)(void *) __attribute__((cdecl))")] delegate* unmanaged[Cdecl]<void*, void> deallocate);
|
||||
|
||||
public static int meshopt_quantizeUnorm(float v, int N)
|
||||
{
|
||||
var scale = (float)((1 << N) - 1);
|
||||
|
||||
v = (v >= 0) ? v : 0;
|
||||
v = (v <= 1) ? v : 1;
|
||||
return (int)(v * scale + 0.5f);
|
||||
}
|
||||
|
||||
public static int meshopt_quantizeSnorm(float v, int N)
|
||||
{
|
||||
var scale = (float)((1 << (N - 1)) - 1);
|
||||
var round = (v >= 0 ? 0.5f : -0.5f);
|
||||
|
||||
v = (v >= -1) ? v : -1;
|
||||
v = (v <= +1) ? v : +1;
|
||||
return (int)(v * scale + round);
|
||||
}
|
||||
|
||||
[return: NativeTypeName("size_t")]
|
||||
public static nuint meshopt_encodeVertexBufferLevel([NativeTypeName("unsigned char *")] byte* buffer, [NativeTypeName("size_t")] nuint buffer_size, [NativeTypeName("const void *")] void* vertices, [NativeTypeName("size_t")] nuint vertex_count, [NativeTypeName("size_t")] nuint vertex_size, int level)
|
||||
{
|
||||
return meshopt_encodeVertexBufferLevel(buffer, buffer_size, vertices, vertex_count, vertex_size, level, unchecked(-1));
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.Zeux.MeshOptimizer
|
||||
{
|
||||
public unsafe partial struct meshopt_Allocator
|
||||
{
|
||||
private static readonly Storage s_storage;
|
||||
|
||||
[NativeTypeName("void *[24]")]
|
||||
private _blocks_e__FixedBuffer _blocks;
|
||||
|
||||
[NativeTypeName("size_t")]
|
||||
private nuint _count;
|
||||
|
||||
static meshopt_Allocator()
|
||||
{
|
||||
s_storage = new Storage
|
||||
{
|
||||
// Use .NET's unmanaged memory functions.
|
||||
allocate = &NativeMemory_Alloc,
|
||||
deallocate = &NativeMemory_Free,
|
||||
};
|
||||
}
|
||||
|
||||
public meshopt_Allocator()
|
||||
{
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
|
||||
private static void* NativeMemory_Alloc(nuint size)
|
||||
{
|
||||
return NativeMemory.Alloc(size);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
|
||||
private static void NativeMemory_Free(void* ptr)
|
||||
{
|
||||
NativeMemory.Free(ptr);
|
||||
}
|
||||
|
||||
public T* allocate<T>(nuint size) where T : unmanaged
|
||||
{
|
||||
Debug.Assert(_count < 24, "Allocator block limit reached");
|
||||
|
||||
// Calculate total bytes needed
|
||||
var bytes = size * (nuint)sizeof(T);
|
||||
var result = (T*)s_storage.allocate(bytes);
|
||||
|
||||
_blocks[_count++] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void deallocate(void* ptr)
|
||||
{
|
||||
Debug.Assert(_count > 0 && _blocks[_count - 1] == ptr, "Deallocation is not in LIFO order");
|
||||
s_storage.deallocate(ptr);
|
||||
_count--;
|
||||
}
|
||||
|
||||
public unsafe partial struct Storage
|
||||
{
|
||||
[NativeTypeName("void *(*)(size_t) __attribute__((cdecl))")]
|
||||
public delegate* unmanaged[Cdecl]<nuint, void*> allocate;
|
||||
|
||||
[NativeTypeName("void (*)(void *) __attribute__((cdecl))")]
|
||||
public delegate* unmanaged[Cdecl]<void*, void> deallocate;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
for (var i = _count; i > 0; --i)
|
||||
{
|
||||
s_storage.deallocate(_blocks[i - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe partial struct _blocks_e__FixedBuffer
|
||||
{
|
||||
private void* e0;
|
||||
private void* e1;
|
||||
private void* e2;
|
||||
private void* e3;
|
||||
private void* e4;
|
||||
private void* e5;
|
||||
private void* e6;
|
||||
private void* e7;
|
||||
private void* e8;
|
||||
private void* e9;
|
||||
private void* e10;
|
||||
private void* e11;
|
||||
private void* e12;
|
||||
private void* e13;
|
||||
private void* e14;
|
||||
private void* e15;
|
||||
private void* e16;
|
||||
private void* e17;
|
||||
private void* e18;
|
||||
private void* e19;
|
||||
private void* e20;
|
||||
private void* e21;
|
||||
private void* e22;
|
||||
private void* e23;
|
||||
|
||||
public ref void* this[nuint index]
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (void** pThis = &e0)
|
||||
{
|
||||
return ref pThis[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
namespace Ghost.Zeux.MeshOptimizer
|
||||
{
|
||||
public enum meshopt_SimplifyOptions
|
||||
{
|
||||
meshopt_SimplifyLockBorder = 1 << 0,
|
||||
meshopt_SimplifySparse = 1 << 1,
|
||||
meshopt_SimplifyErrorAbsolute = 1 << 2,
|
||||
meshopt_SimplifyPrune = 1 << 3,
|
||||
meshopt_SimplifyRegularize = 1 << 4,
|
||||
meshopt_SimplifyPermissive = 1 << 5,
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace Ghost.Zeux.MeshOptimizer
|
||||
{
|
||||
public enum meshopt_SimplifyVertexFlags
|
||||
{
|
||||
meshopt_SimplifyVertex_Lock = 1 << 0,
|
||||
meshopt_SimplifyVertex_Protect = 1 << 1,
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<Solution>
|
||||
<Configurations>
|
||||
<Platform Name="ARM64" />
|
||||
<Platform Name="x64" />
|
||||
<Platform Name="x86" />
|
||||
</Configurations>
|
||||
<Folder Name="/Editor/">
|
||||
<Project Path="Ghost.Data/Ghost.Data.csproj" />
|
||||
<Project Path="Ghost.Editor.Core/Ghost.Editor.Core.csproj" />
|
||||
<Project Path="Ghost.Editor/Ghost.Editor.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
<Platform Solution="*|x86" Project="x86" />
|
||||
<Deploy />
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/Library/">
|
||||
<Project Path="Ghost.FMOD/Ghost.FMOD.csproj" />
|
||||
<Project Path="Ghost.Zeux.MeshOptimizer/Ghost.Zeux.MeshOptimizer.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Runtime/">
|
||||
<Project Path="Ghost.Core/Ghost.Core.csproj" />
|
||||
<Project Path="Ghost.Engine/Ghost.Engine.csproj" />
|
||||
<Project Path="Ghost.Entities/Ghost.Entities.csproj" />
|
||||
<Project Path="Ghost.Generator/Ghost.Generator.csproj" />
|
||||
<Project Path="Ghost.Graphics/Ghost.Graphics.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Test/">
|
||||
<Project Path="Ghost.Entities.Test/Ghost.Entities.Test.csproj" />
|
||||
<Project Path="Ghost.Graphics.Test/Ghost.Graphics.Test.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
<Platform Solution="*|x86" Project="x86" />
|
||||
<Deploy />
|
||||
</Project>
|
||||
<Project Path="Ghost.Shader.Test/Ghost.Shader.Test.csproj" />
|
||||
<Project Path="Ghost.Test.Core/Ghost.Test.Core.csproj" />
|
||||
</Folder>
|
||||
<Project Path="Ghost.DSL/Ghost.DSL.csproj" />
|
||||
</Solution>
|
||||
19
README_julian.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Julian's Workspace: GhostEngine
|
||||
|
||||
Misaki has set up my environment to work from my own fork of "GhostEngine." Here's how I'll manage my workflow:
|
||||
|
||||
## What I'll Do
|
||||
|
||||
1. Write, commit, and push code changes directly to my local fork.
|
||||
2. Use the "tea" CLI tool when I'm ready to create PRs back to the original repository.
|
||||
|
||||
## Tools & Resources
|
||||
|
||||
- **Gitea:** The repository is hosted here. I can authenticate directly.
|
||||
- **Tea CLI:** Details for creating PRs can be found in the `gitea-pr` and `gitea` skill documentation.
|
||||
|
||||
## Collaboration Rules
|
||||
|
||||
We'll refine separately; I'll merge when...
|
||||
...the functionality is complete and tested.
|
||||
**PR Description Tip:** Include concise changelog markdown.
|
||||