Refactor Vector3Field and update project structure

Changed Vector3Field.cs to derive from ValueControl<Vector3> and use NumberBox controls for better UI handling.
Changed EditorControls.xaml to update resource paths for new controls.
Changed InternalControls.xaml to simplify the resource dictionary by removing unnecessary references.
Changed IComponentEditor.cs to reflect updates in the component editor's lifecycle methods.
Changed project files for Ghost.Editor and Ghost.Core to include new dependencies and project references.
Changed FileExtensions.cs and IInspectorService.cs to align with the new namespace structure.
Changed Result.cs to enhance error handling and success checking methods.
Changed TypeHandle.cs to improve type handling compatibility.
Changed AssemblyInfo.cs files to include new assembly visibility attributes for better encapsulation.
Added new graphics-related classes and interfaces in the Ghost.Engine project, including IGraphicsDevice and DX12GraphicsDevice.
Added a new Mesh class to handle 3D mesh data and provide methods for creating geometric shapes.
Added GraphicsPipeline.cs to manage the graphics rendering loop and device initialization.
Added ScenePage.xaml and ScenePage.xaml.cs to create a new page for rendering scenes.
Updated HierarchyPage.xaml.cs and InspectorPage.xaml.cs to use the new service locator pattern for service retrieval.
Updated LandingWindow.xaml.cs and EngineEditorWindow.xaml.cs to utilize the new service locator pattern for better service access.
Updated Logger.cs to enhance logging capabilities with optional stack traces and assertion logging.
Updated QueryFilter.cs and QueryEnumerable.cs to use the new TypeHandle structure for improved efficiency.
Updated WorldNode.cs and WorldNodeSerializer.cs to enhance serialization and management of world nodes.
Updated AssetDatabase and related classes to improve asset management and metadata generation.
Updated Ghost.UnitTest.csproj to include new project references and package dependencies for unit tests.
This commit is contained in:
2025-06-27 20:02:02 +09:00
parent 1724072f7e
commit 4110c166cf
191 changed files with 2286 additions and 1462 deletions

View File

@@ -5,5 +5,5 @@ global using WorldID = System.UInt16;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Ghost.Engine")]
[assembly: InternalsVisibleTo("Ghost.Editor")]
[assembly: InternalsVisibleTo("Ghost.Editor.Core")]
[assembly: InternalsVisibleTo("Ghost.UnitTest")]

View File

@@ -1,4 +1,4 @@
using Ghost.Entities.Utilities;
using Ghost.Core;
using Misaki.HighPerformance.Unsafe.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
@@ -463,8 +463,8 @@ internal class ScriptComponentPool : IComponentPool<ScriptComponent>
[SkipLocalsInit]
internal readonly struct ComponentStorage : IDisposable
{
private readonly Dictionary<nint, IComponentPool> _componentPools = new();
private readonly Dictionary<nint, BitSet> _componentEntityMasks = new();
private readonly Dictionary<TypeHandle, IComponentPool> _componentPools = new();
private readonly Dictionary<TypeHandle, BitSet> _componentEntityMasks = new();
private readonly ScriptComponentPool _scriptComponentPool = new();
private readonly World _world;
@@ -474,12 +474,12 @@ internal readonly struct ComponentStorage : IDisposable
_world = world;
}
internal Dictionary<nint, IComponentPool> ComponentPools => _componentPools;
internal Dictionary<nint, BitSet> ComponentEntityMasks => _componentEntityMasks;
internal Dictionary<TypeHandle, IComponentPool> ComponentPools => _componentPools;
internal Dictionary<TypeHandle, BitSet> ComponentEntityMasks => _componentEntityMasks;
internal ScriptComponentPool ScriptComponentPool => _scriptComponentPool;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetPool(nint typeHandle, [MaybeNullWhen(false)] out IComponentPool pool)
public bool TryGetPool(TypeHandle typeHandle, [MaybeNullWhen(false)] out IComponentPool pool)
{
return _componentPools.TryGetValue(typeHandle, out pool);
}
@@ -514,7 +514,7 @@ internal readonly struct ComponentStorage : IDisposable
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetMask(nint typeHandle, [MaybeNullWhen(false)] out BitSet bitSet)
public bool TryGetMask(TypeHandle typeHandle, [MaybeNullWhen(false)] out BitSet bitSet)
{
return _componentEntityMasks.TryGetValue(typeHandle, out bitSet);
}
@@ -522,7 +522,7 @@ internal readonly struct ComponentStorage : IDisposable
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetMask(Type type, [MaybeNullWhen(false)] out BitSet bitSet)
{
return TryGetMask(type.TypeHandle.Value, out bitSet);
return TryGetMask(TypeHandle.Get(type), out bitSet);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -532,7 +532,7 @@ internal readonly struct ComponentStorage : IDisposable
return TryGetMask(TypeHandle.Get<T>(), out bitSet);
}
public BitSet GetOrCreateMask(nint typeHandle)
public BitSet GetOrCreateMask(TypeHandle typeHandle)
{
if (!_componentEntityMasks.TryGetValue(typeHandle, out var mask))
{

View File

@@ -1,10 +1,11 @@
using Ghost.Entities.Components;
using Ghost.Core;
using Ghost.Entities.Components;
using Ghost.Entities.Query;
using Ghost.Entities.Utilities;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ghost.Entities;
public readonly struct EntityManager : IDisposable
{
private readonly List<Entity> _entities;
@@ -181,7 +182,7 @@ public readonly struct EntityManager : IDisposable
/// <param name="typeHandle">The handle of the component type.</param>
/// <returns>True if the entity has the component; otherwise, false.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool HasComponent(Entity entity, nint typeHandle)
public readonly bool HasComponent(Entity entity, TypeHandle typeHandle)
{
return _world.ComponentStorage.TryGetMask(typeHandle, out var bitSet) && bitSet.IsSet(entity.ID);
}
@@ -238,7 +239,7 @@ public readonly struct EntityManager : IDisposable
/// <returns>An enumerable collection of <see cref="IntPtr"/> representing the memory addresses of the components associated
/// with the specified entity. The collection will be empty if the entity has no associated components.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly IEnumerable<(IntPtr, IntPtr)> GetComponentsUnsafe(Entity entity)
public readonly IEnumerable<(TypeHandle, IntPtr)> GetComponentsUnsafe(Entity entity)
{
foreach (var (typeHandle, pool) in _world.ComponentStorage.ComponentPools)
{

View File

@@ -90,4 +90,8 @@
<Folder Include="Helpers\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ghost.Core\Ghost.Core.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,4 +1,5 @@
using Misaki.HighPerformance.Unsafe.Collections;
using Ghost.Core;
using Misaki.HighPerformance.Unsafe.Collections;
namespace Ghost.Entities.Query;
@@ -11,30 +12,29 @@ internal enum FilterMode
Disabled = 1 << 3,
}
internal readonly struct FilterEntry(nint id, FilterMode mode)
internal readonly struct FilterEntry(TypeHandle id, FilterMode mode)
{
public readonly nint typeHandle = id;
public readonly TypeHandle typeHandle = id;
public readonly FilterMode mode = mode;
}
internal struct QueryFilter()
{
internal List<nint> _all = new(6);
internal List<nint> _any = new(6);
internal List<nint> _absent = new(6);
internal List<nint> _disabled = new(6);
internal List<TypeHandle> _all = new(6);
internal List<TypeHandle> _any = new(6);
internal List<TypeHandle> _absent = new(6);
internal List<TypeHandle> _disabled = new(6);
public readonly void ComputeFilterBitMask(World world, BitSet result)
public readonly BitSet ComputeFilterBitMask(World world)
{
BitSet allMask = new();
BitSet anyMask = new();
BitSet absentMask = new();
BitSet? allMask = null;
BitSet? anyMask = null;
BitSet? absentMask = null;
var hasAll = false;
var hasAny = false;
var hasAbsent = false;
// Compute All mask (intersection)
foreach (var typeHandle in _all)
{
var mask = world.ComponentStorage.GetOrCreateMask(typeHandle);
@@ -49,7 +49,6 @@ internal struct QueryFilter()
allMask &= mask;
}
// Compute Any mask (union)
foreach (var typeHandle in _any)
{
var mask = world.ComponentStorage.GetOrCreateMask(typeHandle);
@@ -63,7 +62,6 @@ internal struct QueryFilter()
anyMask |= mask;
}
// Compute Absent mask (union for exclusion)
foreach (var typeHandle in _absent)
{
var mask = world.ComponentStorage.GetOrCreateMask(typeHandle);
@@ -77,21 +75,24 @@ internal struct QueryFilter()
absentMask |= mask;
}
var result = new BitSet(world.EntityManager.EntityCount);
result.SetAll();
if (hasAll)
{
result &= allMask;
result &= allMask!;
}
if (hasAny)
{
result &= anyMask;
result &= anyMask!;
}
if (hasAbsent)
{
result &= ~absentMask;
result &= ~absentMask!;
}
return result;
}
}

View File

@@ -1,8 +1,8 @@
using Ghost.Core;
using Ghost.Entities.Components;
using Ghost.Entities.Query;
using Ghost.Entities.Utilities;
using Misaki.HighPerformance.Unsafe.Collections;
namespace Ghost.Entities;
@@ -59,8 +59,7 @@ public struct QueryEnumerable<T0>
_count = count;
_index = -1;
_filterMask = new BitSet(_world.EntityManager.EntityCount);
filters.ComputeFilterBitMask(_world, _filterMask);
_filterMask = filters.ComputeFilterBitMask(_world);
Current = default;
}
@@ -232,8 +231,7 @@ public struct QueryEnumerable<T0, T1>
_count = count;
_index = -1;
_filterMask = new BitSet(_world.EntityManager.EntityCount);
filters.ComputeFilterBitMask(_world, _filterMask);
_filterMask = filters.ComputeFilterBitMask(_world);
Current = default;
}
@@ -410,8 +408,7 @@ public struct QueryEnumerable<T0, T1, T2>
_count = count;
_index = -1;
_filterMask = new BitSet(_world.EntityManager.EntityCount);
filters.ComputeFilterBitMask(_world, _filterMask);
_filterMask = filters.ComputeFilterBitMask(_world);
Current = default;
}
@@ -593,8 +590,7 @@ public struct QueryEnumerable<T0, T1, T2, T3>
_count = count;
_index = -1;
_filterMask = new BitSet(_world.EntityManager.EntityCount);
filters.ComputeFilterBitMask(_world, _filterMask);
_filterMask = filters.ComputeFilterBitMask(_world);
Current = default;
}
@@ -781,8 +777,7 @@ public struct QueryEnumerable<T0, T1, T2, T3, T4>
_count = count;
_index = -1;
_filterMask = new BitSet(_world.EntityManager.EntityCount);
filters.ComputeFilterBitMask(_world, _filterMask);
_filterMask = filters.ComputeFilterBitMask(_world);
Current = default;
}
@@ -974,8 +969,7 @@ public struct QueryEnumerable<T0, T1, T2, T3, T4, T5>
_count = count;
_index = -1;
_filterMask = new BitSet(_world.EntityManager.EntityCount);
filters.ComputeFilterBitMask(_world, _filterMask);
_filterMask = filters.ComputeFilterBitMask(_world);
Current = default;
}
@@ -1172,8 +1166,7 @@ public struct QueryEnumerable<T0, T1, T2, T3, T4, T5, T6>
_count = count;
_index = -1;
_filterMask = new BitSet(_world.EntityManager.EntityCount);
filters.ComputeFilterBitMask(_world, _filterMask);
_filterMask = filters.ComputeFilterBitMask(_world);
Current = default;
}
@@ -1375,8 +1368,7 @@ public struct QueryEnumerable<T0, T1, T2, T3, T4, T5, T6, T7>
_count = count;
_index = -1;
_filterMask = new BitSet(_world.EntityManager.EntityCount);
filters.ComputeFilterBitMask(_world, _filterMask);
_filterMask = filters.ComputeFilterBitMask(_world);
Current = default;
}

View File

@@ -5,9 +5,9 @@
<#@ import namespace="System.Linq" #>
<#@ include file="Helpers.ttinclude" #>
using Ghost.Core;
using Ghost.Entities.Components;
using Ghost.Entities.Query;
using Ghost.Entities.Utilities;
using Misaki.HighPerformance.Unsafe.Collections;
namespace Ghost.Entities;
@@ -85,8 +85,7 @@ public struct QueryEnumerable<<#= generics #>>
_count = count;
_index = -1;
_filterMask = new BitSet(_world.EntityManager.EntityCount);
filters.ComputeFilterBitMask(_world, _filterMask);
_filterMask = filters.ComputeFilterBitMask(_world);
Current = default;
}

View File

@@ -1,33 +0,0 @@
using System.Runtime.CompilerServices;
namespace Ghost.Entities.Utilities;
internal static class TypeHandle
{
/// <summary>
/// Gets the type handle for the specified type.
/// </summary>
/// <typeparam name="T">The type to get the handle for.</typeparam>
/// <returns>The type handle as a nint.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static nint Get<T>()
{
return typeof(T).TypeHandle.Value;
}
/// <summary>
/// Gets the type handle for the specified type.
/// </summary>
/// <param name="type">The type to get the handle for.</param>
/// <returns>The type handle as a nint.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static nint Get(Type type)
{
return type.TypeHandle.Value;
}
public static Type? ToType(nint handle)
{
return Type.GetTypeFromHandle(RuntimeTypeHandle.FromIntPtr(handle));
}
}

View File

@@ -57,6 +57,8 @@ public partial class World : IDisposable, IEquatable<World>
public EntityManager EntityManager => _entityManager;
public SystemStorage SystemStorage => _systemStorage;
public event Action<World, Entity, Type>? ComponentChanged;
private World(WorldID id, int entityCapacity)
{
_id = id;
@@ -84,6 +86,21 @@ public partial class World : IDisposable, IEquatable<World>
return Enumerable.Empty<ScriptComponent>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void NotifyComponentChanged(Entity entity, Type type)
{
//#if GHOST_EDITOR
ComponentChanged?.Invoke(this, entity, type);
//#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void NotifyComponentChanged<T>(Entity entity)
where T : unmanaged, IComponentData
{
NotifyComponentChanged(entity, typeof(T));
}
public void Dispose()
{
_entityManager.Dispose();