Compare commits
10 Commits
bb6900a60a
...
15214c9e21
| Author | SHA1 | Date | |
|---|---|---|---|
| 15214c9e21 | |||
| a806e3b764 | |||
| e51d28ce89 | |||
| 99624e152d | |||
| 64d72cc053 | |||
| 07224d684d | |||
| c8f0de6aab | |||
| 503473c6b3 | |||
| 20036be50f | |||
| fdcf29d6e0 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -50,3 +50,5 @@ Voile.Fmod/runtimes/**/*.so*
|
||||
# DocFX
|
||||
.cache
|
||||
/**/_site/
|
||||
|
||||
*.dmp
|
||||
6
TODO.md
6
TODO.md
@@ -7,6 +7,7 @@
|
||||
- ~~TextDataResource providing a convenient wrapper around TOML data files.~~
|
||||
- ~~Add documentation for common classes.~~
|
||||
- ~~Hot reloading of resources.~~
|
||||
- Separate render and update thread.
|
||||
|
||||
## I/O
|
||||
|
||||
@@ -14,7 +15,8 @@
|
||||
- ~~Reimplement unloading.~~
|
||||
- Finalize ResourceManager and ResourceLoader APIs for 1.0.
|
||||
- Add async API for ResourceManager.
|
||||
- Virtual file system.
|
||||
- ~~Virtual file system.~~
|
||||
- Custom PAK format using the VFS implementation.
|
||||
- (stretch goal) Streamed resource loading.
|
||||
|
||||
## Serialization
|
||||
@@ -29,7 +31,7 @@
|
||||
- ~~API for drawing textured quads~~
|
||||
- ~~Camera API~~
|
||||
- Arbitrary mesh rendering API.
|
||||
- Create WebGPU renderer (StandardRenderer)
|
||||
- Create WebGPU renderer (StandardRenderSystem)
|
||||
|
||||
## Audio
|
||||
|
||||
|
||||
@@ -17,4 +17,4 @@ LinearVelocityRandom = 0.5
|
||||
ScaleBegin = 0.1
|
||||
ScaleEnd = 5.0
|
||||
ColorBegin = [255, 162, 0]
|
||||
ColorEnd = [64, 64, 64, 0]
|
||||
ColorEnd = [0, 0, 0, 0]
|
||||
|
||||
BIN
TestGame/Resources/sounds/test_sound_mono.ogg
Normal file
BIN
TestGame/Resources/sounds/test_sound_mono.ogg
Normal file
Binary file not shown.
@@ -5,17 +5,21 @@ using Voile.Systems.Particles;
|
||||
using System.Numerics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Voile.Rendering;
|
||||
using Voile.OpenAL;
|
||||
|
||||
public class TestGame : Game
|
||||
{
|
||||
public override string Name => "Jump Adventures 2";
|
||||
public override string Name => "Test Game";
|
||||
public override string ResourceRoot => "Resources/";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
InitializeSystemsDefault();
|
||||
|
||||
_audioSystem = new OpenALSystem();
|
||||
_particleSystem = new ParticleSystem();
|
||||
|
||||
AddSystemToUpdate(_audioSystem);
|
||||
AddSystemToUpdate(_particleSystem);
|
||||
}
|
||||
|
||||
@@ -34,6 +38,7 @@ public class TestGame : Game
|
||||
}
|
||||
|
||||
ResourceManager.TryLoad("icon.png", out _icon);
|
||||
ResourceManager.TryLoad("sounds/test_sound_mono.ogg", out _sound);
|
||||
|
||||
if (!ResourceManager.TryLoad("fire_effect.toml", out _fireEffect))
|
||||
{
|
||||
@@ -43,22 +48,18 @@ public class TestGame : Game
|
||||
|
||||
protected override void Ready()
|
||||
{
|
||||
Input.AddInputMapping("reload", new InputAction[] { new KeyInputAction(KeyboardKey.R) });
|
||||
Input.AddInputMapping("reload", new IInputAction[] { new KeyInputAction(KeyboardKey.R) });
|
||||
_emitterId = _particleSystem.CreateEmitter(Renderer.WindowSize / 2, _fireEffect);
|
||||
}
|
||||
|
||||
|
||||
protected override void Update(double deltaTime)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override void Render(double deltaTime)
|
||||
{
|
||||
if (Input.IsActionPressed("reload"))
|
||||
{
|
||||
ResourceManager.Reload();
|
||||
_particleSystem!.RestartEmitter(_emitterId);
|
||||
// ResourceManager.Reload();
|
||||
// _particleSystem!.RestartEmitter(_emitterId);
|
||||
_audioSystem.PlaySound(_sound.Value, 1.0f);
|
||||
}
|
||||
|
||||
if (Input.KeyboardKeyJustPressed(KeyboardKey.One))
|
||||
@@ -70,7 +71,10 @@ public class TestGame : Game
|
||||
{
|
||||
_particleSystem.SetEmitterPosition(_emitterId, Input.GetMousePosition());
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Render(double deltaTime)
|
||||
{
|
||||
Renderer.ClearBackground(Color.Black);
|
||||
foreach (var emitter in _particleSystem!.Emitters)
|
||||
{
|
||||
@@ -105,8 +109,10 @@ public class TestGame : Game
|
||||
}
|
||||
|
||||
[NotNull] private ParticleSystem _particleSystem;
|
||||
private OpenALSystem _audioSystem;
|
||||
private int _emitterId;
|
||||
private ResourceRef<ParticleEmitterSettingsResource> _fireEffect;
|
||||
private ResourceRef<Font> _font;
|
||||
private ResourceRef<Sound> _sound;
|
||||
private ResourceRef<Texture2d> _icon;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>disable</Nullable>
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../Voile/Voile.csproj" />
|
||||
<ProjectReference Include="../Voile.OpenAL/Voile.OpenAL.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
59
Voile.OpenAL/OpenALSystem.cs
Normal file
59
Voile.OpenAL/OpenALSystem.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using Voile.Audio;
|
||||
using Silk.NET.OpenAL;
|
||||
|
||||
namespace Voile.OpenAL;
|
||||
|
||||
public class OpenALSystem : AudioSystem
|
||||
{
|
||||
public OpenALSystem()
|
||||
{
|
||||
_al = AL.GetApi();
|
||||
_alc = ALContext.GetApi();
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
public override void PlaySound(Sound sound, float volume)
|
||||
{
|
||||
var buffer = CreateAlBuffer(sound.Buffer, sound.SampleRate, sound.Channel);
|
||||
var source = CreateAlSource(buffer);
|
||||
_al.SourcePlay(source);
|
||||
}
|
||||
|
||||
public override void Update(double deltaTime)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private unsafe void Init()
|
||||
{
|
||||
_device = _alc.OpenDevice("");
|
||||
_context = _alc.CreateContext(_device, null);
|
||||
_alc.MakeContextCurrent(_context);
|
||||
}
|
||||
|
||||
private uint CreateAlBuffer(ReadOnlyMemory<short> data, int sampleRate, SoundChannel channels)
|
||||
{
|
||||
var buffer = _al.GenBuffer();
|
||||
var format = channels == SoundChannel.Mono ? BufferFormat.Mono16 : BufferFormat.Stereo16;
|
||||
|
||||
unsafe
|
||||
{
|
||||
_al.BufferData(buffer, format, data.Pin().Pointer, data.Length, sampleRate);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private uint CreateAlSource(uint buffer)
|
||||
{
|
||||
var source = _al.GenSource();
|
||||
_al.SetSourceProperty(source, SourceInteger.Buffer, buffer);
|
||||
return source;
|
||||
}
|
||||
|
||||
private unsafe Device* _device;
|
||||
private ALContext _alc;
|
||||
private unsafe Context* _context;
|
||||
private AL _al;
|
||||
}
|
||||
17
Voile.OpenAL/Voile.OpenAL.csproj
Normal file
17
Voile.OpenAL/Voile.OpenAL.csproj
Normal file
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../Voile/Voile.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Silk.NET.OpenAL" Version="2.21.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
16
Voile.sln
16
Voile.sln
@@ -13,19 +13,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Folder", "Solution
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestGame", "TestGame\TestGame.csproj", "{DBA85D7B-0A91-405B-9078-5463F49AE47E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Voile.OpenAL", "Voile.OpenAL\Voile.OpenAL.csproj", "{3ABB7D30-4B64-43AD-A14F-E532B12AFC60}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{393AA04F-A0DE-42F2-AAEC-6B2DCFB7A852}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{393AA04F-A0DE-42F2-AAEC-6B2DCFB7A852}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{393AA04F-A0DE-42F2-AAEC-6B2DCFB7A852}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{393AA04F-A0DE-42F2-AAEC-6B2DCFB7A852}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DA4FDEDC-AA81-4336-844F-562F9E763974}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DA4FDEDC-AA81-4336-844F-562F9E763974}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DA4FDEDC-AA81-4336-844F-562F9E763974}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@@ -34,5 +29,12 @@ Global
|
||||
{DBA85D7B-0A91-405B-9078-5463F49AE47E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DBA85D7B-0A91-405B-9078-5463F49AE47E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DBA85D7B-0A91-405B-9078-5463F49AE47E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3ABB7D30-4B64-43AD-A14F-E532B12AFC60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3ABB7D30-4B64-43AD-A14F-E532B12AFC60}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3ABB7D30-4B64-43AD-A14F-E532B12AFC60}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3ABB7D30-4B64-43AD-A14F-E532B12AFC60}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Voile
|
||||
{
|
||||
public class AudioBus
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Voile
|
||||
{
|
||||
public class AudioEffect { }
|
||||
|
||||
public class AudioEffectReverb : AudioEffect
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,40 +1,7 @@
|
||||
using Voile;
|
||||
|
||||
namespace Voile.Audio;
|
||||
|
||||
public abstract class AudioSystem : IStartableSystem, IUpdatableSystem, IDisposable
|
||||
public abstract class AudioSystem : IUpdatableSystem
|
||||
{
|
||||
public void Start() => Initialize();
|
||||
|
||||
public void Update(double deltaTime) => Update();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Shutdown();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected abstract void Initialize();
|
||||
protected abstract void Update();
|
||||
protected abstract void Shutdown();
|
||||
// BUS
|
||||
public abstract void CreateBus(string busName);
|
||||
public abstract void SetBusVolume(string busName, float volume);
|
||||
public abstract float GetBusVolume(string busName);
|
||||
|
||||
// SOUND
|
||||
public abstract void PlaySound(Sound sound, float pitch, float volume, string bus = "Master");
|
||||
public void PlaySound(Sound sound, string bus = "Master") => PlaySound(sound, 1.0f, 1.0f, bus);
|
||||
|
||||
public SoundInstance CreateInstance(Sound sound)
|
||||
{
|
||||
var instance = new SoundInstance(this, sound);
|
||||
return instance;
|
||||
}
|
||||
|
||||
// EFFECTS
|
||||
public abstract void AddBusEffect<T>(T effect, string bus = "Master") where T : AudioEffect;
|
||||
|
||||
|
||||
private LehmerRandom _random = new LehmerRandom();
|
||||
public abstract void Update(double deltaTime);
|
||||
public abstract void PlaySound(Sound sound, float volume);
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
namespace Voile.Audio
|
||||
{
|
||||
/// <summary>
|
||||
/// Dummy audio system.
|
||||
/// </summary>
|
||||
public class DummyAudioSystem : AudioSystem
|
||||
{
|
||||
public override void AddBusEffect<T>(T effect, string bus = "Master")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public override void CreateBus(string busName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public override float GetBusVolume(string busName)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public override void SetBusVolume(string busName, float volume)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
protected override void Shutdown()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public override void PlaySound(Sound sound, float pitch, float volume, string bus = "Master")
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
namespace Voile.Audio
|
||||
{
|
||||
public class SoundInstance
|
||||
{
|
||||
protected virtual Sound Sound => _sound;
|
||||
public SoundInstance(AudioSystem backend, Sound sound)
|
||||
{
|
||||
_backend = backend;
|
||||
_sound = sound;
|
||||
}
|
||||
|
||||
public SoundInstance PitchVariation(float min, float max)
|
||||
{
|
||||
var random = new LehmerRandom();
|
||||
_pitch = (float)random.NextDouble() * (max - min) + min;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SoundInstance VolumeVariation(float min, float max)
|
||||
{
|
||||
var random = new LehmerRandom();
|
||||
_volume = (float)random.NextDouble() * (max - min) + min;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SoundInstance OnBus(string bus = "Master")
|
||||
{
|
||||
_bus = bus;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Play()
|
||||
{
|
||||
_backend.PlaySound(Sound, _pitch, _volume, _bus);
|
||||
}
|
||||
|
||||
private readonly AudioSystem _backend;
|
||||
private readonly Sound _sound;
|
||||
private string _bus = "Master";
|
||||
private float _pitch, _volume = 1.0f;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Voile.Audio;
|
||||
using Voile.Input;
|
||||
using Voile.Rendering;
|
||||
using Voile.Resources;
|
||||
using Voile.VFS;
|
||||
|
||||
namespace Voile
|
||||
{
|
||||
@@ -41,6 +43,8 @@ namespace Voile
|
||||
/// </summary>
|
||||
protected RenderSystem? Renderer { get; set; }
|
||||
|
||||
protected AudioSystem? AudioSystem { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of this game. Also used as a default window title.
|
||||
/// </summary>
|
||||
@@ -88,6 +92,7 @@ namespace Voile
|
||||
throw new NullReferenceException("No ResourceManager provided.");
|
||||
}
|
||||
|
||||
Mount();
|
||||
LoadResources();
|
||||
Ready();
|
||||
Run();
|
||||
@@ -239,6 +244,12 @@ namespace Voile
|
||||
Renderer?.Start(renderSettings);
|
||||
}
|
||||
|
||||
private void Mount()
|
||||
{
|
||||
var resourceRootMount = new FileSystemMountPoint(ResourceRoot);
|
||||
VirtualFileSystem.Mount(resourceRootMount);
|
||||
}
|
||||
|
||||
private List<IStartableSystem> _startableSystems = new();
|
||||
private List<IUpdatableSystem> _updatableSystems = new();
|
||||
private List<IUpdatableSystem> _renderableSystems = new();
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
namespace Voile.Input
|
||||
{
|
||||
public abstract class InputAction
|
||||
|
||||
public interface IInputAction
|
||||
{
|
||||
public abstract bool IsDown(InputSystem inputHandler);
|
||||
public abstract bool IsPressed(InputSystem inputHandler);
|
||||
public abstract bool IsReleased(InputSystem inputHandler);
|
||||
bool IsDown(InputSystem inputSystem);
|
||||
bool IsPressed(InputSystem inputSystem);
|
||||
bool IsReleased(InputSystem inputSystem);
|
||||
}
|
||||
|
||||
public class KeyInputAction : InputAction
|
||||
public struct KeyInputAction : IInputAction
|
||||
{
|
||||
public KeyboardKey Key => _keyboardKey;
|
||||
|
||||
@@ -15,19 +16,19 @@ namespace Voile.Input
|
||||
{
|
||||
_keyboardKey = keyboardKey;
|
||||
}
|
||||
public override bool IsDown(InputSystem inputHandler)
|
||||
public bool IsDown(InputSystem inputSystem)
|
||||
{
|
||||
return inputHandler.IsKeyboardKeyDown(_keyboardKey);
|
||||
return inputSystem.IsKeyboardKeyDown(_keyboardKey);
|
||||
}
|
||||
|
||||
public override bool IsPressed(InputSystem inputHandler)
|
||||
public bool IsPressed(InputSystem inputSystem)
|
||||
{
|
||||
return inputHandler.KeyboardKeyJustPressed(_keyboardKey);
|
||||
return inputSystem.KeyboardKeyJustPressed(_keyboardKey);
|
||||
}
|
||||
|
||||
public override bool IsReleased(InputSystem inputHandler)
|
||||
public bool IsReleased(InputSystem inputSystem)
|
||||
{
|
||||
return inputHandler.KeyboardKeyJustReleased(_keyboardKey);
|
||||
return inputSystem.KeyboardKeyJustReleased(_keyboardKey);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -12,11 +12,11 @@ namespace Voile.Input
|
||||
/// <summary>
|
||||
/// The list of all available input mappings, custom and built-in.
|
||||
/// </summary>
|
||||
public static IReadOnlyDictionary<string, List<InputAction>> InputMappings => inputMappings;
|
||||
public static IReadOnlyDictionary<string, List<IInputAction>> InputMappings => inputMappings;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
inputMappings = new Dictionary<string, List<InputAction>>();
|
||||
inputMappings = new Dictionary<string, List<IInputAction>>();
|
||||
CreateDefaultMappings();
|
||||
}
|
||||
|
||||
@@ -25,17 +25,82 @@ namespace Voile.Input
|
||||
public void Dispose() => GC.SuppressFinalize(this);
|
||||
|
||||
public bool Handled { get => _handled; set => _handled = value; }
|
||||
|
||||
public bool IsActionDown(string action)
|
||||
{
|
||||
List<IInputAction>? mappings;
|
||||
|
||||
if (TryGetInputMappings(action, out mappings))
|
||||
{
|
||||
foreach (IInputAction inputAction in mappings)
|
||||
if (inputAction.IsDown(this)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsActionPressed(string action)
|
||||
{
|
||||
List<IInputAction>? mappings;
|
||||
|
||||
if (TryGetInputMappings(action, out mappings))
|
||||
{
|
||||
foreach (IInputAction inputAction in mappings)
|
||||
if (inputAction.IsPressed(this)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsActionReleased(string action)
|
||||
{
|
||||
List<IInputAction>? mappings;
|
||||
|
||||
if (TryGetInputMappings(action, out mappings))
|
||||
{
|
||||
foreach (IInputAction inputAction in mappings)
|
||||
if (inputAction.IsReleased(this)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Vector2 GetInputDirection(KeyboardKey leftKey, KeyboardKey rightKey, KeyboardKey upKey, KeyboardKey downKey)
|
||||
{
|
||||
Vector2 dir = Vector2.Zero;
|
||||
|
||||
if (IsKeyboardKeyDown(leftKey))
|
||||
dir.X -= 1;
|
||||
if (IsKeyboardKeyDown(rightKey))
|
||||
dir.X += 1;
|
||||
if (IsKeyboardKeyDown(upKey))
|
||||
dir.Y -= 1;
|
||||
if (IsKeyboardKeyDown(downKey))
|
||||
dir.Y += 1;
|
||||
|
||||
return dir == Vector2.Zero ? Vector2.Zero : Vector2.Normalize(dir);
|
||||
}
|
||||
public Vector2 GetInputDirection(string leftAction, string rightAction, string upAction, string downAction)
|
||||
{
|
||||
Vector2 dir = Vector2.Zero;
|
||||
|
||||
if (IsActionDown(leftAction))
|
||||
dir.X -= 1;
|
||||
if (IsActionDown(rightAction))
|
||||
dir.X += 1;
|
||||
if (IsActionDown(upAction))
|
||||
dir.Y -= 1;
|
||||
if (IsActionDown(downAction))
|
||||
dir.Y += 1;
|
||||
|
||||
return dir == Vector2.Zero ? Vector2.Zero : Vector2.Normalize(dir);
|
||||
}
|
||||
|
||||
public abstract bool IsKeyboardKeyDown(KeyboardKey key);
|
||||
public abstract bool KeyboardKeyJustPressed(KeyboardKey key);
|
||||
public abstract bool KeyboardKeyJustReleased(KeyboardKey key);
|
||||
public abstract Vector2 GetInputDirection(KeyboardKey leftKey, KeyboardKey rightKey, KeyboardKey upKey, KeyboardKey downKey);
|
||||
public abstract Vector2 GetInputDirection(string leftAction, string rightAction, string upAction, string downAction);
|
||||
|
||||
public abstract int GetCharPressed();
|
||||
|
||||
public abstract bool IsActionDown(string action);
|
||||
public abstract bool IsActionPressed(string action);
|
||||
public abstract bool IsActionReleased(string action);
|
||||
|
||||
public abstract bool IsMouseButtonDown(MouseButton button);
|
||||
public abstract float GetMouseWheelMovement();
|
||||
public abstract void SetMousePosition(Vector2 position);
|
||||
@@ -46,48 +111,47 @@ namespace Voile.Input
|
||||
|
||||
public void SetAsHandled() => _handled = true;
|
||||
|
||||
public void AddInputMapping(string actionName, IEnumerable<InputAction> inputActions)
|
||||
public void AddInputMapping(string actionName, IEnumerable<IInputAction> inputActions)
|
||||
{
|
||||
inputMappings.Add(actionName, inputActions.ToList());
|
||||
}
|
||||
|
||||
private void CreateDefaultMappings()
|
||||
{
|
||||
AddInputMapping("up", new InputAction[] {
|
||||
AddInputMapping("up", [
|
||||
new KeyInputAction(KeyboardKey.W),
|
||||
new KeyInputAction(KeyboardKey.Up),
|
||||
});
|
||||
AddInputMapping("down", new InputAction[] {
|
||||
]);
|
||||
AddInputMapping("down", [
|
||||
new KeyInputAction(KeyboardKey.S),
|
||||
new KeyInputAction(KeyboardKey.Down),
|
||||
});
|
||||
AddInputMapping("left", new InputAction[] {
|
||||
]);
|
||||
AddInputMapping("left", [
|
||||
new KeyInputAction(KeyboardKey.A),
|
||||
new KeyInputAction(KeyboardKey.Left),
|
||||
});
|
||||
AddInputMapping("right", new InputAction[] {
|
||||
]);
|
||||
AddInputMapping("right", [
|
||||
new KeyInputAction(KeyboardKey.D),
|
||||
new KeyInputAction(KeyboardKey.Right),
|
||||
});
|
||||
]);
|
||||
}
|
||||
|
||||
protected bool TryGetInputMappings(string forAction, [NotNullWhen(true)] out IEnumerable<InputAction>? inputActions)
|
||||
protected bool TryGetInputMappings(string forAction, [NotNullWhen(true)] out List<IInputAction>? inputActions)
|
||||
{
|
||||
var contains = inputMappings.ContainsKey(forAction);
|
||||
inputActions = null;
|
||||
|
||||
if (!contains)
|
||||
if (inputMappings.TryGetValue(forAction, out var actions))
|
||||
{
|
||||
_logger.Error($"The action \"{forAction}\" is not present in the input mappings!");
|
||||
return false;
|
||||
inputActions = actions;
|
||||
return true;
|
||||
}
|
||||
|
||||
inputActions = inputMappings[forAction];
|
||||
_logger.Error($"The action \"{forAction}\" is not present in the input mappings!");
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static Dictionary<string, List<InputAction>> inputMappings = new();
|
||||
protected static Dictionary<string, List<IInputAction>> inputMappings = new();
|
||||
private bool _handled;
|
||||
private Logger _logger = new(nameof(InputSystem));
|
||||
}
|
||||
|
||||
@@ -13,39 +13,6 @@ namespace Voile.Input
|
||||
return Raylib.GetCharPressed();
|
||||
}
|
||||
|
||||
public override Vector2 GetInputDirection(KeyboardKey leftKey, KeyboardKey rightKey, KeyboardKey upKey, KeyboardKey downKey)
|
||||
{
|
||||
Vector2 dir = Vector2.Zero;
|
||||
|
||||
if (IsKeyboardKeyDown(leftKey))
|
||||
dir += new Vector2(-1, 0);
|
||||
if (IsKeyboardKeyDown(rightKey))
|
||||
dir += new Vector2(1, 0);
|
||||
if (IsKeyboardKeyDown(upKey))
|
||||
dir += new Vector2(0, -1);
|
||||
if (IsKeyboardKeyDown(downKey))
|
||||
dir += new Vector2(0, 1);
|
||||
|
||||
return dir == Vector2.Zero ? Vector2.Zero : Vector2.Normalize(dir);
|
||||
}
|
||||
|
||||
public override Vector2 GetInputDirection(string leftAction, string rightAction, string upAction, string downAction)
|
||||
{
|
||||
Vector2 dir = Vector2.Zero;
|
||||
|
||||
if (IsActionDown(leftAction))
|
||||
dir += new Vector2(-1, 0);
|
||||
if (IsActionDown(rightAction))
|
||||
dir += new Vector2(1, 0);
|
||||
if (IsActionDown(upAction))
|
||||
dir += new Vector2(0, -1);
|
||||
if (IsActionDown(downAction))
|
||||
dir += new Vector2(0, 1);
|
||||
|
||||
|
||||
return dir == Vector2.Zero ? Vector2.Zero : Vector2.Normalize(dir);
|
||||
}
|
||||
|
||||
public override Vector2 GetMousePosition()
|
||||
{
|
||||
return Raylib.GetMousePosition();
|
||||
@@ -61,45 +28,6 @@ namespace Voile.Input
|
||||
Raylib.HideCursor();
|
||||
}
|
||||
|
||||
public override bool IsActionDown(string action)
|
||||
{
|
||||
IEnumerable<InputAction>? mappings;
|
||||
|
||||
if (TryGetInputMappings(action, out mappings))
|
||||
{
|
||||
foreach (InputAction inputAction in mappings)
|
||||
if (inputAction.IsDown(this)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool IsActionPressed(string action)
|
||||
{
|
||||
IEnumerable<InputAction>? mappings;
|
||||
|
||||
if (TryGetInputMappings(action, out mappings))
|
||||
{
|
||||
foreach (InputAction inputAction in mappings)
|
||||
if (inputAction.IsPressed(this)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool IsActionReleased(string action)
|
||||
{
|
||||
IEnumerable<InputAction>? mappings;
|
||||
|
||||
if (TryGetInputMappings(action, out mappings))
|
||||
{
|
||||
foreach (InputAction inputAction in mappings)
|
||||
if (inputAction.IsReleased(this)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool IsKeyboardKeyDown(KeyboardKey key)
|
||||
{
|
||||
Raylib_cs.KeyboardKey rayKey = (Raylib_cs.KeyboardKey)key;
|
||||
|
||||
@@ -39,10 +39,10 @@ namespace Voile.Rendering
|
||||
ConfigFlags flags = 0;
|
||||
|
||||
// MSAA
|
||||
flags |= settings.Msaa == Msaa.Msaa4x ? ConfigFlags.FLAG_MSAA_4X_HINT : 0;
|
||||
flags |= settings.Msaa == Msaa.Msaa4x ? ConfigFlags.Msaa4xHint : 0;
|
||||
|
||||
// VSync
|
||||
flags |= settings.UseVSync ? ConfigFlags.FLAG_VSYNC_HINT : 0;
|
||||
flags |= settings.UseVSync ? ConfigFlags.VSyncHint : 0;
|
||||
|
||||
_fullscreen = settings.Fullscreen;
|
||||
|
||||
@@ -59,13 +59,13 @@ namespace Voile.Rendering
|
||||
|
||||
public override void CreateWindow(WindowSettings windowSettings)
|
||||
{
|
||||
Raylib.SetTraceLogLevel(TraceLogLevel.LOG_NONE);
|
||||
Raylib.SetTraceLogLevel(TraceLogLevel.None);
|
||||
|
||||
_defaultWindowSettings = windowSettings;
|
||||
|
||||
_windowSize = windowSettings.Size;
|
||||
ConfigFlags windowFlags = 0;
|
||||
windowFlags |= windowSettings.Resizable ? ConfigFlags.FLAG_WINDOW_RESIZABLE : 0;
|
||||
windowFlags |= windowSettings.Resizable ? ConfigFlags.ResizableWindow : 0;
|
||||
|
||||
_windowTitle = windowSettings.Title;
|
||||
|
||||
@@ -180,10 +180,10 @@ namespace Voile.Rendering
|
||||
{
|
||||
Raylib.DrawRectanglePro(new Rectangle()
|
||||
{
|
||||
x = transformPosition.X,
|
||||
y = transformPosition.Y,
|
||||
width = size.X,
|
||||
height = size.Y
|
||||
X = transformPosition.X,
|
||||
Y = transformPosition.Y,
|
||||
Width = size.X,
|
||||
Height = size.Y
|
||||
}, transformPivot, transformRotation, VoileColorToRaylibColor(color));
|
||||
}
|
||||
|
||||
@@ -252,28 +252,17 @@ namespace Voile.Rendering
|
||||
return Raylib.GetCurrentMonitor();
|
||||
}
|
||||
|
||||
private unsafe void LoadFont(Font font)
|
||||
private void LoadFont(Font font)
|
||||
{
|
||||
Raylib_cs.Font fontRay;
|
||||
|
||||
ReadOnlySpan<char> ext = ".ttf"; // TODO: don't use a hardcoded extension.
|
||||
Span<byte> extBytes = new byte[Encoding.Default.GetByteCount(ext) + 1];
|
||||
Encoding.Default.GetBytes(ext, extBytes);
|
||||
int fontChars = 2048; // TODO: control this dynamically to not load the entire font.
|
||||
string ext = ".ttf"; // TODO: don't use a hardcoded extension.
|
||||
int fontChars = 250; // TODO: control this dynamically to not load the entire font.
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* extP = extBytes)
|
||||
{
|
||||
fixed (byte* bufferP = font.Buffer)
|
||||
{
|
||||
fontRay = Raylib.LoadFontFromMemory((sbyte*)extP, bufferP, (int)font.BufferSize, font.Size, null, fontChars);
|
||||
}
|
||||
}
|
||||
}
|
||||
fontRay = Raylib.LoadFontFromMemory(ext, font.Buffer, font.Size, null, fontChars);
|
||||
|
||||
Raylib.GenTextureMipmaps(ref fontRay.texture);
|
||||
Raylib.SetTextureFilter(fontRay.texture, TextureFilter.TEXTURE_FILTER_BILINEAR);
|
||||
Raylib.GenTextureMipmaps(ref fontRay.Texture);
|
||||
Raylib.SetTextureFilter(fontRay.Texture, TextureFilter.Bilinear);
|
||||
|
||||
_fontPool.Add(fontRay);
|
||||
|
||||
@@ -288,14 +277,15 @@ namespace Voile.Rendering
|
||||
{
|
||||
fixed (void* dataPtr = texture.Buffer)
|
||||
{
|
||||
image.data = dataPtr;
|
||||
image.Data = dataPtr;
|
||||
}
|
||||
}
|
||||
|
||||
image.width = texture.Width;
|
||||
image.height = texture.Height;
|
||||
image.mipmaps = texture.Mipmaps;
|
||||
image.format = (PixelFormat)texture.Format;
|
||||
|
||||
image.Width = texture.Width;
|
||||
image.Height = texture.Height;
|
||||
image.Mipmaps = texture.Mipmaps;
|
||||
image.Format = (PixelFormat)texture.Format;
|
||||
|
||||
Texture2D rayTexture;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Voile.Resources.DataReaders;
|
||||
/// <summary>
|
||||
/// Reads key/value data from a TOML file.
|
||||
/// </summary>
|
||||
public class TomlDataReader : IStreamDataReader, IDataValidator, IStreamKeyValueGetter, IDisposable
|
||||
public class TomlDataReader : IStreamDataReader, IDataValidator, IStreamKeyValueGetter
|
||||
{
|
||||
public string ExpectedHeader { get; private set; } = string.Empty;
|
||||
public TomlDataReader(string expectedHeader)
|
||||
@@ -17,14 +17,7 @@ public class TomlDataReader : IStreamDataReader, IDataValidator, IStreamKeyValue
|
||||
|
||||
public void Read(Stream data)
|
||||
{
|
||||
if (data is not FileStream fs)
|
||||
{
|
||||
throw new ArgumentException("Toml data reader only supports file streams.");
|
||||
}
|
||||
|
||||
_fs = fs;
|
||||
|
||||
using (var reader = new StreamReader(_fs))
|
||||
using (var reader = new StreamReader(data))
|
||||
{
|
||||
_table = TOML.Parse(reader);
|
||||
_valid = _table.HasKey(ExpectedHeader);
|
||||
@@ -231,12 +224,6 @@ public class TomlDataReader : IStreamDataReader, IDataValidator, IStreamKeyValue
|
||||
|
||||
public bool Valid() => _valid;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_fs?.Dispose();
|
||||
}
|
||||
|
||||
private TomlTable? _table;
|
||||
private FileStream? _fs;
|
||||
private bool _valid;
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
|
||||
using Voile.VFS;
|
||||
|
||||
namespace Voile.Resources;
|
||||
|
||||
public class FontLoader : ResourceLoader<Font>
|
||||
@@ -11,9 +13,14 @@ public class FontLoader : ResourceLoader<Font>
|
||||
|
||||
protected override Font LoadResource(string path)
|
||||
{
|
||||
byte[] fileBuffer = File.ReadAllBytes(path);
|
||||
using Stream stream = VirtualFileSystem.Read(path);
|
||||
|
||||
byte[] fileBuffer = new byte[stream.Length];
|
||||
int bytesRead = stream.Read(fileBuffer, 0, fileBuffer.Length);
|
||||
var result = new Font(path, fileBuffer);
|
||||
result.BufferSize = fileBuffer.Length;
|
||||
|
||||
result.BufferSize = bytesRead;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,6 @@ namespace Voile.Resources
|
||||
var resource = LoadResource(path);
|
||||
var guid = Guid.NewGuid();
|
||||
|
||||
|
||||
var loadedResources = ResourceManager.LoadedResources;
|
||||
var oldResourceGuid = loadedResources.FirstOrDefault(loadedResource => loadedResource.Value.Path == path).Key;
|
||||
|
||||
@@ -46,6 +45,7 @@ namespace Voile.Resources
|
||||
{
|
||||
foreach (var loadedResource in ResourceManager.LoadedResources)
|
||||
{
|
||||
if (loadedResource.Value is not T) continue;
|
||||
Load(loadedResource.Value.Path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using StbVorbisSharp;
|
||||
using Voile.VFS;
|
||||
|
||||
namespace Voile.Resources
|
||||
{
|
||||
@@ -6,7 +7,7 @@ namespace Voile.Resources
|
||||
{
|
||||
public override IEnumerable<string> SupportedExtensions => new string[]
|
||||
{
|
||||
"ogg"
|
||||
".ogg"
|
||||
};
|
||||
|
||||
protected override Sound LoadResource(string path)
|
||||
@@ -14,9 +15,11 @@ namespace Voile.Resources
|
||||
Vorbis vorbis;
|
||||
Sound result;
|
||||
|
||||
var fileBuffer = File.ReadAllBytes(path);
|
||||
vorbis = Vorbis.FromMemory(fileBuffer);
|
||||
using var stream = VirtualFileSystem.Read(path);
|
||||
byte[] fileBuffer = new byte[stream.Length];
|
||||
int bytesRead = stream.Read(fileBuffer, 0, fileBuffer.Length);
|
||||
|
||||
vorbis = Vorbis.FromMemory(fileBuffer);
|
||||
vorbis.SubmitBuffer();
|
||||
|
||||
if (vorbis.Decoded == 0)
|
||||
@@ -27,18 +30,9 @@ namespace Voile.Resources
|
||||
|
||||
var audioShort = vorbis.SongBuffer;
|
||||
int length = vorbis.Decoded * vorbis.Channels;
|
||||
byte[] audioData = new byte[length * 2];
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
if (i * 2 >= audioData.Length) break;
|
||||
|
||||
var b1 = (byte)(audioShort[i] >> 8);
|
||||
var b2 = (byte)(audioShort[i] & 256);
|
||||
|
||||
audioData[i * 2] = b2;
|
||||
audioData[i * 2 + 1] = b1;
|
||||
}
|
||||
short[] audioData = new short[length];
|
||||
Array.Copy(audioShort, audioData, length);
|
||||
|
||||
result = new Sound(path, audioData)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Voile.Resources;
|
||||
using StbImageSharp;
|
||||
using Voile.VFS;
|
||||
|
||||
namespace Voile
|
||||
{
|
||||
@@ -18,7 +19,7 @@ namespace Voile
|
||||
protected override Texture2d LoadResource(string path)
|
||||
{
|
||||
ImageResult image;
|
||||
using (var stream = File.OpenRead(path))
|
||||
using (var stream = VirtualFileSystem.Read(path))
|
||||
{
|
||||
image = ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
using Voile.Utils;
|
||||
using Voile.VFS;
|
||||
|
||||
namespace Voile.Resources
|
||||
{
|
||||
@@ -92,10 +92,7 @@ namespace Voile.Resources
|
||||
T? resource = default;
|
||||
result = null;
|
||||
|
||||
var fullPath = Path.Combine(ResourceRoot, path);
|
||||
|
||||
// TODO: don't check if file doesn't exist in the file system, make it more generic but for now it's fine
|
||||
if (!File.Exists(fullPath))
|
||||
if (!VirtualFileSystem.FileExists(path))
|
||||
{
|
||||
_logger.Error($"File at \"{path}\" doesn't exist!");
|
||||
return false;
|
||||
@@ -108,7 +105,7 @@ namespace Voile.Resources
|
||||
return false;
|
||||
}
|
||||
|
||||
var extension = Path.GetExtension(fullPath);
|
||||
var extension = Path.GetExtension(path);
|
||||
var hasExtension = loader.SupportedExtensions.Any(ext => ext == extension);
|
||||
|
||||
if (!hasExtension)
|
||||
@@ -116,7 +113,7 @@ namespace Voile.Resources
|
||||
_logger.Error($"Extension {extension} is not supported!");
|
||||
}
|
||||
|
||||
var resourceGuid = loader.Load(fullPath);
|
||||
var resourceGuid = loader.Load(path);
|
||||
|
||||
if (!GetResource(resourceGuid, out T? loadedResource))
|
||||
{
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
namespace Voile
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents raw audio samples.
|
||||
/// Represents raw audio samples in 16-bit PCM format.
|
||||
/// </summary>
|
||||
public class Sound : Resource
|
||||
{
|
||||
public SoundChannel Channel { get; set; }
|
||||
public int SampleRate { get; set; }
|
||||
|
||||
public byte[]? Buffer { get; private set; }
|
||||
public short[]? Buffer { get; private set; }
|
||||
public long BufferSize { get; set; }
|
||||
|
||||
public Sound(string path, byte[] buffer) : base(path)
|
||||
public Sound(string path, short[] buffer) : base(path)
|
||||
{
|
||||
Buffer = buffer;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Numerics;
|
||||
using Voile.Resources;
|
||||
using Voile.Resources.DataReaders;
|
||||
using Voile.Utils;
|
||||
using Voile.VFS;
|
||||
|
||||
namespace Voile.Systems.Particles;
|
||||
|
||||
@@ -62,28 +62,26 @@ public class ParticleEmitterSettingsResourceLoader : ResourceLoader<ParticleEmit
|
||||
{
|
||||
var settings = new ParticleEmitterSettings();
|
||||
|
||||
using (var reader = new TomlDataReader("ParticleEmitterSettings"))
|
||||
{
|
||||
reader.Read(File.Open(path, FileMode.Open));
|
||||
var reader = new TomlDataReader("ParticleEmitterSettings");
|
||||
reader.Read(VirtualFileSystem.Read(path));
|
||||
|
||||
settings.Local = reader.GetBool("Local", true);
|
||||
settings.MaxParticles = reader.GetInt("MaxParticles");
|
||||
settings.EmitRadius = reader.GetInt("EmitRadius");
|
||||
settings.LifeTime = reader.GetFloat("LifeTime", 1.0f);
|
||||
settings.Explosiveness = reader.GetFloat("Explosiveness");
|
||||
settings.Direction = reader.GetVector2("Direction", Vector2.Zero);
|
||||
settings.LinearVelocity = reader.GetFloat("LinearVelocity", 980f);
|
||||
settings.AngularVelocity = reader.GetFloat("AngularVelocity");
|
||||
settings.AngularVelocityRandom = reader.GetFloat("AngularVelocityRandom", 1.0f);
|
||||
settings.LinearVelocityRandom = reader.GetFloat("LinearVelocityRandom", 1.0f);
|
||||
settings.Gravity = reader.GetVector2("Gravity", Vector2.UnitY * 980f);
|
||||
settings.ScaleBegin = reader.GetFloat("ScaleBegin");
|
||||
settings.ScaleEnd = reader.GetFloat("ScaleEnd");
|
||||
settings.ColorBegin = reader.GetColor("ColorBegin", Color.White);
|
||||
settings.ColorEnd = reader.GetColor("ColorEnd", Color.Black);
|
||||
settings.LinearVelocityDamping = reader.GetFloat("LinearVelocityDamping", 1.0f);
|
||||
settings.AngularVelocityDamping = reader.GetFloat("AngularVelocityDamping", 1.0f);
|
||||
}
|
||||
settings.Local = reader.GetBool("Local", true);
|
||||
settings.MaxParticles = reader.GetInt("MaxParticles");
|
||||
settings.EmitRadius = reader.GetInt("EmitRadius");
|
||||
settings.LifeTime = reader.GetFloat("LifeTime", 1.0f);
|
||||
settings.Explosiveness = reader.GetFloat("Explosiveness");
|
||||
settings.Direction = reader.GetVector2("Direction", Vector2.Zero);
|
||||
settings.LinearVelocity = reader.GetFloat("LinearVelocity", 980f);
|
||||
settings.AngularVelocity = reader.GetFloat("AngularVelocity");
|
||||
settings.AngularVelocityRandom = reader.GetFloat("AngularVelocityRandom", 1.0f);
|
||||
settings.LinearVelocityRandom = reader.GetFloat("LinearVelocityRandom", 1.0f);
|
||||
settings.Gravity = reader.GetVector2("Gravity", Vector2.UnitY * 980f);
|
||||
settings.ScaleBegin = reader.GetFloat("ScaleBegin");
|
||||
settings.ScaleEnd = reader.GetFloat("ScaleEnd");
|
||||
settings.ColorBegin = reader.GetColor("ColorBegin", Color.White);
|
||||
settings.ColorEnd = reader.GetColor("ColorEnd", Color.Black);
|
||||
settings.LinearVelocityDamping = reader.GetFloat("LinearVelocityDamping", 1.0f);
|
||||
settings.AngularVelocityDamping = reader.GetFloat("AngularVelocityDamping", 1.0f);
|
||||
|
||||
return new ParticleEmitterSettingsResource(path, settings);
|
||||
}
|
||||
|
||||
69
Voile/Source/VFS/FileSystemMountPoint.cs
Normal file
69
Voile/Source/VFS/FileSystemMountPoint.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using Voile.Utils;
|
||||
|
||||
namespace Voile.VFS;
|
||||
|
||||
/// <summary>
|
||||
/// A file in the OS file system.
|
||||
/// </summary>
|
||||
public class FileSystemFile : VirtualFile
|
||||
{
|
||||
public FileSystemFile(string path)
|
||||
{
|
||||
_fsPath = path;
|
||||
}
|
||||
|
||||
public override Stream GetStream()
|
||||
{
|
||||
return new FileStream(_fsPath, FileMode.Open, FileAccess.Read);
|
||||
}
|
||||
|
||||
private string _fsPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="IVirtualMountPoint"/> implementation for an OS file system.
|
||||
/// </summary>
|
||||
public class FileSystemMountPoint : IVirtualMountPoint
|
||||
{
|
||||
public int Order => int.MaxValue;
|
||||
|
||||
public FileSystemMountPoint(string path)
|
||||
{
|
||||
_fsPath = path;
|
||||
}
|
||||
|
||||
public void Mount()
|
||||
{
|
||||
_logger.Info($"Mounting from file system path \"{_fsPath}\".");
|
||||
|
||||
int rootLength = _fsPath.Length;
|
||||
var files =
|
||||
Directory.GetFiles(_fsPath, "*", SearchOption.AllDirectories)
|
||||
.Select(p => p.Remove(0, rootLength))
|
||||
.ToList();
|
||||
|
||||
foreach (string file in files)
|
||||
{
|
||||
var relativePath = NormalizePath(file);
|
||||
var fullPath = NormalizePath(Path.Combine(_fsPath, file));
|
||||
_files[relativePath] = new FileSystemFile(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
public VirtualFile GetFile(string path) => _files[path];
|
||||
|
||||
public IDictionary<string, VirtualFile> GetFiles() => _files;
|
||||
|
||||
public bool HasFile(string path) => _files.ContainsKey(path);
|
||||
|
||||
private string NormalizePath(string path)
|
||||
{
|
||||
return path
|
||||
.Replace(@"\\", @"\")
|
||||
.Replace(@"\", @"/");
|
||||
}
|
||||
|
||||
private Logger _logger = new(nameof(FileSystemMountPoint));
|
||||
private string _fsPath;
|
||||
private Dictionary<string, VirtualFile> _files = new();
|
||||
}
|
||||
6
Voile/Source/VFS/IVirtualFile.cs
Normal file
6
Voile/Source/VFS/IVirtualFile.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Voile.VFS;
|
||||
|
||||
public abstract class VirtualFile
|
||||
{
|
||||
public abstract Stream GetStream();
|
||||
}
|
||||
33
Voile/Source/VFS/IVirtualMountPoint.cs
Normal file
33
Voile/Source/VFS/IVirtualMountPoint.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
namespace Voile.VFS;
|
||||
|
||||
/// <summary>
|
||||
/// A virtual mounting point.
|
||||
/// </summary>
|
||||
public interface IVirtualMountPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// Order of mounting for this mount point. Lower values indicate higher priority for lookup.
|
||||
/// </summary>
|
||||
int Order { get; }
|
||||
/// <summary>
|
||||
/// Mounts this <see cref="IVirtualMountPoint"/>.
|
||||
/// </summary>
|
||||
void Mount();
|
||||
/// <summary>
|
||||
/// Gets a file.
|
||||
/// </summary>
|
||||
/// <param name="path">Relative path to the file.</param>
|
||||
/// <returns>An instance of <see cref="VirtualFile"/> if the file exists; otherwise, an exception is thrown.</returns>
|
||||
VirtualFile GetFile(string path);
|
||||
/// <summary>
|
||||
/// Gets all files available at this <see cref="IVirtualMountPoint"/>.
|
||||
/// </summary>
|
||||
/// <returns>A dictionary mapping a relative path to an instance of a <see cref="VirtualFile"/>.</returns>
|
||||
IDictionary<string, VirtualFile> GetFiles();
|
||||
/// <summary>
|
||||
/// Determines whether a file exists at the given relative path within this mount point.
|
||||
/// </summary>
|
||||
/// <param name="path">The relative path of the file to check.</param>
|
||||
/// <returns><c>true</c> if the file exists; otherwise, <c>false</c>.</returns>
|
||||
bool HasFile(string path);
|
||||
}
|
||||
59
Voile/Source/VFS/VirtualFileSystem.cs
Normal file
59
Voile/Source/VFS/VirtualFileSystem.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
namespace Voile.VFS;
|
||||
|
||||
/// <summary>
|
||||
/// A virtual file system that provides an abstract interface for manipulating files from various sources.
|
||||
/// </summary>
|
||||
public static class VirtualFileSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Mounts a <see cref="IVirtualMountPoint"/>.
|
||||
/// This will make files available for this mount point accessible.
|
||||
/// </summary>
|
||||
/// <param name="mountPoint"></param>
|
||||
public static void Mount(IVirtualMountPoint mountPoint)
|
||||
{
|
||||
mountPoint.Mount();
|
||||
_mounts.Add(mountPoint);
|
||||
|
||||
_mounts = _mounts.OrderBy(mount => mount.Order).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if file exists or not.
|
||||
/// </summary>
|
||||
/// <param name="path">Relative path to the file.</param>
|
||||
/// <returns></returns>
|
||||
public static bool FileExists(string path)
|
||||
{
|
||||
return _mounts.Any(mount => mount.HasFile(path));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="VirtualFile"/> from path.
|
||||
/// </summary>
|
||||
/// <param name="path">Relative path to the file.</param>
|
||||
/// <returns>A virtual file at the path.</returns>
|
||||
public static VirtualFile GetFile(string path)
|
||||
{
|
||||
var mount = _mounts.FirstOrDefault(mount => mount.HasFile(path));
|
||||
if (mount == null)
|
||||
{
|
||||
throw new FileNotFoundException($"File \"{path}\" was not found in any mounted point.");
|
||||
}
|
||||
|
||||
return mount.GetFile(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a file.
|
||||
/// </summary>
|
||||
/// <param name="path">Relative path to the file.</param>
|
||||
/// <returns>A readable stream.</returns>
|
||||
public static Stream Read(string path)
|
||||
{
|
||||
var file = GetFile(path);
|
||||
return file.GetStream();
|
||||
}
|
||||
|
||||
private static List<IVirtualMountPoint> _mounts = new();
|
||||
}
|
||||
@@ -12,18 +12,14 @@
|
||||
<PackageReference Include="Silk.NET.WebGPU" Version="2.20.0" />
|
||||
<PackageReference Include="Silk.NET.WebGPU.Native.WGPU" Version="2.20.0" />
|
||||
<PackageReference Include="Silk.NET.Windowing" Version="2.20.0" />
|
||||
<PackageReference Include="SoLoud.NET" Version="2020.2.7.1" />
|
||||
<PackageReference Include="Tommy" Version="3.1.2" />
|
||||
<PackageReference Include="Voile.Fmod" Version="0.2.2.8" />
|
||||
<PackageReference Include="ImGui.NET" Version="1.89.4" />
|
||||
<PackageReference Include="Raylib-cs" Version="4.2.0.1" />
|
||||
<PackageReference Include="Raylib-cs" Version="6.1.1" />
|
||||
<PackageReference Include="SharpFont" Version="4.0.1" />
|
||||
<PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta19" />
|
||||
<PackageReference Include="StbImageSharp" Version="2.27.13" />
|
||||
<PackageReference Include="StbVorbisSharp" Version="1.22.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="BuildFmod" BeforeTargets="BeforeBuild">
|
||||
<MSBuild Projects="../Voile.Fmod/Voile.Fmod.csproj" Targets="Restore;Build" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user