reformat
This commit is contained in:
@@ -39,16 +39,22 @@ public class ConsumableComponent : Engine.Scene.Component.Component
|
|||||||
private void OnCollision(AABBColliderComponent parCollider)
|
private void OnCollision(AABBColliderComponent parCollider)
|
||||||
{
|
{
|
||||||
if (parCollider.ColliderGroups.Contains("player"))
|
if (parCollider.ColliderGroups.Contains("player"))
|
||||||
|
{
|
||||||
TryConsume(parCollider);
|
TryConsume(parCollider);
|
||||||
|
}
|
||||||
else if (parCollider.ColliderGroups.Contains("consumable"))
|
else if (parCollider.ColliderGroups.Contains("consumable"))
|
||||||
|
{
|
||||||
MoveAway(parCollider);
|
MoveAway(parCollider);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void TryConsume(AABBColliderComponent parCollider)
|
private void TryConsume(AABBColliderComponent parCollider)
|
||||||
{
|
{
|
||||||
var playerController = parCollider.GameObject.GetComponent<PlayerController>();
|
var playerController = parCollider.GameObject.GetComponent<PlayerController>();
|
||||||
if (playerController == null)
|
if (playerController == null)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_consumable.Consume(playerController);
|
_consumable.Consume(playerController);
|
||||||
|
|
||||||
@@ -61,17 +67,21 @@ public class ConsumableComponent : Engine.Scene.Component.Component
|
|||||||
parCollider.GameObject.Transform.GetFullTranslation();
|
parCollider.GameObject.Transform.GetFullTranslation();
|
||||||
|
|
||||||
if (direction.LengthSquared <= float.Epsilon)
|
if (direction.LengthSquared <= float.Epsilon)
|
||||||
|
{
|
||||||
direction = GetRandomDirection();
|
direction = GetRandomDirection();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
direction.Normalize();
|
direction.Normalize();
|
||||||
|
}
|
||||||
|
|
||||||
_movementController.ApplyMovement(direction);
|
_movementController.ApplyMovement(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Vector3 GetRandomDirection()
|
private Vector3 GetRandomDirection()
|
||||||
{
|
{
|
||||||
var x = _random.NextSingle() * 2 - 1;
|
var x = (_random.NextSingle() * 2) - 1;
|
||||||
var y = _random.NextSingle() * 2 - 1;
|
var y = (_random.NextSingle() * 2) - 1;
|
||||||
|
|
||||||
return new Vector3(x, y, 0).Normalized();
|
return new Vector3(x, y, 0).Normalized();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,16 +8,15 @@ namespace DoomDeathmatch.Component.MVC.Enemy;
|
|||||||
|
|
||||||
public class EnemyController : Engine.Scene.Component.Component
|
public class EnemyController : Engine.Scene.Component.Component
|
||||||
{
|
{
|
||||||
public HealthController HealthController => _healthController;
|
public HealthController HealthController { get; private set; } = null!;
|
||||||
|
|
||||||
private GameController _gameController = null!;
|
|
||||||
private HealthController _healthController = null!;
|
|
||||||
private EnemyView _enemyView = null!;
|
|
||||||
private MovementController _movementController = null!;
|
|
||||||
private AttackBehavior _attackBehavior = null!;
|
|
||||||
|
|
||||||
private readonly EnemyData _enemyData;
|
private readonly EnemyData _enemyData;
|
||||||
|
|
||||||
|
private GameController _gameController = null!;
|
||||||
|
private MovementController _movementController = null!;
|
||||||
|
private AttackBehavior _attackBehavior = null!;
|
||||||
|
private EnemyView _enemyView = null!;
|
||||||
|
|
||||||
public EnemyController(EnemyData parEnemyData)
|
public EnemyController(EnemyData parEnemyData)
|
||||||
{
|
{
|
||||||
_enemyData = parEnemyData;
|
_enemyData = parEnemyData;
|
||||||
@@ -26,19 +25,19 @@ public class EnemyController : Engine.Scene.Component.Component
|
|||||||
public override void Awake()
|
public override void Awake()
|
||||||
{
|
{
|
||||||
_gameController = EngineUtil.SceneManager.CurrentScene!.FindFirstComponent<GameController>()!;
|
_gameController = EngineUtil.SceneManager.CurrentScene!.FindFirstComponent<GameController>()!;
|
||||||
_healthController = GameObject.GetComponent<HealthController>()!;
|
HealthController = GameObject.GetComponent<HealthController>()!;
|
||||||
_enemyView = GameObject.GetComponent<EnemyView>()!;
|
_enemyView = GameObject.GetComponent<EnemyView>()!;
|
||||||
_movementController = GameObject.GetComponent<MovementController>()!;
|
_movementController = GameObject.GetComponent<MovementController>()!;
|
||||||
|
|
||||||
ArgumentNullException.ThrowIfNull(_gameController);
|
ArgumentNullException.ThrowIfNull(_gameController);
|
||||||
ArgumentNullException.ThrowIfNull(_healthController);
|
ArgumentNullException.ThrowIfNull(HealthController);
|
||||||
ArgumentNullException.ThrowIfNull(_enemyView);
|
ArgumentNullException.ThrowIfNull(_enemyView);
|
||||||
ArgumentNullException.ThrowIfNull(_movementController);
|
ArgumentNullException.ThrowIfNull(_movementController);
|
||||||
|
|
||||||
_attackBehavior = _enemyData.AttackBehaviorCreator.Create(this, _gameController.PlayerController.HealthController);
|
_attackBehavior = _enemyData.AttackBehaviorCreator.Create(this, _gameController.PlayerController.HealthController);
|
||||||
|
|
||||||
_healthController.SetMaxHealth(_enemyData.BaseHealth);
|
HealthController.SetMaxHealth(_enemyData.BaseHealth);
|
||||||
_healthController.OnDeath += OnDeath;
|
HealthController.OnDeath += OnDeath;
|
||||||
_enemyView.UpdateView(_enemyData);
|
_enemyView.UpdateView(_enemyData);
|
||||||
_movementController.Speed = _enemyData.BaseSpeed;
|
_movementController.Speed = _enemyData.BaseSpeed;
|
||||||
}
|
}
|
||||||
@@ -47,7 +46,9 @@ public class EnemyController : Engine.Scene.Component.Component
|
|||||||
{
|
{
|
||||||
var billboardComponent = GameObject.GetComponentInChildren<BillboardComponent>();
|
var billboardComponent = GameObject.GetComponentInChildren<BillboardComponent>();
|
||||||
if (billboardComponent == null)
|
if (billboardComponent == null)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
billboardComponent.Target = _gameController.PlayerController.Camera.GameObject.Transform;
|
billboardComponent.Target = _gameController.PlayerController.Camera.GameObject.Transform;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,16 +9,16 @@ namespace DoomDeathmatch.Component.MVC;
|
|||||||
|
|
||||||
public class GameController : Engine.Scene.Component.Component
|
public class GameController : Engine.Scene.Component.Component
|
||||||
{
|
{
|
||||||
public bool IsPaused { get; private set; } = false;
|
public bool IsPaused { get; private set; }
|
||||||
public bool IsGameOver { get; private set; } = false;
|
public bool IsGameOver { get; private set; }
|
||||||
|
|
||||||
public PlayerController PlayerController { get; private set; } = null!;
|
public PlayerController PlayerController { get; private set; } = null!;
|
||||||
public ScoreController ScoreController { get; private set; } = null!;
|
public ScoreController ScoreController { get; private set; } = null!;
|
||||||
|
|
||||||
private TimerController _timerController = null!;
|
|
||||||
|
|
||||||
private readonly MenuControllerComponent _menuController;
|
|
||||||
private readonly IInputHandler _inputHandler = EngineUtil.InputHandler;
|
private readonly IInputHandler _inputHandler = EngineUtil.InputHandler;
|
||||||
|
private readonly MenuControllerComponent _menuController;
|
||||||
|
|
||||||
|
private TimerController _timerController = null!;
|
||||||
|
|
||||||
public GameController(MenuControllerComponent parMenuController)
|
public GameController(MenuControllerComponent parMenuController)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ public class HealthView : Engine.Scene.Component.Component
|
|||||||
{
|
{
|
||||||
var percentage = parHealthModel.Health / parHealthModel.MaxHealth * 100;
|
var percentage = parHealthModel.Health / parHealthModel.MaxHealth * 100;
|
||||||
if (parHealthModel.Health != 0)
|
if (parHealthModel.Health != 0)
|
||||||
|
{
|
||||||
percentage = Math.Max(1, percentage);
|
percentage = Math.Max(1, percentage);
|
||||||
|
}
|
||||||
|
|
||||||
_healthTextRenderer.Text = $"Здоровье: {percentage:000}";
|
_healthTextRenderer.Text = $"Здоровье: {percentage:000}";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,6 @@ public class MovementController : Engine.Scene.Component.Component
|
|||||||
|
|
||||||
public void ApplyMovement(Vector3 parDirection)
|
public void ApplyMovement(Vector3 parDirection)
|
||||||
{
|
{
|
||||||
_rigidbody.AddForce(_dragComponent.Drag * Speed * parDirection.Normalized());
|
_rigidbody.Force += _dragComponent.Drag * Speed * parDirection.Normalized();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,71 +12,77 @@ namespace DoomDeathmatch.Component.MVC;
|
|||||||
public class PlayerController : Engine.Scene.Component.Component
|
public class PlayerController : Engine.Scene.Component.Component
|
||||||
{
|
{
|
||||||
public event Action? OnDeath;
|
public event Action? OnDeath;
|
||||||
public bool IsAlive => _healthController.IsAlive;
|
|
||||||
|
|
||||||
public HealthController HealthController => _healthController;
|
public bool IsAlive => HealthController.IsAlive;
|
||||||
public WeaponController WeaponController => _weaponController;
|
public HealthController HealthController { get; private set; } = null!;
|
||||||
public PerspectiveCamera Camera => _camera;
|
public WeaponController WeaponController { get; private set; } = null!;
|
||||||
|
public PerspectiveCamera Camera { get; }
|
||||||
|
|
||||||
private readonly IInputHandler _inputHandler = EngineUtil.InputHandler;
|
private readonly IInputHandler _inputHandler = EngineUtil.InputHandler;
|
||||||
|
|
||||||
private HealthController _healthController = null!;
|
|
||||||
private WeaponController _weaponController = null!;
|
|
||||||
private readonly PerspectiveCamera _camera;
|
|
||||||
|
|
||||||
public PlayerController(PerspectiveCamera parCamera)
|
public PlayerController(PerspectiveCamera parCamera)
|
||||||
{
|
{
|
||||||
_camera = parCamera;
|
Camera = parCamera;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Awake()
|
public override void Awake()
|
||||||
{
|
{
|
||||||
_healthController = GameObject.GetComponent<HealthController>()!;
|
HealthController = GameObject.GetComponent<HealthController>()!;
|
||||||
_weaponController = GameObject.GetComponent<WeaponController>()!;
|
WeaponController = GameObject.GetComponent<WeaponController>()!;
|
||||||
|
|
||||||
ArgumentNullException.ThrowIfNull(_healthController);
|
ArgumentNullException.ThrowIfNull(HealthController);
|
||||||
ArgumentNullException.ThrowIfNull(_weaponController);
|
ArgumentNullException.ThrowIfNull(WeaponController);
|
||||||
|
|
||||||
_healthController.OnDeath += () => OnDeath?.Invoke();
|
HealthController.OnDeath += () => OnDeath?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update(double parDeltaTime)
|
public override void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
if (!IsAlive || parDeltaTime == 0)
|
if (!IsAlive || parDeltaTime == 0)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < 10; i++)
|
for (var i = 0; i < 10; i++)
|
||||||
{
|
{
|
||||||
if (KeyboardButtonCode.D1 + i > KeyboardButtonCode.D0)
|
if (KeyboardButtonCode.D1 + i > KeyboardButtonCode.D0)
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (!_inputHandler.IsKeyJustPressed(KeyboardButtonCode.D1 + i))
|
if (!_inputHandler.IsKeyJustPressed(KeyboardButtonCode.D1 + i))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
_weaponController.SelectWeapon(i);
|
WeaponController.SelectWeapon(i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_inputHandler.IsKeyJustPressed(KeyboardButtonCode.Space))
|
if (_inputHandler.IsKeyJustPressed(KeyboardButtonCode.Space))
|
||||||
{
|
{
|
||||||
if (!_weaponController.TryShoot())
|
if (!WeaponController.TryShoot())
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var position = _camera.GameObject.Transform.GetFullTranslation();
|
var position = Camera.GameObject.Transform.GetFullTranslation();
|
||||||
var forward = (_camera.Forward - position).Normalized();
|
var forward = (Camera.Forward - position).Normalized();
|
||||||
var right = Vector3.Cross(forward, Vector3.UnitZ).Normalized();
|
var right = Vector3.Cross(forward, Vector3.UnitZ).Normalized();
|
||||||
|
|
||||||
var collisionManager = EngineUtil.SceneManager.CurrentScene!.FindFirstComponent<CollisionManagerComponent>();
|
var collisionManager = EngineUtil.SceneManager.CurrentScene!.FindFirstComponent<CollisionManagerComponent>();
|
||||||
|
|
||||||
var offsets = _weaponController.WeaponData.ShootPattern.GetShootPattern(forward, Vector3.UnitZ, right);
|
var offsets = WeaponController.WeaponData.ShootPattern.GetShootPattern(forward, Vector3.UnitZ, right);
|
||||||
foreach (var offset in offsets)
|
foreach (var offset in offsets)
|
||||||
{
|
{
|
||||||
var direction = forward + offset;
|
var direction = forward + offset;
|
||||||
if (!collisionManager!.Raycast(position, direction, ["enemy", "wall"], out var result))
|
if (!collisionManager!.Raycast(position, direction, ["enemy", "wall"], out var result))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var enemyController = result.HitObject.GetComponent<EnemyController>();
|
var enemyController = result.HitObject.GetComponent<EnemyController>();
|
||||||
enemyController?.HealthController.TakeDamage(_weaponController.WeaponData.Damage);
|
enemyController?.HealthController.TakeDamage(WeaponController.WeaponData.Damage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ public class TimerController : Engine.Scene.Component.Component
|
|||||||
public event Action? OnFinished;
|
public event Action? OnFinished;
|
||||||
|
|
||||||
private readonly TickableTimer _tickableTimer = new(60 + 10);
|
private readonly TickableTimer _tickableTimer = new(60 + 10);
|
||||||
|
|
||||||
private TimerView _timerView = null!;
|
private TimerView _timerView = null!;
|
||||||
|
|
||||||
public override void Awake()
|
public override void Awake()
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ public class WeaponController : Engine.Scene.Component.Component
|
|||||||
public bool TryShoot()
|
public bool TryShoot()
|
||||||
{
|
{
|
||||||
if (_weaponModel.SelectedWeapon.Ammo <= 0)
|
if (_weaponModel.SelectedWeapon.Ammo <= 0)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
_weaponModel.SelectedWeapon.Ammo--;
|
_weaponModel.SelectedWeapon.Ammo--;
|
||||||
|
|
||||||
@@ -41,7 +43,9 @@ public class WeaponController : Engine.Scene.Component.Component
|
|||||||
public void AddWeapon(WeaponData parWeaponData)
|
public void AddWeapon(WeaponData parWeaponData)
|
||||||
{
|
{
|
||||||
if (_weaponModel.Weapons.Contains(parWeaponData))
|
if (_weaponModel.Weapons.Contains(parWeaponData))
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_weaponModel.Weapons.Add(parWeaponData);
|
_weaponModel.Weapons.Add(parWeaponData);
|
||||||
}
|
}
|
||||||
@@ -49,15 +53,21 @@ public class WeaponController : Engine.Scene.Component.Component
|
|||||||
public void AddOrMergeWeapon(WeaponData parWeaponData)
|
public void AddOrMergeWeapon(WeaponData parWeaponData)
|
||||||
{
|
{
|
||||||
if (!_weaponModel.Weapons.Contains(parWeaponData))
|
if (!_weaponModel.Weapons.Contains(parWeaponData))
|
||||||
|
{
|
||||||
_weaponModel.Weapons.Add(parWeaponData);
|
_weaponModel.Weapons.Add(parWeaponData);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
_weaponModel.Weapons.First(parData => parData.Id == parWeaponData.Id).Ammo += parWeaponData.Ammo;
|
_weaponModel.Weapons.First(parData => parData.Id == parWeaponData.Id).Ammo += parWeaponData.Ammo;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void RemoveWeapon(int parIndex)
|
public void RemoveWeapon(int parIndex)
|
||||||
{
|
{
|
||||||
if (parIndex <= 0 || parIndex >= _weaponModel.Weapons.Count)
|
if (parIndex <= 0 || parIndex >= _weaponModel.Weapons.Count)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var newSelectedIndex = parIndex >= _weaponModel.SelectedWeaponIndex ? _weaponModel.SelectedWeaponIndex : 0;
|
var newSelectedIndex = parIndex >= _weaponModel.SelectedWeaponIndex ? _weaponModel.SelectedWeaponIndex : 0;
|
||||||
|
|
||||||
@@ -68,20 +78,19 @@ public class WeaponController : Engine.Scene.Component.Component
|
|||||||
public void SelectWeapon(int parIndex)
|
public void SelectWeapon(int parIndex)
|
||||||
{
|
{
|
||||||
if (parIndex >= _weaponModel.Weapons.Count)
|
if (parIndex >= _weaponModel.Weapons.Count)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_weaponModel.SelectedWeaponIndex = parIndex;
|
_weaponModel.SelectedWeaponIndex = parIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
// public WeaponData? FindWeapon(string parId)
|
|
||||||
// {
|
|
||||||
// return _weaponModel.Weapons.FirstOrDefault(parData => parData.Id == parId);
|
|
||||||
// }
|
|
||||||
|
|
||||||
private void WeaponSelected(WeaponData? parOldWeapon, WeaponData parNewWeapon)
|
private void WeaponSelected(WeaponData? parOldWeapon, WeaponData parNewWeapon)
|
||||||
{
|
{
|
||||||
if (parOldWeapon != null)
|
if (parOldWeapon != null)
|
||||||
|
{
|
||||||
parOldWeapon.OnAmmoChanged -= _weaponView.UpdateAmmoView;
|
parOldWeapon.OnAmmoChanged -= _weaponView.UpdateAmmoView;
|
||||||
|
}
|
||||||
|
|
||||||
parNewWeapon.OnAmmoChanged += _weaponView.UpdateAmmoView;
|
parNewWeapon.OnAmmoChanged += _weaponView.UpdateAmmoView;
|
||||||
_weaponView.UpdateView(parNewWeapon);
|
_weaponView.UpdateView(parNewWeapon);
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ public class WeaponView : Engine.Scene.Component.Component
|
|||||||
private readonly TextRenderer _weaponAmmo;
|
private readonly TextRenderer _weaponAmmo;
|
||||||
private readonly Box2DRenderer _weaponSprite;
|
private readonly Box2DRenderer _weaponSprite;
|
||||||
|
|
||||||
private AnimationPlayer<Texture>? _weaponFireAnimation;
|
|
||||||
private Texture? _idleTexture;
|
private Texture? _idleTexture;
|
||||||
|
|
||||||
|
private AnimationPlayer<Texture>? _weaponFireAnimation;
|
||||||
|
|
||||||
public WeaponView(TextRenderer parWeaponName, TextRenderer parWeaponAmmo, Box2DRenderer parWeaponSprite)
|
public WeaponView(TextRenderer parWeaponName, TextRenderer parWeaponAmmo, Box2DRenderer parWeaponSprite)
|
||||||
{
|
{
|
||||||
_weaponName = parWeaponName;
|
_weaponName = parWeaponName;
|
||||||
|
|||||||
@@ -16,13 +16,17 @@ public class ColliderForceFieldComponent : Engine.Scene.Component.Component
|
|||||||
{
|
{
|
||||||
var rigidbody = parCollider.GameObject.GetComponent<RigidbodyComponent>();
|
var rigidbody = parCollider.GameObject.GetComponent<RigidbodyComponent>();
|
||||||
if (rigidbody == null)
|
if (rigidbody == null)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var normal = _collider.Collider.GetCollisionNormal(parCollider.Collider);
|
var normal = _collider.Collider.GetCollisionNormal(parCollider.Collider);
|
||||||
var speedAlongNormal = Vector3.Dot(rigidbody.Velocity, normal);
|
var speedAlongNormal = Vector3.Dot(rigidbody.Velocity, normal);
|
||||||
if (speedAlongNormal > 0)
|
if (speedAlongNormal > 0)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
rigidbody.AddVelocity(-normal * (speedAlongNormal * 1.75f));
|
rigidbody.Velocity -= normal * (speedAlongNormal * 1.75f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30,19 +30,27 @@ public class CollisionManagerComponent : Engine.Scene.Component.Component
|
|||||||
!colliderA.ColliderGroups.Overlaps(colliderB.ExcludeColliderCollideGroups);
|
!colliderA.ColliderGroups.Overlaps(colliderB.ExcludeColliderCollideGroups);
|
||||||
|
|
||||||
if (!canCollideAB && !canCollideBA)
|
if (!canCollideAB && !canCollideBA)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!colliderA.Collider.Intersects(colliderB.Collider))
|
if (!colliderA.Collider.Intersects(colliderB.Collider))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (canCollideAB)
|
if (canCollideAB)
|
||||||
|
{
|
||||||
colliderA.CollideWith(colliderB);
|
colliderA.CollideWith(colliderB);
|
||||||
|
}
|
||||||
|
|
||||||
if (canCollideBA)
|
if (canCollideBA)
|
||||||
|
{
|
||||||
colliderB.CollideWith(colliderA);
|
colliderB.CollideWith(colliderA);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool Raycast(Vector3 parStart, Vector3 parDirection, HashSet<string> parColliderGroups,
|
public bool Raycast(Vector3 parStart, Vector3 parDirection, HashSet<string> parColliderGroups,
|
||||||
[MaybeNullWhen(false)] out RaycastResult parResult)
|
[MaybeNullWhen(false)] out RaycastResult parResult)
|
||||||
@@ -56,14 +64,20 @@ public class CollisionManagerComponent : Engine.Scene.Component.Component
|
|||||||
foreach (var collider in _colliders)
|
foreach (var collider in _colliders)
|
||||||
{
|
{
|
||||||
if (!collider.ColliderGroups.Overlaps(parColliderGroups))
|
if (!collider.ColliderGroups.Overlaps(parColliderGroups))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!RaycastAABB(start, direction, collider.Collider, out var hitPoint, out var normal))
|
if (!RaycastAABB(start, direction, collider.Collider, out var hitPoint, out var normal))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var distance = (start - hitPoint).Length;
|
var distance = (start - hitPoint).Length;
|
||||||
if (distance > closestDistance)
|
if (distance > closestDistance)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
closestDistance = distance;
|
closestDistance = distance;
|
||||||
|
|
||||||
@@ -128,36 +142,46 @@ public class CollisionManagerComponent : Engine.Scene.Component.Component
|
|||||||
for (var i = 0; i < 3; i++)
|
for (var i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
if (quadrant[i] != MIDDLE && parDirection[i] != 0.0f)
|
if (quadrant[i] != MIDDLE && parDirection[i] != 0.0f)
|
||||||
|
{
|
||||||
maxT[i] = (candidatePlane[i] - parOrigin[i]) / parDirection[i];
|
maxT[i] = (candidatePlane[i] - parOrigin[i]) / parDirection[i];
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
maxT[i] = -1.0f;
|
maxT[i] = -1.0f;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get largest of the maxT's for final choice of intersection
|
// Get largest of the maxT's for final choice of intersection
|
||||||
var whichPlane = 0;
|
var whichPlane = 0;
|
||||||
for (var i = 1; i < 3; i++)
|
for (var i = 1; i < 3; i++)
|
||||||
{
|
{
|
||||||
if (maxT[whichPlane] < maxT[i])
|
if (maxT[whichPlane] < maxT[i])
|
||||||
|
{
|
||||||
whichPlane = i;
|
whichPlane = i;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check final candidate actually inside box
|
// Check final candidate actually inside box
|
||||||
if (maxT[whichPlane] < 0.0f)
|
if (maxT[whichPlane] < 0.0f)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < 3; i++)
|
for (var i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
if (whichPlane != i)
|
if (whichPlane != i)
|
||||||
{
|
{
|
||||||
parHitPoint[i] = parOrigin[i] + maxT[whichPlane] * parDirection[i];
|
parHitPoint[i] = parOrigin[i] + (maxT[whichPlane] * parDirection[i]);
|
||||||
if (parHitPoint[i] < minB[i] || parHitPoint[i] > maxB[i])
|
if (parHitPoint[i] < minB[i] || parHitPoint[i] > maxB[i])
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
parHitPoint[i] = candidatePlane[i];
|
parHitPoint[i] = candidatePlane[i];
|
||||||
// Calculate normal for the intersection plane
|
// Calculate normal for the intersection plane
|
||||||
parHitNormal[i] = (quadrant[i] == LEFT) ? -1.0f : 1.0f;
|
parHitNormal[i] = quadrant[i] == LEFT ? -1.0f : 1.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ public class DragComponent : Engine.Scene.Component.Component
|
|||||||
|
|
||||||
public override void Update(double parDeltaTime)
|
public override void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
_rigidbody.AddForce(-Drag * (_rigidbody.Velocity * Multiplier));
|
_rigidbody.Force -= Drag * (_rigidbody.Velocity * Multiplier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,8 @@ namespace DoomDeathmatch.Component.Physics;
|
|||||||
|
|
||||||
public class GravityComponent : Engine.Scene.Component.Component
|
public class GravityComponent : Engine.Scene.Component.Component
|
||||||
{
|
{
|
||||||
|
public bool IsInAir { get; private set; }
|
||||||
|
|
||||||
public float Strength { get; set; } = 10.0f;
|
public float Strength { get; set; } = 10.0f;
|
||||||
|
|
||||||
public Vector3 Direction
|
public Vector3 Direction
|
||||||
@@ -14,7 +16,6 @@ public class GravityComponent : Engine.Scene.Component.Component
|
|||||||
|
|
||||||
public float Floor { get; set; } = 5.0f;
|
public float Floor { get; set; } = 5.0f;
|
||||||
|
|
||||||
public bool IsInAir { get; private set; } = false;
|
|
||||||
|
|
||||||
private RigidbodyComponent _rigidbody = null!;
|
private RigidbodyComponent _rigidbody = null!;
|
||||||
private Vector3 _direction = -Vector3.UnitZ;
|
private Vector3 _direction = -Vector3.UnitZ;
|
||||||
@@ -34,13 +35,15 @@ public class GravityComponent : Engine.Scene.Component.Component
|
|||||||
IsInAir = false;
|
IsInAir = false;
|
||||||
var velocityAlongDirection = Vector3.Dot(_rigidbody.Velocity, Direction);
|
var velocityAlongDirection = Vector3.Dot(_rigidbody.Velocity, Direction);
|
||||||
if (velocityAlongDirection > 0)
|
if (velocityAlongDirection > 0)
|
||||||
_rigidbody.AddVelocity(-velocityAlongDirection * Direction);
|
{
|
||||||
|
_rigidbody.Velocity -= velocityAlongDirection * Direction;
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IsInAir = true;
|
IsInAir = true;
|
||||||
var gravity = Strength * Direction;
|
var gravity = Strength * Direction;
|
||||||
_rigidbody.AddForce(gravity);
|
_rigidbody.Force += gravity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,35 +4,50 @@ namespace DoomDeathmatch.Component.Physics;
|
|||||||
|
|
||||||
public class RigidbodyComponent : Engine.Scene.Component.Component
|
public class RigidbodyComponent : Engine.Scene.Component.Component
|
||||||
{
|
{
|
||||||
public Vector3 Velocity { get; private set; } = Vector3.Zero;
|
|
||||||
public Vector3 Acceleration { get; private set; } = Vector3.Zero;
|
|
||||||
public Vector3 Force { get; private set; } = Vector3.Zero;
|
|
||||||
public float Mass { get; set; } = 1.0f;
|
public float Mass { get; set; } = 1.0f;
|
||||||
public bool IsStatic { get; set; } = false;
|
public bool IsStatic { get; set; } = false;
|
||||||
|
|
||||||
public void AddForce(Vector3 parForce)
|
public Vector3 Force
|
||||||
|
{
|
||||||
|
get => _force;
|
||||||
|
set
|
||||||
{
|
{
|
||||||
if (IsStatic)
|
if (IsStatic)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Force += parForce;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddVelocity(Vector3 parVelocity)
|
_force = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 Velocity
|
||||||
|
{
|
||||||
|
get => _velocity;
|
||||||
|
set
|
||||||
{
|
{
|
||||||
if (IsStatic)
|
if (IsStatic)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Velocity += parVelocity;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_velocity = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector3 _force = Vector3.Zero;
|
||||||
|
private Vector3 _velocity = Vector3.Zero;
|
||||||
|
private Vector3 _acceleration = Vector3.Zero;
|
||||||
|
|
||||||
public override void Update(double parDeltaTime)
|
public override void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
if (IsStatic)
|
if (IsStatic)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Acceleration = Force / Mass;
|
_acceleration = Force / Mass;
|
||||||
Velocity += Acceleration * (float)parDeltaTime;
|
Velocity += _acceleration * (float)parDeltaTime;
|
||||||
GameObject.Transform.Translation += Velocity * (float)parDeltaTime;
|
GameObject.Transform.Translation += Velocity * (float)parDeltaTime;
|
||||||
|
|
||||||
Force = Vector3.Zero;
|
Force = Vector3.Zero;
|
||||||
|
|||||||
@@ -5,18 +5,16 @@ namespace DoomDeathmatch.Component.UI;
|
|||||||
|
|
||||||
public class SelectorComponent : Engine.Scene.Component.Component
|
public class SelectorComponent : Engine.Scene.Component.Component
|
||||||
{
|
{
|
||||||
public List<UiComponent> Children => _children;
|
public event Action<UiComponent>? OnSelect;
|
||||||
public KeyboardButtonCode SelectKey { get; set; } = KeyboardButtonCode.Space;
|
|
||||||
|
public List<UiComponent> Children { get; } = [];
|
||||||
public KeyboardButtonCode NextKey { get; set; } = KeyboardButtonCode.Down;
|
public KeyboardButtonCode NextKey { get; set; } = KeyboardButtonCode.Down;
|
||||||
public KeyboardButtonCode PrevKey { get; set; } = KeyboardButtonCode.Up;
|
public KeyboardButtonCode PrevKey { get; set; } = KeyboardButtonCode.Up;
|
||||||
|
public KeyboardButtonCode SelectKey { get; set; } = KeyboardButtonCode.Space;
|
||||||
public event Action<UiComponent>? OnSelect;
|
|
||||||
|
|
||||||
private readonly IInputHandler _inputHandler = EngineUtil.InputHandler;
|
private readonly IInputHandler _inputHandler = EngineUtil.InputHandler;
|
||||||
|
|
||||||
private readonly List<UiComponent> _children = [];
|
private int _selectedIndex;
|
||||||
|
|
||||||
private int _selectedIndex = 0;
|
|
||||||
|
|
||||||
public override void Start()
|
public override void Start()
|
||||||
{
|
{
|
||||||
@@ -31,13 +29,17 @@ public class SelectorComponent : Engine.Scene.Component.Component
|
|||||||
public override void Update(double parDeltaTime)
|
public override void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
if (_inputHandler.IsKeyJustPressed(SelectKey))
|
if (_inputHandler.IsKeyJustPressed(SelectKey))
|
||||||
_children[_selectedIndex].InvokeClick();
|
{
|
||||||
|
Children[_selectedIndex].InvokeClick();
|
||||||
|
}
|
||||||
|
|
||||||
if (_inputHandler.IsKeyJustPressed(NextKey))
|
if (_inputHandler.IsKeyJustPressed(NextKey))
|
||||||
{
|
{
|
||||||
_selectedIndex++;
|
_selectedIndex++;
|
||||||
if (_selectedIndex >= Children.Count)
|
if (_selectedIndex >= Children.Count)
|
||||||
|
{
|
||||||
_selectedIndex = 0;
|
_selectedIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
SelectionChanged();
|
SelectionChanged();
|
||||||
}
|
}
|
||||||
@@ -46,7 +48,9 @@ public class SelectorComponent : Engine.Scene.Component.Component
|
|||||||
{
|
{
|
||||||
_selectedIndex--;
|
_selectedIndex--;
|
||||||
if (_selectedIndex < 0)
|
if (_selectedIndex < 0)
|
||||||
|
{
|
||||||
_selectedIndex = Children.Count - 1;
|
_selectedIndex = Children.Count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
SelectionChanged();
|
SelectionChanged();
|
||||||
}
|
}
|
||||||
@@ -77,10 +81,14 @@ public class SelectorComponent : Engine.Scene.Component.Component
|
|||||||
{
|
{
|
||||||
var index = Children.IndexOf(parComponent);
|
var index = Children.IndexOf(parComponent);
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
|
{
|
||||||
index = 0;
|
index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (index == _selectedIndex)
|
if (index == _selectedIndex)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_selectedIndex = index;
|
_selectedIndex = index;
|
||||||
|
|
||||||
|
|||||||
@@ -5,25 +5,23 @@ namespace DoomDeathmatch.Component.UI;
|
|||||||
|
|
||||||
public class StackComponent : UiContainerComponent
|
public class StackComponent : UiContainerComponent
|
||||||
{
|
{
|
||||||
public List<UiComponent> Children => _children;
|
public List<UiComponent> Children { get; } = [];
|
||||||
public Orientation Orientation { get; set; } = Orientation.Vertical;
|
public Orientation Orientation { get; set; } = Orientation.Vertical;
|
||||||
|
|
||||||
private readonly List<UiComponent> _children = [];
|
|
||||||
|
|
||||||
public override void Update(double parDeltaTime)
|
public override void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
base.Update(parDeltaTime);
|
base.Update(parDeltaTime);
|
||||||
|
|
||||||
var size = GameObject.Transform.Size.Xy;
|
var size = GameObject.Transform.Size.Xy;
|
||||||
var count = _children.Count;
|
var count = Children.Count;
|
||||||
|
|
||||||
size *= Orientation == Orientation.Horizontal ? new Vector2(1, 0) : new Vector2(0, 1);
|
size *= Orientation == Orientation.Horizontal ? new Vector2(1, 0) : new Vector2(0, 1);
|
||||||
|
|
||||||
var offset = new Vector2(-size.X / 2 + size.X / count / 2, -size.Y / 2 + size.Y / count / 2);
|
var offset = new Vector2((-size.X / 2) + (size.X / count / 2), (-size.Y / 2) + (size.Y / count / 2));
|
||||||
|
|
||||||
for (var i = count - 1; i >= 0; i--)
|
for (var i = count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
var child = _children[i];
|
var child = Children[i];
|
||||||
|
|
||||||
child.Offset = offset;
|
child.Offset = offset;
|
||||||
offset += size / count;
|
offset += size / count;
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ public class TextAlignComponent : Engine.Scene.Component.Component
|
|||||||
public Align Alignment { get; set; } = Align.Left;
|
public Align Alignment { get; set; } = Align.Left;
|
||||||
|
|
||||||
private TextRenderer _textRenderer = null!;
|
private TextRenderer _textRenderer = null!;
|
||||||
|
|
||||||
private string? _cachedText;
|
private string? _cachedText;
|
||||||
|
|
||||||
public override void Awake()
|
public override void Awake()
|
||||||
@@ -20,10 +19,14 @@ public class TextAlignComponent : Engine.Scene.Component.Component
|
|||||||
public override void Update(double parDeltaTime)
|
public override void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
if (_textRenderer.Text == null)
|
if (_textRenderer.Text == null)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_cachedText == _textRenderer.Text)
|
if (_cachedText == _textRenderer.Text)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_cachedText = _textRenderer.Text;
|
_cachedText = _textRenderer.Text;
|
||||||
var font = _textRenderer.Font;
|
var font = _textRenderer.Font;
|
||||||
|
|||||||
@@ -12,40 +12,37 @@ public class TextInputComponent : Engine.Scene.Component.Component
|
|||||||
|
|
||||||
public float InputDelay
|
public float InputDelay
|
||||||
{
|
{
|
||||||
get => _inputDelay;
|
get => (float)_inputTimer.TotalTime;
|
||||||
set
|
set => _inputTimer.TotalTime = value;
|
||||||
{
|
|
||||||
_inputDelay = value;
|
|
||||||
_inputTimer.TotalTime = value;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private readonly IInputHandler _inputHandler = EngineUtil.InputHandler;
|
|
||||||
|
|
||||||
private readonly KeyboardButtonCode[] _acceptedKeys =
|
private readonly KeyboardButtonCode[] _acceptedKeys =
|
||||||
KeyboardButtonCodeHelper.GetAllPrintableKeys().Append(KeyboardButtonCode.Backspace).ToArray();
|
KeyboardButtonCodeHelper.GetAllPrintableKeys().Append(KeyboardButtonCode.Backspace).ToArray();
|
||||||
|
|
||||||
|
private readonly IInputHandler _inputHandler = EngineUtil.InputHandler;
|
||||||
private readonly TickableTimer _inputTimer;
|
private readonly TickableTimer _inputTimer;
|
||||||
private float _inputDelay = 0;
|
|
||||||
private readonly HashSet<KeyboardButtonCode> _lastKeys = [];
|
private readonly HashSet<KeyboardButtonCode> _lastKeys = [];
|
||||||
|
|
||||||
public TextInputComponent(float parInputDelay = 0.2f)
|
public TextInputComponent(float parInputDelay = 0.2f)
|
||||||
{
|
{
|
||||||
_inputDelay = parInputDelay;
|
_inputTimer = new TickableTimer(parInputDelay) { CurrentTime = 0 };
|
||||||
_inputTimer = new TickableTimer(_inputDelay) { CurrentTime = 0 };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update(double parDeltaTime)
|
public override void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
if (!IsActive)
|
if (!IsActive)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_inputTimer.Update(parDeltaTime);
|
_inputTimer.Update(parDeltaTime);
|
||||||
|
|
||||||
foreach (var key in _acceptedKeys)
|
foreach (var key in _acceptedKeys)
|
||||||
{
|
{
|
||||||
if (_lastKeys.Contains(key) && !_inputTimer.IsFinished)
|
if (_lastKeys.Contains(key) && !_inputTimer.IsFinished)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (_inputHandler.IsKeyPressed(key))
|
if (_inputHandler.IsKeyPressed(key))
|
||||||
{
|
{
|
||||||
@@ -69,7 +66,9 @@ public class TextInputComponent : Engine.Scene.Component.Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (invoke)
|
if (invoke)
|
||||||
|
{
|
||||||
OnInput?.Invoke(Input);
|
OnInput?.Invoke(Input);
|
||||||
|
}
|
||||||
|
|
||||||
_lastKeys.Add(key);
|
_lastKeys.Add(key);
|
||||||
|
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ namespace DoomDeathmatch.Component.UI;
|
|||||||
|
|
||||||
public class UiComponent : Engine.Scene.Component.Component
|
public class UiComponent : Engine.Scene.Component.Component
|
||||||
{
|
{
|
||||||
public UiContainerComponent? Container { get; set; }
|
|
||||||
public Anchor Anchor { get; set; } = Anchor.Center;
|
|
||||||
public Vector2 Offset { get; set; } = Vector2.Zero;
|
|
||||||
public Anchor Center { get; set; } = Anchor.Center;
|
|
||||||
|
|
||||||
public event Action<UiComponent>? OnClick;
|
public event Action<UiComponent>? OnClick;
|
||||||
public event Action<UiComponent>? OnMouseOver;
|
public event Action<UiComponent>? OnMouseOver;
|
||||||
|
|
||||||
|
public UiContainerComponent? Container { get; set; }
|
||||||
|
public Anchor Center { get; set; } = Anchor.Center;
|
||||||
|
public Anchor Anchor { get; set; } = Anchor.Center;
|
||||||
|
public Vector2 Offset { get; set; } = Vector2.Zero;
|
||||||
|
|
||||||
private readonly IInputHandler _inputHandler = EngineUtil.InputHandler;
|
private readonly IInputHandler _inputHandler = EngineUtil.InputHandler;
|
||||||
|
|
||||||
public override void Update(double parDeltaTime)
|
public override void Update(double parDeltaTime)
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ namespace DoomDeathmatch.Component.UI;
|
|||||||
|
|
||||||
public class UiContainerComponent : UiComponent
|
public class UiContainerComponent : UiComponent
|
||||||
{
|
{
|
||||||
private readonly IInputHandler _inputHandler = EngineUtil.InputHandler;
|
|
||||||
|
|
||||||
public Camera? Camera { get; set; }
|
public Camera? Camera { get; set; }
|
||||||
public Vector3 MousePosition { get; private set; }
|
public Vector3 MousePosition { get; private set; }
|
||||||
|
|
||||||
|
private readonly IInputHandler _inputHandler = EngineUtil.InputHandler;
|
||||||
|
|
||||||
public override void Update(double parDeltaTime)
|
public override void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
base.Update(parDeltaTime);
|
base.Update(parDeltaTime);
|
||||||
|
|||||||
@@ -21,11 +21,15 @@ public class BillboardComponent : Engine.Scene.Component.Component
|
|||||||
var forward = targetPosition - currentPosition;
|
var forward = targetPosition - currentPosition;
|
||||||
forward -= Vector3.Dot(forward, Up) * Up;
|
forward -= Vector3.Dot(forward, Up) * Up;
|
||||||
if (forward.LengthSquared > 0)
|
if (forward.LengthSquared > 0)
|
||||||
|
{
|
||||||
forward.Normalize();
|
forward.Normalize();
|
||||||
|
}
|
||||||
|
|
||||||
var right = Vector3.Cross(Up, forward);
|
var right = Vector3.Cross(Up, forward);
|
||||||
if (right.LengthSquared > 0)
|
if (right.LengthSquared > 0)
|
||||||
|
{
|
||||||
right.Normalize();
|
right.Normalize();
|
||||||
|
}
|
||||||
|
|
||||||
var recalculatedUp = Vector3.Cross(forward, right).Normalized();
|
var recalculatedUp = Vector3.Cross(forward, right).Normalized();
|
||||||
|
|
||||||
|
|||||||
@@ -5,21 +5,29 @@ namespace DoomDeathmatch.Component.Util;
|
|||||||
|
|
||||||
public class CopySizeComponent : Engine.Scene.Component.Component
|
public class CopySizeComponent : Engine.Scene.Component.Component
|
||||||
{
|
{
|
||||||
public Vector3 Multiplier { get; set; } = Vector3.One;
|
|
||||||
public Transform? Target { get; set; }
|
public Transform? Target { get; set; }
|
||||||
|
public Vector3 Multiplier { get; set; } = Vector3.One;
|
||||||
|
|
||||||
public override void Update(double parDeltaTime)
|
public override void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
if (Target == null)
|
if (Target == null)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (Multiplier.X != 0)
|
if (Multiplier.X != 0)
|
||||||
|
{
|
||||||
GameObject.Transform.Size.X = Target.Size.X * Multiplier.X;
|
GameObject.Transform.Size.X = Target.Size.X * Multiplier.X;
|
||||||
|
}
|
||||||
|
|
||||||
if (Multiplier.Y != 0)
|
if (Multiplier.Y != 0)
|
||||||
|
{
|
||||||
GameObject.Transform.Size.Y = Target.Size.Y * Multiplier.Y;
|
GameObject.Transform.Size.Y = Target.Size.Y * Multiplier.Y;
|
||||||
|
}
|
||||||
|
|
||||||
if (Multiplier.Z != 0)
|
if (Multiplier.Z != 0)
|
||||||
|
{
|
||||||
GameObject.Transform.Size.Z = Target.Size.Z * Multiplier.Z;
|
GameObject.Transform.Size.Z = Target.Size.Z * Multiplier.Z;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ public class PlayerMovementComponent : Engine.Scene.Component.Component
|
|||||||
public float RotationSpeed { get; set; } = 110.0f;
|
public float RotationSpeed { get; set; } = 110.0f;
|
||||||
|
|
||||||
private readonly IInputHandler _inputHandler = EngineUtil.InputHandler;
|
private readonly IInputHandler _inputHandler = EngineUtil.InputHandler;
|
||||||
|
|
||||||
private MovementController _movementController = null!;
|
private MovementController _movementController = null!;
|
||||||
|
|
||||||
public override void Awake()
|
public override void Awake()
|
||||||
@@ -25,18 +26,34 @@ public class PlayerMovementComponent : Engine.Scene.Component.Component
|
|||||||
var rotation = 0.0f;
|
var rotation = 0.0f;
|
||||||
|
|
||||||
if (_inputHandler.IsKeyPressed(KeyboardButtonCode.W))
|
if (_inputHandler.IsKeyPressed(KeyboardButtonCode.W))
|
||||||
|
{
|
||||||
movement.Y += 1;
|
movement.Y += 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (_inputHandler.IsKeyPressed(KeyboardButtonCode.S))
|
if (_inputHandler.IsKeyPressed(KeyboardButtonCode.S))
|
||||||
|
{
|
||||||
movement.Y -= 1;
|
movement.Y -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (_inputHandler.IsKeyPressed(KeyboardButtonCode.D))
|
if (_inputHandler.IsKeyPressed(KeyboardButtonCode.D))
|
||||||
|
{
|
||||||
movement.X += 1;
|
movement.X += 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (_inputHandler.IsKeyPressed(KeyboardButtonCode.A))
|
if (_inputHandler.IsKeyPressed(KeyboardButtonCode.A))
|
||||||
|
{
|
||||||
movement.X -= 1;
|
movement.X -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (_inputHandler.IsKeyPressed(KeyboardButtonCode.Q))
|
if (_inputHandler.IsKeyPressed(KeyboardButtonCode.Q))
|
||||||
|
{
|
||||||
rotation += RotationSpeed;
|
rotation += RotationSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
if (_inputHandler.IsKeyPressed(KeyboardButtonCode.E))
|
if (_inputHandler.IsKeyPressed(KeyboardButtonCode.E))
|
||||||
|
{
|
||||||
rotation -= RotationSpeed;
|
rotation -= RotationSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
if (movement.LengthSquared > 0)
|
if (movement.LengthSquared > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -41,7 +41,9 @@ public static class GameOverScene
|
|||||||
nextUi.OnClick += _ =>
|
nextUi.OnClick += _ =>
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(nameInputComponent.Input))
|
if (string.IsNullOrEmpty(nameInputComponent.Input))
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SubmitScore(nameInputComponent.Input, parScore);
|
SubmitScore(nameInputComponent.Input, parScore);
|
||||||
EngineUtil.SceneManager.TransitionTo(MainScene.Create);
|
EngineUtil.SceneManager.TransitionTo(MainScene.Create);
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ public static class MainScene
|
|||||||
backUi.OnClick += _ => parMenuController.SelectMenuItem("main");
|
backUi.OnClick += _ => parMenuController.SelectMenuItem("main");
|
||||||
|
|
||||||
var (stackObject, stack) = UiUtil.CreateStackUi(parScene,
|
var (stackObject, stack) = UiUtil.CreateStackUi(parScene,
|
||||||
new StackComponent { Offset = new Vector2(0, -1.5f), Container = parUiContainer, Children = { } });
|
new StackComponent { Offset = new Vector2(0, -1.5f), Container = parUiContainer });
|
||||||
stackObject.Transform.Size.Xy = new Vector2(1f, 6f);
|
stackObject.Transform.Size.Xy = new Vector2(1f, 6f);
|
||||||
|
|
||||||
var leadersTable = CreateLeadersTable(parScene, parUiContainer, EngineUtil.DataFolder);
|
var leadersTable = CreateLeadersTable(parScene, parUiContainer, EngineUtil.DataFolder);
|
||||||
@@ -134,8 +134,10 @@ public static class MainScene
|
|||||||
{
|
{
|
||||||
leadersTableList.Add(CreateLeadersRow(parScene, parUiContainer, row.Name, row.Score.ToString()));
|
leadersTableList.Add(CreateLeadersRow(parScene, parUiContainer, row.Name, row.Score.ToString()));
|
||||||
if (++rows >= 5)
|
if (++rows >= 5)
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return leadersTableList;
|
return leadersTableList;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ public static class PlayScene
|
|||||||
new ScoreController(),
|
new ScoreController(),
|
||||||
playScoreView,
|
playScoreView,
|
||||||
|
|
||||||
new CollisionManagerComponent(),
|
new CollisionManagerComponent()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
@@ -186,7 +186,7 @@ public static class PlayScene
|
|||||||
}
|
}
|
||||||
|
|
||||||
var size = max - min;
|
var size = max - min;
|
||||||
var offset = min + size / 2;
|
var offset = min + (size / 2);
|
||||||
|
|
||||||
var colliderObject = GameObjectUtil.CreateColliderForceField(
|
var colliderObject = GameObjectUtil.CreateColliderForceField(
|
||||||
parScene,
|
parScene,
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public static class EnemyPrefab
|
|||||||
var enemyVisualObject = GameObjectUtil.CreateGameObject(parScene,
|
var enemyVisualObject = GameObjectUtil.CreateGameObject(parScene,
|
||||||
new Transform { Translation = new Vector3(0, 0f, 1f), Size = new Vector3(1, 2, 1) }, [
|
new Transform { Translation = new Vector3(0, 0f, 1f), Size = new Vector3(1, 2, 1) }, [
|
||||||
enemyBox2DRenderer,
|
enemyBox2DRenderer,
|
||||||
new BillboardComponent(),
|
new BillboardComponent()
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ public static class EnemyPrefab
|
|||||||
Vector3 parVelocity, float parDamage, Transform? parBillboardTarget = null)
|
Vector3 parVelocity, float parDamage, Transform? parBillboardTarget = null)
|
||||||
{
|
{
|
||||||
var rigidbodyComponent = new RigidbodyComponent();
|
var rigidbodyComponent = new RigidbodyComponent();
|
||||||
rigidbodyComponent.AddVelocity(parVelocity);
|
rigidbodyComponent.Velocity += parVelocity;
|
||||||
|
|
||||||
var fireballObject = GameObjectUtil.CreateGameObject(parScene,
|
var fireballObject = GameObjectUtil.CreateGameObject(parScene,
|
||||||
new Transform { Translation = parPosition, Size = new Vector3(0.5f) },
|
new Transform { Translation = parPosition, Size = new Vector3(0.5f) },
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public static class PlayerPrefab
|
|||||||
parWeaponView,
|
parWeaponView,
|
||||||
|
|
||||||
new HealthController(),
|
new HealthController(),
|
||||||
parHealthView,
|
parHealthView
|
||||||
]);
|
]);
|
||||||
|
|
||||||
parScene.AddChild(playerObject, perspectiveCameraObject);
|
parScene.AddChild(playerObject, perspectiveCameraObject);
|
||||||
|
|||||||
@@ -5,29 +5,25 @@ namespace DoomDeathmatch.Script;
|
|||||||
|
|
||||||
public class AnimationPlayer<T>(float parInterval) : IUpdate
|
public class AnimationPlayer<T>(float parInterval) : IUpdate
|
||||||
{
|
{
|
||||||
public List<T> Frames { get; init; } = [];
|
|
||||||
public bool IsPlaying { get; private set; } = false;
|
|
||||||
public int NextFrame { get; private set; } = 0;
|
|
||||||
|
|
||||||
public event Action<T>? OnFrameChanged;
|
|
||||||
public event Action? OnFinish;
|
public event Action? OnFinish;
|
||||||
|
public event Action<T>? OnFrameChanged;
|
||||||
|
|
||||||
|
public bool IsPlaying { get; private set; }
|
||||||
|
public List<T> Frames { get; init; } = [];
|
||||||
|
public int NextFrame { get; private set; }
|
||||||
|
|
||||||
private readonly TickableTimer _timer = new(parInterval);
|
private readonly TickableTimer _timer = new(parInterval);
|
||||||
|
|
||||||
public void Start()
|
|
||||||
{
|
|
||||||
Reset();
|
|
||||||
IsPlaying = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update(double parDeltaTime)
|
public void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
if (!IsPlaying)
|
if (!IsPlaying)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_timer.Update(parDeltaTime);
|
_timer.Update(parDeltaTime);
|
||||||
|
|
||||||
if (_timer.CurrentTime < _timer.TotalTime * (1.0f - 1.0f / Frames.Count * NextFrame))
|
if (_timer.CurrentTime < _timer.TotalTime * (1.0f - (1.0f / Frames.Count * NextFrame)))
|
||||||
{
|
{
|
||||||
OnFrameChanged?.Invoke(Frames[NextFrame]);
|
OnFrameChanged?.Invoke(Frames[NextFrame]);
|
||||||
NextFrame++;
|
NextFrame++;
|
||||||
@@ -41,6 +37,12 @@ public class AnimationPlayer<T>(float parInterval) : IUpdate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
IsPlaying = true;
|
||||||
|
}
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
_timer.Reset();
|
_timer.Reset();
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ namespace DoomDeathmatch.Script.Collision;
|
|||||||
|
|
||||||
public class AABBCollider
|
public class AABBCollider
|
||||||
{
|
{
|
||||||
public Vector3 Size { get; set; }
|
|
||||||
public Vector3 Position { get; set; }
|
public Vector3 Position { get; set; }
|
||||||
|
public Vector3 Size { get; set; }
|
||||||
|
|
||||||
public Vector3 Max => Position + Size / 2;
|
public Vector3 Min => Position - (Size / 2);
|
||||||
public Vector3 Min => Position - Size / 2;
|
public Vector3 Max => Position + (Size / 2);
|
||||||
|
|
||||||
public bool Intersects(AABBCollider parCollider)
|
public bool Intersects(AABBCollider parCollider)
|
||||||
{
|
{
|
||||||
@@ -27,9 +27,9 @@ public class AABBCollider
|
|||||||
var diff = parOther.Position - Position;
|
var diff = parOther.Position - Position;
|
||||||
|
|
||||||
// Calculate penetration depths for each axis
|
// Calculate penetration depths for each axis
|
||||||
var penX = (Size.X / 2 + parOther.Size.X / 2) - Math.Abs(diff.X);
|
var penX = (Size.X / 2) + (parOther.Size.X / 2) - Math.Abs(diff.X);
|
||||||
var penY = (Size.Y / 2 + parOther.Size.Y / 2) - Math.Abs(diff.Y);
|
var penY = (Size.Y / 2) + (parOther.Size.Y / 2) - Math.Abs(diff.Y);
|
||||||
var penZ = (Size.Z / 2 + parOther.Size.Z / 2) - Math.Abs(diff.Z);
|
var penZ = (Size.Z / 2) + (parOther.Size.Z / 2) - Math.Abs(diff.Z);
|
||||||
|
|
||||||
// Use the axis with the smallest penetration
|
// Use the axis with the smallest penetration
|
||||||
if (penX < penY && penX < penZ)
|
if (penX < penY && penX < penZ)
|
||||||
|
|||||||
@@ -8,5 +8,6 @@ public class RaycastResult
|
|||||||
public float Distance { get; init; }
|
public float Distance { get; init; }
|
||||||
public Vector3 HitPoint { get; init; }
|
public Vector3 HitPoint { get; init; }
|
||||||
public Vector3 Normal { get; init; }
|
public Vector3 Normal { get; init; }
|
||||||
|
|
||||||
public GameObject HitObject { get; init; }
|
public GameObject HitObject { get; init; }
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ namespace DoomDeathmatch.Script.Condition;
|
|||||||
public class TickableTimerCondition : ICondition
|
public class TickableTimerCondition : ICondition
|
||||||
{
|
{
|
||||||
public event Action? OnTrue;
|
public event Action? OnTrue;
|
||||||
|
|
||||||
public bool IsTrue => _timer.IsFinished;
|
public bool IsTrue => _timer.IsFinished;
|
||||||
|
|
||||||
private readonly TickableTimer _timer;
|
private readonly TickableTimer _timer;
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ public abstract class CooldownAttackBehavior(
|
|||||||
if (CanAttack())
|
if (CanAttack())
|
||||||
{
|
{
|
||||||
if (!_tickableTimer.IsFinished)
|
if (!_tickableTimer.IsFinished)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var result = ActivateAttack();
|
var result = ActivateAttack();
|
||||||
|
|
||||||
|
|||||||
@@ -63,10 +63,13 @@ public class EnemyData
|
|||||||
|
|
||||||
public string Id { get; private init; } = "";
|
public string Id { get; private init; } = "";
|
||||||
public string Name { get; private init; } = "";
|
public string Name { get; private init; } = "";
|
||||||
|
|
||||||
public string Texture { get; private init; } = "";
|
public string Texture { get; private init; } = "";
|
||||||
|
|
||||||
public float BaseHealth { get; private init; }
|
public float BaseHealth { get; private init; }
|
||||||
public int BaseScore { get; private init; }
|
public int BaseScore { get; private init; }
|
||||||
public float BaseSpeed { get; private init; }
|
public float BaseSpeed { get; private init; }
|
||||||
|
|
||||||
public IMovementBehavior MovementBehavior { get; private init; }
|
public IMovementBehavior MovementBehavior { get; private init; }
|
||||||
public IAttackBehaviorCreator AttackBehaviorCreator { get; private init; }
|
public IAttackBehaviorCreator AttackBehaviorCreator { get; private init; }
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ public class FollowPlayerMovementBehavior(float parRadius) : IMovementBehavior
|
|||||||
public Vector3 GetNextPosition(Vector3 parPosition, Vector3 parPlayerPosition)
|
public Vector3 GetNextPosition(Vector3 parPosition, Vector3 parPlayerPosition)
|
||||||
{
|
{
|
||||||
var direction = (parPosition - parPlayerPosition).Normalized();
|
var direction = (parPosition - parPlayerPosition).Normalized();
|
||||||
return parPlayerPosition + parRadius * direction;
|
return parPlayerPosition + (parRadius * direction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,24 @@
|
|||||||
|
|
||||||
public class HealthModel
|
public class HealthModel
|
||||||
{
|
{
|
||||||
|
public event Action<HealthModel>? HealthChanged;
|
||||||
|
|
||||||
|
public float Health
|
||||||
|
{
|
||||||
|
get => _health;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
value = Math.Clamp(value, 0, MaxHealth);
|
||||||
|
if (Math.Abs(_health - value) < float.Epsilon)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_health = value;
|
||||||
|
HealthChanged?.Invoke(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public float MaxHealth
|
public float MaxHealth
|
||||||
{
|
{
|
||||||
get => _maxHealth;
|
get => _maxHealth;
|
||||||
@@ -13,22 +31,6 @@ public class HealthModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public float Health
|
|
||||||
{
|
|
||||||
get => _health;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
value = Math.Clamp(value, 0, MaxHealth);
|
|
||||||
if (Math.Abs(_health - value) < float.Epsilon)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_health = value;
|
|
||||||
HealthChanged?.Invoke(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public event Action<HealthModel>? HealthChanged;
|
|
||||||
|
|
||||||
private float _health;
|
private float _health;
|
||||||
private float _maxHealth;
|
private float _maxHealth;
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public class RandomFlatSpreadShootPattern(float parAngle, uint parCount) : IShoo
|
|||||||
{
|
{
|
||||||
for (var i = 0; i < parCount; i++)
|
for (var i = 0; i < parCount; i++)
|
||||||
{
|
{
|
||||||
var angle = parAngle * ((float)_random.NextDouble() * 2 - 1);
|
var angle = parAngle * (((float)_random.NextDouble() * 2) - 1);
|
||||||
var delta = MathF.Tan(angle);
|
var delta = MathF.Tan(angle);
|
||||||
|
|
||||||
var offset = parRight * delta;
|
var offset = parRight * delta;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public class WeaponData
|
|||||||
FireAnimationDuration = 0.25f,
|
FireAnimationDuration = 0.25f,
|
||||||
FireAnimation =
|
FireAnimation =
|
||||||
{
|
{
|
||||||
"texture/pistol/fire1.png", "texture/pistol/fire2.png", "texture/pistol/fire3.png", "texture/pistol/fire4.png",
|
"texture/pistol/fire1.png", "texture/pistol/fire2.png", "texture/pistol/fire3.png", "texture/pistol/fire4.png"
|
||||||
},
|
},
|
||||||
Damage = 30,
|
Damage = 30,
|
||||||
ShootPattern = new LineShootPattern()
|
ShootPattern = new LineShootPattern()
|
||||||
@@ -31,14 +31,11 @@ public class WeaponData
|
|||||||
ShootPattern = new RandomFlatSpreadShootPattern(MathHelper.DegreesToRadians(10), 40)
|
ShootPattern = new RandomFlatSpreadShootPattern(MathHelper.DegreesToRadians(10), 40)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public event Action<WeaponData>? OnAmmoChanged;
|
||||||
|
|
||||||
|
|
||||||
public string Id { get; private init; } = "";
|
public string Id { get; private init; } = "";
|
||||||
public string Name { get; private init; } = "";
|
public string Name { get; private init; } = "";
|
||||||
public string IdleTexture { get; private init; } = "";
|
|
||||||
public float FireAnimationDuration { get; private init; } = 0;
|
|
||||||
public List<string> FireAnimation { get; private init; } = [];
|
|
||||||
public int Damage { get; private init; }
|
|
||||||
public int MaxAmmo { get; }
|
|
||||||
public IShootPattern ShootPattern { get; private init; }
|
|
||||||
|
|
||||||
public int Ammo
|
public int Ammo
|
||||||
{
|
{
|
||||||
@@ -46,19 +43,34 @@ public class WeaponData
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value < 0)
|
if (value < 0)
|
||||||
|
{
|
||||||
value = 0;
|
value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (value > MaxAmmo)
|
if (value > MaxAmmo)
|
||||||
|
{
|
||||||
value = MaxAmmo;
|
value = MaxAmmo;
|
||||||
|
}
|
||||||
|
|
||||||
if (_ammo == value)
|
if (_ammo == value)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_ammo = value;
|
_ammo = value;
|
||||||
OnAmmoChanged?.Invoke(this);
|
OnAmmoChanged?.Invoke(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public event Action<WeaponData>? OnAmmoChanged;
|
public int MaxAmmo { get; }
|
||||||
|
|
||||||
|
public int Damage { get; private init; }
|
||||||
|
|
||||||
|
public string IdleTexture { get; private init; } = "";
|
||||||
|
public float FireAnimationDuration { get; private init; }
|
||||||
|
public List<string> FireAnimation { get; } = [];
|
||||||
|
|
||||||
|
public IShootPattern ShootPattern { get; private init; }
|
||||||
|
|
||||||
private int _ammo;
|
private int _ammo;
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ public class WeaponModel
|
|||||||
public event Action<WeaponData, WeaponData>? OnWeaponSelected;
|
public event Action<WeaponData, WeaponData>? OnWeaponSelected;
|
||||||
|
|
||||||
public IList<WeaponData> Weapons => _weapons;
|
public IList<WeaponData> Weapons => _weapons;
|
||||||
|
|
||||||
public WeaponData SelectedWeapon => _weapons[_selectedWeaponIndex];
|
public WeaponData SelectedWeapon => _weapons[_selectedWeaponIndex];
|
||||||
|
|
||||||
public int SelectedWeaponIndex
|
public int SelectedWeaponIndex
|
||||||
@@ -18,7 +17,9 @@ public class WeaponModel
|
|||||||
value = Math.Clamp(value, 0, _weapons.Count - 1);
|
value = Math.Clamp(value, 0, _weapons.Count - 1);
|
||||||
|
|
||||||
if (_selectedWeaponIndex == value)
|
if (_selectedWeaponIndex == value)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var oldSelectedWeapon = SelectedWeapon;
|
var oldSelectedWeapon = SelectedWeapon;
|
||||||
_selectedWeaponIndex = value;
|
_selectedWeaponIndex = value;
|
||||||
@@ -27,5 +28,6 @@ public class WeaponModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<WeaponData> _weapons = [WeaponData.Pistol];
|
private readonly List<WeaponData> _weapons = [WeaponData.Pistol];
|
||||||
private int _selectedWeaponIndex = 0;
|
|
||||||
|
private int _selectedWeaponIndex;
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,7 @@ public class WeightedRandomValueProvider<T> : IValueProvider<T>
|
|||||||
{
|
{
|
||||||
private readonly List<(int, IValueProvider<T>)> _providers = [];
|
private readonly List<(int, IValueProvider<T>)> _providers = [];
|
||||||
private readonly Random _random = new();
|
private readonly Random _random = new();
|
||||||
private readonly int _totalWeight = 0;
|
private readonly int _totalWeight;
|
||||||
|
|
||||||
public WeightedRandomValueProvider(IEnumerable<(int, IValueProvider<T>)> parProviders)
|
public WeightedRandomValueProvider(IEnumerable<(int, IValueProvider<T>)> parProviders)
|
||||||
{
|
{
|
||||||
@@ -15,8 +15,10 @@ public class WeightedRandomValueProvider<T> : IValueProvider<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_totalWeight <= 0)
|
if (_totalWeight <= 0)
|
||||||
|
{
|
||||||
throw new InvalidOperationException($"{nameof(WeightedRandomValueProvider<T>)} is empty");
|
throw new InvalidOperationException($"{nameof(WeightedRandomValueProvider<T>)} is empty");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public T GetValue()
|
public T GetValue()
|
||||||
{
|
{
|
||||||
@@ -25,7 +27,9 @@ public class WeightedRandomValueProvider<T> : IValueProvider<T>
|
|||||||
foreach (var (weight, provider) in _providers)
|
foreach (var (weight, provider) in _providers)
|
||||||
{
|
{
|
||||||
if (random < weight)
|
if (random < weight)
|
||||||
|
{
|
||||||
return provider.GetValue();
|
return provider.GetValue();
|
||||||
|
}
|
||||||
|
|
||||||
random -= weight;
|
random -= weight;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ namespace DoomDeathmatch.Script.Score;
|
|||||||
[JsonSerializable(typeof(ScoreTable))]
|
[JsonSerializable(typeof(ScoreTable))]
|
||||||
public class ScoreTable
|
public class ScoreTable
|
||||||
{
|
{
|
||||||
private static readonly JsonSerializerOptions OPTIONS = new() { Converters = { new ScoreTableJsonConverter() } };
|
|
||||||
|
|
||||||
public List<ScoreRow> Rows { get; } = new();
|
public List<ScoreRow> Rows { get; } = new();
|
||||||
|
|
||||||
|
private static readonly JsonSerializerOptions OPTIONS = new() { Converters = { new ScoreTableJsonConverter() } };
|
||||||
|
|
||||||
public static ScoreTable LoadOrCreate(string parPath)
|
public static ScoreTable LoadOrCreate(string parPath)
|
||||||
{
|
{
|
||||||
ScoreTable? table = null;
|
ScoreTable? table = null;
|
||||||
@@ -26,10 +26,12 @@ public class ScoreTable
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (table != null)
|
if (table != null)
|
||||||
|
{
|
||||||
return table;
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
table = new ScoreTable();
|
table = new ScoreTable();
|
||||||
ScoreTable.Save(table, parPath);
|
Save(table, parPath);
|
||||||
|
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
@@ -51,7 +53,9 @@ public class ScoreTableJsonConverter : JsonConverter<ScoreTable>
|
|||||||
{
|
{
|
||||||
var rows = JsonSerializer.Deserialize<ScoreRow[]>(ref parReader, parOptions);
|
var rows = JsonSerializer.Deserialize<ScoreRow[]>(ref parReader, parOptions);
|
||||||
if (rows == null)
|
if (rows == null)
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var scoreTable = new ScoreTable();
|
var scoreTable = new ScoreTable();
|
||||||
foreach (var row in rows)
|
foreach (var row in rows)
|
||||||
|
|||||||
@@ -138,7 +138,9 @@ public static class UiUtil
|
|||||||
StackComponent parStackComponent, RenderLayer? parRenderLayer = null)
|
StackComponent parStackComponent, RenderLayer? parRenderLayer = null)
|
||||||
{
|
{
|
||||||
foreach (var child in parStackComponent.Children)
|
foreach (var child in parStackComponent.Children)
|
||||||
|
{
|
||||||
child.Container = parStackComponent;
|
child.Container = parStackComponent;
|
||||||
|
}
|
||||||
|
|
||||||
var stackObject = new GameObject { Transform = { Translation = new Vector3(0, 0, -1) } };
|
var stackObject = new GameObject { Transform = { Translation = new Vector3(0, 0, -1) } };
|
||||||
stackObject.AddComponent(parStackComponent);
|
stackObject.AddComponent(parStackComponent);
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
<AutoGen>True</AutoGen>
|
<AutoGen>True</AutoGen>
|
||||||
<DependentUpon>ShaderResource.resx</DependentUpon>
|
<DependentUpon>ShaderResource.resx</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Remove="src\Resource\ResourceHandle.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -6,24 +6,20 @@ namespace Engine.Asset.Font;
|
|||||||
|
|
||||||
public class Font
|
public class Font
|
||||||
{
|
{
|
||||||
public StaticTexture AtlasTexture => _atlasTexture;
|
public StaticTexture AtlasTexture { get; }
|
||||||
public Metadata.Metadata Metadata => _metadata;
|
public Metadata.Metadata Metadata { get; }
|
||||||
public Vector2 UnitRange => _unitRange;
|
public Vector2 UnitRange { get; }
|
||||||
|
|
||||||
private readonly StaticTexture _atlasTexture;
|
|
||||||
private readonly Metadata.Metadata _metadata;
|
|
||||||
|
|
||||||
private readonly Dictionary<int, Glyph> _glyphs = new();
|
private readonly Dictionary<int, Glyph> _glyphs = new();
|
||||||
private readonly Dictionary<int, GlyphData> _glyphData = new();
|
private readonly Dictionary<int, GlyphData> _glyphData = new();
|
||||||
private readonly Dictionary<(int, int), Kerning> _kernings = new();
|
private readonly Dictionary<(int, int), Kerning> _kernings = new();
|
||||||
private readonly Vector2 _unitRange;
|
|
||||||
|
|
||||||
public Font(StaticTexture parAtlasTexture, Metadata.Metadata parMetadata)
|
public Font(StaticTexture parAtlasTexture, Metadata.Metadata parMetadata)
|
||||||
{
|
{
|
||||||
_atlasTexture = parAtlasTexture;
|
AtlasTexture = parAtlasTexture;
|
||||||
_metadata = parMetadata;
|
Metadata = parMetadata;
|
||||||
_unitRange = new Vector2(_metadata.Atlas.DistanceRange / _metadata.Atlas.Width,
|
UnitRange = new Vector2(Metadata.Atlas.DistanceRange / Metadata.Atlas.Width,
|
||||||
_metadata.Atlas.DistanceRange / _metadata.Atlas.Height);
|
Metadata.Atlas.DistanceRange / Metadata.Atlas.Height);
|
||||||
|
|
||||||
LoadGlyphs();
|
LoadGlyphs();
|
||||||
LoadKernings();
|
LoadKernings();
|
||||||
@@ -59,7 +55,7 @@ public class Font
|
|||||||
|
|
||||||
private void LoadGlyphs()
|
private void LoadGlyphs()
|
||||||
{
|
{
|
||||||
foreach (var glyph in _metadata.Glyphs)
|
foreach (var glyph in Metadata.Glyphs)
|
||||||
{
|
{
|
||||||
_glyphs.Add(glyph.Unicode, glyph);
|
_glyphs.Add(glyph.Unicode, glyph);
|
||||||
|
|
||||||
@@ -68,13 +64,13 @@ public class Font
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_glyphData.Add(glyph.Unicode, new GlyphData(_metadata, glyph));
|
_glyphData.Add(glyph.Unicode, new GlyphData(Metadata, glyph));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadKernings()
|
private void LoadKernings()
|
||||||
{
|
{
|
||||||
foreach (var kerning in _metadata.Kerning)
|
foreach (var kerning in Metadata.Kerning)
|
||||||
{
|
{
|
||||||
_kernings.Add((kerning.Unicode1, kerning.Unicode2), kerning);
|
_kernings.Add((kerning.Unicode1, kerning.Unicode2), kerning);
|
||||||
}
|
}
|
||||||
@@ -93,7 +89,7 @@ public class Font
|
|||||||
new Vector2(parGlyph.PlaneBounds!.Left, parGlyph.PlaneBounds.Bottom),
|
new Vector2(parGlyph.PlaneBounds!.Left, parGlyph.PlaneBounds.Bottom),
|
||||||
new Vector2(parGlyph.PlaneBounds.Left, parGlyph.PlaneBounds.Top),
|
new Vector2(parGlyph.PlaneBounds.Left, parGlyph.PlaneBounds.Top),
|
||||||
new Vector2(parGlyph.PlaneBounds.Right, parGlyph.PlaneBounds.Bottom),
|
new Vector2(parGlyph.PlaneBounds.Right, parGlyph.PlaneBounds.Bottom),
|
||||||
new Vector2(parGlyph.PlaneBounds.Right, parGlyph.PlaneBounds.Top),
|
new Vector2(parGlyph.PlaneBounds.Right, parGlyph.PlaneBounds.Top)
|
||||||
];
|
];
|
||||||
|
|
||||||
UVs =
|
UVs =
|
||||||
@@ -101,8 +97,7 @@ public class Font
|
|||||||
new Vector2(parGlyph.AtlasBounds!.Left, parGlyph.AtlasBounds.Bottom) / size,
|
new Vector2(parGlyph.AtlasBounds!.Left, parGlyph.AtlasBounds.Bottom) / size,
|
||||||
new Vector2(parGlyph.AtlasBounds.Left, parGlyph.AtlasBounds.Top) / size,
|
new Vector2(parGlyph.AtlasBounds.Left, parGlyph.AtlasBounds.Top) / size,
|
||||||
new Vector2(parGlyph.AtlasBounds.Right, parGlyph.AtlasBounds.Bottom) / size,
|
new Vector2(parGlyph.AtlasBounds.Right, parGlyph.AtlasBounds.Bottom) / size,
|
||||||
|
new Vector2(parGlyph.AtlasBounds.Right, parGlyph.AtlasBounds.Top) / size
|
||||||
new Vector2(parGlyph.AtlasBounds.Right, parGlyph.AtlasBounds.Top) / size,
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ namespace Engine.Asset.Font;
|
|||||||
|
|
||||||
public class FontIterator : IEnumerable<FontIterator.NextGlyphData>
|
public class FontIterator : IEnumerable<FontIterator.NextGlyphData>
|
||||||
{
|
{
|
||||||
public float MaxWidth => _maxWidth;
|
public float MaxWidth { get; private set; }
|
||||||
public float MaxHeight => _maxHeight;
|
public float MaxHeight { get; private set; }
|
||||||
|
|
||||||
private readonly Font _font;
|
private readonly Font _font;
|
||||||
private readonly string _text;
|
private readonly string _text;
|
||||||
@@ -17,44 +17,12 @@ public class FontIterator : IEnumerable<FontIterator.NextGlyphData>
|
|||||||
private Vector2 _cursor = Vector2.Zero;
|
private Vector2 _cursor = Vector2.Zero;
|
||||||
private Vector2 _kerning = Vector2.Zero;
|
private Vector2 _kerning = Vector2.Zero;
|
||||||
|
|
||||||
private float _maxWidth;
|
|
||||||
private float _maxHeight;
|
|
||||||
|
|
||||||
public FontIterator(Font parFont, string parText)
|
public FontIterator(Font parFont, string parText)
|
||||||
{
|
{
|
||||||
_font = parFont;
|
_font = parFont;
|
||||||
_text = parText;
|
_text = parText;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsLineBreak(int parCodepoint)
|
|
||||||
{
|
|
||||||
return parCodepoint == '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsTab(int parCodepoint)
|
|
||||||
{
|
|
||||||
return parCodepoint == '\t';
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Tab()
|
|
||||||
{
|
|
||||||
var spaceGlyph = _font.GetGlyph(' ');
|
|
||||||
if (spaceGlyph == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var missingSpaces = 4 - _lineCharCount % 4;
|
|
||||||
_cursor.X += missingSpaces * spaceGlyph.Advance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LineBreak()
|
|
||||||
{
|
|
||||||
_kerning = Vector2.Zero;
|
|
||||||
_cursor.X = 0;
|
|
||||||
_cursor.Y += _font.Metadata.Metrics.LineHeight;
|
|
||||||
_lineCharCount = 0;
|
|
||||||
_previousCodepoint = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerator<NextGlyphData> GetEnumerator()
|
public IEnumerator<NextGlyphData> GetEnumerator()
|
||||||
{
|
{
|
||||||
while (_currentIndex < _text.Length)
|
while (_currentIndex < _text.Length)
|
||||||
@@ -97,11 +65,13 @@ public class FontIterator : IEnumerable<FontIterator.NextGlyphData>
|
|||||||
|
|
||||||
var glyphData = _font.GetGlyphData(glyph.Unicode);
|
var glyphData = _font.GetGlyphData(glyph.Unicode);
|
||||||
|
|
||||||
_maxWidth = Math.Max(_maxWidth, _cursor.X + glyph.Advance);
|
MaxWidth = Math.Max(MaxWidth, _cursor.X + glyph.Advance);
|
||||||
_maxHeight = Math.Max(_maxHeight, _cursor.Y + _font.Metadata.Metrics.Ascender);
|
MaxHeight = Math.Max(MaxHeight, _cursor.Y + _font.Metadata.Metrics.Ascender);
|
||||||
|
|
||||||
if (glyphData != null)
|
if (glyphData != null)
|
||||||
|
{
|
||||||
yield return new NextGlyphData(glyphData.Positions, glyphData.UVs, (_cursor + _kerning) * new Vector2(1, -1));
|
yield return new NextGlyphData(glyphData.Positions, glyphData.UVs, (_cursor + _kerning) * new Vector2(1, -1));
|
||||||
|
}
|
||||||
|
|
||||||
_cursor.X += glyph.Advance;
|
_cursor.X += glyph.Advance;
|
||||||
_lineCharCount++;
|
_lineCharCount++;
|
||||||
@@ -115,5 +85,36 @@ public class FontIterator : IEnumerable<FontIterator.NextGlyphData>
|
|||||||
return GetEnumerator();
|
return GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsLineBreak(int parCodepoint)
|
||||||
|
{
|
||||||
|
return parCodepoint == '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsTab(int parCodepoint)
|
||||||
|
{
|
||||||
|
return parCodepoint == '\t';
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Tab()
|
||||||
|
{
|
||||||
|
var spaceGlyph = _font.GetGlyph(' ');
|
||||||
|
if (spaceGlyph == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var missingSpaces = 4 - (_lineCharCount % 4);
|
||||||
|
_cursor.X += missingSpaces * spaceGlyph.Advance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LineBreak()
|
||||||
|
{
|
||||||
|
_kerning = Vector2.Zero;
|
||||||
|
_cursor.X = 0;
|
||||||
|
_cursor.Y += _font.Metadata.Metrics.LineHeight;
|
||||||
|
_lineCharCount = 0;
|
||||||
|
_previousCodepoint = -1;
|
||||||
|
}
|
||||||
|
|
||||||
public record NextGlyphData(Vector2[] Positions, Vector2[] UVs, Vector2 Offset);
|
public record NextGlyphData(Vector2[] Positions, Vector2[] UVs, Vector2 Offset);
|
||||||
}
|
}
|
||||||
@@ -9,8 +9,6 @@ public class Image<T> where T : struct, IPixel
|
|||||||
public int Height { get; }
|
public int Height { get; }
|
||||||
public T[,] Pixels { get; }
|
public T[,] Pixels { get; }
|
||||||
|
|
||||||
public T this[int parY, int parX] => Pixels[parY, parX];
|
|
||||||
|
|
||||||
public Image(int parWidth, int parHeight)
|
public Image(int parWidth, int parHeight)
|
||||||
{
|
{
|
||||||
Width = parWidth;
|
Width = parWidth;
|
||||||
@@ -18,6 +16,8 @@ public class Image<T> where T : struct, IPixel
|
|||||||
Pixels = new T[parHeight, parWidth];
|
Pixels = new T[parHeight, parWidth];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public T this[int parY, int parX] => Pixels[parY, parX];
|
||||||
|
|
||||||
public DynamicTexture ToDynamicTexture()
|
public DynamicTexture ToDynamicTexture()
|
||||||
{
|
{
|
||||||
var texture = DynamicTexture.Create<T>(Width, Height);
|
var texture = DynamicTexture.Create<T>(Width, Height);
|
||||||
|
|||||||
@@ -7,15 +7,15 @@ public class ObjMeshLoader : IMeshLoader
|
|||||||
{
|
{
|
||||||
private static readonly ObjMeshLoader INSTANCE = new();
|
private static readonly ObjMeshLoader INSTANCE = new();
|
||||||
|
|
||||||
|
private ObjMeshLoader()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public static Mesh Load(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
public static Mesh Load(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
||||||
{
|
{
|
||||||
return INSTANCE.LoadMesh(parReader, parParameters);
|
return INSTANCE.LoadMesh(parReader, parParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ObjMeshLoader()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public Mesh LoadMesh(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
public Mesh LoadMesh(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
||||||
{
|
{
|
||||||
var mesh = new Mesh();
|
var mesh = new Mesh();
|
||||||
|
|||||||
@@ -7,15 +7,15 @@ public class StlMeshLoader : IMeshLoader
|
|||||||
{
|
{
|
||||||
private static readonly StlMeshLoader INSTANCE = new();
|
private static readonly StlMeshLoader INSTANCE = new();
|
||||||
|
|
||||||
|
private StlMeshLoader()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public static Mesh Load(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
public static Mesh Load(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
||||||
{
|
{
|
||||||
return INSTANCE.LoadMesh(parReader, parParameters);
|
return INSTANCE.LoadMesh(parReader, parParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
private StlMeshLoader()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public Mesh LoadMesh(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
public Mesh LoadMesh(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
||||||
{
|
{
|
||||||
var mesh = new Mesh();
|
var mesh = new Mesh();
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ namespace Engine.Asset.Mesh;
|
|||||||
|
|
||||||
public class Mesh
|
public class Mesh
|
||||||
{
|
{
|
||||||
public IReadOnlyList<Vertex> Vertices => _vertices;
|
|
||||||
public IReadOnlyList<uint> Indices => _indices;
|
public IReadOnlyList<uint> Indices => _indices;
|
||||||
|
public IReadOnlyList<Vertex> Vertices => _vertices;
|
||||||
|
|
||||||
internal IList<Vertex> VerticesInternal => _vertices;
|
|
||||||
internal IList<uint> IndicesInternal => _indices;
|
internal IList<uint> IndicesInternal => _indices;
|
||||||
|
internal IList<Vertex> VerticesInternal => _vertices;
|
||||||
|
|
||||||
private readonly List<Vertex> _vertices = [];
|
|
||||||
private readonly List<uint> _indices = [];
|
private readonly List<uint> _indices = [];
|
||||||
|
private readonly List<Vertex> _vertices = [];
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
@@ -23,9 +23,7 @@ public class Mesh
|
|||||||
public record struct Vertex : IVertex
|
public record struct Vertex : IVertex
|
||||||
{
|
{
|
||||||
[Vertex(VertexAttribType.Float, 3)] public Vector3 _position;
|
[Vertex(VertexAttribType.Float, 3)] public Vector3 _position;
|
||||||
|
|
||||||
[Vertex(VertexAttribType.Float, 3)] public Vector3 _normal;
|
[Vertex(VertexAttribType.Float, 3)] public Vector3 _normal;
|
||||||
|
|
||||||
[Vertex(VertexAttribType.Float, 2)] public Vector2 _uv;
|
[Vertex(VertexAttribType.Float, 2)] public Vector2 _uv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,12 +22,14 @@ namespace Engine;
|
|||||||
|
|
||||||
public sealed class Engine
|
public sealed class Engine
|
||||||
{
|
{
|
||||||
internal static Engine Instance { get; private set; } = null!;
|
public IInputHandler? InputHandler { get; internal set; }
|
||||||
|
|
||||||
public Renderer Renderer { get; }
|
|
||||||
public SceneManager SceneManager { get; } = new();
|
public SceneManager SceneManager { get; } = new();
|
||||||
|
public IResourceManager AssetResourceManager { get; }
|
||||||
|
public string DataFolder { get; }
|
||||||
|
|
||||||
internal Window Window { get; }
|
internal static Engine Instance { get; private set; } = null!;
|
||||||
|
internal ResourceManager EngineResourceManager { get; }
|
||||||
|
internal Renderer Renderer { get; }
|
||||||
|
|
||||||
internal IPresenter? Presenter
|
internal IPresenter? Presenter
|
||||||
{
|
{
|
||||||
@@ -48,25 +50,12 @@ public sealed class Engine
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IInputHandler? InputHandler
|
internal Window Window { get; }
|
||||||
{
|
|
||||||
get => _inputHandler;
|
|
||||||
internal set => _inputHandler = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
private readonly object _sceneLock = new();
|
||||||
|
|
||||||
private IInputHandler? _inputHandler;
|
|
||||||
private IPresenter? _presenter;
|
private IPresenter? _presenter;
|
||||||
|
|
||||||
internal ResourceManager EngineResourceManager => _engineResourceManager;
|
|
||||||
public IResourceManager AssetResourceManager => _assetResourceManager;
|
|
||||||
|
|
||||||
public string DataFolder { get; }
|
|
||||||
|
|
||||||
private readonly ResourceManager _engineResourceManager;
|
|
||||||
private readonly ResourceManager _assetResourceManager;
|
|
||||||
|
|
||||||
private Thread? _updateThread;
|
private Thread? _updateThread;
|
||||||
|
|
||||||
public Engine(int parWidth, int parHeight, bool parHeadless, string parTitle, string parAssetFolder,
|
public Engine(int parWidth, int parHeight, bool parHeadless, string parTitle, string parAssetFolder,
|
||||||
@@ -94,8 +83,8 @@ public sealed class Engine
|
|||||||
StencilBits = 0
|
StencilBits = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
_engineResourceManager = CreateEngineResourceManager();
|
EngineResourceManager = CreateEngineResourceManager();
|
||||||
_assetResourceManager = CreateAssetResourceManager(parAssetFolder);
|
AssetResourceManager = CreateAssetResourceManager(parAssetFolder);
|
||||||
|
|
||||||
_logger.Information("Created asset resource manager in {AssetFolder}", parAssetFolder);
|
_logger.Information("Created asset resource manager in {AssetFolder}", parAssetFolder);
|
||||||
|
|
||||||
@@ -139,8 +128,6 @@ public sealed class Engine
|
|||||||
parResourceManager.RegisterLoader<Font>(new FontLoader());
|
parResourceManager.RegisterLoader<Font>(new FontLoader());
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly object _sceneLock = new();
|
|
||||||
|
|
||||||
public void Run()
|
public void Run()
|
||||||
{
|
{
|
||||||
_updateThread = new Thread(RunUpdate) { Name = "UpdateThread" };
|
_updateThread = new Thread(RunUpdate) { Name = "UpdateThread" };
|
||||||
@@ -227,7 +214,9 @@ public sealed class Engine
|
|||||||
private void PresenterResize(ResizeEventArgs parEventArgs)
|
private void PresenterResize(ResizeEventArgs parEventArgs)
|
||||||
{
|
{
|
||||||
if (parEventArgs.Width == 0 || parEventArgs.Height == 0)
|
if (parEventArgs.Width == 0 || parEventArgs.Height == 0)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Renderer.Resize(parEventArgs.Width, parEventArgs.Height);
|
Renderer.Resize(parEventArgs.Width, parEventArgs.Height);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ public sealed class EngineBuilder
|
|||||||
private string _assetFolder = "./asset";
|
private string _assetFolder = "./asset";
|
||||||
private string _dataFolder = "./data";
|
private string _dataFolder = "./data";
|
||||||
|
|
||||||
private Func<Engine, IPresenter>? _presenterFunc;
|
|
||||||
private Func<Engine, IInputHandler>? _inputHandlerFunc;
|
private Func<Engine, IInputHandler>? _inputHandlerFunc;
|
||||||
|
private Func<Engine, IPresenter>? _presenterFunc;
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
private bool _logToConsole;
|
private bool _logToConsole;
|
||||||
@@ -65,24 +65,18 @@ public sealed class EngineBuilder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EngineBuilder Presenter(Func<Engine, IPresenter> parPresenterFunc)
|
|
||||||
{
|
|
||||||
_presenterFunc = parPresenterFunc;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EngineBuilder InputHandler(IInputHandler parInputHandler)
|
|
||||||
{
|
|
||||||
_inputHandlerFunc = _ => parInputHandler;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EngineBuilder InputHandler(Func<Engine, IInputHandler> parInputHandlerFunc)
|
public EngineBuilder InputHandler(Func<Engine, IInputHandler> parInputHandlerFunc)
|
||||||
{
|
{
|
||||||
_inputHandlerFunc = parInputHandlerFunc;
|
_inputHandlerFunc = parInputHandlerFunc;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EngineBuilder Presenter(Func<Engine, IPresenter> parPresenterFunc)
|
||||||
|
{
|
||||||
|
_presenterFunc = parPresenterFunc;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public EngineBuilder LogToConsole(bool parLogToConsole = true)
|
public EngineBuilder LogToConsole(bool parLogToConsole = true)
|
||||||
{
|
{
|
||||||
_logToConsole = parLogToConsole;
|
_logToConsole = parLogToConsole;
|
||||||
@@ -92,7 +86,9 @@ public sealed class EngineBuilder
|
|||||||
public EngineBuilder LogToFile(bool parLogToFile = true, string? parLogFilePath = null)
|
public EngineBuilder LogToFile(bool parLogToFile = true, string? parLogFilePath = null)
|
||||||
{
|
{
|
||||||
if (parLogToFile && parLogFilePath == null)
|
if (parLogToFile && parLogFilePath == null)
|
||||||
|
{
|
||||||
throw new ArgumentNullException(nameof(parLogFilePath));
|
throw new ArgumentNullException(nameof(parLogFilePath));
|
||||||
|
}
|
||||||
|
|
||||||
_logToFile = parLogToFile;
|
_logToFile = parLogToFile;
|
||||||
_logFilePath = parLogFilePath;
|
_logFilePath = parLogFilePath;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace Engine.Graphics.Buffer;
|
|||||||
|
|
||||||
public class VertexArray : OpenGlObject
|
public class VertexArray : OpenGlObject
|
||||||
{
|
{
|
||||||
private int _enabledAttribs = 0;
|
private int _enabledAttributes;
|
||||||
|
|
||||||
public VertexArray()
|
public VertexArray()
|
||||||
{
|
{
|
||||||
@@ -41,9 +41,9 @@ public class VertexArray : OpenGlObject
|
|||||||
var attribute = field.GetCustomAttribute<VertexAttribute>()!;
|
var attribute = field.GetCustomAttribute<VertexAttribute>()!;
|
||||||
|
|
||||||
var offset = Marshal.OffsetOf<T>(field.Name).ToInt32();
|
var offset = Marshal.OffsetOf<T>(field.Name).ToInt32();
|
||||||
SetupAttribute(attribute, _enabledAttribs, offset, parBindingIndex);
|
SetupAttribute(attribute, _enabledAttributes, offset, parBindingIndex);
|
||||||
|
|
||||||
_enabledAttribs += attribute.RepeatCount;
|
_enabledAttributes += attribute.RepeatCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
GL.VertexArrayBindingDivisor(Handle, parBindingIndex, parDivisor);
|
GL.VertexArrayBindingDivisor(Handle, parBindingIndex, parDivisor);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using Evergine.Bindings.RenderDoc;
|
||||||
using OpenTK.Graphics.OpenGL;
|
using OpenTK.Graphics.OpenGL;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
using Evergine.Bindings.RenderDoc;
|
|
||||||
|
|
||||||
namespace Engine.Graphics;
|
namespace Engine.Graphics;
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public class Framebuffer : OpenGlObject
|
|||||||
internal Texture.Texture? TextureInternal => GetAttachment<DynamicTexture>(FramebufferAttachment.ColorAttachment0);
|
internal Texture.Texture? TextureInternal => GetAttachment<DynamicTexture>(FramebufferAttachment.ColorAttachment0);
|
||||||
|
|
||||||
private readonly IDictionary<FramebufferAttachment, IFramebufferAttachment> _attachments;
|
private readonly IDictionary<FramebufferAttachment, IFramebufferAttachment> _attachments;
|
||||||
|
|
||||||
private int _width;
|
private int _width;
|
||||||
private int _height;
|
private int _height;
|
||||||
|
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ public class GenericRenderer : IRenderer
|
|||||||
public AnyMeshRenderer AnyMeshRenderer => _anyMeshRenderer ??= new AnyMeshRenderer(_engine, 1024);
|
public AnyMeshRenderer AnyMeshRenderer => _anyMeshRenderer ??= new AnyMeshRenderer(_engine, 1024);
|
||||||
public TextRenderer TextRenderer => _textRenderer ??= new TextRenderer(_engine, 1024 * 8);
|
public TextRenderer TextRenderer => _textRenderer ??= new TextRenderer(_engine, 1024 * 8);
|
||||||
|
|
||||||
|
internal readonly Framebuffer.Framebuffer _framebuffer;
|
||||||
|
|
||||||
|
private readonly Engine _engine;
|
||||||
|
|
||||||
private QuadRenderer? _quadRenderer;
|
private QuadRenderer? _quadRenderer;
|
||||||
private AnyMeshRenderer? _anyMeshRenderer;
|
private AnyMeshRenderer? _anyMeshRenderer;
|
||||||
private TextRenderer? _textRenderer;
|
private TextRenderer? _textRenderer;
|
||||||
|
|
||||||
private readonly Engine _engine;
|
|
||||||
internal readonly Framebuffer.Framebuffer _framebuffer;
|
|
||||||
|
|
||||||
private bool _frameStarted;
|
private bool _frameStarted;
|
||||||
|
|
||||||
public GenericRenderer(Engine parEngine, int parWidth, int parHeight)
|
public GenericRenderer(Engine parEngine, int parWidth, int parHeight)
|
||||||
@@ -40,7 +40,9 @@ public class GenericRenderer : IRenderer
|
|||||||
public void EndFrame(in Matrix4 parProjectionMatrix, in Matrix4 parViewMatrix)
|
public void EndFrame(in Matrix4 parProjectionMatrix, in Matrix4 parViewMatrix)
|
||||||
{
|
{
|
||||||
if (!_frameStarted)
|
if (!_frameStarted)
|
||||||
|
{
|
||||||
throw new InvalidOperationException("Frame not started");
|
throw new InvalidOperationException("Frame not started");
|
||||||
|
}
|
||||||
|
|
||||||
_framebuffer.Bind();
|
_framebuffer.Bind();
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ namespace Engine.Graphics;
|
|||||||
|
|
||||||
public interface IPresenter : IUpdate, IRender
|
public interface IPresenter : IUpdate, IRender
|
||||||
{
|
{
|
||||||
bool IsExiting { get; }
|
public event Action<ResizeEventArgs> Resize;
|
||||||
int Width { get; }
|
|
||||||
int Height { get; }
|
|
||||||
|
|
||||||
event Action<ResizeEventArgs> Resize;
|
public int Width { get; }
|
||||||
|
public int Height { get; }
|
||||||
|
public bool IsExiting { get; }
|
||||||
|
|
||||||
void Present(IConstTexture parTexture);
|
public void Present(IConstTexture parTexture);
|
||||||
void Exit();
|
public void Exit();
|
||||||
}
|
}
|
||||||
@@ -5,10 +5,10 @@ public class RenderLayer : IComparable<RenderLayer>
|
|||||||
public static readonly RenderLayer DEFAULT = new("default", 0);
|
public static readonly RenderLayer DEFAULT = new("default", 0);
|
||||||
public static readonly RenderLayer OVERLAY = new("overlay", 1);
|
public static readonly RenderLayer OVERLAY = new("overlay", 1);
|
||||||
public static readonly RenderLayer HUD = new("hud", 2);
|
public static readonly RenderLayer HUD = new("hud", 2);
|
||||||
|
|
||||||
public static readonly IReadOnlyList<RenderLayer> ALL = new List<RenderLayer> { DEFAULT, OVERLAY, HUD }.AsReadOnly();
|
public static readonly IReadOnlyList<RenderLayer> ALL = new List<RenderLayer> { DEFAULT, OVERLAY, HUD }.AsReadOnly();
|
||||||
|
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
|
|
||||||
private readonly int _order;
|
private readonly int _order;
|
||||||
|
|
||||||
private RenderLayer(string parName, int parOrder)
|
private RenderLayer(string parName, int parOrder)
|
||||||
@@ -17,16 +17,16 @@ public class RenderLayer : IComparable<RenderLayer>
|
|||||||
_order = parOrder;
|
_order = parOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int CompareTo(RenderLayer? parOther)
|
public int CompareTo(RenderLayer? parOther)
|
||||||
{
|
{
|
||||||
return parOther == null ? 1 : _order.CompareTo(parOther._order);
|
return parOther == null ? 1 : _order.CompareTo(parOther._order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return Name.GetHashCode();
|
return Name.GetHashCode();
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Engine.Graphics.Pipeline;
|
|
||||||
|
|
||||||
public class RenderPipeline
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -10,18 +10,18 @@ public abstract class InstancedRenderer<C, I>
|
|||||||
where C : struct, IVertex
|
where C : struct, IVertex
|
||||||
where I : struct, IVertex
|
where I : struct, IVertex
|
||||||
{
|
{
|
||||||
|
protected readonly int _instanceCount;
|
||||||
|
protected readonly I[] _instanceVertices;
|
||||||
|
|
||||||
|
protected int _queuedInstanceCount;
|
||||||
|
|
||||||
|
private readonly PrimitiveType _primitiveType;
|
||||||
|
private readonly Program _program;
|
||||||
private readonly IndexBuffer _indexBuffer;
|
private readonly IndexBuffer _indexBuffer;
|
||||||
private readonly VertexBuffer<C> _commonVertexBuffer;
|
private readonly VertexBuffer<C> _commonVertexBuffer;
|
||||||
private readonly VertexBuffer<I> _instanceVertexBuffer;
|
private readonly VertexBuffer<I> _instanceVertexBuffer;
|
||||||
private readonly VertexArray _vertexArray;
|
private readonly VertexArray _vertexArray;
|
||||||
|
|
||||||
protected readonly int _instanceCount;
|
|
||||||
protected int _queuedInstanceCount;
|
|
||||||
protected readonly I[] _instanceVertices;
|
|
||||||
|
|
||||||
private readonly PrimitiveType _primitiveType;
|
|
||||||
private readonly Program _program;
|
|
||||||
|
|
||||||
protected InstancedRenderer(PrimitiveType parPrimitiveType, int parInstanceCount,
|
protected InstancedRenderer(PrimitiveType parPrimitiveType, int parInstanceCount,
|
||||||
uint[] parIndexBuffer, C[] parInstanceBuffer,
|
uint[] parIndexBuffer, C[] parInstanceBuffer,
|
||||||
Program parProgram)
|
Program parProgram)
|
||||||
@@ -39,7 +39,7 @@ public abstract class InstancedRenderer<C, I>
|
|||||||
_vertexArray = new VertexArray();
|
_vertexArray = new VertexArray();
|
||||||
|
|
||||||
_vertexArray.BindIndexBuffer(_indexBuffer);
|
_vertexArray.BindIndexBuffer(_indexBuffer);
|
||||||
_vertexArray.BindVertexBuffer(_commonVertexBuffer, 0, 0);
|
_vertexArray.BindVertexBuffer(_commonVertexBuffer);
|
||||||
_vertexArray.BindVertexBuffer(_instanceVertexBuffer, 1, 1);
|
_vertexArray.BindVertexBuffer(_instanceVertexBuffer, 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +51,10 @@ public abstract class InstancedRenderer<C, I>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (DataChanged())
|
if (DataChanged())
|
||||||
|
{
|
||||||
_instanceVertexBuffer.UploadData(_instanceVertices, _queuedInstanceCount);
|
_instanceVertexBuffer.UploadData(_instanceVertices, _queuedInstanceCount);
|
||||||
|
}
|
||||||
|
|
||||||
_vertexArray.Bind();
|
_vertexArray.Bind();
|
||||||
|
|
||||||
_program.Bind();
|
_program.Bind();
|
||||||
|
|||||||
@@ -46,8 +46,10 @@ public class AnyMeshRenderer(Engine parEngine, int parMaxInstanceCount)
|
|||||||
foreach (var mesh in meshes)
|
foreach (var mesh in meshes)
|
||||||
{
|
{
|
||||||
if (!_frameMeshes.Contains(mesh))
|
if (!_frameMeshes.Contains(mesh))
|
||||||
|
{
|
||||||
_meshRenderers.Remove(mesh);
|
_meshRenderers.Remove(mesh);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_frameMeshes.Clear();
|
_frameMeshes.Clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ namespace Engine.Graphics.Render.Quad;
|
|||||||
public class QuadRenderer : InstancedRenderer<QuadCommonVertex, QuadInstanceVertex>
|
public class QuadRenderer : InstancedRenderer<QuadCommonVertex, QuadInstanceVertex>
|
||||||
{
|
{
|
||||||
private readonly TextureUnitMap _textureUnitMap = new(16);
|
private readonly TextureUnitMap _textureUnitMap = new(16);
|
||||||
|
|
||||||
private readonly int[] _textureUnitIndices = new int[16];
|
private readonly int[] _textureUnitIndices = new int[16];
|
||||||
|
|
||||||
private int _frameHash;
|
private int _frameHash;
|
||||||
@@ -19,7 +18,7 @@ public class QuadRenderer : InstancedRenderer<QuadCommonVertex, QuadInstanceVert
|
|||||||
new QuadCommonVertex { _position = new Vector3(-0.5f, -0.5f, 0), _uv = new Vector2(0, 0) },
|
new QuadCommonVertex { _position = new Vector3(-0.5f, -0.5f, 0), _uv = new Vector2(0, 0) },
|
||||||
new QuadCommonVertex { _position = new Vector3(-0.5f, 0.5f, 0), _uv = new Vector2(0, 1) },
|
new QuadCommonVertex { _position = new Vector3(-0.5f, 0.5f, 0), _uv = new Vector2(0, 1) },
|
||||||
new QuadCommonVertex { _position = new Vector3(0.5f, -0.5f, 0), _uv = new Vector2(1, 0) },
|
new QuadCommonVertex { _position = new Vector3(0.5f, -0.5f, 0), _uv = new Vector2(1, 0) },
|
||||||
new QuadCommonVertex { _position = new Vector3(0.5f, 0.5f, 0), _uv = new Vector2(1, 1) },
|
new QuadCommonVertex { _position = new Vector3(0.5f, 0.5f, 0), _uv = new Vector2(1, 1) }
|
||||||
],
|
],
|
||||||
parEngine.EngineResourceManager.Load<Program>("shader/quad"))
|
parEngine.EngineResourceManager.Load<Program>("shader/quad"))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,41 +9,34 @@ namespace Engine.Graphics.Render.Text;
|
|||||||
|
|
||||||
public class TextRenderer
|
public class TextRenderer
|
||||||
{
|
{
|
||||||
|
private readonly Program _program;
|
||||||
private readonly IndexBuffer _indexBuffer;
|
private readonly IndexBuffer _indexBuffer;
|
||||||
|
private readonly VertexBuffer<GlyphVertex> _glyphVertexBuffer;
|
||||||
private readonly VertexArray _vertexArray;
|
private readonly VertexArray _vertexArray;
|
||||||
|
|
||||||
private readonly VertexBuffer<GlyphVertex> _glyphVertexBuffer;
|
|
||||||
// private readonly VertexBuffer<GlyphCommonVertex> _glyphCommonVertexBuffer;
|
|
||||||
|
|
||||||
private readonly Program _program;
|
|
||||||
private readonly int _characterCount;
|
private readonly int _characterCount;
|
||||||
private int _queuedCharacterCount;
|
private readonly GlyphVertex[] _glyphVertices;
|
||||||
|
|
||||||
private readonly TextureUnitMap _textureUnitMap = new(16);
|
private readonly TextureUnitMap _textureUnitMap = new(16);
|
||||||
private readonly int[] _textureUnitIndices = new int[16];
|
private readonly int[] _textureUnitIndices = new int[16];
|
||||||
|
|
||||||
private readonly GlyphVertex[] _glyphVertices;
|
private int _queuedCharacterCount;
|
||||||
// private readonly GlyphCommonVertex[] _glyphCommonVertices;
|
|
||||||
|
|
||||||
public TextRenderer(Engine parEngine, int parCharacterCount)
|
public TextRenderer(Engine parEngine, int parCharacterCount)
|
||||||
{
|
{
|
||||||
_characterCount = parCharacterCount;
|
_characterCount = parCharacterCount;
|
||||||
_glyphVertices = new GlyphVertex[parCharacterCount * 4];
|
_glyphVertices = new GlyphVertex[parCharacterCount * 4];
|
||||||
// _glyphCommonVertices = new GlyphCommonVertex[parCharacterCount];
|
|
||||||
|
|
||||||
_program = parEngine.EngineResourceManager.Load<Program>("shader/text");
|
_program = parEngine.EngineResourceManager.Load<Program>("shader/text");
|
||||||
|
|
||||||
_indexBuffer = new IndexBuffer(CreateIndices(_characterCount));
|
_indexBuffer = new IndexBuffer(CreateIndices(_characterCount));
|
||||||
_glyphVertexBuffer = new VertexBuffer<GlyphVertex>(_characterCount * 4,
|
_glyphVertexBuffer = new VertexBuffer<GlyphVertex>(_characterCount * 4,
|
||||||
BufferStorageFlags.DynamicStorageBit);
|
BufferStorageFlags.DynamicStorageBit);
|
||||||
// _glyphCommonVertexBuffer = new VertexBuffer<GlyphCommonVertex>(_characterCount,
|
|
||||||
// BufferStorageFlags.DynamicStorageBit);
|
|
||||||
|
|
||||||
_vertexArray = new VertexArray();
|
_vertexArray = new VertexArray();
|
||||||
|
|
||||||
_vertexArray.BindIndexBuffer(_indexBuffer);
|
_vertexArray.BindIndexBuffer(_indexBuffer);
|
||||||
_vertexArray.BindVertexBuffer(_glyphVertexBuffer, 0, 0);
|
_vertexArray.BindVertexBuffer(_glyphVertexBuffer);
|
||||||
// _vertexArray.BindVertexBuffer(_glyphCommonVertexBuffer, 1, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Commit(Font parFont, string parText, Vector4 parColor, in Matrix4 parModelMatrix)
|
public void Commit(Font parFont, string parText, Vector4 parColor, in Matrix4 parModelMatrix)
|
||||||
@@ -61,12 +54,12 @@ public class TextRenderer
|
|||||||
{
|
{
|
||||||
for (var i = 0; i < 4; i++)
|
for (var i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
_glyphVertices[_queuedCharacterCount * 4 + i]._position = glyphData.Positions[i] + glyphData.Offset;
|
_glyphVertices[(_queuedCharacterCount * 4) + i]._position = glyphData.Positions[i] + glyphData.Offset;
|
||||||
_glyphVertices[_queuedCharacterCount * 4 + i]._uv = glyphData.UVs[i];
|
_glyphVertices[(_queuedCharacterCount * 4) + i]._uv = glyphData.UVs[i];
|
||||||
_glyphVertices[_queuedCharacterCount * 4 + i]._color = parColor;
|
_glyphVertices[(_queuedCharacterCount * 4) + i]._color = parColor;
|
||||||
_glyphVertices[_queuedCharacterCount * 4 + i]._atlasId = textureId;
|
_glyphVertices[(_queuedCharacterCount * 4) + i]._atlasId = textureId;
|
||||||
_glyphVertices[_queuedCharacterCount * 4 + i]._unitRange = parFont.UnitRange;
|
_glyphVertices[(_queuedCharacterCount * 4) + i]._unitRange = parFont.UnitRange;
|
||||||
_glyphVertices[_queuedCharacterCount * 4 + i]._modelMatrix = parModelMatrix;
|
_glyphVertices[(_queuedCharacterCount * 4) + i]._modelMatrix = parModelMatrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
_queuedCharacterCount++;
|
_queuedCharacterCount++;
|
||||||
@@ -81,7 +74,6 @@ public class TextRenderer
|
|||||||
}
|
}
|
||||||
|
|
||||||
_glyphVertexBuffer.UploadData(_glyphVertices, _queuedCharacterCount * 4);
|
_glyphVertexBuffer.UploadData(_glyphVertices, _queuedCharacterCount * 4);
|
||||||
// _glyphCommonVertexBuffer.UploadData(_glyphCommonVertices, _queuedCharacterCount);
|
|
||||||
_vertexArray.Bind();
|
_vertexArray.Bind();
|
||||||
|
|
||||||
_program.Bind();
|
_program.Bind();
|
||||||
|
|||||||
@@ -9,31 +9,18 @@ namespace Engine.Graphics;
|
|||||||
|
|
||||||
public class Renderer
|
public class Renderer
|
||||||
{
|
{
|
||||||
internal Framebuffer.Framebuffer RenderFramebuffer => _framebuffer;
|
public int ViewportWidth => RenderFramebuffer.Width;
|
||||||
internal Texture.Texture RenderTexture => _framebuffer.TextureInternal!;
|
public int ViewportHeight => RenderFramebuffer.Height;
|
||||||
|
|
||||||
private QuadRenderer QuadRenderer { get; }
|
|
||||||
public int ViewportWidth => _framebuffer.Width;
|
|
||||||
public int ViewportHeight => _framebuffer.Height;
|
|
||||||
|
|
||||||
private readonly SortedDictionary<RenderLayer, GenericRenderer> _renderers = new();
|
|
||||||
|
|
||||||
public GenericRenderer this[RenderLayer parRenderLayer]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_renderers.TryGetValue(parRenderLayer, out var renderer))
|
|
||||||
return renderer;
|
|
||||||
|
|
||||||
throw new InvalidOperationException($"Renderer for layer {parRenderLayer} not found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
internal Framebuffer.Framebuffer RenderFramebuffer { get; }
|
||||||
|
internal Texture.Texture RenderTexture => RenderFramebuffer.TextureInternal!;
|
||||||
internal NativeWindow NativeWindow { get; }
|
internal NativeWindow NativeWindow { get; }
|
||||||
|
|
||||||
private readonly Framebuffer.Framebuffer _framebuffer;
|
private readonly SortedDictionary<RenderLayer, GenericRenderer> _renderers = new();
|
||||||
private readonly Thread _renderThread;
|
private readonly Thread _renderThread;
|
||||||
|
|
||||||
|
private QuadRenderer QuadRenderer { get; }
|
||||||
|
|
||||||
private readonly Queue<Action> _scheduleActions = new();
|
private readonly Queue<Action> _scheduleActions = new();
|
||||||
|
|
||||||
public Renderer(Engine parEngine, int parWidth, int parHeight, NativeWindowSettings parSettings)
|
public Renderer(Engine parEngine, int parWidth, int parHeight, NativeWindowSettings parSettings)
|
||||||
@@ -49,7 +36,7 @@ public class Renderer
|
|||||||
|
|
||||||
InitializeOpenGl(parWidth, parHeight);
|
InitializeOpenGl(parWidth, parHeight);
|
||||||
|
|
||||||
_framebuffer = Framebuffer.Framebuffer.Builder(parWidth, parHeight)
|
RenderFramebuffer = Framebuffer.Framebuffer.Builder(parWidth, parHeight)
|
||||||
.AddColorAttachment<Rgba8>()
|
.AddColorAttachment<Rgba8>()
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
@@ -61,6 +48,19 @@ public class Renderer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GenericRenderer this[RenderLayer parRenderLayer]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_renderers.TryGetValue(parRenderLayer, out var renderer))
|
||||||
|
{
|
||||||
|
return renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException($"Renderer for layer {parRenderLayer} not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void InitializeOpenGl(int parWidth, int parHeight)
|
private void InitializeOpenGl(int parWidth, int parHeight)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
@@ -129,15 +129,16 @@ public class Renderer
|
|||||||
foreach (var (renderLayer, renderer) in _renderers)
|
foreach (var (renderLayer, renderer) in _renderers)
|
||||||
{
|
{
|
||||||
if (!parMatrices.TryGetValue(renderLayer, out var matrices))
|
if (!parMatrices.TryGetValue(renderLayer, out var matrices))
|
||||||
|
{
|
||||||
renderer.EndFrame(Matrix4.Identity, Matrix4.Identity);
|
renderer.EndFrame(Matrix4.Identity, Matrix4.Identity);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
renderer.EndFrame(in matrices.Item1, in matrices.Item2);
|
renderer.EndFrame(in matrices.Item1, in matrices.Item2);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_framebuffer.Bind();
|
RenderFramebuffer.Bind();
|
||||||
|
|
||||||
// GL.Disable(EnableCap.DepthTest);
|
|
||||||
// GL.Disable(EnableCap.CullFace);
|
|
||||||
|
|
||||||
GL.ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
GL.ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
GL.Clear(ClearBufferMask.ColorBufferBit);
|
GL.Clear(ClearBufferMask.ColorBufferBit);
|
||||||
@@ -145,22 +146,21 @@ public class Renderer
|
|||||||
foreach (var (renderLayer, renderer) in _renderers)
|
foreach (var (renderLayer, renderer) in _renderers)
|
||||||
{
|
{
|
||||||
if (!parMatrices.ContainsKey(renderLayer))
|
if (!parMatrices.ContainsKey(renderLayer))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
QuadRenderer.Commit(Matrix4.CreateScale(2f, -2f, 1f), Vector4.One, renderer._framebuffer.TextureInternal);
|
QuadRenderer.Commit(Matrix4.CreateScale(2f, -2f, 1f), Vector4.One, renderer._framebuffer.TextureInternal);
|
||||||
QuadRenderer.Render(Matrix4.Identity, Matrix4.Identity);
|
QuadRenderer.Render(Matrix4.Identity, Matrix4.Identity);
|
||||||
QuadRenderer.Reset();
|
QuadRenderer.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// GL.Enable(EnableCap.DepthTest);
|
RenderFramebuffer.Unbind();
|
||||||
// GL.Enable(EnableCap.CullFace);
|
|
||||||
|
|
||||||
_framebuffer.Unbind();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Resize(int parWidth, int parHeight)
|
internal void Resize(int parWidth, int parHeight)
|
||||||
{
|
{
|
||||||
_framebuffer.Resize(parWidth, parHeight);
|
RenderFramebuffer.Resize(parWidth, parHeight);
|
||||||
GL.Viewport(0, 0, parWidth, parHeight);
|
GL.Viewport(0, 0, parWidth, parHeight);
|
||||||
|
|
||||||
foreach (var renderer in _renderers.Values)
|
foreach (var renderer in _renderers.Values)
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
using Engine.Asset;
|
using Engine.Asset;
|
||||||
using Engine.Graphics.Pixel;
|
using Engine.Graphics.Pixel;
|
||||||
using OpenTK.Mathematics;
|
|
||||||
|
|
||||||
namespace Engine.Graphics.Texture;
|
namespace Engine.Graphics.Texture;
|
||||||
|
|
||||||
public interface IConstTexture
|
public interface IConstTexture
|
||||||
{
|
{
|
||||||
public Vector2i Size { get; }
|
|
||||||
public int Width { get; }
|
public int Width { get; }
|
||||||
public int Height { get; }
|
public int Height { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Engine.Graphics.Pixel;
|
using Engine.Graphics.Pixel;
|
||||||
using OpenTK.Graphics.OpenGL;
|
using OpenTK.Graphics.OpenGL;
|
||||||
using OpenTK.Mathematics;
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Engine.Graphics.Texture;
|
namespace Engine.Graphics.Texture;
|
||||||
|
|
||||||
public abstract class Texture : OpenGlObject, ITexture
|
public abstract class Texture : OpenGlObject, ITexture
|
||||||
{
|
{
|
||||||
public Vector2i Size => new(Width, Height);
|
|
||||||
|
|
||||||
public int Width
|
public int Width
|
||||||
{
|
{
|
||||||
get => _width;
|
get => _width;
|
||||||
|
|||||||
@@ -66,12 +66,16 @@ public enum KeyboardButtonCode
|
|||||||
|
|
||||||
public static class KeyboardButtonCodeHelper
|
public static class KeyboardButtonCodeHelper
|
||||||
{
|
{
|
||||||
public static List<KeyboardButtonCode> GetAllPrintableKeys() =>
|
public static List<KeyboardButtonCode> GetAllPrintableKeys()
|
||||||
Enum.GetValues<KeyboardButtonCode>().Where(parX => parX.IsPrintableKey()).ToList();
|
{
|
||||||
|
return Enum.GetValues<KeyboardButtonCode>().Where(parX => parX.IsPrintableKey()).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
public static bool IsPrintableKey(this KeyboardButtonCode parKey) =>
|
public static bool IsPrintableKey(this KeyboardButtonCode parKey)
|
||||||
parKey is >= KeyboardButtonCode.A and <= KeyboardButtonCode.Z
|
{
|
||||||
|
return parKey is >= KeyboardButtonCode.A and <= KeyboardButtonCode.Z
|
||||||
or >= KeyboardButtonCode.D1 and <= KeyboardButtonCode.D0 or KeyboardButtonCode.Space;
|
or >= KeyboardButtonCode.D1 and <= KeyboardButtonCode.D0 or KeyboardButtonCode.Space;
|
||||||
|
}
|
||||||
|
|
||||||
public static char GetChar(this KeyboardButtonCode parKey)
|
public static char GetChar(this KeyboardButtonCode parKey)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,11 +9,10 @@ public class WindowInputHandler(Window parWindow) : IInputHandler
|
|||||||
public CultureInfo CurrentInputLanguage => new(1033);
|
public CultureInfo CurrentInputLanguage => new(1033);
|
||||||
public Vector2 MousePosition => parWindow.NativeWindow.MouseState.Position;
|
public Vector2 MousePosition => parWindow.NativeWindow.MouseState.Position;
|
||||||
|
|
||||||
private KeyboardState _previousKeyboardState = parWindow.NativeWindow.KeyboardState.GetSnapshot();
|
|
||||||
private KeyboardState _keyboardState = parWindow.NativeWindow.KeyboardState.GetSnapshot();
|
private KeyboardState _keyboardState = parWindow.NativeWindow.KeyboardState.GetSnapshot();
|
||||||
|
private KeyboardState _previousKeyboardState = parWindow.NativeWindow.KeyboardState.GetSnapshot();
|
||||||
private MouseState _previousMouseState = parWindow.NativeWindow.MouseState.GetSnapshot();
|
|
||||||
private MouseState _mouseState = parWindow.NativeWindow.MouseState.GetSnapshot();
|
private MouseState _mouseState = parWindow.NativeWindow.MouseState.GetSnapshot();
|
||||||
|
private MouseState _previousMouseState = parWindow.NativeWindow.MouseState.GetSnapshot();
|
||||||
|
|
||||||
public void Update(double parDeltaTime)
|
public void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Engine.Graphics.Pixel;
|
using Engine.Graphics.Pixel;
|
||||||
|
using SixLabors.ImageSharp;
|
||||||
using SixLabors.ImageSharp.PixelFormats;
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
using SixLabors.ImageSharp.Processing;
|
using SixLabors.ImageSharp.Processing;
|
||||||
|
|
||||||
@@ -15,10 +16,10 @@ public class ImageLoader : IResourceLoader
|
|||||||
|
|
||||||
internal static Asset.Image<Rgba8> Load(Stream parStream)
|
internal static Asset.Image<Rgba8> Load(Stream parStream)
|
||||||
{
|
{
|
||||||
var sharpImage = SixLabors.ImageSharp.Image.Load<Rgba32>(parStream);
|
var sharpImage = Image.Load<Rgba32>(parStream);
|
||||||
if (sharpImage == null)
|
if (sharpImage == null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"Failed to load image from stream");
|
throw new InvalidOperationException("Failed to load image from stream");
|
||||||
}
|
}
|
||||||
|
|
||||||
sharpImage.Mutate(parImageContext => parImageContext.Flip(FlipMode.Vertical));
|
sharpImage.Mutate(parImageContext => parImageContext.Flip(FlipMode.Vertical));
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ namespace Engine.Resource.Loader;
|
|||||||
|
|
||||||
public partial class ProgramLoader : IResourceLoader
|
public partial class ProgramLoader : IResourceLoader
|
||||||
{
|
{
|
||||||
[GeneratedRegex(@"^//\s+#type\s+(?<type>[a-z]+)$", RegexOptions.Compiled)]
|
|
||||||
private static partial Regex TypeRegex();
|
|
||||||
|
|
||||||
public object Load(string parPath, IResourceStreamProvider parStreamProvider)
|
public object Load(string parPath, IResourceStreamProvider parStreamProvider)
|
||||||
{
|
{
|
||||||
var textReader = new StreamReader(parStreamProvider.GetStream(parPath));
|
var textReader = new StreamReader(parStreamProvider.GetStream(parPath));
|
||||||
@@ -38,4 +35,7 @@ public partial class ProgramLoader : IResourceLoader
|
|||||||
|
|
||||||
return new Program(vertexSource.ToString(), fragmentSource.ToString());
|
return new Program(vertexSource.ToString(), fragmentSource.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[GeneratedRegex(@"^//\s+#type\s+(?<type>[a-z]+)$", RegexOptions.Compiled)]
|
||||||
|
private static partial Regex TypeRegex();
|
||||||
}
|
}
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
namespace Engine.Resource;
|
|
||||||
|
|
||||||
public class ResourceHandle<T>
|
|
||||||
{
|
|
||||||
public T? Value
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_isLoaded)
|
|
||||||
{
|
|
||||||
return _value!;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_task.IsCompleted)
|
|
||||||
{
|
|
||||||
return _defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_value = _task.Result;
|
|
||||||
_isLoaded = true;
|
|
||||||
|
|
||||||
return _value!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly T? _defaultValue;
|
|
||||||
private readonly Task<T> _task;
|
|
||||||
private T? _value;
|
|
||||||
private bool _isLoaded;
|
|
||||||
|
|
||||||
public ResourceHandle(Task<T> parTask, T? parDefaultValue)
|
|
||||||
{
|
|
||||||
_defaultValue = parDefaultValue;
|
|
||||||
_task = parTask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,20 +2,14 @@
|
|||||||
|
|
||||||
public class ResourceManager : IResourceManager
|
public class ResourceManager : IResourceManager
|
||||||
{
|
{
|
||||||
internal IResourceStreamProvider StreamProvider => _streamProvider;
|
internal IResourceStreamProvider StreamProvider { get; }
|
||||||
|
|
||||||
private readonly IResourceStreamProvider _streamProvider;
|
|
||||||
private readonly Dictionary<Type, IResourceLoader> _loaders = new();
|
private readonly Dictionary<Type, IResourceLoader> _loaders = new();
|
||||||
private readonly Dictionary<Type, ResourceStorage> _storages = new();
|
private readonly Dictionary<Type, ResourceStorage> _storages = new();
|
||||||
|
|
||||||
public ResourceManager(IResourceStreamProvider parStreamProvider)
|
public ResourceManager(IResourceStreamProvider parStreamProvider)
|
||||||
{
|
{
|
||||||
_streamProvider = parStreamProvider;
|
StreamProvider = parStreamProvider;
|
||||||
}
|
|
||||||
|
|
||||||
internal void RegisterLoader<T>(IResourceLoader parLoader) where T : class
|
|
||||||
{
|
|
||||||
_loaders.Add(typeof(T), parLoader);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public T Load<T>(string parPath) where T : class
|
public T Load<T>(string parPath) where T : class
|
||||||
@@ -37,13 +31,18 @@ public class ResourceManager : IResourceManager
|
|||||||
throw new InvalidOperationException($"No loader found for type {typeof(T)}");
|
throw new InvalidOperationException($"No loader found for type {typeof(T)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
var resource = loader.Load(parPath, _streamProvider);
|
var resource = loader.Load(parPath, StreamProvider);
|
||||||
|
|
||||||
storage.Add(parPath, resource);
|
storage.Add(parPath, resource);
|
||||||
|
|
||||||
return (T)resource;
|
return (T)resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void RegisterLoader<T>(IResourceLoader parLoader) where T : class
|
||||||
|
{
|
||||||
|
_loaders.Add(typeof(T), parLoader);
|
||||||
|
}
|
||||||
|
|
||||||
internal void Reset()
|
internal void Reset()
|
||||||
{
|
{
|
||||||
_storages.Clear();
|
_storages.Clear();
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<root>
|
<root>
|
||||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
|
||||||
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
id="root"
|
||||||
|
xmlns="">
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
|
|||||||
@@ -11,13 +11,18 @@ public class OrthographicCamera(
|
|||||||
)
|
)
|
||||||
: Camera(parNearPlane, parFarPlane)
|
: Camera(parNearPlane, parFarPlane)
|
||||||
{
|
{
|
||||||
public Axis FixedAxis { get; set; } = parAxis;
|
public enum Axis
|
||||||
public float Size { get; set; } = parSize;
|
{
|
||||||
public bool UseScreenSize { get; set; } = false;
|
X,
|
||||||
|
Y
|
||||||
public override Matrix4 View => GameObject.Transform.TransformMatrix.Inverted();
|
}
|
||||||
|
|
||||||
public override Matrix4 Projection => GetProjectionMatrix();
|
public override Matrix4 Projection => GetProjectionMatrix();
|
||||||
|
public override Matrix4 View => GameObject.Transform.TransformMatrix.Inverted();
|
||||||
|
|
||||||
|
public float Size { get; set; } = parSize;
|
||||||
|
public bool UseScreenSize { get; set; } = false;
|
||||||
|
public Axis FixedAxis { get; set; } = parAxis;
|
||||||
|
|
||||||
private Matrix4 GetProjectionMatrix()
|
private Matrix4 GetProjectionMatrix()
|
||||||
{
|
{
|
||||||
@@ -27,7 +32,7 @@ public class OrthographicCamera(
|
|||||||
|
|
||||||
public override Vector3 ScreenToWorld(Vector2 parScreenPosition)
|
public override Vector3 ScreenToWorld(Vector2 parScreenPosition)
|
||||||
{
|
{
|
||||||
var normalized = parScreenPosition / ScreenSize - new Vector2(0.5f);
|
var normalized = (parScreenPosition / ScreenSize) - new Vector2(0.5f);
|
||||||
normalized.X *= 2;
|
normalized.X *= 2;
|
||||||
normalized.Y *= -2;
|
normalized.Y *= -2;
|
||||||
|
|
||||||
@@ -70,10 +75,4 @@ public class OrthographicCamera(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Axis
|
|
||||||
{
|
|
||||||
X,
|
|
||||||
Y
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -10,9 +10,6 @@ public class PerspectiveCamera(
|
|||||||
)
|
)
|
||||||
: Camera(parNearPlane, parFarPlane)
|
: Camera(parNearPlane, parFarPlane)
|
||||||
{
|
{
|
||||||
public float FieldOfView { get; set; } = parFieldOfView;
|
|
||||||
|
|
||||||
public Vector3 Forward => new Vector4(0, 1, 0, 1).MulProject(GameObject.Transform.TransformMatrix).Xyz;
|
|
||||||
public override Matrix4 View
|
public override Matrix4 View
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -30,9 +27,12 @@ public class PerspectiveCamera(
|
|||||||
Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(FieldOfView), AspectRatio,
|
Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(FieldOfView), AspectRatio,
|
||||||
NearPlane, FarPlane);
|
NearPlane, FarPlane);
|
||||||
|
|
||||||
|
public Vector3 Forward => new Vector4(0, 1, 0, 1).MulProject(GameObject.Transform.TransformMatrix).Xyz;
|
||||||
|
public float FieldOfView { get; set; } = parFieldOfView;
|
||||||
|
|
||||||
public override Vector3 ScreenToWorld(Vector2 parScreenPosition)
|
public override Vector3 ScreenToWorld(Vector2 parScreenPosition)
|
||||||
{
|
{
|
||||||
var normalized = parScreenPosition / ScreenSize - new Vector2(0.5f);
|
var normalized = (parScreenPosition / ScreenSize) - new Vector2(0.5f);
|
||||||
normalized.X *= 2;
|
normalized.X *= 2;
|
||||||
normalized.Y *= -2;
|
normalized.Y *= -2;
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace Engine.Scene.Component.BuiltIn.Renderer;
|
|||||||
public class Box2DRenderer : Component
|
public class Box2DRenderer : Component
|
||||||
{
|
{
|
||||||
public ref Vector4 Color => ref _color;
|
public ref Vector4 Color => ref _color;
|
||||||
public Texture? Texture { get; set; } = null;
|
public Texture? Texture { get; set; }
|
||||||
public RenderLayer RenderLayer { get; set; } = RenderLayer.DEFAULT;
|
public RenderLayer RenderLayer { get; set; } = RenderLayer.DEFAULT;
|
||||||
|
|
||||||
private Vector4 _color = Vector4.One;
|
private Vector4 _color = Vector4.One;
|
||||||
|
|||||||
@@ -7,9 +7,8 @@ namespace Engine.Scene.Component.BuiltIn.Renderer;
|
|||||||
public class TextRenderer : Component
|
public class TextRenderer : Component
|
||||||
{
|
{
|
||||||
public Font Font { get; set; } = null!;
|
public Font Font { get; set; } = null!;
|
||||||
public string? Text { get; set; }
|
|
||||||
public ref Vector4 Color => ref _color;
|
public ref Vector4 Color => ref _color;
|
||||||
|
public string? Text { get; set; }
|
||||||
public RenderLayer RenderLayer { get; set; } = RenderLayer.DEFAULT;
|
public RenderLayer RenderLayer { get; set; } = RenderLayer.DEFAULT;
|
||||||
|
|
||||||
private Vector4 _color = Vector4.One;
|
private Vector4 _color = Vector4.One;
|
||||||
@@ -17,7 +16,9 @@ public class TextRenderer : Component
|
|||||||
public override void Render()
|
public override void Render()
|
||||||
{
|
{
|
||||||
if (Text == null)
|
if (Text == null)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Engine.Instance.Renderer[RenderLayer].TextRenderer
|
Engine.Instance.Renderer[RenderLayer].TextRenderer
|
||||||
.Commit(Font, Text, Color, GameObject.Transform.FullTransformMatrix);
|
.Commit(Font, Text, Color, GameObject.Transform.FullTransformMatrix);
|
||||||
|
|||||||
@@ -4,24 +4,18 @@ namespace Engine.Scene.Component.BuiltIn;
|
|||||||
|
|
||||||
public class Transform : Component
|
public class Transform : Component
|
||||||
{
|
{
|
||||||
private Vector3 _translation = Vector3.Zero;
|
|
||||||
private Quaternion _rotation = Quaternion.Identity;
|
|
||||||
private Vector3 _scale = Vector3.One;
|
|
||||||
private Vector3 _size = Vector3.One;
|
|
||||||
|
|
||||||
public ref Vector3 Translation => ref _translation;
|
|
||||||
public ref Quaternion Rotation => ref _rotation;
|
|
||||||
public ref Vector3 Scale => ref _scale;
|
|
||||||
public ref Vector3 Size => ref _size;
|
public ref Vector3 Size => ref _size;
|
||||||
|
public ref Vector3 Scale => ref _scale;
|
||||||
|
public ref Quaternion Rotation => ref _rotation;
|
||||||
|
public ref Vector3 Translation => ref _translation;
|
||||||
|
|
||||||
|
public Matrix4 FullTransformMatrix => Matrix4.CreateScale(Size) * TransformMatrix;
|
||||||
|
public Matrix4 TransformMatrix => LocalTransformMatrix * ParentTransformMatrix;
|
||||||
|
|
||||||
public Matrix4 LocalTransformMatrix => Matrix4.CreateScale(Scale) *
|
public Matrix4 LocalTransformMatrix => Matrix4.CreateScale(Scale) *
|
||||||
Matrix4.CreateFromQuaternion(Rotation) *
|
Matrix4.CreateFromQuaternion(Rotation) *
|
||||||
Matrix4.CreateTranslation(Translation);
|
Matrix4.CreateTranslation(Translation);
|
||||||
|
|
||||||
public Matrix4 TransformMatrix => LocalTransformMatrix * ParentTransformMatrix;
|
|
||||||
|
|
||||||
public Matrix4 FullTransformMatrix => Matrix4.CreateScale(Size) * TransformMatrix;
|
|
||||||
|
|
||||||
private Matrix4 ParentTransformMatrix
|
private Matrix4 ParentTransformMatrix
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -31,6 +25,11 @@ public class Transform : Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Vector3 _size = Vector3.One;
|
||||||
|
private Vector3 _scale = Vector3.One;
|
||||||
|
private Quaternion _rotation = Quaternion.Identity;
|
||||||
|
private Vector3 _translation = Vector3.Zero;
|
||||||
|
|
||||||
public Vector3 GetFullTranslation()
|
public Vector3 GetFullTranslation()
|
||||||
{
|
{
|
||||||
return FullTransformMatrix.ExtractTranslation();
|
return FullTransformMatrix.ExtractTranslation();
|
||||||
@@ -43,15 +42,4 @@ public class Transform : Component
|
|||||||
|
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float SquaredDistanceTo(Transform parTransform)
|
|
||||||
{
|
|
||||||
var translation = GetFullTranslation();
|
|
||||||
var otherTranslation = parTransform.GetFullTranslation();
|
|
||||||
|
|
||||||
var difference = translation - otherTranslation;
|
|
||||||
var squaredDistance = difference.LengthSquared;
|
|
||||||
|
|
||||||
return squaredDistance;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -14,23 +14,22 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
set => _nextIsSelfEnabled = value;
|
set => _nextIsSelfEnabled = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsSelfEnabled { get; set; } = true;
|
|
||||||
private bool _prevIsSelfEnabled = true;
|
|
||||||
private bool _nextIsSelfEnabled = true;
|
|
||||||
private bool IsParentEnabled => Scene?.Hierarchy.GetParent(this)?.IsEnabled ?? true;
|
|
||||||
|
|
||||||
public Transform Transform { get; }
|
public Transform Transform { get; }
|
||||||
|
|
||||||
internal Scene? Scene { get; set; }
|
internal Scene? Scene { get; set; }
|
||||||
|
|
||||||
private readonly Queue<Action> _componentActions = new();
|
private bool IsParentEnabled => Scene?.Hierarchy.GetParent(this)?.IsEnabled ?? true;
|
||||||
|
private bool IsSelfEnabled { get; set; } = true;
|
||||||
private readonly List<Component.Component> _components = [];
|
|
||||||
private readonly HashSet<Type> _addedComponentTypes = [];
|
|
||||||
|
|
||||||
private readonly HashSet<Component.Component> _addedComponents = [];
|
private readonly HashSet<Component.Component> _addedComponents = [];
|
||||||
|
private readonly HashSet<Type> _addedComponentTypes = [];
|
||||||
|
private readonly Queue<Action> _componentActions = new();
|
||||||
|
private readonly List<Component.Component> _components = [];
|
||||||
private readonly HashSet<Component.Component> _removedComponents = [];
|
private readonly HashSet<Component.Component> _removedComponents = [];
|
||||||
|
|
||||||
|
private bool _nextIsSelfEnabled = true;
|
||||||
|
private bool _prevIsSelfEnabled = true;
|
||||||
|
|
||||||
public GameObject()
|
public GameObject()
|
||||||
{
|
{
|
||||||
AddComponent<Transform>();
|
AddComponent<Transform>();
|
||||||
@@ -58,34 +57,14 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessAddedComponents()
|
|
||||||
{
|
|
||||||
foreach (var component in _addedComponents)
|
|
||||||
{
|
|
||||||
component.Awake();
|
|
||||||
component.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
_addedComponents.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ProcessRemovedComponents()
|
|
||||||
{
|
|
||||||
foreach (var component in _removedComponents)
|
|
||||||
{
|
|
||||||
component.Destroy();
|
|
||||||
component.GameObject = null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
_removedComponents.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update(double parDeltaTime)
|
public void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
if (!IsEnabled)
|
if (!IsEnabled)
|
||||||
{
|
{
|
||||||
if (!_prevIsSelfEnabled)
|
if (!_prevIsSelfEnabled)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var component in _components)
|
foreach (var component in _components)
|
||||||
{
|
{
|
||||||
@@ -115,7 +94,9 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
public void Render()
|
public void Render()
|
||||||
{
|
{
|
||||||
if (!IsEnabled)
|
if (!IsEnabled)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var component in _components)
|
foreach (var component in _components)
|
||||||
{
|
{
|
||||||
@@ -134,13 +115,17 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
public T? GetComponent<T>() where T : Component.Component
|
public T? GetComponent<T>() where T : Component.Component
|
||||||
{
|
{
|
||||||
if (!HasComponent<T>())
|
if (!HasComponent<T>())
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var component in _components)
|
foreach (var component in _components)
|
||||||
{
|
{
|
||||||
if (component is T result)
|
if (component is T result)
|
||||||
|
{
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -149,7 +134,9 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
{
|
{
|
||||||
var component = GetComponent<T>();
|
var component = GetComponent<T>();
|
||||||
if (component != null)
|
if (component != null)
|
||||||
|
{
|
||||||
return component;
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
component = GetComponentInChildren<T>();
|
component = GetComponentInChildren<T>();
|
||||||
return component;
|
return component;
|
||||||
@@ -163,12 +150,16 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
{
|
{
|
||||||
var component = child.GetComponent<T>();
|
var component = child.GetComponent<T>();
|
||||||
if (component != null)
|
if (component != null)
|
||||||
|
{
|
||||||
return component;
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
var childComponent = child.GetComponentInChildren<T>();
|
var childComponent = child.GetComponentInChildren<T>();
|
||||||
if (childComponent != null)
|
if (childComponent != null)
|
||||||
|
{
|
||||||
return childComponent;
|
return childComponent;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -257,6 +248,28 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ProcessAddedComponents()
|
||||||
|
{
|
||||||
|
foreach (var component in _addedComponents)
|
||||||
|
{
|
||||||
|
component.Awake();
|
||||||
|
component.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
_addedComponents.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessRemovedComponents()
|
||||||
|
{
|
||||||
|
foreach (var component in _removedComponents)
|
||||||
|
{
|
||||||
|
component.Destroy();
|
||||||
|
component.GameObject = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
_removedComponents.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return Id.ToString();
|
return Id.ToString();
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ public class Hierarchy<T>
|
|||||||
|
|
||||||
private readonly Dictionary<NullableObject<T>, IList<T>> _childrenLookup = new();
|
private readonly Dictionary<NullableObject<T>, IList<T>> _childrenLookup = new();
|
||||||
private readonly Dictionary<T, T?> _parentLookup = new();
|
private readonly Dictionary<T, T?> _parentLookup = new();
|
||||||
|
|
||||||
private readonly ConcurrentQueue<Action> _hierarchyActions = new();
|
private readonly ConcurrentQueue<Action> _hierarchyActions = new();
|
||||||
|
|
||||||
public Hierarchy()
|
public Hierarchy()
|
||||||
@@ -51,7 +50,9 @@ public class Hierarchy<T>
|
|||||||
_hierarchyActions.Enqueue(() =>
|
_hierarchyActions.Enqueue(() =>
|
||||||
{
|
{
|
||||||
if (!Contains(parObj))
|
if (!Contains(parObj))
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var parent = GetParent(parObj);
|
var parent = GetParent(parObj);
|
||||||
_childrenLookup[parent].Remove(parObj);
|
_childrenLookup[parent].Remove(parObj);
|
||||||
|
|||||||
@@ -7,49 +7,14 @@ namespace Engine.Scene;
|
|||||||
public class Scene : IUpdate, IRender
|
public class Scene : IUpdate, IRender
|
||||||
{
|
{
|
||||||
public bool IsPlaying { get; private set; }
|
public bool IsPlaying { get; private set; }
|
||||||
public IReadOnlyDictionary<RenderLayer, ICamera> Cameras => _cameras;
|
|
||||||
public float TimeScale { get; set; } = 1.0f;
|
public float TimeScale { get; set; } = 1.0f;
|
||||||
|
|
||||||
private readonly Dictionary<RenderLayer, ICamera> _cameras = new();
|
|
||||||
|
|
||||||
internal Hierarchy<GameObject> Hierarchy { get; } = new();
|
internal Hierarchy<GameObject> Hierarchy { get; } = new();
|
||||||
|
internal IReadOnlyDictionary<RenderLayer, ICamera> Cameras => _cameras;
|
||||||
|
|
||||||
|
private readonly Dictionary<RenderLayer, ICamera> _cameras = new();
|
||||||
private readonly Queue<Action> _sceneActions = [];
|
private readonly Queue<Action> _sceneActions = [];
|
||||||
|
|
||||||
internal void Enter()
|
|
||||||
{
|
|
||||||
if (IsPlaying)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Scene is already playing");
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcessChanges();
|
|
||||||
|
|
||||||
var allCameras = FindAllComponents<Camera>();
|
|
||||||
foreach (var camera in allCameras)
|
|
||||||
{
|
|
||||||
_cameras.Add(camera.RenderLayer, camera);
|
|
||||||
}
|
|
||||||
|
|
||||||
IsPlaying = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<T> FindAllComponents<T>(bool parOnlyEnabled = true) where T : Component.Component
|
|
||||||
{
|
|
||||||
return Hierarchy.Objects
|
|
||||||
.Where(parGameObject => !parOnlyEnabled || parGameObject.IsEnabled)
|
|
||||||
.Select(parGameObject => parGameObject.GetComponent<T>())
|
|
||||||
.Where(parComponent => parComponent != null)
|
|
||||||
.Distinct()
|
|
||||||
.ToList()!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T? FindFirstComponent<T>() where T : Component.Component
|
|
||||||
{
|
|
||||||
return Hierarchy.Objects.Select(parGameObject => parGameObject.GetComponent<T>())
|
|
||||||
.FirstOrDefault(parComponent => parComponent != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update(double parDeltaTime)
|
public void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
if (!IsPlaying)
|
if (!IsPlaying)
|
||||||
@@ -85,19 +50,20 @@ public class Scene : IUpdate, IRender
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Exit()
|
public List<T> FindAllComponents<T>(bool parOnlyEnabled = true) where T : Component.Component
|
||||||
{
|
{
|
||||||
if (!IsPlaying)
|
return Hierarchy.Objects
|
||||||
{
|
.Where(parGameObject => !parOnlyEnabled || parGameObject.IsEnabled)
|
||||||
throw new InvalidOperationException("Scene is not playing");
|
.Select(parGameObject => parGameObject.GetComponent<T>())
|
||||||
|
.Where(parComponent => parComponent != null)
|
||||||
|
.Distinct()
|
||||||
|
.ToList()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var gameObject in Hierarchy.Objects)
|
public T? FindFirstComponent<T>() where T : Component.Component
|
||||||
{
|
{
|
||||||
gameObject.Destroy();
|
return Hierarchy.Objects.Select(parGameObject => parGameObject.GetComponent<T>())
|
||||||
}
|
.FirstOrDefault(parComponent => parComponent != null);
|
||||||
|
|
||||||
IsPlaying = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Add(GameObject parGameObject)
|
public void Add(GameObject parGameObject)
|
||||||
@@ -136,6 +102,39 @@ public class Scene : IUpdate, IRender
|
|||||||
return parRecursive ? Hierarchy.GetAllChildren(parParent) : Hierarchy.GetChildren(parParent);
|
return parRecursive ? Hierarchy.GetAllChildren(parParent) : Hierarchy.GetChildren(parParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void Enter()
|
||||||
|
{
|
||||||
|
if (IsPlaying)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Scene is already playing");
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessChanges();
|
||||||
|
|
||||||
|
var allCameras = FindAllComponents<Camera>();
|
||||||
|
foreach (var camera in allCameras)
|
||||||
|
{
|
||||||
|
_cameras.Add(camera.RenderLayer, camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
IsPlaying = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Exit()
|
||||||
|
{
|
||||||
|
if (!IsPlaying)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Scene is not playing");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var gameObject in Hierarchy.Objects)
|
||||||
|
{
|
||||||
|
gameObject.Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
IsPlaying = false;
|
||||||
|
}
|
||||||
|
|
||||||
private void ProcessChanges()
|
private void ProcessChanges()
|
||||||
{
|
{
|
||||||
Hierarchy.ProcessChanges();
|
Hierarchy.ProcessChanges();
|
||||||
|
|||||||
@@ -2,32 +2,33 @@
|
|||||||
|
|
||||||
public class SceneManager : IUpdate, IRender
|
public class SceneManager : IUpdate, IRender
|
||||||
{
|
{
|
||||||
public Scene? CurrentScene => _currentScene;
|
public Scene? CurrentScene { get; private set; }
|
||||||
|
|
||||||
private Scene? _currentScene;
|
|
||||||
private Func<Scene>? _nextScene;
|
private Func<Scene>? _nextScene;
|
||||||
|
|
||||||
public void TransitionTo(Func<Scene>? parScene)
|
|
||||||
{
|
|
||||||
_nextScene = parScene;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update(double parDeltaTime)
|
public void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
if (_nextScene != null)
|
if (_nextScene != null)
|
||||||
{
|
{
|
||||||
_currentScene?.Exit();
|
CurrentScene?.Exit();
|
||||||
_currentScene = _nextScene();
|
CurrentScene = _nextScene();
|
||||||
_nextScene = null;
|
_nextScene = null;
|
||||||
_currentScene.Enter();
|
CurrentScene.Enter();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parDeltaTime != 0)
|
if (parDeltaTime != 0)
|
||||||
_currentScene?.Update(parDeltaTime);
|
{
|
||||||
|
CurrentScene?.Update(parDeltaTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Render()
|
public void Render()
|
||||||
{
|
{
|
||||||
_currentScene?.Render();
|
CurrentScene?.Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TransitionTo(Func<Scene>? parScene)
|
||||||
|
{
|
||||||
|
_nextScene = parScene;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,21 +2,10 @@
|
|||||||
|
|
||||||
public class TickableTimer
|
public class TickableTimer
|
||||||
{
|
{
|
||||||
public event Action? OnFinished;
|
|
||||||
public event Action<double>? OnUpdate;
|
public event Action<double>? OnUpdate;
|
||||||
|
public event Action? OnFinished;
|
||||||
|
|
||||||
public double TotalTime
|
public bool IsFinished => _currentTime <= 0;
|
||||||
{
|
|
||||||
get => _totalTime;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value <= 0)
|
|
||||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value);
|
|
||||||
|
|
||||||
_totalTime = value;
|
|
||||||
CurrentTime = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public double CurrentTime
|
public double CurrentTime
|
||||||
{
|
{
|
||||||
@@ -24,30 +13,54 @@ public class TickableTimer
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value < 0)
|
if (value < 0)
|
||||||
|
{
|
||||||
value = 0;
|
value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (value > TotalTime)
|
if (value > TotalTime)
|
||||||
|
{
|
||||||
value = TotalTime;
|
value = TotalTime;
|
||||||
|
}
|
||||||
|
|
||||||
if (value == _currentTime)
|
if (value == _currentTime)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_currentTime = value;
|
_currentTime = value;
|
||||||
OnUpdate?.Invoke(value);
|
OnUpdate?.Invoke(value);
|
||||||
|
|
||||||
if (IsFinished)
|
if (IsFinished)
|
||||||
|
{
|
||||||
OnFinished?.Invoke();
|
OnFinished?.Invoke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsFinished => _currentTime <= 0;
|
public double TotalTime
|
||||||
|
{
|
||||||
|
get => _totalTime;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value <= 0)
|
||||||
|
{
|
||||||
|
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
_totalTime = value;
|
||||||
|
CurrentTime = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private double _totalTime;
|
|
||||||
private double _currentTime;
|
private double _currentTime;
|
||||||
|
private double _totalTime;
|
||||||
|
|
||||||
public TickableTimer(double parTotalTime)
|
public TickableTimer(double parTotalTime)
|
||||||
{
|
{
|
||||||
if (parTotalTime <= 0)
|
if (parTotalTime <= 0)
|
||||||
|
{
|
||||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(parTotalTime);
|
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(parTotalTime);
|
||||||
|
}
|
||||||
|
|
||||||
_totalTime = parTotalTime;
|
_totalTime = parTotalTime;
|
||||||
_currentTime = parTotalTime;
|
_currentTime = parTotalTime;
|
||||||
|
|||||||
@@ -9,34 +9,34 @@ namespace Engine;
|
|||||||
|
|
||||||
public class Window : IPresenter
|
public class Window : IPresenter
|
||||||
{
|
{
|
||||||
public bool IsExiting => _window.IsExiting;
|
|
||||||
public int Width { get; private set; }
|
|
||||||
public int Height { get; private set; }
|
|
||||||
public event Action<ResizeEventArgs>? Resize;
|
public event Action<ResizeEventArgs>? Resize;
|
||||||
|
|
||||||
internal NativeWindow NativeWindow => _window;
|
public int Width { get; private set; }
|
||||||
|
public int Height { get; private set; }
|
||||||
|
public bool IsExiting => NativeWindow.IsExiting;
|
||||||
|
|
||||||
|
internal NativeWindow NativeWindow { get; }
|
||||||
|
|
||||||
private readonly Engine _engine;
|
private readonly Engine _engine;
|
||||||
private readonly NativeWindow _window;
|
|
||||||
private readonly bool _headless;
|
private readonly bool _headless;
|
||||||
|
|
||||||
public Window(Engine parEngine, NativeWindow parWindow, bool parHeadless)
|
public Window(Engine parEngine, NativeWindow parWindow, bool parHeadless)
|
||||||
{
|
{
|
||||||
_engine = parEngine;
|
_engine = parEngine;
|
||||||
_window = parWindow;
|
NativeWindow = parWindow;
|
||||||
_headless = parHeadless;
|
_headless = parHeadless;
|
||||||
|
|
||||||
(Width, Height) = _window.ClientSize;
|
(Width, Height) = NativeWindow.ClientSize;
|
||||||
|
|
||||||
_window.MakeCurrent();
|
NativeWindow.MakeCurrent();
|
||||||
_window.Resize += parArgs =>
|
NativeWindow.Resize += parArgs =>
|
||||||
{
|
{
|
||||||
Width = parArgs.Width;
|
Width = parArgs.Width;
|
||||||
Height = parArgs.Height;
|
Height = parArgs.Height;
|
||||||
Resize?.Invoke(parArgs);
|
Resize?.Invoke(parArgs);
|
||||||
};
|
};
|
||||||
|
|
||||||
_window.VSync = VSyncMode.On;
|
NativeWindow.VSync = VSyncMode.On;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(double parDeltaTime)
|
public void Update(double parDeltaTime)
|
||||||
@@ -50,9 +50,9 @@ public class Window : IPresenter
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_window.NewInputFrame();
|
NativeWindow.NewInputFrame();
|
||||||
NativeWindow.ProcessWindowEvents(false);
|
NativeWindow.ProcessWindowEvents(false);
|
||||||
_window.SwapBuffers();
|
NativeWindow.SwapBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Present(IConstTexture parTexture)
|
public void Present(IConstTexture parTexture)
|
||||||
@@ -72,7 +72,7 @@ public class Window : IPresenter
|
|||||||
|
|
||||||
public void Exit()
|
public void Exit()
|
||||||
{
|
{
|
||||||
_window.Close();
|
NativeWindow.Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,16 +5,16 @@ namespace PresenterConsole;
|
|||||||
|
|
||||||
public sealed class ConsoleFastOutput : IDisposable
|
public sealed class ConsoleFastOutput : IDisposable
|
||||||
{
|
{
|
||||||
private const uint GENERIC_WRITE = 0x40000000;
|
|
||||||
private const int FILE_SHARE_WRITE = 0x2;
|
private const int FILE_SHARE_WRITE = 0x2;
|
||||||
|
private const uint GENERIC_WRITE = 0x40000000;
|
||||||
|
|
||||||
private readonly SafeFileHandle _handle;
|
|
||||||
private readonly WindowsFFI.Coord _bufferCoord = new(0, 0);
|
private readonly WindowsFFI.Coord _bufferCoord = new(0, 0);
|
||||||
|
private readonly SafeFileHandle _handle;
|
||||||
|
|
||||||
private int _width;
|
|
||||||
private int _height;
|
|
||||||
private WindowsFFI.CharInfo[] _buffer;
|
private WindowsFFI.CharInfo[] _buffer;
|
||||||
private WindowsFFI.Coord _bufferSize;
|
private WindowsFFI.Coord _bufferSize;
|
||||||
|
private int _height;
|
||||||
|
private int _width;
|
||||||
|
|
||||||
|
|
||||||
public ConsoleFastOutput(int parWidth, int parHeight)
|
public ConsoleFastOutput(int parWidth, int parHeight)
|
||||||
@@ -26,7 +26,9 @@ public sealed class ConsoleFastOutput : IDisposable
|
|||||||
IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
|
IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
|
||||||
|
|
||||||
if (_handle.IsInvalid)
|
if (_handle.IsInvalid)
|
||||||
|
{
|
||||||
throw new InvalidOperationException("Failed to open console handle");
|
throw new InvalidOperationException("Failed to open console handle");
|
||||||
|
}
|
||||||
|
|
||||||
Resize(parWidth, parHeight);
|
Resize(parWidth, parHeight);
|
||||||
}
|
}
|
||||||
@@ -34,7 +36,7 @@ public sealed class ConsoleFastOutput : IDisposable
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void WriteChar(char parCharacter, int parX, int parY, ConsoleColor parForeground, ConsoleColor parBackground)
|
public void WriteChar(char parCharacter, int parX, int parY, ConsoleColor parForeground, ConsoleColor parBackground)
|
||||||
{
|
{
|
||||||
var index = parX + parY * _width;
|
var index = parX + (parY * _width);
|
||||||
ref var charInfo = ref _buffer[index];
|
ref var charInfo = ref _buffer[index];
|
||||||
charInfo.Char.UnicodeChar = parCharacter;
|
charInfo.Char.UnicodeChar = parCharacter;
|
||||||
charInfo.Attributes = (short)((ushort)parForeground | ((ushort)parBackground << 4));
|
charInfo.Attributes = (short)((ushort)parForeground | ((ushort)parBackground << 4));
|
||||||
@@ -53,7 +55,9 @@ public sealed class ConsoleFastOutput : IDisposable
|
|||||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(parHeight);
|
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(parHeight);
|
||||||
|
|
||||||
if (parWidth == _width && parHeight == _height)
|
if (parWidth == _width && parHeight == _height)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_width = parWidth;
|
_width = parWidth;
|
||||||
_height = parHeight;
|
_height = parHeight;
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ namespace PresenterConsole;
|
|||||||
public class ConsoleInputHandler : IInputHandler
|
public class ConsoleInputHandler : IInputHandler
|
||||||
{
|
{
|
||||||
public CultureInfo CurrentInputLanguage => WindowsFFI.GetCurrentKeyboardLayout();
|
public CultureInfo CurrentInputLanguage => WindowsFFI.GetCurrentKeyboardLayout();
|
||||||
|
|
||||||
public Vector2 MousePosition => Vector2.Zero;
|
public Vector2 MousePosition => Vector2.Zero;
|
||||||
|
|
||||||
private readonly bool[] _currentKeys = new bool[256];
|
private readonly bool[] _currentKeys = new bool[256];
|
||||||
@@ -46,17 +45,20 @@ public class ConsoleInputHandler : IInputHandler
|
|||||||
return _currentKeys[consoleKey] && !_previousKeys[consoleKey];
|
return _currentKeys[consoleKey] && !_previousKeys[consoleKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int ConvertToConsoleKey(MouseButtonCode parMouseButtonCode) =>
|
private static int ConvertToConsoleKey(MouseButtonCode parMouseButtonCode)
|
||||||
parMouseButtonCode switch
|
{
|
||||||
|
return parMouseButtonCode switch
|
||||||
{
|
{
|
||||||
MouseButtonCode.Left => 0x01,
|
MouseButtonCode.Left => 0x01,
|
||||||
MouseButtonCode.Right => 0x02,
|
MouseButtonCode.Right => 0x02,
|
||||||
MouseButtonCode.Middle => 0x04,
|
MouseButtonCode.Middle => 0x04,
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(parMouseButtonCode), parMouseButtonCode, null)
|
_ => throw new ArgumentOutOfRangeException(nameof(parMouseButtonCode), parMouseButtonCode, null)
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static int ConvertToConsoleKey(KeyboardButtonCode parKeyboardButtonCode) =>
|
private static int ConvertToConsoleKey(KeyboardButtonCode parKeyboardButtonCode)
|
||||||
parKeyboardButtonCode switch
|
{
|
||||||
|
return parKeyboardButtonCode switch
|
||||||
{
|
{
|
||||||
KeyboardButtonCode.A => 0x41,
|
KeyboardButtonCode.A => 0x41,
|
||||||
KeyboardButtonCode.B => 0x42,
|
KeyboardButtonCode.B => 0x42,
|
||||||
@@ -115,3 +117,4 @@ public class ConsoleInputHandler : IInputHandler
|
|||||||
_ => throw new ArgumentOutOfRangeException(nameof(parKeyboardButtonCode), parKeyboardButtonCode, null)
|
_ => throw new ArgumentOutOfRangeException(nameof(parKeyboardButtonCode), parKeyboardButtonCode, null)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@@ -14,31 +14,30 @@ namespace PresenterConsole;
|
|||||||
|
|
||||||
public class ConsolePresenter : IPresenter
|
public class ConsolePresenter : IPresenter
|
||||||
{
|
{
|
||||||
public bool IsExiting { get; private set; }
|
|
||||||
public int Width { get; private set; } = 2;
|
|
||||||
public int Height { get; private set; } = 1;
|
|
||||||
public event Action<ResizeEventArgs>? Resize;
|
public event Action<ResizeEventArgs>? Resize;
|
||||||
|
|
||||||
private readonly Engine.Engine _engine;
|
public int Width { get; private set; } = 2;
|
||||||
|
public int Height { get; private set; } = 1;
|
||||||
|
public bool IsExiting { get; private set; }
|
||||||
|
|
||||||
private readonly Framebuffer _framebuffer;
|
private static readonly char[] LIGHTMAP = " .,:;=*#%@".Reverse().ToArray();
|
||||||
private readonly Engine.Graphics.Shader.Program _asciiProgram;
|
|
||||||
private Image<AsciiPixel>? _asciiImage;
|
|
||||||
|
|
||||||
private readonly IndexBuffer _indexBuffer;
|
|
||||||
private readonly VertexBuffer<AsciiVertex> _vertexBuffer;
|
|
||||||
private readonly VertexArray _vertexArray;
|
|
||||||
|
|
||||||
private readonly ConsoleFastOutput _consoleOutput;
|
private readonly ConsoleFastOutput _consoleOutput;
|
||||||
private static readonly char[] LIGHTMAP = " .,:;=*#%@".Reverse().ToArray();
|
|
||||||
|
private readonly Engine.Graphics.Shader.Program _asciiProgram;
|
||||||
|
private readonly Framebuffer _framebuffer;
|
||||||
|
private readonly IndexBuffer _indexBuffer;
|
||||||
|
private readonly VertexArray _vertexArray;
|
||||||
|
private readonly VertexBuffer<AsciiVertex> _vertexBuffer;
|
||||||
|
|
||||||
|
private Image<AsciiPixel>? _asciiImage;
|
||||||
|
|
||||||
public ConsolePresenter(Engine.Engine parEngine)
|
public ConsolePresenter(Engine.Engine parEngine)
|
||||||
{
|
{
|
||||||
_engine = parEngine;
|
((MemoryResourceStreamProvider)parEngine.EngineResourceManager.StreamProvider).AddResource("shader/ascii",
|
||||||
((MemoryResourceStreamProvider)_engine.EngineResourceManager.StreamProvider).AddResource("shader/ascii",
|
|
||||||
Encoding.UTF8.GetBytes(ShaderResource.Ascii));
|
Encoding.UTF8.GetBytes(ShaderResource.Ascii));
|
||||||
|
|
||||||
_asciiProgram = _engine.EngineResourceManager.Load<Engine.Graphics.Shader.Program>("shader/ascii");
|
_asciiProgram = parEngine.EngineResourceManager.Load<Engine.Graphics.Shader.Program>("shader/ascii");
|
||||||
|
|
||||||
_framebuffer = Framebuffer.Builder(Width / 2, Height)
|
_framebuffer = Framebuffer.Builder(Width / 2, Height)
|
||||||
.AddColorAttachment<AsciiPixel>()
|
.AddColorAttachment<AsciiPixel>()
|
||||||
@@ -77,36 +76,21 @@ public class ConsolePresenter : IPresenter
|
|||||||
|
|
||||||
_framebuffer.Unbind();
|
_framebuffer.Unbind();
|
||||||
|
|
||||||
// GL.Viewport(0, 0, Width / 2 * 4, Height * 4);
|
|
||||||
|
|
||||||
var asciiTexture = _framebuffer.TextureInternal;
|
var asciiTexture = _framebuffer.TextureInternal;
|
||||||
if (asciiTexture == null)
|
if (asciiTexture == null)
|
||||||
|
{
|
||||||
throw new InvalidOperationException("Framebuffer texture is null");
|
throw new InvalidOperationException("Framebuffer texture is null");
|
||||||
|
}
|
||||||
|
|
||||||
if (_asciiImage == null || asciiTexture.Width != _asciiImage.Width || asciiTexture.Height != _asciiImage.Height)
|
if (_asciiImage == null || asciiTexture.Width != _asciiImage.Width || asciiTexture.Height != _asciiImage.Height)
|
||||||
|
{
|
||||||
_asciiImage = new Image<AsciiPixel>(asciiTexture.Width, asciiTexture.Height);
|
_asciiImage = new Image<AsciiPixel>(asciiTexture.Width, asciiTexture.Height);
|
||||||
|
}
|
||||||
|
|
||||||
asciiTexture.ReadPixels(_asciiImage);
|
asciiTexture.ReadPixels(_asciiImage);
|
||||||
DrawImage(_asciiImage);
|
DrawImage(_asciiImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawImage(Image<AsciiPixel> parImage)
|
|
||||||
{
|
|
||||||
for (var y = 0; y < parImage.Height; y++)
|
|
||||||
{
|
|
||||||
for (var x = 0; x < parImage.Width; x++)
|
|
||||||
{
|
|
||||||
var pixel = parImage[y, x];
|
|
||||||
var lightnessIndex = (byte)(pixel.Luminance / 255.0f * (LIGHTMAP.Length - 1));
|
|
||||||
var colorIndex = (ConsoleColor)(pixel.Color / 255.0f * 15.0f);
|
|
||||||
_consoleOutput.WriteChar(LIGHTMAP[lightnessIndex], 2 * x, y, 0, colorIndex);
|
|
||||||
_consoleOutput.WriteChar(LIGHTMAP[lightnessIndex], 2 * x + 1, y, 0, colorIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_consoleOutput.Flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update(double parDeltaTime)
|
public void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -131,4 +115,21 @@ public class ConsolePresenter : IPresenter
|
|||||||
{
|
{
|
||||||
IsExiting = true;
|
IsExiting = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawImage(Image<AsciiPixel> parImage)
|
||||||
|
{
|
||||||
|
for (var y = 0; y < parImage.Height; y++)
|
||||||
|
{
|
||||||
|
for (var x = 0; x < parImage.Width; x++)
|
||||||
|
{
|
||||||
|
var pixel = parImage[y, x];
|
||||||
|
var lightnessIndex = (byte)(pixel.Luminance / 255.0f * (LIGHTMAP.Length - 1));
|
||||||
|
var colorIndex = (ConsoleColor)(pixel.Color / 255.0f * 15.0f);
|
||||||
|
_consoleOutput.WriteChar(LIGHTMAP[lightnessIndex], 2 * x, y, 0, colorIndex);
|
||||||
|
_consoleOutput.WriteChar(LIGHTMAP[lightnessIndex], (2 * x) + 1, y, 0, colorIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_consoleOutput.Flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Serilog.Events;
|
using Engine;
|
||||||
|
using Serilog.Events;
|
||||||
|
|
||||||
namespace PresenterConsole;
|
namespace PresenterConsole;
|
||||||
|
|
||||||
@@ -6,7 +7,7 @@ internal static class Program
|
|||||||
{
|
{
|
||||||
public static void Main(string[] parArgs)
|
public static void Main(string[] parArgs)
|
||||||
{
|
{
|
||||||
var engine = new Engine.EngineBuilder()
|
var engine = new EngineBuilder()
|
||||||
.Headless()
|
.Headless()
|
||||||
.LogToFile(true, "log.txt")
|
.LogToFile(true, "log.txt")
|
||||||
.LogLevel(LogEventLevel.Debug)
|
.LogLevel(LogEventLevel.Debug)
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<root>
|
<root>
|
||||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
|
||||||
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
id="root"
|
||||||
|
xmlns="">
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<Application x:Class="PresenterWpf.App"
|
<Application x:Class="PresenterWpf.App"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
xmlns:local="clr-namespace:PresenterWpf">
|
|
||||||
</Application>
|
</Application>
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Threading;
|
||||||
using Engine;
|
using Engine;
|
||||||
using Engine.Graphics;
|
using Engine.Graphics;
|
||||||
using Engine.Graphics.Texture;
|
using Engine.Graphics.Texture;
|
||||||
@@ -35,11 +36,11 @@ public partial class App : Application
|
|||||||
// Since engine claims current thread for rendering, we need to create a new thread to run WPF
|
// Since engine claims current thread for rendering, we need to create a new thread to run WPF
|
||||||
var thread = new Thread(() =>
|
var thread = new Thread(() =>
|
||||||
{
|
{
|
||||||
var window = new MainWindow(engine);
|
var window = new MainWindow();
|
||||||
presenter.Presenter = window;
|
presenter.Presenter = window;
|
||||||
inputHandler.InputHandler = new WpfInputHandler(window);
|
inputHandler.InputHandler = new WpfInputHandler(window);
|
||||||
window.Show();
|
window.Show();
|
||||||
System.Windows.Threading.Dispatcher.Run();
|
Dispatcher.Run();
|
||||||
});
|
});
|
||||||
|
|
||||||
thread.SetApartmentState(ApartmentState.STA);
|
thread.SetApartmentState(ApartmentState.STA);
|
||||||
@@ -56,50 +57,41 @@ public partial class App : Application
|
|||||||
|
|
||||||
private class InputHandlerWrapper : IInputHandler
|
private class InputHandlerWrapper : IInputHandler
|
||||||
{
|
{
|
||||||
public CultureInfo CurrentInputLanguage => _inputHandler?.CurrentInputLanguage ?? new CultureInfo(1033);
|
public IInputHandler? InputHandler { get; set; }
|
||||||
|
|
||||||
public Vector2 MousePosition => _inputHandler?.MousePosition ?? Vector2.Zero;
|
public CultureInfo CurrentInputLanguage => InputHandler?.CurrentInputLanguage ?? new CultureInfo(1033);
|
||||||
|
|
||||||
private IInputHandler? _inputHandler;
|
public Vector2 MousePosition => InputHandler?.MousePosition ?? Vector2.Zero;
|
||||||
|
|
||||||
public IInputHandler? InputHandler
|
|
||||||
{
|
|
||||||
get => _inputHandler;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_inputHandler = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsKeyPressed(KeyboardButtonCode parKeyboardButtonCode)
|
public bool IsKeyPressed(KeyboardButtonCode parKeyboardButtonCode)
|
||||||
{
|
{
|
||||||
return _inputHandler?.IsKeyPressed(parKeyboardButtonCode) ?? false;
|
return InputHandler?.IsKeyPressed(parKeyboardButtonCode) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsKeyJustPressed(KeyboardButtonCode parKeyboardButtonCode)
|
public bool IsKeyJustPressed(KeyboardButtonCode parKeyboardButtonCode)
|
||||||
{
|
{
|
||||||
return _inputHandler?.IsKeyJustPressed(parKeyboardButtonCode) ?? false;
|
return InputHandler?.IsKeyJustPressed(parKeyboardButtonCode) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsMouseButtonPressed(MouseButtonCode parButtonCode)
|
public bool IsMouseButtonPressed(MouseButtonCode parButtonCode)
|
||||||
{
|
{
|
||||||
return _inputHandler?.IsMouseButtonPressed(parButtonCode) ?? false;
|
return InputHandler?.IsMouseButtonPressed(parButtonCode) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsMouseButtonJustPressed(MouseButtonCode parButtonCode)
|
public bool IsMouseButtonJustPressed(MouseButtonCode parButtonCode)
|
||||||
{
|
{
|
||||||
return _inputHandler?.IsMouseButtonJustPressed(parButtonCode) ?? false;
|
return InputHandler?.IsMouseButtonJustPressed(parButtonCode) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(double parDeltaTime)
|
public void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
_inputHandler?.Update(parDeltaTime);
|
InputHandler?.Update(parDeltaTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PresenterWrapper : IPresenter
|
private class PresenterWrapper : IPresenter
|
||||||
{
|
{
|
||||||
private IPresenter? _presenter;
|
public event Action<ResizeEventArgs>? Resize;
|
||||||
|
|
||||||
public IPresenter? Presenter
|
public IPresenter? Presenter
|
||||||
{
|
{
|
||||||
@@ -120,10 +112,11 @@ public partial class App : Application
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsExiting => Presenter?.IsExiting ?? false;
|
|
||||||
public int Width => Presenter?.Width ?? 0;
|
public int Width => Presenter?.Width ?? 0;
|
||||||
public int Height => Presenter?.Height ?? 0;
|
public int Height => Presenter?.Height ?? 0;
|
||||||
public event Action<ResizeEventArgs>? Resize;
|
public bool IsExiting => Presenter?.IsExiting ?? false;
|
||||||
|
|
||||||
|
private IPresenter? _presenter;
|
||||||
|
|
||||||
public void Present(IConstTexture parTexture)
|
public void Present(IConstTexture parTexture)
|
||||||
{
|
{
|
||||||
@@ -140,14 +133,14 @@ public partial class App : Application
|
|||||||
Presenter?.Render();
|
Presenter?.Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PresenterResize(ResizeEventArgs e)
|
|
||||||
{
|
|
||||||
Resize?.Invoke(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Exit()
|
public void Exit()
|
||||||
{
|
{
|
||||||
Presenter?.Exit();
|
Presenter?.Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void PresenterResize(ResizeEventArgs e)
|
||||||
|
{
|
||||||
|
Resize?.Invoke(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:local="clr-namespace:PresenterWpf"
|
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Title="MainWindow" Height="450" Width="800" Closing="MainWindow_OnClosing">
|
Title="MainWindow" Height="450" Width="800" Closing="MainWindow_OnClosing">
|
||||||
<Grid>
|
<Grid>
|
||||||
|
|||||||
@@ -12,23 +12,19 @@ namespace PresenterWpf;
|
|||||||
|
|
||||||
public partial class MainWindow : Window, IPresenter
|
public partial class MainWindow : Window, IPresenter
|
||||||
{
|
{
|
||||||
public bool IsExiting { get; private set; }
|
|
||||||
public new int Width { get; private set; }
|
|
||||||
public new int Height { get; private set; }
|
|
||||||
|
|
||||||
private bool _scheduledResize;
|
|
||||||
|
|
||||||
public event Action<ResizeEventArgs>? Resize;
|
public event Action<ResizeEventArgs>? Resize;
|
||||||
|
|
||||||
private readonly Engine.Engine _engine;
|
public new int Width { get; private set; }
|
||||||
private Image<Rgb8>? _image;
|
public new int Height { get; private set; }
|
||||||
private WriteableBitmap? _bitmap;
|
public bool IsExiting { get; private set; }
|
||||||
|
|
||||||
public MainWindow(Engine.Engine parEngine)
|
private WriteableBitmap? _bitmap;
|
||||||
|
private Image<Rgb8>? _image;
|
||||||
|
private bool _scheduledResize;
|
||||||
|
|
||||||
|
public MainWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
_engine = parEngine;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(double parDeltaTime)
|
public void Update(double parDeltaTime)
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ namespace PresenterWpf;
|
|||||||
public class WpfInputHandler : IInputHandler
|
public class WpfInputHandler : IInputHandler
|
||||||
{
|
{
|
||||||
public CultureInfo CurrentInputLanguage { get; private set; }
|
public CultureInfo CurrentInputLanguage { get; private set; }
|
||||||
public Vector2 MousePosition => _mousePosition;
|
public Vector2 MousePosition { get; private set; } = Vector2.Zero;
|
||||||
|
|
||||||
private readonly Window _window;
|
|
||||||
|
|
||||||
private readonly bool[] _actualKeys = new bool[(int)KeyboardButtonCode.TotalCount];
|
private readonly bool[] _actualKeys = new bool[(int)KeyboardButtonCode.TotalCount];
|
||||||
private readonly bool[] _currentKeys = new bool[(int)KeyboardButtonCode.TotalCount];
|
private readonly bool[] _currentKeys = new bool[(int)KeyboardButtonCode.TotalCount];
|
||||||
@@ -21,8 +19,7 @@ public class WpfInputHandler : IInputHandler
|
|||||||
private readonly bool[] _currentMouseButtons = new bool[(int)MouseButtonCode.TotalCount];
|
private readonly bool[] _currentMouseButtons = new bool[(int)MouseButtonCode.TotalCount];
|
||||||
private readonly bool[] _previousMouseButtons = new bool[(int)MouseButtonCode.TotalCount];
|
private readonly bool[] _previousMouseButtons = new bool[(int)MouseButtonCode.TotalCount];
|
||||||
|
|
||||||
|
private readonly Window _window;
|
||||||
private Vector2 _mousePosition = Vector2.Zero;
|
|
||||||
|
|
||||||
public WpfInputHandler(Window parWindow)
|
public WpfInputHandler(Window parWindow)
|
||||||
{
|
{
|
||||||
@@ -35,6 +32,14 @@ public class WpfInputHandler : IInputHandler
|
|||||||
_window.MouseMove += Window_MouseMove;
|
_window.MouseMove += Window_MouseMove;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~WpfInputHandler()
|
||||||
|
{
|
||||||
|
_window.PreviewKeyDown -= Window_PreviewKeyDown;
|
||||||
|
_window.PreviewKeyUp -= Window_PreviewKeyUp;
|
||||||
|
_window.PreviewMouseDown -= Window_PreviewMouseDown;
|
||||||
|
_window.PreviewMouseUp -= Window_PreviewMouseUp;
|
||||||
|
}
|
||||||
|
|
||||||
public void Update(double parDeltaTime)
|
public void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
_window.Dispatcher.Invoke(() =>
|
_window.Dispatcher.Invoke(() =>
|
||||||
@@ -55,6 +60,30 @@ public class WpfInputHandler : IInputHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsKeyPressed(KeyboardButtonCode parKeyboardButtonCode)
|
||||||
|
{
|
||||||
|
var keyCode = (int)parKeyboardButtonCode;
|
||||||
|
return _currentKeys[keyCode];
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsKeyJustPressed(KeyboardButtonCode parKeyboardButtonCode)
|
||||||
|
{
|
||||||
|
var keyCode = (int)parKeyboardButtonCode;
|
||||||
|
return _currentKeys[keyCode] && !_previousKeys[keyCode];
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsMouseButtonPressed(MouseButtonCode parButtonCode)
|
||||||
|
{
|
||||||
|
var buttonCode = (int)parButtonCode;
|
||||||
|
return _currentMouseButtons[buttonCode];
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsMouseButtonJustPressed(MouseButtonCode parButtonCode)
|
||||||
|
{
|
||||||
|
var buttonCode = (int)parButtonCode;
|
||||||
|
return _currentMouseButtons[buttonCode] && !_previousMouseButtons[buttonCode];
|
||||||
|
}
|
||||||
|
|
||||||
private void Window_PreviewKeyDown(object parSender, KeyEventArgs parEventArgs)
|
private void Window_PreviewKeyDown(object parSender, KeyEventArgs parEventArgs)
|
||||||
{
|
{
|
||||||
var keyCode = ConvertToKeyboardButtonCode(parEventArgs.Key);
|
var keyCode = ConvertToKeyboardButtonCode(parEventArgs.Key);
|
||||||
@@ -94,34 +123,12 @@ public class WpfInputHandler : IInputHandler
|
|||||||
private void Window_MouseMove(object parSender, MouseEventArgs parEventArgs)
|
private void Window_MouseMove(object parSender, MouseEventArgs parEventArgs)
|
||||||
{
|
{
|
||||||
var position = parEventArgs.GetPosition(null);
|
var position = parEventArgs.GetPosition(null);
|
||||||
_mousePosition = new Vector2((float)position.X, (float)position.Y);
|
MousePosition = new Vector2((float)position.X, (float)position.Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsKeyPressed(KeyboardButtonCode parKeyboardButtonCode)
|
private static int ConvertToKeyboardButtonCode(Key parKey)
|
||||||
{
|
{
|
||||||
var keyCode = (int)parKeyboardButtonCode;
|
return parKey switch
|
||||||
return _currentKeys[keyCode];
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsKeyJustPressed(KeyboardButtonCode parKeyboardButtonCode)
|
|
||||||
{
|
|
||||||
var keyCode = (int)parKeyboardButtonCode;
|
|
||||||
return _currentKeys[keyCode] && !_previousKeys[keyCode];
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsMouseButtonPressed(MouseButtonCode parButtonCode)
|
|
||||||
{
|
|
||||||
var buttonCode = (int)parButtonCode;
|
|
||||||
return _currentMouseButtons[buttonCode];
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsMouseButtonJustPressed(MouseButtonCode parButtonCode)
|
|
||||||
{
|
|
||||||
var buttonCode = (int)parButtonCode;
|
|
||||||
return _currentMouseButtons[buttonCode] && !_previousMouseButtons[buttonCode];
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int ConvertToKeyboardButtonCode(Key parKey) => parKey switch
|
|
||||||
{
|
{
|
||||||
Key.A => (int)KeyboardButtonCode.A,
|
Key.A => (int)KeyboardButtonCode.A,
|
||||||
Key.B => (int)KeyboardButtonCode.B,
|
Key.B => (int)KeyboardButtonCode.B,
|
||||||
@@ -182,21 +189,16 @@ public class WpfInputHandler : IInputHandler
|
|||||||
Key.D9 => (int)KeyboardButtonCode.D9,
|
Key.D9 => (int)KeyboardButtonCode.D9,
|
||||||
_ => -1
|
_ => -1
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static int ConvertToMouseButtonCode(MouseButton parButton) =>
|
private static int ConvertToMouseButtonCode(MouseButton parButton)
|
||||||
parButton switch
|
{
|
||||||
|
return parButton switch
|
||||||
{
|
{
|
||||||
MouseButton.Left => (int)MouseButtonCode.Left,
|
MouseButton.Left => (int)MouseButtonCode.Left,
|
||||||
MouseButton.Right => (int)MouseButtonCode.Right,
|
MouseButton.Right => (int)MouseButtonCode.Right,
|
||||||
MouseButton.Middle => (int)MouseButtonCode.Middle,
|
MouseButton.Middle => (int)MouseButtonCode.Middle,
|
||||||
_ => -1
|
_ => -1
|
||||||
};
|
};
|
||||||
|
|
||||||
~WpfInputHandler()
|
|
||||||
{
|
|
||||||
_window.PreviewKeyDown -= Window_PreviewKeyDown;
|
|
||||||
_window.PreviewKeyUp -= Window_PreviewKeyUp;
|
|
||||||
_window.PreviewMouseDown -= Window_PreviewMouseDown;
|
|
||||||
_window.PreviewMouseUp -= Window_PreviewMouseUp;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user