From 3ac5d36982bf0d602aad127c45cf852ec4e1b031 Mon Sep 17 00:00:00 2001 From: Miron Alexandru Date: Wed, 11 Sep 2024 01:06:38 +0300 Subject: [PATCH] SFML.Net 3 --- .editorconfig | 21 +- .github/workflows/ci.yml | 6 +- SFML.CodeStyle.props | 2 +- SFML.Module.props | 2 +- SFML.NuGet.props | 2 +- SFML.sln | 19 + examples/netcore/Program.cs | 55 ++- .../netcore/Properties/launchSettings.json | 8 + examples/opengl/OpenGL.cs | 6 +- .../opengl/Properties/launchSettings.json | 8 + examples/shader/Shader.cs | 15 +- examples/visualbasic/OpenGL.vb | 6 +- examples/window/Program.cs | 2 +- src/SFML.Audio/Cone.cs | 64 ++++ src/SFML.Audio/EffectProcessor.cs | 91 +++++ src/SFML.Audio/Listener.cs | 35 ++ src/SFML.Audio/Music.cs | 301 +++++++++++++++- src/SFML.Audio/Properties/launchSettings.json | 1 + src/SFML.Audio/Sound.cs | 279 ++++++++++++++- src/SFML.Audio/SoundBuffer.cs | 41 ++- src/SFML.Audio/SoundBufferRecorder.cs | 6 +- src/SFML.Audio/SoundChannel.cs | 40 +++ src/SFML.Audio/SoundRecorder.cs | 51 ++- src/SFML.Audio/SoundStream.cs | 309 +++++++++++++++- src/SFML.Graphics/CircleShape.cs | 19 + src/SFML.Graphics/CoordinateType.cs | 13 + src/SFML.Graphics/Font.cs | 22 ++ src/SFML.Graphics/Glsl.cs | 18 +- src/SFML.Graphics/IRenderTarget.cs | 59 ++- src/SFML.Graphics/Image.cs | 56 ++- src/SFML.Graphics/PrimitiveType.cs | 19 +- src/SFML.Graphics/Rect.cs | 241 ++++--------- src/SFML.Graphics/RectangleShape.cs | 27 +- src/SFML.Graphics/RenderStates.cs | 39 +- src/SFML.Graphics/RenderTexture.cs | 93 +++-- src/SFML.Graphics/RenderWindow.cs | 109 ++++-- src/SFML.Graphics/Shader.cs | 175 +-------- src/SFML.Graphics/Shape.cs | 16 + src/SFML.Graphics/Sprite.cs | 16 +- src/SFML.Graphics/StencilMode.cs | 197 ++++++++++ src/SFML.Graphics/Text.cs | 43 +-- src/SFML.Graphics/Texture.cs | 103 +++--- src/SFML.Graphics/Transform.cs | 86 +---- src/SFML.Graphics/VertexBuffer.cs | 3 + src/SFML.Graphics/View.cs | 32 +- src/SFML.System/Angle.cs | 335 ++++++++++++++++++ src/SFML.System/Clock.cs | 48 +++ src/SFML.System/SharedLibName.cs | 4 +- src/SFML.System/StreamAdaptor.cs | 10 +- src/SFML.System/Time.cs | 2 +- src/SFML.System/Vector2.cs | 319 ++++++++++++++++- src/SFML.System/Vector3.cs | 70 +++- src/SFML.Window/Context.cs | 5 + src/SFML.Window/ContextSettings.cs | 26 +- src/SFML.Window/Cursor.cs | 8 +- src/SFML.Window/Event.cs | 100 ++---- src/SFML.Window/EventArgs.cs | 172 +++------ src/SFML.Window/Joystick.cs | 3 + src/SFML.Window/Keyboard.cs | 47 +-- src/SFML.Window/Mouse.cs | 19 +- src/SFML.Window/Sensor.cs | 13 +- src/SFML.Window/Touch.cs | 1 + src/SFML.Window/VideoMode.cs | 126 ++++++- src/SFML.Window/Vulkan.cs | 1 + src/SFML.Window/Window.cs | 38 +- src/SFML.Window/WindowBase.cs | 76 ++-- test/SFML.System.Test/Angle.test.cs | 266 ++++++++++++++ test/SFML.System.Test/SFML.System.Test.csproj | 34 ++ test/SFML.System.Test/Vector2f.test.cs | 282 +++++++++++++++ test/SFML.System.Test/Vector2i.test.cs | 141 ++++++++ test/SFML.System.Test/Vector2u.test.cs | 131 +++++++ 71 files changed, 3958 insertions(+), 1075 deletions(-) create mode 100644 examples/netcore/Properties/launchSettings.json create mode 100644 examples/opengl/Properties/launchSettings.json create mode 100644 src/SFML.Audio/Cone.cs create mode 100644 src/SFML.Audio/EffectProcessor.cs create mode 100644 src/SFML.Audio/Properties/launchSettings.json create mode 100644 src/SFML.Audio/SoundChannel.cs create mode 100644 src/SFML.Graphics/CoordinateType.cs create mode 100644 src/SFML.Graphics/StencilMode.cs create mode 100644 src/SFML.System/Angle.cs create mode 100644 test/SFML.System.Test/Angle.test.cs create mode 100644 test/SFML.System.Test/SFML.System.Test.csproj create mode 100644 test/SFML.System.Test/Vector2f.test.cs create mode 100644 test/SFML.System.Test/Vector2i.test.cs create mode 100644 test/SFML.System.Test/Vector2u.test.cs diff --git a/.editorconfig b/.editorconfig index 60c11a10..93d1f029 100644 --- a/.editorconfig +++ b/.editorconfig @@ -17,6 +17,22 @@ indent_style = space indent_size = 4 insert_final_newline = false trim_trailing_whitespace = true +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:warning +csharp_style_namespace_declarations = file_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = false:warning +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_expression_bodied_methods = true:warning +csharp_style_expression_bodied_constructors = true:warning +csharp_style_expression_bodied_operators = true:warning +csharp_style_expression_bodied_properties = true:warning +csharp_style_expression_bodied_indexers = true:warning +csharp_style_expression_bodied_accessors = true:warning +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_indent_labels = no_change ######################### # File Extension Settings @@ -350,4 +366,7 @@ dotnet_diagnostic.IDE0130.severity = suggestion dotnet_diagnostic.IDE0060.severity = suggestion # CA1805: Do not initialize unnecessarily -dotnet_diagnostic.CA1805.severity = warning \ No newline at end of file +dotnet_diagnostic.CA1805.severity = warning +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 833f62cd..582abe03 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,6 +46,10 @@ jobs: run: dotnet restore - name: Build SFML.Net run: dotnet build --configuration Release --no-restore + # FIXME: Run tests on .NET 7 / 8 / 9 + - name: Test SFML.Net + if: matrix.dotnet.name == '.NET 6' + run: dotnet test --configuration Release --no-restore - name: Pack SFML.Net run: dotnet pack --configuration Release -o Publish @@ -58,4 +62,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: SFML.Net (${{ matrix.platform.name }} ${{ matrix.dotnet.name }}) - path: Publish/SFML.*.*.nupkg + path: Publish/SFML.*.*.nupkg \ No newline at end of file diff --git a/SFML.CodeStyle.props b/SFML.CodeStyle.props index 110d2673..1f81b68a 100644 --- a/SFML.CodeStyle.props +++ b/SFML.CodeStyle.props @@ -1,7 +1,7 @@ - + true true diff --git a/SFML.Module.props b/SFML.Module.props index f6a6da25..6117e69b 100644 --- a/SFML.Module.props +++ b/SFML.Module.props @@ -18,7 +18,7 @@ - + diff --git a/SFML.NuGet.props b/SFML.NuGet.props index 55b7df31..5eec42d3 100644 --- a/SFML.NuGet.props +++ b/SFML.NuGet.props @@ -5,7 +5,7 @@ true - 2.6.0 + 3.0.0-alpha1 Laurent Gomila sfml sfml.net Copyright © Laurent Gomila diff --git a/SFML.sln b/SFML.sln index f25045b8..c062dbb2 100644 --- a/SFML.sln +++ b/SFML.sln @@ -18,6 +18,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{0A84335E-59C1-4969-9F5F-4D42DC7F2BFC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SFML.System.Test", "test\SFML.System.Test\SFML.System.Test.csproj", "{90D86010-580C-4D2A-8AD1-C18CD982C5A0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -88,10 +92,25 @@ Global {88DD6B5D-3013-4737-A77C-EC2563FCED38}.Release|x64.Build.0 = Release|x64 {88DD6B5D-3013-4737-A77C-EC2563FCED38}.Release|x86.ActiveCfg = Release|x86 {88DD6B5D-3013-4737-A77C-EC2563FCED38}.Release|x86.Build.0 = Release|x86 + {90D86010-580C-4D2A-8AD1-C18CD982C5A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {90D86010-580C-4D2A-8AD1-C18CD982C5A0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {90D86010-580C-4D2A-8AD1-C18CD982C5A0}.Debug|x64.ActiveCfg = Debug|x64 + {90D86010-580C-4D2A-8AD1-C18CD982C5A0}.Debug|x64.Build.0 = Debug|x64 + {90D86010-580C-4D2A-8AD1-C18CD982C5A0}.Debug|x86.ActiveCfg = Debug|x86 + {90D86010-580C-4D2A-8AD1-C18CD982C5A0}.Debug|x86.Build.0 = Debug|x86 + {90D86010-580C-4D2A-8AD1-C18CD982C5A0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {90D86010-580C-4D2A-8AD1-C18CD982C5A0}.Release|Any CPU.Build.0 = Release|Any CPU + {90D86010-580C-4D2A-8AD1-C18CD982C5A0}.Release|x64.ActiveCfg = Release|x64 + {90D86010-580C-4D2A-8AD1-C18CD982C5A0}.Release|x64.Build.0 = Release|x64 + {90D86010-580C-4D2A-8AD1-C18CD982C5A0}.Release|x86.ActiveCfg = Release|x86 + {90D86010-580C-4D2A-8AD1-C18CD982C5A0}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {90D86010-580C-4D2A-8AD1-C18CD982C5A0} = {0A84335E-59C1-4969-9F5F-4D42DC7F2BFC} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {384AFDD8-7EEF-462A-B496-B5F71D0EEF1D} EndGlobalSection diff --git a/examples/netcore/Program.cs b/examples/netcore/Program.cs index 08351421..e52f5c3b 100644 --- a/examples/netcore/Program.cs +++ b/examples/netcore/Program.cs @@ -4,44 +4,43 @@ using SFML.System; using SFML.Window; -namespace netcore +namespace netcore; + +internal class Program { - internal class Program + private static void Main() { - private static void Main() + var shape = new RectangleShape(new Vector2f(100, 100)) { - var shape = new RectangleShape(new Vector2f(100, 100)) - { - FillColor = Color.Black - }; - - var sound = new Sound(GenerateSineWave(frequency: 440.0, volume: .25, seconds: 1)); + FillColor = Color.Black + }; - var window = new RenderWindow(new VideoMode(800, 600), "SFML running in .NET Core"); - window.Closed += (_, _) => window.Close(); + var sound = new Sound(GenerateSineWave(frequency: 440.0, volume: .25, seconds: 1)); - sound.Play(); + var window = new RenderWindow(new VideoMode((800, 600)), "SFML running in .NET Core"); + window.Closed += (_, _) => window.Close(); - while (window.IsOpen) - { - window.DispatchEvents(); - window.Clear(Color.White); - window.Draw(shape); - window.Display(); - } - } + sound.Play(); - private static SoundBuffer GenerateSineWave(double frequency, double volume, int seconds) + while (window.IsOpen) { - uint sampleRate = 44100; - var samples = new short[seconds * sampleRate]; + window.DispatchEvents(); + window.Clear(Color.White); + window.Draw(shape); + window.Display(); + } + } - for (var i = 0; i < samples.Length; i++) - { - samples[i] = (short)(Math.Sin(frequency * (2 * Math.PI) * i / sampleRate) * volume * short.MaxValue); - } + private static SoundBuffer GenerateSineWave(double frequency, double volume, int seconds) + { + uint sampleRate = 44100; + var samples = new short[seconds * sampleRate]; - return new SoundBuffer(samples, 1, sampleRate); + for (var i = 0; i < samples.Length; i++) + { + samples[i] = (short)(Math.Sin(frequency * (2 * Math.PI) * i / sampleRate) * volume * short.MaxValue); } + + return new SoundBuffer(samples, 1, sampleRate, new SoundChannel[] { SoundChannel.Mono }); } } diff --git a/examples/netcore/Properties/launchSettings.json b/examples/netcore/Properties/launchSettings.json new file mode 100644 index 00000000..fa088e9b --- /dev/null +++ b/examples/netcore/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "netcore": { + "commandName": "Project", + "nativeDebugging": true + } + } +} \ No newline at end of file diff --git a/examples/opengl/OpenGL.cs b/examples/opengl/OpenGL.cs index 3c51d401..6de548cd 100644 --- a/examples/opengl/OpenGL.cs +++ b/examples/opengl/OpenGL.cs @@ -24,7 +24,7 @@ private static void Main() }; // Create the main window - var window = new RenderWindow(new VideoMode(800, 600), "SFML graphics with OpenGL", Styles.Default, contextSettings); + var window = new RenderWindow(new VideoMode((800, 600)), "SFML graphics with OpenGL", Styles.Default, State.Windowed, contextSettings); window.SetVerticalSyncEnabled(true); // Initialize OpenTK @@ -41,7 +41,7 @@ private static void Main() var background = new Sprite(new Texture("resources/background.jpg")); // Create a text to display on top of the OpenGL object - var text = new Text("SFML / OpenGL demo", new Font("resources/sansation.ttf")) + var text = new Text(new Font("resources/sansation.ttf"), "SFML / OpenGL demo") { Position = new Vector2f(250, 450), FillColor = new SFML.Graphics.Color(255, 255, 255, 170) @@ -212,6 +212,6 @@ private static void OnKeyPressed(object sender, KeyEventArgs e) /// /// Function called when the window is resized /// - private static void OnResized(object sender, SizeEventArgs e) => GL.Viewport(0, 0, (int)e.Width, (int)e.Height); + private static void OnResized(object sender, SizeEventArgs e) => GL.Viewport(0, 0, (int)e.Size.X, (int)e.Size.Y); } } diff --git a/examples/opengl/Properties/launchSettings.json b/examples/opengl/Properties/launchSettings.json new file mode 100644 index 00000000..b87f96f5 --- /dev/null +++ b/examples/opengl/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "opengl": { + "commandName": "Project", + "nativeDebugging": true + } + } +} \ No newline at end of file diff --git a/examples/shader/Shader.cs b/examples/shader/Shader.cs index f2c09694..b27fd509 100644 --- a/examples/shader/Shader.cs +++ b/examples/shader/Shader.cs @@ -27,7 +27,7 @@ public void Draw(IRenderTarget target, RenderStates states) } else { - var error = new Text("Shader not\nsupported", Font) + var error = new Text(Font, "Shader not\nsupported") { Position = new Vector2f(320, 200), CharacterSize = 36 @@ -78,7 +78,7 @@ internal class WaveBlur : Effect public WaveBlur() : base("wave + blur") { // Create the text - _text = new Text + _text = new Text(Font) { DisplayedString = "Praesent suscipit augue in velit pulvinar hendrerit varius purus aliquam.\n" + "Mauris mi odio, bibendum quis fringilla a, laoreet vel orci. Proin vitae vulputate tortor.\n" + @@ -98,7 +98,6 @@ public WaveBlur() : base("wave + blur") "Mauris ultricies dolor sed massa convallis sed aliquet augue fringilla.\n" + "Duis erat eros, porta in accumsan in, blandit quis sem.\n" + "In hac habitasse platea dictumst. Etiam fringilla est id odio dapibus sit amet semper dui laoreet.\n", - Font = Font, CharacterSize = 22, Position = new Vector2f(30, 20) }; @@ -178,7 +177,7 @@ internal class Edge : Effect public Edge() : base("edge post-effect") { // Create the off-screen surface - _surface = new RenderTexture(800, 600) + _surface = new RenderTexture((800, 600)) { Smooth = true }; @@ -203,7 +202,7 @@ public Edge() : base("edge post-effect") _entities = new Sprite[6]; for (var i = 0; i < _entities.Length; ++i) { - _entities[i] = new Sprite(_entityTexture, new IntRect(96 * i, 0, 96, 96)); + _entities[i] = new Sprite(_entityTexture, new IntRect((96 * i, 0), (96, 96))); } // Load the shader @@ -263,7 +262,7 @@ internal static class Program private static void Main() { // Create the main window - var window = new RenderWindow(new VideoMode(800, 600), "SFML.Net Shader"); + var window = new RenderWindow(new VideoMode((800, 600)), "SFML.Net Shader"); window.SetVerticalSyncEnabled(true); // Setup event handlers @@ -293,14 +292,14 @@ private static void Main() }; // Create the description text - _description = new Text("Current effect: " + _effects[_current].Name, font, 20) + _description = new Text(font, "Current effect: " + _effects[_current].Name, 20) { Position = new Vector2f(10, 530), FillColor = new Color(80, 80, 80) }; // Create the instructions text - var instructions = new Text("Press left and right arrows to change the current shader", font, 20) + var instructions = new Text(font, "Press left and right arrows to change the current shader", 20) { Position = new Vector2f(280, 555), FillColor = new Color(80, 80, 80) diff --git a/examples/visualbasic/OpenGL.vb b/examples/visualbasic/OpenGL.vb index d7642c87..58c81539 100644 --- a/examples/visualbasic/OpenGL.vb +++ b/examples/visualbasic/OpenGL.vb @@ -20,7 +20,7 @@ Friend Module OpenGL } ' Create main window - window = New RenderWindow(New VideoMode(800, 600), "SFML graphics with OpenGL (Visual Basic)", Styles.Default, contextSettings) + window = New RenderWindow(New VideoMode((800, 600)), "SFML graphics with OpenGL (Visual Basic)", Styles.Default, State.Windowed, contextSettings) window.SetVerticalSyncEnabled(True) ' Initialize OpenTK @@ -31,7 +31,7 @@ Friend Module OpenGL Dim background = New Sprite(New Texture("resources/background.jpg")) ' Create a text to display on top of the OpenGL object - Dim text = New Text("SFML / OpenGL demo", New Font("resources/sansation.ttf")) With { + Dim text = New Text(New Font("resources/sansation.ttf"), "SFML / OpenGL demo") With { .Position = New Vector2f(250, 450), .FillColor = New SFML.Graphics.Color(255, 255, 255, 170) } @@ -196,7 +196,7 @@ Friend Module OpenGL ''' Function called when the window is resized ''' Public Sub App_Resized(sender As Object, e As SizeEventArgs) Handles window.Resized - GL.Viewport(0, 0, e.Width, e.Height) + GL.Viewport(0, 0, e.Size.X, e.Size.Y) End Sub End Module diff --git a/examples/window/Program.cs b/examples/window/Program.cs index 1c94acb2..55585075 100644 --- a/examples/window/Program.cs +++ b/examples/window/Program.cs @@ -18,7 +18,7 @@ internal class SimpleWindow { public void Run() { - var mode = new SFML.Window.VideoMode(800, 600); + var mode = new SFML.Window.VideoMode((800, 600)); var window = new SFML.Graphics.RenderWindow(mode, "SFML works!"); window.KeyPressed += Window_KeyPressed; diff --git a/src/SFML.Audio/Cone.cs b/src/SFML.Audio/Cone.cs new file mode 100644 index 00000000..93eb236f --- /dev/null +++ b/src/SFML.Audio/Cone.cs @@ -0,0 +1,64 @@ +using System.Runtime.InteropServices; +using SFML.System; + +namespace SFML.Audio +{ + //////////////////////////////////////////////////////////// + /// + /// Structure defining the properties of a directional cone + /// + /// When set on the listener, sounds will play at gain 1 when + /// they are positioned within the inner angle of the cone. + /// Sounds will play at outerGain when they are positioned + /// outside the outer angle of the cone. + /// The gain declines linearly from 1 to outerGain as the + /// sound moves from the inner angle to the outer angle. + /// + /// When set on sound sources, they will play at gain 1 when the + /// listener is positioned within the inner angle of the cone. + /// Sounds will play at `outerGain` when the listener is + /// positioned outside the outer angle of the cone. + /// The gain declines linearly from 1 to outerGain as the + /// listener moves from the inner angle to the outer angle. + /// + //////////////////////////////////////////////////////////// + public struct Cone + { + /// Inner angle + public Angle InnerAngle; + + /// Outer angle + public Angle OuterAngle; + + /// Outer angle + public float OuterGain; + + [StructLayout(LayoutKind.Sequential)] + internal struct MarshalData + { + public float InnerAngleDegrees; + public float OuterAngleDegrees; + public float OuterGain; + } + + internal Cone(MarshalData data) + { + InnerAngle = Angle.FromDegrees(data.InnerAngleDegrees); + OuterAngle = Angle.FromDegrees(data.OuterAngleDegrees); + OuterGain = data.OuterGain; + } + + // Return a marshalled version of the instance, that can directly be passed to the C API + internal MarshalData Marshal() + { + var data = new MarshalData + { + InnerAngleDegrees = InnerAngle.Degrees, + OuterAngleDegrees = OuterAngle.Degrees, + OuterGain = OuterGain + }; + + return data; + } + } +} diff --git a/src/SFML.Audio/EffectProcessor.cs b/src/SFML.Audio/EffectProcessor.cs new file mode 100644 index 00000000..0d9be093 --- /dev/null +++ b/src/SFML.Audio/EffectProcessor.cs @@ -0,0 +1,91 @@ + +using System.Runtime.InteropServices; +using System; + +namespace SFML.Audio +{ + //////////////////////////////////////////////////////////// + /// + /// Callable that is provided with sound data for processing + /// + /// When the audio engine sources sound data from sound + /// sources it will pass the data through an effects + /// processor if one is set. The sound data will already be + /// converted to the internal floating point format. + /// + /// Sound data that is processed this way is provided in + /// frames. Each frame contains 1 floating point sample per + /// channel. If e.g. the data source provides stereo data, + /// each frame will contain 2 floats. + /// + /// The effects processor function takes 4 parameters: + /// - The input data frames, channels interleaved + /// - The number of input data frames available + /// - The buffer to write output data frames to, channels interleaved + /// - The number of output data frames that the output buffer can hold + /// - The channel count + /// + /// The input and output frame counts are in/out parameters. + /// + /// When this function is called, the input count will + /// contain the number of frames available in the input + /// buffer. The output count will contain the size of the + /// output buffer i.e. the maximum number of frames that + /// can be written to the output buffer. + /// + /// Attempting to read more frames than the input frame + /// count or write more frames than the output frame count + /// will result in undefined behaviour. + /// + /// Attempting to read more frames than the input frame + /// count or write more frames than the output frame count + /// will result in undefined behaviour. + /// + /// It is important to note that the channel count of the + /// audio engine currently sourcing data from this sound + /// will always be provided in `frameChannelCount`. This can + /// be different from the channel count of the sound source + /// so make sure to size necessary processing buffers + /// according to the engine channel count and not the sound + /// source channel count. + /// + /// When done processing the frames, the input and output + /// frame counts must be updated to reflect the actual + /// number of frames that were read from the input and + /// written to the output. + /// + /// The processing function should always try to process as + /// much sound data as possible i.e. always try to fill the + /// output buffer to the maximum. In certain situations for + /// specific effects it can be possible that the input frame + /// count and output frame count aren't equal. As long as + /// the frame counts are updated accordingly this is + /// perfectly valid. + /// + /// If the audio engine determines that no audio data is + /// available from the data source, the input data frames + /// pointer is set to `nullptr` and the input frame count is + /// set to 0. In this case it is up to the function to + /// decide how to handle the situation. For specific effects + /// e.g. Echo/Delay buffered data might still be able to be + /// written to the output buffer even if there is no longer + /// any input data. + /// + /// An important thing to remember is that this function is + /// directly called by the audio engine. Because the audio + /// engine runs on an internal thread of its own, make sure + /// access to shared data is synchronized appropriately. + /// + /// Because this function is stored by the `SoundSource` + /// object it will be able to be called as long as the + /// `SoundSource` object hasn't yet been destroyed. Make sure + /// that any data this function references outlives the + /// SoundSource object otherwise use-after-free errors will + /// occur. + /// + //////////////////////////////////////////////////////////// + public delegate long EffectProcessor(float[] inputFrames, float[] outputFrames, uint frameChannelCount); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate long EffectProcessorInternal(IntPtr inputFrames, uint inputFrameCount, IntPtr outputFrames, uint outputFrameCount, uint frameChannelCount); +} \ No newline at end of file diff --git a/src/SFML.Audio/Listener.cs b/src/SFML.Audio/Listener.cs index bea4d727..bef5f8ea 100644 --- a/src/SFML.Audio/Listener.cs +++ b/src/SFML.Audio/Listener.cs @@ -52,6 +52,29 @@ public static Vector3f Direction set => sfListener_setDirection(value); } + //////////////////////////////////////////////////////////// + /// + /// The velocity of the listener in the scene (default is (0, 0, -1)) + /// + //////////////////////////////////////////////////////////// + public static Vector3f Velocity + { + get => sfListener_getVelocity(); + set => sfListener_setVelocity(value); + } + + //////////////////////////////////////////////////////////// + /// + /// The cone defines how directional attenuation is applied. + /// The default cone of a sound is {2 * PI, 2 * PI, 1}. + /// + //////////////////////////////////////////////////////////// + public static Cone Cone + { + get => new Cone(sfListener_getCone()); + set => sfListener_setCone(value.Marshal()); + } + //////////////////////////////////////////////////////////// /// /// The up vector is the vector that points upward from the @@ -87,6 +110,18 @@ public static Vector3f UpVector [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern Vector3f sfListener_getDirection(); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfListener_setVelocity(Vector3f direction); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern Vector3f sfListener_getVelocity(); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfListener_setCone(Cone.MarshalData cone); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern Cone.MarshalData sfListener_getCone(); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfListener_setUpVector(Vector3f upVector); diff --git a/src/SFML.Audio/Music.cs b/src/SFML.Audio/Music.cs index 47508468..b9dbfdd5 100644 --- a/src/SFML.Audio/Music.cs +++ b/src/SFML.Audio/Music.cs @@ -132,6 +132,33 @@ public Music(byte[] bytes) : //////////////////////////////////////////////////////////// public uint SampleRate => sfMusic_getSampleRate(CPointer); + //////////////////////////////////////////////////////////// + /// + /// Get the map of position in sample frame to sound channel + /// + /// This is used to map a sample in the sample stream to a + /// position during spatialisation. + /// + //////////////////////////////////////////////////////////// + public SoundChannel[] ChannelMap + { + get + { + unsafe + { + var channels = sfMusic_getChannelMap(CPointer, out var count); + var arr = new SoundChannel[(int)count]; + + for (var i = 0; i < arr.Length; i++) + { + arr[i] = channels[i]; + } + + return arr; + } + } + } + //////////////////////////////////////////////////////////// /// /// Number of channels (1 = mono, 2 = stereo) @@ -159,14 +186,14 @@ public Music(byte[] bytes) : /// /// If set, the music will restart from beginning after /// reaching the end and so on, until it is stopped or - /// Loop = false is set. + /// IsLooping = false is set. /// The default looping state for music is false. /// //////////////////////////////////////////////////////////// - public bool Loop + public bool IsLooping { - get => sfMusic_getLoop(CPointer); - set => sfMusic_setLoop(CPointer, value); + get => sfMusic_isLooping(CPointer); + set => sfMusic_setLooping(CPointer, value); } //////////////////////////////////////////////////////////// @@ -186,6 +213,22 @@ public float Pitch set => sfMusic_setPitch(CPointer, value); } + //////////////////////////////////////////////////////////// + /// + /// Set the pan of the sound + /// + /// Using panning, a mono sound can be panned between + /// stereo channels. When the pan is set to -1, the sound + /// is played only on the left channel, when the pan is set + /// to +1, the sound is played only on the right channel. + /// + //////////////////////////////////////////////////////////// + public float Pan + { + get => sfMusic_getPan(CPointer); + set => sfMusic_setPan(CPointer, value); + } + //////////////////////////////////////////////////////////// /// /// Volume of the music. @@ -200,10 +243,26 @@ public float Volume set => sfMusic_setVolume(CPointer, value); } + //////////////////////////////////////////////////////////// + /// + /// Gets or sets whether spatialization of the sound is enabled + /// + /// Spatialization is the application of various effects to + /// simulate a sound being emitted at a virtual position in + /// 3D space and exhibiting various physical phenomena such as + /// directional attenuation and doppler shift. + /// + //////////////////////////////////////////////////////////// + public bool IsSpatializationEnabled + { + get => sfMusic_isSpatializationEnabled(CPointer); + set => sfMusic_setSpatializationEnabled(CPointer, value); + } + //////////////////////////////////////////////////////////// /// /// 3D position of the music in the audio scene. - /// + /// /// Only sounds with one channel (mono sounds) can be /// spatialized. /// The default position of a sound is (0, 0, 0). @@ -215,6 +274,84 @@ public Vector3f Position set => sfMusic_setPosition(CPointer, value); } + //////////////////////////////////////////////////////////// + /// + /// 3D direction of the sound in the audio scene + /// + /// The direction defines where the sound source is facing + /// in 3D space. It will affect how the sound is attenuated + /// if facing away from the listener. + /// The default direction of a sound is (0, 0, -1). + /// + //////////////////////////////////////////////////////////// + public Vector3f Direction + { + get => sfMusic_getPosition(CPointer); + set => sfMusic_setPosition(CPointer, value); + } + + //////////////////////////////////////////////////////////// + /// + /// Set the cone properties of the sound in the audio scene + /// + /// The cone defines how directional attenuation is applied. + /// The default cone of a sound is (2 * PI, 2 * PI, 1). + /// + //////////////////////////////////////////////////////////// + public Cone Cone + { + get => new Cone(sfMusic_getCone(CPointer)); + set => sfMusic_setCone(CPointer, value.Marshal()); + } + + //////////////////////////////////////////////////////////// + /// + /// 3D velocity of the sound in the audio scene + /// + /// The velocity is used to determine how to doppler shift + /// the sound. Sounds moving towards the listener will be + /// perceived to have a higher pitch and sounds moving away + /// from the listener will be perceived to have a lower pitch. + /// + //////////////////////////////////////////////////////////// + public Vector3f Velocity + { + get => sfMusic_getVelocity(CPointer); + set => sfMusic_setVelocity(CPointer, value); + } + + //////////////////////////////////////////////////////////// + /// + /// Doppler factor of the sound + /// + /// The doppler factor determines how strong the doppler + /// shift will be. + /// + //////////////////////////////////////////////////////////// + public float DopplerFactor + { + get => sfMusic_getDopplerFactor(CPointer); + set => sfMusic_setDopplerFactor(CPointer, value); + } + + //////////////////////////////////////////////////////////// + /// + /// Directional attenuation factor of the sound + /// + /// Depending on the virtual position of an output channel + /// relative to the listener (such as in surround sound + /// setups), sounds will be attenuated when emitting them + /// from certain channels. This factor determines how strong + /// the attenuation based on output channel position + /// relative to the listener is. + /// + //////////////////////////////////////////////////////////// + public float DirectionalAttenuationFactor + { + get => sfMusic_getDirectionalAttenuationFactor(CPointer); + set => sfMusic_setDirectionalAttenuationFactor(CPointer, value); + } + //////////////////////////////////////////////////////////// /// /// Make the music's position relative to the listener or absolute. @@ -250,6 +387,54 @@ public float MinDistance set => sfMusic_setMinDistance(CPointer, value); } + //////////////////////////////////////////////////////////// + /// + /// Maximum distance of the sound + /// + /// The "maximum distance" of a sound is the minimum + /// distance at which it is heard at its minimum volume. Closer + /// than the maximum distance, it will start to fade in according + /// to its attenuation factor. + /// The default value of the maximum distance is the maximum + /// value a float can represent. + /// + //////////////////////////////////////////////////////////// + public float MaxDistance + { + get => sfMusic_getMaxDistance(CPointer); + set => sfMusic_setMaxDistance(CPointer, value); + } + + //////////////////////////////////////////////////////////// + /// + /// Minimum gain of the sound + /// + /// When the sound is further away from the listener than + /// the "maximum distance" the attenuated gain is clamped + /// so it cannot go below the minimum gain value. + /// + //////////////////////////////////////////////////////////// + public float MinGain + { + get => sfMusic_getMinGain(CPointer); + set => sfMusic_setMinGain(CPointer, value); + } + + //////////////////////////////////////////////////////////// + /// + /// Maximum gain of the sound + /// + /// When the sound is closer from the listener than + /// the "minimum distance" the attenuated gain is clamped + /// so it cannot go above the maximum gain value. + /// + //////////////////////////////////////////////////////////// + public float MaxGain + { + get => sfMusic_getMaxGain(CPointer); + set => sfMusic_setMaxGain(CPointer, value); + } + //////////////////////////////////////////////////////////// /// /// Attenuation factor of the music. @@ -302,6 +487,32 @@ public TimeSpan LoopPoints set => sfMusic_setLoopPoints(CPointer, value); } + //////////////////////////////////////////////////////////// + /// + /// Set the effect processor to be applied to the sound. + /// + /// The effect processor is a callable that will be called + /// with sound data to be processed. + /// + /// The effect processor to attach to this sound, attach a null processor to disable processing + //////////////////////////////////////////////////////////// + public void SetEffectProcessor(EffectProcessor effectProcessor) + { + _effectProcessor = (inputFrames, inputFrameCount, outputFrames, outputFrameCount, frameChannelCount) => + { + var inputFramesArray = new float[inputFrameCount]; + var outputFramesArray = new float[outputFrameCount]; + + Marshal.Copy(inputFrames, inputFramesArray, 0, inputFramesArray.Length); + var written = effectProcessor(inputFramesArray, outputFramesArray, frameChannelCount); + Marshal.Copy(outputFramesArray, 0, outputFrames, outputFramesArray.Length); + + return written; + }; + + sfMusic_setEffectProcessor(CPointer, Marshal.GetFunctionPointerForDelegate(_effectProcessor)); + } + //////////////////////////////////////////////////////////// /// /// Provide a string describing the object @@ -320,7 +531,7 @@ public override string ToString() " ChannelCount(" + ChannelCount + ")" + " Status(" + Status + ")" + " Duration(" + Duration + ")" + - " Loop(" + Loop + ")" + + " IsLooping(" + IsLooping + ")" + " Pitch(" + Pitch + ")" + " Volume(" + Volume + ")" + " Position(" + Position + ")" + @@ -348,6 +559,7 @@ protected override void Destroy(bool disposing) } private readonly StreamAdaptor _stream; + private EffectProcessorInternal _effectProcessor; /// /// Structure defining a Time range. @@ -399,9 +611,15 @@ public TimeSpan(Time offset, Time length) [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfMusic_stop(IntPtr music); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern unsafe SoundChannel* sfMusic_getChannelMap(IntPtr music, out UIntPtr count); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern SoundStatus sfMusic_getStatus(IntPtr music); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern Time sfMusic_getPlayingOffset(IntPtr music); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern Time sfMusic_getDuration(IntPtr music); @@ -421,20 +639,50 @@ public TimeSpan(Time offset, Time length) private static extern void sfMusic_setPitch(IntPtr music, float pitch); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern void sfMusic_setLoop(IntPtr music, bool loop); + private static extern void sfMusic_setPan(IntPtr music, float pan); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfMusic_setLooping(IntPtr music, bool loop); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfMusic_setVolume(IntPtr music, float volume); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfMusic_setSpatializationEnabled(IntPtr music, bool enabled); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfMusic_setPosition(IntPtr music, Vector3f position); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfMusic_setDirection(IntPtr music, Vector3f direction); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfMusic_setCone(IntPtr music, Cone.MarshalData cone); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfMusic_setVelocity(IntPtr music, Vector3f velocity); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfMusic_setDopplerFactor(IntPtr music, float factor); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfMusic_setDirectionalAttenuationFactor(IntPtr music, float factor); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfMusic_setRelativeToListener(IntPtr music, bool relative); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfMusic_setMinDistance(IntPtr music, float minDistance); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfMusic_setMaxDistance(IntPtr music, float maxDistance); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfMusic_setMinGain(IntPtr music, float gain); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfMusic_setMaxGain(IntPtr music, float gain); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfMusic_setAttenuation(IntPtr music, float attenuation); @@ -442,11 +690,22 @@ public TimeSpan(Time offset, Time length) private static extern void sfMusic_setPlayingOffset(IntPtr music, Time timeOffset); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern bool sfMusic_getLoop(IntPtr music); + [return: MarshalAs(UnmanagedType.I1)] + private static extern bool sfMusic_isLooping(IntPtr music); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfMusic_setEffectProcessor(IntPtr music, IntPtr effectProcessor); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern float sfMusic_getPitch(IntPtr music); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern float sfMusic_getPan(IntPtr music); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] + private static extern bool sfMusic_isSpatializationEnabled(IntPtr music); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern float sfMusic_getVolume(IntPtr music); @@ -454,16 +713,38 @@ public TimeSpan(Time offset, Time length) private static extern Vector3f sfMusic_getPosition(IntPtr music); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern Vector3f sfMusic_getDirection(IntPtr music); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern Cone.MarshalData sfMusic_getCone(IntPtr music); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern Vector3f sfMusic_getVelocity(IntPtr music); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern float sfMusic_getDopplerFactor(IntPtr music); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern float sfMusic_getDirectionalAttenuationFactor(IntPtr music); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfMusic_isRelativeToListener(IntPtr music); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern float sfMusic_getMinDistance(IntPtr music); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern float sfMusic_getAttenuation(IntPtr music); + private static extern float sfMusic_getMaxDistance(IntPtr music); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern Time sfMusic_getPlayingOffset(IntPtr music); + private static extern float sfMusic_getMinGain(IntPtr music); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern float sfMusic_getMaxGain(IntPtr music); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern float sfMusic_getAttenuation(IntPtr music); #endregion } } diff --git a/src/SFML.Audio/Properties/launchSettings.json b/src/SFML.Audio/Properties/launchSettings.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/src/SFML.Audio/Properties/launchSettings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/SFML.Audio/Sound.cs b/src/SFML.Audio/Sound.cs index b2507b4b..852374d6 100644 --- a/src/SFML.Audio/Sound.cs +++ b/src/SFML.Audio/Sound.cs @@ -29,16 +29,6 @@ public enum SoundStatus //////////////////////////////////////////////////////////// public class Sound : ObjectBase { - //////////////////////////////////////////////////////////// - /// - /// Default constructor (invalid sound) - /// - //////////////////////////////////////////////////////////// - public Sound() : - base(sfSound_create()) - { - } - //////////////////////////////////////////////////////////// /// /// Construct the sound with a buffer @@ -46,7 +36,7 @@ public Sound() : /// Sound buffer containing the audio data to play with the sound //////////////////////////////////////////////////////////// public Sound(SoundBuffer buffer) : - base(sfSound_create()) => SoundBuffer = buffer; + base(sfSound_create(buffer.CPointer)) => SoundBuffer = buffer; //////////////////////////////////////////////////////////// /// @@ -119,14 +109,14 @@ public SoundBuffer SoundBuffer /// /// If set, the sound will restart from beginning after /// reaching the end and so on, until it is stopped or - /// Loop = false is set. + /// IsLooping = false is set. /// The default looping state for sounds is false. /// //////////////////////////////////////////////////////////// - public bool Loop + public bool IsLooping { - get => sfSound_getLoop(CPointer); - set => sfSound_setLoop(CPointer, value); + get => sfSound_isLooping(CPointer); + set => sfSound_setLooping(CPointer, value); } //////////////////////////////////////////////////////////// @@ -146,6 +136,22 @@ public float Pitch set => sfSound_setPitch(CPointer, value); } + //////////////////////////////////////////////////////////// + /// + /// Set the pan of the sound + /// + /// Using panning, a mono sound can be panned between + /// stereo channels. When the pan is set to -1, the sound + /// is played only on the left channel, when the pan is set + /// to +1, the sound is played only on the right channel. + /// + //////////////////////////////////////////////////////////// + public float Pan + { + get => sfSound_getPan(CPointer); + set => sfSound_setPan(CPointer, value); + } + //////////////////////////////////////////////////////////// /// /// Volume of the sound. @@ -160,6 +166,22 @@ public float Volume set => sfSound_setVolume(CPointer, value); } + //////////////////////////////////////////////////////////// + /// + /// Gets or sets whether spatialization of the sound is enabled + /// + /// Spatialization is the application of various effects to + /// simulate a sound being emitted at a virtual position in + /// 3D space and exhibiting various physical phenomena such as + /// directional attenuation and doppler shift. + /// + //////////////////////////////////////////////////////////// + public bool IsSpatializationEnabled + { + get => sfSound_isSpatializationEnabled(CPointer); + set => sfSound_setSpatializationEnabled(CPointer, value); + } + //////////////////////////////////////////////////////////// /// /// Current playing position of the sound. @@ -189,6 +211,84 @@ public Vector3f Position set => sfSound_setPosition(CPointer, value); } + //////////////////////////////////////////////////////////// + /// + /// 3D direction of the sound in the audio scene + /// + /// The direction defines where the sound source is facing + /// in 3D space. It will affect how the sound is attenuated + /// if facing away from the listener. + /// The default direction of a sound is (0, 0, -1). + /// + //////////////////////////////////////////////////////////// + public Vector3f Direction + { + get => sfSound_getPosition(CPointer); + set => sfSound_setPosition(CPointer, value); + } + + //////////////////////////////////////////////////////////// + /// + /// Set the cone properties of the sound in the audio scene + /// + /// The cone defines how directional attenuation is applied. + /// The default cone of a sound is (2 * PI, 2 * PI, 1). + /// + //////////////////////////////////////////////////////////// + public Cone Cone + { + get => new Cone(sfSound_getCone(CPointer)); + set => sfSound_setCone(CPointer, value.Marshal()); + } + + //////////////////////////////////////////////////////////// + /// + /// 3D velocity of the sound in the audio scene + /// + /// The velocity is used to determine how to doppler shift + /// the sound. Sounds moving towards the listener will be + /// perceived to have a higher pitch and sounds moving away + /// from the listener will be perceived to have a lower pitch. + /// + //////////////////////////////////////////////////////////// + public Vector3f Velocity + { + get => sfSound_getVelocity(CPointer); + set => sfSound_setVelocity(CPointer, value); + } + + //////////////////////////////////////////////////////////// + /// + /// Doppler factor of the sound + /// + /// The doppler factor determines how strong the doppler + /// shift will be. + /// + //////////////////////////////////////////////////////////// + public float DopplerFactor + { + get => sfSound_getDopplerFactor(CPointer); + set => sfSound_setDopplerFactor(CPointer, value); + } + + //////////////////////////////////////////////////////////// + /// + /// Directional attenuation factor of the sound + /// + /// Depending on the virtual position of an output channel + /// relative to the listener (such as in surround sound + /// setups), sounds will be attenuated when emitting them + /// from certain channels. This factor determines how strong + /// the attenuation based on output channel position + /// relative to the listener is. + /// + //////////////////////////////////////////////////////////// + public float DirectionalAttenuationFactor + { + get => sfSound_getDirectionalAttenuationFactor(CPointer); + set => sfSound_setDirectionalAttenuationFactor(CPointer, value); + } + //////////////////////////////////////////////////////////// /// /// Make the music's position relative to the listener or absolute. @@ -224,6 +324,54 @@ public float MinDistance set => sfSound_setMinDistance(CPointer, value); } + //////////////////////////////////////////////////////////// + /// + /// Maximum distance of the sound + /// + /// The "maximum distance" of a sound is the minimum + /// distance at which it is heard at its minimum volume. Closer + /// than the maximum distance, it will start to fade in according + /// to its attenuation factor. + /// The default value of the maximum distance is the maximum + /// value a float can represent. + /// + //////////////////////////////////////////////////////////// + public float MaxDistance + { + get => sfSound_getMaxDistance(CPointer); + set => sfSound_setMaxDistance(CPointer, value); + } + + //////////////////////////////////////////////////////////// + /// + /// Minimum gain of the sound + /// + /// When the sound is further away from the listener than + /// the "maximum distance" the attenuated gain is clamped + /// so it cannot go below the minimum gain value. + /// + //////////////////////////////////////////////////////////// + public float MinGain + { + get => sfSound_getMinGain(CPointer); + set => sfSound_setMinGain(CPointer, value); + } + + //////////////////////////////////////////////////////////// + /// + /// Maximum gain of the sound + /// + /// When the sound is closer from the listener than + /// the "minimum distance" the attenuated gain is clamped + /// so it cannot go above the maximum gain value. + /// + //////////////////////////////////////////////////////////// + public float MaxGain + { + get => sfSound_getMaxGain(CPointer); + set => sfSound_setMaxGain(CPointer, value); + } + //////////////////////////////////////////////////////////// /// /// Attenuation factor of the music. @@ -244,6 +392,32 @@ public float Attenuation set => sfSound_setAttenuation(CPointer, value); } + //////////////////////////////////////////////////////////// + /// + /// Set the effect processor to be applied to the sound. + /// + /// The effect processor is a callable that will be called + /// with sound data to be processed. + /// + /// The effect processor to attach to this sound, attach a null processor to disable processing + //////////////////////////////////////////////////////////// + public void SetEffectProcessor(EffectProcessor effectProcessor) + { + _effectProcessor = (inputFrames, inputFrameCount, outputFrames, outputFrameCount, frameChannelCount) => + { + var inputFramesArray = new float[inputFrameCount]; + var outputFramesArray = new float[outputFrameCount]; + + Marshal.Copy(inputFrames, inputFramesArray, 0, inputFramesArray.Length); + var written = effectProcessor(inputFramesArray, outputFramesArray, frameChannelCount); + Marshal.Copy(outputFramesArray, 0, outputFrames, outputFramesArray.Length); + + return written; + }; + + sfSound_setEffectProcessor(CPointer, Marshal.GetFunctionPointerForDelegate(_effectProcessor)); + } + //////////////////////////////////////////////////////////// /// /// Provide a string describing the object @@ -259,7 +433,7 @@ public override string ToString() return "[Sound]" + " Status(" + Status + ")" + - " Loop(" + Loop + ")" + + " IsLooping(" + IsLooping + ")" + " Pitch(" + Pitch + ")" + " Volume(" + Volume + ")" + " Position(" + Position + ")" + @@ -279,10 +453,11 @@ public override string ToString() protected override void Destroy(bool disposing) => sfSound_destroy(CPointer); private SoundBuffer _buffer; + private EffectProcessorInternal _effectProcessor; #region Imports [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern IntPtr sfSound_create(); + private static extern IntPtr sfSound_create(IntPtr soundBuffer); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern IntPtr sfSound_copy(IntPtr sound); @@ -303,10 +478,11 @@ public override string ToString() private static extern void sfSound_setBuffer(IntPtr sound, IntPtr buffer); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern void sfSound_setLoop(IntPtr sound, bool loop); + private static extern void sfSound_setLooping(IntPtr sound, bool loop); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern bool sfSound_getLoop(IntPtr sound); + [return: MarshalAs(UnmanagedType.I1)] + private static extern bool sfSound_isLooping(IntPtr sound); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern SoundStatus sfSound_getStatus(IntPtr sound); @@ -314,39 +490,104 @@ public override string ToString() [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfSound_setPitch(IntPtr sound, float pitch); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSound_setPan(IntPtr sound, float pan); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfSound_setVolume(IntPtr sound, float volume); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSound_setSpatializationEnabled(IntPtr sound, bool enabled); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfSound_setPosition(IntPtr sound, Vector3f position); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSound_setDirection(IntPtr sound, Vector3f direction); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSound_setCone(IntPtr sound, Cone.MarshalData cone); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSound_setVelocity(IntPtr sound, Vector3f velocity); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSound_setDopplerFactor(IntPtr sound, float factor); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSound_setDirectionalAttenuationFactor(IntPtr sound, float factor); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfSound_setRelativeToListener(IntPtr sound, bool relative); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfSound_setMinDistance(IntPtr sound, float minDistance); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSound_setMaxDistance(IntPtr sound, float maxDistance); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSound_setMinGain(IntPtr sound, float gain); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSound_setMaxGain(IntPtr sound, float gain); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfSound_setAttenuation(IntPtr sound, float attenuation); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfSound_setPlayingOffset(IntPtr sound, Time timeOffset); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSound_setEffectProcessor(IntPtr sound, IntPtr effectProcessor); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern float sfSound_getPitch(IntPtr sound); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern float sfSound_getPan(IntPtr sound); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern float sfSound_getVolume(IntPtr sound); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] + private static extern bool sfSound_isSpatializationEnabled(IntPtr sound); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern Vector3f sfSound_getPosition(IntPtr sound); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern Vector3f sfSound_getDirection(IntPtr sound); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern Cone.MarshalData sfSound_getCone(IntPtr sound); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern Vector3f sfSound_getVelocity(IntPtr sound); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern float sfSound_getDopplerFactor(IntPtr sound); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern float sfSound_getDirectionalAttenuationFactor(IntPtr sound); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfSound_isRelativeToListener(IntPtr sound); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern float sfSound_getMinDistance(IntPtr sound); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern float sfSound_getMaxDistance(IntPtr sound); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern float sfSound_getMinGain(IntPtr sound); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern float sfSound_getMaxGain(IntPtr sound); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern float sfSound_getAttenuation(IntPtr sound); diff --git a/src/SFML.Audio/SoundBuffer.cs b/src/SFML.Audio/SoundBuffer.cs index 2eee6269..fc756449 100644 --- a/src/SFML.Audio/SoundBuffer.cs +++ b/src/SFML.Audio/SoundBuffer.cs @@ -93,16 +93,20 @@ public SoundBuffer(byte[] bytes) : /// Array of samples /// Channel count /// Sample rate + /// Map of position in sample frame to sound channel /// //////////////////////////////////////////////////////////// - public SoundBuffer(short[] samples, uint channelCount, uint sampleRate) : + public SoundBuffer(short[] samples, uint channelCount, uint sampleRate, SoundChannel[] channelMapData) : base(IntPtr.Zero) { unsafe { fixed (short* samplesPtr = samples) { - CPointer = sfSoundBuffer_createFromSamples(samplesPtr, (uint)samples.Length, channelCount, sampleRate); + fixed (SoundChannel* channels = channelMapData) + { + CPointer = sfSoundBuffer_createFromSamples(samplesPtr, (uint)samples.Length, channelCount, sampleRate, channels, (UIntPtr)channelMapData.Length); + } } } @@ -178,6 +182,33 @@ public short[] Samples } } + //////////////////////////////////////////////////////////// + /// + /// Get the map of position in sample frame to sound channel + /// + /// This is used to map a sample in the sample stream to a + /// position during spatialisation. + /// + //////////////////////////////////////////////////////////// + public virtual SoundChannel[] ChannelMap + { + get + { + unsafe + { + var channels = sfSoundBuffer_getChannelMap(CPointer, out var count); + var arr = new SoundChannel[(int)count]; + + for (var i = 0; i < arr.Length; i++) + { + arr[i] = channels[i]; + } + + return arr; + } + } + } + //////////////////////////////////////////////////////////// /// /// Provide a string describing the object @@ -216,7 +247,7 @@ public override string ToString() private static extern unsafe IntPtr sfSoundBuffer_createFromMemory(IntPtr data, UIntPtr size); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern unsafe IntPtr sfSoundBuffer_createFromSamples(short* samples, ulong sampleCount, uint channelsCount, uint sampleRate); + private static extern unsafe IntPtr sfSoundBuffer_createFromSamples(short* samples, ulong sampleCount, uint channelsCount, uint sampleRate, SoundChannel* channelMapData, UIntPtr channelMapSize); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern IntPtr sfSoundBuffer_copy(IntPtr soundBuffer); @@ -225,6 +256,7 @@ public override string ToString() private static extern void sfSoundBuffer_destroy(IntPtr soundBuffer); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfSoundBuffer_saveToFile(IntPtr soundBuffer, string filename); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] @@ -239,6 +271,9 @@ public override string ToString() [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern uint sfSoundBuffer_getChannelCount(IntPtr soundBuffer); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern unsafe SoundChannel* sfSoundBuffer_getChannelMap(IntPtr soundBuffer, out UIntPtr count); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern Time sfSoundBuffer_getDuration(IntPtr soundBuffer); #endregion diff --git a/src/SFML.Audio/SoundBufferRecorder.cs b/src/SFML.Audio/SoundBufferRecorder.cs index 9ab0fb08..98c885d4 100644 --- a/src/SFML.Audio/SoundBufferRecorder.cs +++ b/src/SFML.Audio/SoundBufferRecorder.cs @@ -70,8 +70,12 @@ protected override bool OnProcessSamples(short[] samples) /// Called when the current capture stops /// //////////////////////////////////////////////////////////// - protected override void OnStop() => SoundBuffer = new SoundBuffer(_samplesArray.ToArray(), 1, SampleRate); + protected override void OnStop() => SoundBuffer = new SoundBuffer(_samplesArray.ToArray(), 1, SampleRate, _channels); private readonly List _samplesArray = new List(); + private static readonly SoundChannel[] _channels = new SoundChannel[] + { + SoundChannel.Mono + }; } } diff --git a/src/SFML.Audio/SoundChannel.cs b/src/SFML.Audio/SoundChannel.cs new file mode 100644 index 00000000..0aa04e9d --- /dev/null +++ b/src/SFML.Audio/SoundChannel.cs @@ -0,0 +1,40 @@ +namespace SFML.Audio +{ + //////////////////////////////////////////////////////////// + /// + /// Types of sound channels that can be read/written from sound buffers/files + /// + /// In multi-channel audio, each sound channel can be + /// assigned a position. The position of the channel is + /// used to determine where to place a sound when it + /// is spatialised. Assigning an incorrect sound channel + /// will result in multi-channel audio being positioned + /// incorrectly when using spatialisation. + /// + //////////////////////////////////////////////////////////// +#pragma warning disable CS1591 // TODO: add documentation when available + public enum SoundChannel + { + Unspecified, + Mono, + FrontLeft, + FrontRight, + FrontCenter, + FrontLeftOfCenter, + FrontRightOfCenter, + LowFrequencyEffects, + BackLeft, + BackRight, + BackCenter, + SideLeft, + SideRight, + TopCenter, + TopFrontLeft, + TopFrontRight, + TopFrontCenter, + TopBackLeft, + TopBackRight, + TopBackCenter + } +#pragma warning restore CS1591 +} diff --git a/src/SFML.Audio/SoundRecorder.cs b/src/SFML.Audio/SoundRecorder.cs index 001416ce..ed5a7318 100644 --- a/src/SFML.Audio/SoundRecorder.cs +++ b/src/SFML.Audio/SoundRecorder.cs @@ -89,6 +89,33 @@ public uint ChannelCount set => sfSoundRecorder_setChannelCount(CPointer, value); } + //////////////////////////////////////////////////////////// + /// + /// Get the map of position in sample frame to sound channel + /// + /// This is used to map a sample in the sample stream to a + /// position during spatialisation. + /// + //////////////////////////////////////////////////////////// + public SoundChannel[] ChannelMap + { + get + { + unsafe + { + var channels = sfSoundRecorder_getChannelMap(CPointer, out var count); + var arr = new SoundChannel[(int)count]; + + for (var i = 0; i < arr.Length; i++) + { + arr[i] = channels[i]; + } + + return arr; + } + } + } + //////////////////////////////////////////////////////////// /// /// Check if the system supports audio capture. @@ -163,21 +190,6 @@ protected virtual void OnStop() // Does nothing by default } - //////////////////////////////////////////////////////////// - /// - /// The processing interval controls the period - /// between calls to the onProcessSamples function. You may - /// want to use a small interval if you want to process the - /// recorded data in real time, for example. - /// - /// Note: this is only a hint, the actual period may vary. - /// So don't rely on this parameter to implement precise timing. - /// - /// The default processing interval is 100 ms. - /// - //////////////////////////////////////////////////////////// - protected void SetProcessingInterval(Time interval) => sfSoundRecorder_setProcessingInterval(CPointer, interval); - //////////////////////////////////////////////////////////// /// /// Get the list of the names of all available audio capture devices @@ -291,6 +303,7 @@ private bool ProcessSamples(IntPtr samples, UIntPtr nbSamples, IntPtr userData) private static extern void sfSoundRecorder_destroy(IntPtr soundRecorder); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfSoundRecorder_start(IntPtr soundRecorder, uint sampleRate); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] @@ -300,11 +313,9 @@ private bool ProcessSamples(IntPtr samples, UIntPtr nbSamples, IntPtr userData) private static extern uint sfSoundRecorder_getSampleRate(IntPtr soundRecorder); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfSoundRecorder_isAvailable(); - [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern void sfSoundRecorder_setProcessingInterval(IntPtr soundRecorder, Time interval); - [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern unsafe IntPtr* sfSoundRecorder_getAvailableDevices(out UIntPtr count); @@ -312,6 +323,7 @@ private bool ProcessSamples(IntPtr samples, UIntPtr nbSamples, IntPtr userData) private static extern IntPtr sfSoundRecorder_getDefaultDevice(); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfSoundRecorder_setDevice(IntPtr soundRecorder, string name); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] @@ -322,6 +334,9 @@ private bool ProcessSamples(IntPtr samples, UIntPtr nbSamples, IntPtr userData) [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern uint sfSoundRecorder_getChannelCount(IntPtr soundRecorder); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern unsafe SoundChannel* sfSoundRecorder_getChannelMap(IntPtr soundRecorder, out UIntPtr count); #endregion } } diff --git a/src/SFML.Audio/SoundStream.cs b/src/SFML.Audio/SoundStream.cs index f009930d..5698ee12 100644 --- a/src/SFML.Audio/SoundStream.cs +++ b/src/SFML.Audio/SoundStream.cs @@ -66,6 +66,33 @@ public SoundStream() : //////////////////////////////////////////////////////////// public uint SampleRate => sfSoundStream_getSampleRate(CPointer); + //////////////////////////////////////////////////////////// + /// + /// Get the map of position in sample frame to sound channel + /// + /// This is used to map a sample in the sample stream to a + /// position during spatialisation. + /// + //////////////////////////////////////////////////////////// + public SoundChannel[] ChannelMap + { + get + { + unsafe + { + var channels = sfSoundStream_getChannelMap(CPointer, out var count); + var arr = new SoundChannel[(int)count]; + + for (var i = 0; i < arr.Length; i++) + { + arr[i] = channels[i]; + } + + return arr; + } + } + } + //////////////////////////////////////////////////////////// /// /// Number of channels (1 = mono, 2 = stereo) @@ -86,14 +113,14 @@ public SoundStream() : /// /// If set, the music will restart from beginning after /// reaching the end and so on, until it is stopped or - /// Loop = false is set. + /// IsLooping = false is set. /// The default looping state for music is false. /// //////////////////////////////////////////////////////////// - public bool Loop + public bool IsLooping { - get => sfSoundStream_getLoop(CPointer); - set => sfSoundStream_setLoop(CPointer, value); + get => sfSoundStream_isLooping(CPointer); + set => sfSoundStream_setLooping(CPointer, value); } //////////////////////////////////////////////////////////// @@ -113,6 +140,22 @@ public float Pitch set => sfSoundStream_setPitch(CPointer, value); } + //////////////////////////////////////////////////////////// + /// + /// Set the pan of the sound + /// + /// Using panning, a mono sound can be panned between + /// stereo channels. When the pan is set to -1, the sound + /// is played only on the left channel, when the pan is set + /// to +1, the sound is played only on the right channel. + /// + //////////////////////////////////////////////////////////// + public float Pan + { + get => sfSoundStream_getPan(CPointer); + set => sfSoundStream_setPan(CPointer, value); + } + //////////////////////////////////////////////////////////// /// /// Volume of the stream. @@ -127,6 +170,22 @@ public float Volume set => sfSoundStream_setVolume(CPointer, value); } + //////////////////////////////////////////////////////////// + /// + /// Gets or sets whether spatialization of the sound is enabled + /// + /// Spatialization is the application of various effects to + /// simulate a sound being emitted at a virtual position in + /// 3D space and exhibiting various physical phenomena such as + /// directional attenuation and doppler shift. + /// + //////////////////////////////////////////////////////////// + public bool IsSpatializationEnabled + { + get => sfSoundStream_isSpatializationEnabled(CPointer); + set => sfSoundStream_setSpatializationEnabled(CPointer, value); + } + //////////////////////////////////////////////////////////// /// /// 3D position of the stream in the audio scene. @@ -142,6 +201,84 @@ public Vector3f Position set => sfSoundStream_setPosition(CPointer, value); } + //////////////////////////////////////////////////////////// + /// + /// 3D direction of the sound in the audio scene + /// + /// The direction defines where the sound source is facing + /// in 3D space. It will affect how the sound is attenuated + /// if facing away from the listener. + /// The default direction of a sound is (0, 0, -1). + /// + //////////////////////////////////////////////////////////// + public Vector3f Direction + { + get => sfSoundStream_getPosition(CPointer); + set => sfSoundStream_setPosition(CPointer, value); + } + + //////////////////////////////////////////////////////////// + /// + /// Set the cone properties of the sound in the audio scene + /// + /// The cone defines how directional attenuation is applied. + /// The default cone of a sound is (2 * PI, 2 * PI, 1). + /// + //////////////////////////////////////////////////////////// + public Cone Cone + { + get => new Cone(sfSoundStream_getCone(CPointer)); + set => sfSoundStream_setCone(CPointer, value.Marshal()); + } + + //////////////////////////////////////////////////////////// + /// + /// 3D velocity of the sound in the audio scene + /// + /// The velocity is used to determine how to doppler shift + /// the sound. Sounds moving towards the listener will be + /// perceived to have a higher pitch and sounds moving away + /// from the listener will be perceived to have a lower pitch. + /// + //////////////////////////////////////////////////////////// + public Vector3f Velocity + { + get => sfSoundStream_getVelocity(CPointer); + set => sfSoundStream_setVelocity(CPointer, value); + } + + //////////////////////////////////////////////////////////// + /// + /// Doppler factor of the sound + /// + /// The doppler factor determines how strong the doppler + /// shift will be. + /// + //////////////////////////////////////////////////////////// + public float DopplerFactor + { + get => sfSoundStream_getDopplerFactor(CPointer); + set => sfSoundStream_setDopplerFactor(CPointer, value); + } + + //////////////////////////////////////////////////////////// + /// + /// Directional attenuation factor of the sound + /// + /// Depending on the virtual position of an output channel + /// relative to the listener (such as in surround sound + /// setups), sounds will be attenuated when emitting them + /// from certain channels. This factor determines how strong + /// the attenuation based on output channel position + /// relative to the listener is. + /// + //////////////////////////////////////////////////////////// + public float DirectionalAttenuationFactor + { + get => sfSoundStream_getDirectionalAttenuationFactor(CPointer); + set => sfSoundStream_setDirectionalAttenuationFactor(CPointer, value); + } + //////////////////////////////////////////////////////////// /// /// Make the stream's position relative to the listener or absolute. @@ -177,6 +314,54 @@ public float MinDistance set => sfSoundStream_setMinDistance(CPointer, value); } + //////////////////////////////////////////////////////////// + /// + /// Maximum distance of the sound + /// + /// The "maximum distance" of a sound is the minimum + /// distance at which it is heard at its minimum volume. Closer + /// than the maximum distance, it will start to fade in according + /// to its attenuation factor. + /// The default value of the maximum distance is the maximum + /// value a float can represent. + /// + //////////////////////////////////////////////////////////// + public float MaxDistance + { + get => sfSoundStream_getMaxDistance(CPointer); + set => sfSoundStream_setMaxDistance(CPointer, value); + } + + //////////////////////////////////////////////////////////// + /// + /// Minimum gain of the sound + /// + /// When the sound is further away from the listener than + /// the "maximum distance" the attenuated gain is clamped + /// so it cannot go below the minimum gain value. + /// + //////////////////////////////////////////////////////////// + public float MinGain + { + get => sfSoundStream_getMinGain(CPointer); + set => sfSoundStream_setMinGain(CPointer, value); + } + + //////////////////////////////////////////////////////////// + /// + /// Maximum gain of the sound + /// + /// When the sound is closer from the listener than + /// the "minimum distance" the attenuated gain is clamped + /// so it cannot go above the maximum gain value. + /// + //////////////////////////////////////////////////////////// + public float MaxGain + { + get => sfSoundStream_getMaxGain(CPointer); + set => sfSoundStream_setMaxGain(CPointer, value); + } + //////////////////////////////////////////////////////////// /// /// Attenuation factor of the stream. @@ -211,6 +396,32 @@ public Time PlayingOffset set => sfSoundStream_setPlayingOffset(CPointer, value); } + //////////////////////////////////////////////////////////// + /// + /// Set the effect processor to be applied to the sound. + /// + /// The effect processor is a callable that will be called + /// with sound data to be processed. + /// + /// The effect processor to attach to this sound, attach a null processor to disable processing + //////////////////////////////////////////////////////////// + public void SetEffectProcessor(EffectProcessor effectProcessor) + { + _effectProcessor = (inputFrames, inputFrameCount, outputFrames, outputFrameCount, frameChannelCount) => + { + var inputFramesArray = new float[inputFrameCount]; + var outputFramesArray = new float[outputFrameCount]; + + Marshal.Copy(inputFrames, inputFramesArray, 0, inputFramesArray.Length); + var written = effectProcessor(inputFramesArray, outputFramesArray, frameChannelCount); + Marshal.Copy(outputFramesArray, 0, outputFrames, outputFramesArray.Length); + + return written; + }; + + sfSoundStream_setEffectProcessor(CPointer, Marshal.GetFunctionPointerForDelegate(_effectProcessor)); + } + //////////////////////////////////////////////////////////// /// /// Provide a string describing the object @@ -228,7 +439,7 @@ public override string ToString() " SampleRate(" + SampleRate + ")" + " ChannelCount(" + ChannelCount + ")" + " Status(" + Status + ")" + - " Loop(" + Loop + ")" + + " IsLooping(" + IsLooping + ")" + " Pitch(" + Pitch + ")" + " Volume(" + Volume + ")" + " Position(" + Position + ")" + @@ -244,12 +455,20 @@ public override string ToString() /// /// Number of channels /// Sample rate, in samples per second + /// Map of position in sample frame to sound channel //////////////////////////////////////////////////////////// - protected void Initialize(uint channelCount, uint sampleRate) + protected void Initialize(uint channelCount, uint sampleRate, SoundChannel[] channelMapData) { _getDataCallback = new GetDataCallbackType(GetData); _seekCallback = new SeekCallbackType(Seek); - CPointer = sfSoundStream_create(_getDataCallback, _seekCallback, channelCount, sampleRate, IntPtr.Zero); + + unsafe + { + fixed (SoundChannel* data = channelMapData) + { + CPointer = sfSoundStream_create(_getDataCallback, _seekCallback, channelCount, sampleRate, data, (UIntPtr)channelMapData.Length, IntPtr.Zero); + } + } } //////////////////////////////////////////////////////////// @@ -336,11 +555,12 @@ private bool GetData(ref Chunk dataChunk, IntPtr userData) private GetDataCallbackType _getDataCallback; private SeekCallbackType _seekCallback; + private EffectProcessorInternal _effectProcessor; private short[] _tempBuffer; #region Imports [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern IntPtr sfSoundStream_create(GetDataCallbackType onGetData, SeekCallbackType onSeek, uint channelCount, uint sampleRate, IntPtr userData); + private static extern unsafe IntPtr sfSoundStream_create(GetDataCallbackType onGetData, SeekCallbackType onSeek, uint channelCount, uint sampleRate, SoundChannel* channelMapData, UIntPtr channelMapSize, IntPtr userData); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfSoundStream_destroy(IntPtr soundStream); @@ -364,23 +584,53 @@ private bool GetData(ref Chunk dataChunk, IntPtr userData) private static extern uint sfSoundStream_getSampleRate(IntPtr soundStream); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern void sfSoundStream_setLoop(IntPtr soundStream, bool loop); + private static extern unsafe SoundChannel* sfSoundStream_getChannelMap(IntPtr soundStream, out UIntPtr count); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfSoundStream_setPitch(IntPtr soundStream, float pitch); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSoundStream_setPan(IntPtr soundStream, float pan); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfSoundStream_setVolume(IntPtr soundStream, float volume); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSoundStream_setSpatializationEnabled(IntPtr soundStream, bool enabled); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfSoundStream_setPosition(IntPtr soundStream, Vector3f position); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSoundStream_setDirection(IntPtr soundStream, Vector3f direction); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSoundStream_setCone(IntPtr soundStream, Cone.MarshalData cone); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSoundStream_setVelocity(IntPtr soundStream, Vector3f velocity); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSoundStream_setDopplerFactor(IntPtr soundStream, float factor); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSoundStream_setDirectionalAttenuationFactor(IntPtr soundStream, float factor); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfSoundStream_setRelativeToListener(IntPtr soundStream, bool relative); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfSoundStream_setMinDistance(IntPtr soundStream, float minDistance); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSoundStream_setMaxDistance(IntPtr soundStream, float maxDistance); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSoundStream_setMinGain(IntPtr soundStream, float gain); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSoundStream_setMaxGain(IntPtr soundStream, float gain); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfSoundStream_setAttenuation(IntPtr soundStream, float attenuation); @@ -388,26 +638,65 @@ private bool GetData(ref Chunk dataChunk, IntPtr userData) private static extern void sfSoundStream_setPlayingOffset(IntPtr soundStream, Time timeOffset); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern bool sfSoundStream_getLoop(IntPtr soundStream); + private static extern void sfSoundStream_setLooping(IntPtr soundStream, bool loop); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern float sfSoundStream_getPitch(IntPtr soundStream); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern float sfSoundStream_getPan(IntPtr soundStream); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern float sfSoundStream_getVolume(IntPtr soundStream); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] + private static extern bool sfSoundStream_isSpatializationEnabled(IntPtr soundStream); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern Vector3f sfSoundStream_getPosition(IntPtr soundStream); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern Vector3f sfSoundStream_getDirection(IntPtr soundStream); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern Cone.MarshalData sfSoundStream_getCone(IntPtr soundStream); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern Vector3f sfSoundStream_getVelocity(IntPtr soundStream); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern float sfSoundStream_getDopplerFactor(IntPtr soundStream); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern float sfSoundStream_getDirectionalAttenuationFactor(IntPtr soundStream); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfSoundStream_isRelativeToListener(IntPtr soundStream); [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern float sfSoundStream_getMinDistance(IntPtr soundStream); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern float sfSoundStream_getMaxDistance(IntPtr soundStream); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern float sfSoundStream_getMinGain(IntPtr soundStream); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern float sfSoundStream_getMaxGain(IntPtr soundStream); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern float sfSoundStream_getAttenuation(IntPtr soundStream); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] + private static extern bool sfSoundStream_isLooping(IntPtr soundStream); + + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfSoundStream_setEffectProcessor(IntPtr soundStream, IntPtr effectProcessor); + [DllImport(CSFML.Audio, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern Time sfSoundStream_getPlayingOffset(IntPtr soundStream); #endregion diff --git a/src/SFML.Graphics/CircleShape.cs b/src/SFML.Graphics/CircleShape.cs index 9b3e0a84..53b32a0c 100644 --- a/src/SFML.Graphics/CircleShape.cs +++ b/src/SFML.Graphics/CircleShape.cs @@ -1,4 +1,6 @@ using System; +using System.Runtime.InteropServices; +using System.Security; using SFML.System; namespace SFML.Graphics @@ -103,7 +105,24 @@ public override Vector2f GetPoint(uint index) return new Vector2f(_radius + x, _radius + y); } + //////////////////////////////////////////////////////////// + /// + /// Get the geometric center of the circle + /// + /// The returned point is in local coordinates, that is, + /// the shape's transforms (position, rotation, scale) are + /// not taken into account. + /// + /// The geometric center of the shape + //////////////////////////////////////////////////////////// + public override Vector2f GetGeometricCenter() => sfCircleShape_getGeometricCenter(CPointer); + private float _radius; private uint _pointCount; + + #region Imports + [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern Vector2f sfCircleShape_getGeometricCenter(IntPtr cPointer); + #endregion } } diff --git a/src/SFML.Graphics/CoordinateType.cs b/src/SFML.Graphics/CoordinateType.cs new file mode 100644 index 00000000..fd249b1c --- /dev/null +++ b/src/SFML.Graphics/CoordinateType.cs @@ -0,0 +1,13 @@ +namespace SFML.Graphics +{ + /// + /// Types of texture coordinates that can be used for rendering. + /// + public enum CoordinateType + { + /// Texture coordinates in range [0 .. 1]. + Normalized, + /// Texture coordinates in range [0 .. size]. + Pixels + } +} \ No newline at end of file diff --git a/src/SFML.Graphics/Font.cs b/src/SFML.Graphics/Font.cs index 7abf1b86..ee89c707 100644 --- a/src/SFML.Graphics/Font.cs +++ b/src/SFML.Graphics/Font.cs @@ -96,6 +96,23 @@ public Font(Font copy) : base(sfFont_copy(copy.CPointer)) { } //////////////////////////////////////////////////////////// public Glyph GetGlyph(uint codePoint, uint characterSize, bool bold, float outlineThickness) => sfFont_getGlyph(CPointer, codePoint, characterSize, bold, outlineThickness); + //////////////////////////////////////////////////////////// + /// + /// Determine if this font has a glyph representing the requested code point + /// + /// Most fonts only include a very limited selection of glyphs from + /// specific Unicode subsets, like Latin, Cyrillic, or Asian characters. + /// + /// While code points without representation will return a font specific + /// default character, it might be useful to verify whether specific + /// code points are included to determine whether a font is suited + /// to display text in a specific language. + /// + /// Unicode code point to check + /// True if the codepoint has a glyph representation, false otherwise + //////////////////////////////////////////////////////////// + public bool HasGlyph(uint codePoint) => sfFont_hasGlyph(CPointer, codePoint); + //////////////////////////////////////////////////////////// /// /// Get the kerning value corresponding to a given pair of @@ -288,6 +305,10 @@ internal struct InfoMarshalData [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern Glyph sfFont_getGlyph(IntPtr cPointer, uint codePoint, uint characterSize, bool bold, float outlineThickness); + [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] + private static extern bool sfFont_hasGlyph(IntPtr font, uint codePoint); + [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern float sfFont_getKerning(IntPtr cPointer, uint first, uint second, uint characterSize); @@ -310,6 +331,7 @@ internal struct InfoMarshalData private static extern void sfFont_setSmooth(IntPtr cPointer, bool smooth); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfFont_isSmooth(IntPtr cPointer); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] diff --git a/src/SFML.Graphics/Glsl.cs b/src/SFML.Graphics/Glsl.cs index f381dcae..332cfd9e 100644 --- a/src/SFML.Graphics/Glsl.cs +++ b/src/SFML.Graphics/Glsl.cs @@ -118,11 +118,11 @@ public Bvec2(bool x, bool y) } /// Horizontal component of the vector - [MarshalAs(UnmanagedType.Bool)] + [MarshalAs(UnmanagedType.I1)] public bool X; /// Vertical component of the vector - [MarshalAs(UnmanagedType.Bool)] + [MarshalAs(UnmanagedType.I1)] public bool Y; } #endregion @@ -237,15 +237,15 @@ public Bvec3(bool x, bool y, bool z) } /// Horizontal component of the vector - [MarshalAs(UnmanagedType.Bool)] + [MarshalAs(UnmanagedType.I1)] public bool X; /// Vertical component of the vector - [MarshalAs(UnmanagedType.Bool)] + [MarshalAs(UnmanagedType.I1)] public bool Y; /// Depth component of the vector - [MarshalAs(UnmanagedType.Bool)] + [MarshalAs(UnmanagedType.I1)] public bool Z; } #endregion @@ -380,19 +380,19 @@ public Bvec4(bool x, bool y, bool z, bool w) } /// Horizontal component of the vector - [MarshalAs(UnmanagedType.Bool)] + [MarshalAs(UnmanagedType.I1)] public bool X; /// Vertical component of the vector - [MarshalAs(UnmanagedType.Bool)] + [MarshalAs(UnmanagedType.I1)] public bool Y; /// Depth component of the vector - [MarshalAs(UnmanagedType.Bool)] + [MarshalAs(UnmanagedType.I1)] public bool Z; /// Projective/Homogeneous component of the vector - [MarshalAs(UnmanagedType.Bool)] + [MarshalAs(UnmanagedType.I1)] public bool W; } #endregion diff --git a/src/SFML.Graphics/IRenderTarget.cs b/src/SFML.Graphics/IRenderTarget.cs index cebdcd3c..dffd5fc2 100644 --- a/src/SFML.Graphics/IRenderTarget.cs +++ b/src/SFML.Graphics/IRenderTarget.cs @@ -18,7 +18,6 @@ public interface IRenderTarget //////////////////////////////////////////////////////////// Vector2u Size { get; } - //////////////////////////////////////////////////////////// /// /// Tell if the render target will use sRGB encoding when drawing on it @@ -58,6 +57,20 @@ public interface IRenderTarget //////////////////////////////////////////////////////////// IntRect GetViewport(View view); + //////////////////////////////////////////////////////////// + /// + /// Get the scissor rectangle of a view, applied to this render target + /// + /// The scissor rectangle is defined in the view as a ratio. This + /// function simply applies this ratio to the current dimensions + /// of the render target to calculate the pixels rectangle + /// that the scissor rectangle actually covers in the target. + /// + /// The view for which we want to compute the scissor rectangle + /// Scissor rectangle, expressed in pixels + //////////////////////////////////////////////////////////// + IntRect GetScissor(View view); + //////////////////////////////////////////////////////////// /// /// Convert a point from target coordinates to world @@ -155,6 +168,29 @@ public interface IRenderTarget //////////////////////////////////////////////////////////// void Clear(Color color); + //////////////////////////////////////////////////////////// + /// + /// Clear the stencil buffer to a specific value + /// + /// The specified value is truncated to the bit width of + /// the current stencil buffer. + /// + /// Stencil value to clear to + //////////////////////////////////////////////////////////// + void ClearStencil(StencilValue stencilValue); + + //////////////////////////////////////////////////////////// + /// + /// Clear the entire target with a single color and stencil value + /// + /// The specified stencil value is truncated to the bit + /// width of the current stencil buffer. + /// + /// Fill color to use to clear the render target + /// Stencil value to clear to + //////////////////////////////////////////////////////////// + void Clear(Color color, StencilValue stencilValue); + //////////////////////////////////////////////////////////// /// /// Draw a drawable object to the render-target, with default render states @@ -214,6 +250,27 @@ public interface IRenderTarget //////////////////////////////////////////////////////////// void Draw(Vertex[] vertices, uint start, uint count, PrimitiveType type, RenderStates states); + //////////////////////////////////////////////////////////// + /// + /// Activate or deactivate the render target for rendering + /// + /// This function makes the render target's context current for + /// future OpenGL rendering operations (so you shouldn't care + /// about it if you're not doing direct OpenGL stuff). + /// A render target's context is active only on the current thread, + /// if you want to make it active on another thread you have + /// to deactivate it on the previous thread first if it was active. + /// Only one context can be current in a thread, so if you + /// want to draw OpenGL geometry to another render target + /// don't forget to activate it again. Activating a render + /// target will automatically deactivate the previously active + /// context (if any). + /// + /// True to activate, false to deactivate + /// True if operation was successful, false otherwise + //////////////////////////////////////////////////////////// + bool SetActive(bool active); + //////////////////////////////////////////////////////////// /// /// Save the current OpenGL render states and matrices. diff --git a/src/SFML.Graphics/Image.cs b/src/SFML.Graphics/Image.cs index 274456c4..9421e3ee 100644 --- a/src/SFML.Graphics/Image.cs +++ b/src/SFML.Graphics/Image.cs @@ -18,22 +18,20 @@ public class Image : ObjectBase /// /// Construct the image with black color /// - /// Image width - /// Image height + /// Width and height of the image /// //////////////////////////////////////////////////////////// - public Image(uint width, uint height) : this(width, height, Color.Black) { } + public Image(Vector2u size) : this(size, Color.Black) { } //////////////////////////////////////////////////////////// /// /// Construct the image from a single color /// - /// Image width - /// Image height + /// Width and height of the image /// Color to fill the image with /// //////////////////////////////////////////////////////////// - public Image(uint width, uint height, Color color) : base(sfImage_createFromColor(width, height, color)) + public Image(Vector2u size, Color color) : base(sfImage_createFromColor(size, color)) { if (IsInvalid) { @@ -128,7 +126,7 @@ public Image(Color[,] pixels) : { fixed (Color* pixelsPtr = transposed) { - CPointer = sfImage_createFromPixels(width, height, (byte*)pixelsPtr); + CPointer = sfImage_createFromPixels(new Vector2u(width, height), (byte*)pixelsPtr); } } @@ -142,19 +140,18 @@ public Image(Color[,] pixels) : /// /// Construct the image directly from an array of pixels /// - /// Image width - /// Image height + /// Width and height of the image /// array containing the pixels /// //////////////////////////////////////////////////////////// - public Image(uint width, uint height, byte[] pixels) : + public Image(Vector2u size, byte[] pixels) : base(IntPtr.Zero) { unsafe { fixed (byte* pixelsPtr = pixels) { - CPointer = sfImage_createFromPixels(width, height, pixelsPtr); + CPointer = sfImage_createFromPixels(size, pixelsPtr); } } @@ -232,10 +229,9 @@ public bool SaveToMemory(out byte[] output, string format) /// be used at initialization time /// /// Source image to copy - /// X coordinate of the destination position - /// Y coordinate of the destination position + /// Coordinates of the destination position //////////////////////////////////////////////////////////// - public void Copy(Image source, uint destX, uint destY) => Copy(source, destX, destY, new IntRect(0, 0, 0, 0)); + public void Copy(Image source, Vector2u dest) => Copy(source, dest, new IntRect((0, 0), (0, 0))); //////////////////////////////////////////////////////////// /// @@ -244,11 +240,10 @@ public bool SaveToMemory(out byte[] output, string format) /// be used at initialization time /// /// Source image to copy - /// X coordinate of the destination position - /// Y coordinate of the destination position + /// Coordinates of the destination position /// Sub-rectangle of the source image to copy //////////////////////////////////////////////////////////// - public void Copy(Image source, uint destX, uint destY, IntRect sourceRect) => Copy(source, destX, destY, sourceRect, false); + public void Copy(Image source, Vector2u dest, IntRect sourceRect) => Copy(source, dest, sourceRect, false); //////////////////////////////////////////////////////////// /// @@ -257,32 +252,29 @@ public bool SaveToMemory(out byte[] output, string format) /// be used at initialization time /// /// Source image to copy - /// X coordinate of the destination position - /// Y coordinate of the destination position + /// Coordinates of the destination position /// Sub-rectangle of the source image to copy /// Should the copy take in account the source transparency? //////////////////////////////////////////////////////////// - public void Copy(Image source, uint destX, uint destY, IntRect sourceRect, bool applyAlpha) => sfImage_copyImage(CPointer, source.CPointer, destX, destY, sourceRect, applyAlpha); + public void Copy(Image source, Vector2u dest, IntRect sourceRect, bool applyAlpha) => sfImage_copyImage(CPointer, source.CPointer, dest, sourceRect, applyAlpha); //////////////////////////////////////////////////////////// /// /// Get a pixel from the image /// - /// X coordinate of pixel in the image - /// Y coordinate of pixel in the image + /// Coordinates of pixel to change /// Color of pixel (x, y) //////////////////////////////////////////////////////////// - public Color GetPixel(uint x, uint y) => sfImage_getPixel(CPointer, x, y); + public Color GetPixel(Vector2u coords) => sfImage_getPixel(CPointer, coords); //////////////////////////////////////////////////////////// /// /// Change the color of a pixel /// - /// X coordinate of pixel in the image - /// Y coordinate of pixel in the image + /// Coordinates of pixel to change /// New color for pixel (x, y) //////////////////////////////////////////////////////////// - public void SetPixel(uint x, uint y, Color color) => sfImage_setPixel(CPointer, x, y, color); + public void SetPixel(Vector2u coords, Color color) => sfImage_setPixel(CPointer, coords, color); //////////////////////////////////////////////////////////// /// @@ -357,10 +349,10 @@ internal Image(IntPtr cPointer) : base(cPointer) { } #region Imports [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern IntPtr sfImage_createFromColor(uint width, uint height, Color col); + private static extern IntPtr sfImage_createFromColor(Vector2u size, Color col); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern unsafe IntPtr sfImage_createFromPixels(uint width, uint height, byte* pixels); + private static extern unsafe IntPtr sfImage_createFromPixels(Vector2u size, byte* pixels); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern IntPtr sfImage_createFromFile(string filename); @@ -378,22 +370,24 @@ internal Image(IntPtr cPointer) : base(cPointer) { } private static extern void sfImage_destroy(IntPtr cPointer); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfImage_saveToFile(IntPtr cPointer, string filename); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfImage_saveToMemory(IntPtr cPointer, IntPtr bufferOutput, string format); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfImage_createMaskFromColor(IntPtr cPointer, Color col, byte alpha); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern void sfImage_copyImage(IntPtr cPointer, IntPtr source, uint destX, uint destY, IntRect sourceRect, bool applyAlpha); + private static extern void sfImage_copyImage(IntPtr cPointer, IntPtr source, Vector2u dest, IntRect sourceRect, bool applyAlpha); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern void sfImage_setPixel(IntPtr cPointer, uint x, uint y, Color col); + private static extern void sfImage_setPixel(IntPtr cPointer, Vector2u coords, Color col); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern Color sfImage_getPixel(IntPtr cPointer, uint x, uint y); + private static extern Color sfImage_getPixel(IntPtr cPointer, Vector2u coords); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern IntPtr sfImage_getPixelsPtr(IntPtr cPointer); diff --git a/src/SFML.Graphics/PrimitiveType.cs b/src/SFML.Graphics/PrimitiveType.cs index 9068f13d..c2f68e4e 100644 --- a/src/SFML.Graphics/PrimitiveType.cs +++ b/src/SFML.Graphics/PrimitiveType.cs @@ -1,5 +1,3 @@ -using System; - namespace SFML.Graphics { //////////////////////////////////////////////////////////// @@ -31,21 +29,6 @@ public enum PrimitiveType TriangleStrip, /// Connected Triangles; each point uses the first point and the previous point to form a triangle - TriangleFan, - - /// Quadrilaterals; each set of four points forms a 4-sided shape - Quads, - - /// DEPRECATED: Use LineStrip - [Obsolete("LineStrip")] - LinesStrip = LineStrip, - - /// DEPRECATED: Use TriangleStrip - [Obsolete("Use TriangleStrip")] - TrianglesStrip = TriangleStrip, - - /// DEPRECATED: Use TriangleFan - [Obsolete("Use TriangleFan")] - TrianglesFan = TriangleFan, + TriangleFan } } diff --git a/src/SFML.Graphics/Rect.cs b/src/SFML.Graphics/Rect.cs index 9cfe5a01..61fe5a1d 100644 --- a/src/SFML.Graphics/Rect.cs +++ b/src/SFML.Graphics/Rect.cs @@ -13,23 +13,6 @@ namespace SFML.Graphics [StructLayout(LayoutKind.Sequential)] public struct IntRect : IEquatable { - //////////////////////////////////////////////////////////// - /// - /// Construct the rectangle from its coordinates - /// - /// Left coordinate of the rectangle - /// Top coordinate of the rectangle - /// Width of the rectangle - /// Height of the rectangle - //////////////////////////////////////////////////////////// - public IntRect(int left, int top, int width, int height) - { - Left = left; - Top = top; - Width = width; - Height = height; - } - //////////////////////////////////////////////////////////// /// /// Construct the rectangle from position and size @@ -38,39 +21,30 @@ public IntRect(int left, int top, int width, int height) /// Size of the rectangle //////////////////////////////////////////////////////////// public IntRect(Vector2i position, Vector2i size) - : this(position.X, position.Y, size.X, size.Y) { + Position = position; + Size = size; } //////////////////////////////////////////////////////////// /// /// Check if a point is inside the rectangle's area /// - /// X coordinate of the point to test - /// Y coordinate of the point to test + /// Point to test /// True if the point is inside //////////////////////////////////////////////////////////// - public bool Contains(int x, int y) + public bool Contains(Vector2i point) { var minX = Math.Min(Left, Left + Width); var maxX = Math.Max(Left, Left + Width); var minY = Math.Min(Top, Top + Height); var maxY = Math.Max(Top, Top + Height); - return (x >= minX) && (x < maxX) && (y >= minY) && (y < maxY); + return + (point.X >= minX) && (point.X < maxX) && + (point.Y >= minY) && (point.Y < maxY); } - - //////////////////////////////////////////////////////////// - /// - /// Check if a point is inside the rectangle's area - /// - /// Vector2 position of the point to test - /// True if the point is inside - //////////////////////////////////////////////////////////// - public bool Contains(Vector2i point) => Contains(point.X, point.Y); - - //////////////////////////////////////////////////////////// /// /// Check if a point is inside the rectangle's area @@ -80,7 +54,6 @@ public bool Contains(int x, int y) //////////////////////////////////////////////////////////// public bool Contains(Vector2u point) => Contains((Vector2i)point); - //////////////////////////////////////////////////////////// /// /// Check if a point is inside the rectangle's area @@ -92,22 +65,12 @@ public bool Contains(int x, int y) //////////////////////////////////////////////////////////// /// - /// Check intersection between two rectangles + /// Check the intersection between two rectangles /// /// Rectangle to test - /// True if rectangles overlap + /// Intersection rectangle if intersecting, null otherwise //////////////////////////////////////////////////////////// - public bool Intersects(IntRect rect) => Intersects(rect, out _); - - //////////////////////////////////////////////////////////// - /// - /// Check intersection between two rectangles - /// - /// Rectangle to test - /// Rectangle to be filled with overlapping rect - /// True if rectangles overlap - //////////////////////////////////////////////////////////// - public bool Intersects(IntRect rect, out IntRect overlap) + public IntRect? FindIntersection(IntRect rect) { // Rectangles with negative dimensions are allowed, so we must handle them correctly @@ -132,38 +95,14 @@ public bool Intersects(IntRect rect, out IntRect overlap) // If the intersection is valid (positive non zero area), then there is an intersection if ((interLeft < interRight) && (interTop < interBottom)) { - overlap.Left = interLeft; - overlap.Top = interTop; - overlap.Width = interRight - interLeft; - overlap.Height = interBottom - interTop; - return true; + return new IntRect((interLeft, interTop), (interRight - interLeft, interBottom - interTop)); } else { - overlap.Left = 0; - overlap.Top = 0; - overlap.Width = 0; - overlap.Height = 0; - return false; + return null; } } - //////////////////////////////////////////////////////////// - /// - /// Get the position of the rectangle's top-left corner - /// - /// Position of rectangle - //////////////////////////////////////////////////////////// - public Vector2i Position => new Vector2i(Left, Top); - - //////////////////////////////////////////////////////////// - /// - /// Get the size of the rectangle - /// - /// Size of rectangle - //////////////////////////////////////////////////////////// - public Vector2i Size => new Vector2i(Width, Height); - /// /// Deconstructs an IntRect into a tuple of ints /// @@ -173,10 +112,10 @@ public bool Intersects(IntRect rect, out IntRect overlap) /// Height of the rectangle public void Deconstruct(out int left, out int top, out int width, out int height) { - left = Left; - top = Top; - width = Width; - height = Height; + left = Position.X; + top = Position.Y; + width = Size.X; + height = Size.Y; } //////////////////////////////////////////////////////////// @@ -185,7 +124,7 @@ public void Deconstruct(out int left, out int top, out int width, out int height /// /// String description of the object //////////////////////////////////////////////////////////// - public override string ToString() => $"[IntRect] Left({Left}) Top({Top}) Width({Width}) Height({Height})"; + public override string ToString() => $"[IntRect] Position({Left}, {Top}) Size({Width}, {Height})"; //////////////////////////////////////////////////////////// /// @@ -194,7 +133,7 @@ public void Deconstruct(out int left, out int top, out int width, out int height /// Object to check /// Object and rectangle are equal //////////////////////////////////////////////////////////// - public override bool Equals(object obj) => (obj is IntRect) && Equals((IntRect)obj); + public override bool Equals(object obj) => (obj is IntRect rect) && Equals(rect); /////////////////////////////////////////////////////////// /// @@ -243,7 +182,7 @@ public override int GetHashCode() => unchecked((int)((uint)Left ^ /// Converts a tuple of ints to an IntRect /// /// The tuple to convert - public static implicit operator IntRect((int Left, int Top, int Width, int Height) tuple) => new IntRect(tuple.Left, tuple.Top, tuple.Width, tuple.Height); + public static implicit operator IntRect((int Left, int Top, int Width, int Height) tuple) => new IntRect((tuple.Left, tuple.Top), (tuple.Width, tuple.Height)); //////////////////////////////////////////////////////////// /// @@ -252,22 +191,30 @@ public override int GetHashCode() => unchecked((int)((uint)Left ^ /// Rectangle being casted /// Casting result //////////////////////////////////////////////////////////// - public static explicit operator FloatRect(IntRect r) => new FloatRect(r.Left, - r.Top, - r.Width, - r.Height); + public static explicit operator FloatRect(IntRect r) => new FloatRect( + (r.Left, r.Top), + (r.Width, r.Height)); /// Left coordinate of the rectangle - public int Left; + public int Left => Position.X; /// Top coordinate of the rectangle - public int Top; + public int Top => Position.Y; /// Width of the rectangle - public int Width; + public int Width => Size.X; /// Height of the rectangle - public int Height; + public int Height => Size.Y; + + /// Position of the center of the rectangle + public Vector2i Center => Position + (Size / 2); + + /// Position of the top-left corner of the rectangle + public Vector2i Position; + + /// Size of the rectangle + public Vector2i Size; } //////////////////////////////////////////////////////////// @@ -279,23 +226,6 @@ public override int GetHashCode() => unchecked((int)((uint)Left ^ [StructLayout(LayoutKind.Sequential)] public struct FloatRect : IEquatable { - //////////////////////////////////////////////////////////// - /// - /// Construct the rectangle from its coordinates - /// - /// Left coordinate of the rectangle - /// Top coordinate of the rectangle - /// Width of the rectangle - /// Height of the rectangle - //////////////////////////////////////////////////////////// - public FloatRect(float left, float top, float width, float height) - { - Left = left; - Top = top; - Width = width; - Height = height; - } - //////////////////////////////////////////////////////////// /// /// Construct the rectangle from position and size @@ -304,37 +234,28 @@ public FloatRect(float left, float top, float width, float height) /// Size of the rectangle //////////////////////////////////////////////////////////// public FloatRect(Vector2f position, Vector2f size) - : this(position.X, position.Y, size.X, size.Y) { + Position = position; + Size = size; } //////////////////////////////////////////////////////////// /// /// Check if a point is inside the rectangle's area /// - /// X coordinate of the point to test - /// Y coordinate of the point to test + /// Point to test /// True if the point is inside //////////////////////////////////////////////////////////// - public bool Contains(float x, float y) + public bool Contains(Vector2f point) { var minX = Math.Min(Left, Left + Width); var maxX = Math.Max(Left, Left + Width); var minY = Math.Min(Top, Top + Height); var maxY = Math.Max(Top, Top + Height); - return (x >= minX) && (x < maxX) && (y >= minY) && (y < maxY); + return (point.X >= minX) && (point.X < maxX) && (point.Y >= minY) && (point.Y < maxY); } - //////////////////////////////////////////////////////////// - /// - /// Check if a point is inside the rectangle's area - /// - /// Vector2 position of the point to test - /// True if the point is inside - //////////////////////////////////////////////////////////// - public bool Contains(Vector2f point) => Contains(point.X, point.Y); - //////////////////////////////////////////////////////////// /// /// Check if a point is inside the rectangle's area @@ -344,7 +265,6 @@ public bool Contains(float x, float y) //////////////////////////////////////////////////////////// public bool Contains(Vector2i point) => Contains((Vector2f)point); - //////////////////////////////////////////////////////////// /// /// Check if a point is inside the rectangle's area @@ -361,17 +281,7 @@ public bool Contains(float x, float y) /// Rectangle to test /// True if rectangles overlap //////////////////////////////////////////////////////////// - public bool Intersects(FloatRect rect) => Intersects(rect, out _); - - //////////////////////////////////////////////////////////// - /// - /// Check intersection between two rectangles - /// - /// Rectangle to test - /// Rectangle to be filled with overlapping rect - /// True if rectangles overlap - //////////////////////////////////////////////////////////// - public bool Intersects(FloatRect rect, out FloatRect overlap) + public FloatRect? FindIntersection(FloatRect rect) { // Rectangles with negative dimensions are allowed, so we must handle them correctly @@ -396,38 +306,14 @@ public bool Intersects(FloatRect rect, out FloatRect overlap) // If the intersection is valid (positive non zero area), then there is an intersection if ((interLeft < interRight) && (interTop < interBottom)) { - overlap.Left = interLeft; - overlap.Top = interTop; - overlap.Width = interRight - interLeft; - overlap.Height = interBottom - interTop; - return true; + return new FloatRect((interLeft, interTop), (interRight - interLeft, interBottom - interTop)); } else { - overlap.Left = 0; - overlap.Top = 0; - overlap.Width = 0; - overlap.Height = 0; - return false; + return null; } } - //////////////////////////////////////////////////////////// - /// - /// Get the position of the rectangle's top-left corner - /// - /// Position of rectangle - //////////////////////////////////////////////////////////// - public Vector2f Position => new Vector2f(Left, Top); - - //////////////////////////////////////////////////////////// - /// - /// Get the size of the rectangle - /// - /// Size of rectangle - //////////////////////////////////////////////////////////// - public Vector2f Size => new Vector2f(Width, Height); - /// /// Deconstructs a FloatRect into a tuple of floats /// @@ -449,11 +335,7 @@ public void Deconstruct(out float left, out float top, out float width, out floa /// /// String description of the object //////////////////////////////////////////////////////////// - public override string ToString() => "[FloatRect]" + - " Left(" + Left + ")" + - " Top(" + Top + ")" + - " Width(" + Width + ")" + - " Height(" + Height + ")"; + public override string ToString() => $"[FloatRect] Position({Left}, {Top}) Size({Width}, {Height})"; //////////////////////////////////////////////////////////// /// @@ -462,7 +344,7 @@ public override string ToString() => "[FloatRect]" + /// Object to check /// Object and rectangle are equal //////////////////////////////////////////////////////////// - public override bool Equals(object obj) => (obj is FloatRect) && Equals((FloatRect)obj); + public override bool Equals(object obj) => (obj is FloatRect rect) && Equals(rect); /////////////////////////////////////////////////////////// /// @@ -471,10 +353,9 @@ public override string ToString() => "[FloatRect]" + /// Rectangle to check /// Rectangles are equal //////////////////////////////////////////////////////////// - public bool Equals(FloatRect other) => (Left == other.Left) && - (Top == other.Top) && - (Width == other.Width) && - (Height == other.Height); + public bool Equals(FloatRect other) => + Position == other.Position && + Size == other.Size; //////////////////////////////////////////////////////////// /// @@ -511,7 +392,7 @@ public override int GetHashCode() => unchecked((int)((uint)Left ^ /// Converts a tuple of floats to a FloatRect /// /// The tuple to convert - public static implicit operator FloatRect((float Left, float Top, float Width, float Height) tuple) => new FloatRect(tuple.Left, tuple.Top, tuple.Width, tuple.Height); + public static implicit operator FloatRect((float Left, float Top, float Width, float Height) tuple) => new FloatRect((tuple.Left, tuple.Top), (tuple.Width, tuple.Height)); //////////////////////////////////////////////////////////// /// @@ -520,21 +401,29 @@ public override int GetHashCode() => unchecked((int)((uint)Left ^ /// Rectangle being casted /// Casting result //////////////////////////////////////////////////////////// - public static explicit operator IntRect(FloatRect r) => new IntRect((int)r.Left, - (int)r.Top, - (int)r.Width, - (int)r.Height); + public static explicit operator IntRect(FloatRect r) => new IntRect( + ((int)r.Left, (int)r.Top), + ((int)r.Width, (int)r.Height)); /// Left coordinate of the rectangle - public float Left; + public float Left => Position.X; /// Top coordinate of the rectangle - public float Top; + public float Top => Position.Y; /// Width of the rectangle - public float Width; + public float Width => Size.X; /// Height of the rectangle - public float Height; + public float Height => Size.Y; + + /// Position of the center of the rectangle + public Vector2f Center => Position + (Size / 2f); + + /// Position of the top-left corner of the rectangle + public Vector2f Position; + + /// Size of the rectangle + public Vector2f Size; } } diff --git a/src/SFML.Graphics/RectangleShape.cs b/src/SFML.Graphics/RectangleShape.cs index d5dae6e5..8f8b30f6 100644 --- a/src/SFML.Graphics/RectangleShape.cs +++ b/src/SFML.Graphics/RectangleShape.cs @@ -1,3 +1,6 @@ +using System; +using System.Runtime.InteropServices; +using System.Security; using SFML.System; namespace SFML.Graphics @@ -44,7 +47,11 @@ public RectangleShape(RectangleShape copy) : public Vector2f Size { get => _size; - set { _size = value; Update(); } + set + { + _size = value; + Update(); + } } //////////////////////////////////////////////////////////// @@ -84,6 +91,24 @@ public override Vector2f GetPoint(uint index) } } + //////////////////////////////////////////////////////////// + /// + /// Get the geometric center of the rectangle + /// + /// The returned point is in local coordinates, that is, + /// the shape's transforms (position, rotation, scale) are + /// not taken into account. + /// + /// + /// The geometric center of the shape + //////////////////////////////////////////////////////////// + public override Vector2f GetGeometricCenter() => sfRectangleShape_getGeometricCenter(CPointer); + private Vector2f _size; + + #region Imports + [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern Vector2f sfRectangleShape_getGeometricCenter(IntPtr cPointer); + #endregion } } diff --git a/src/SFML.Graphics/RenderStates.cs b/src/SFML.Graphics/RenderStates.cs index 0531395d..c7a5a5fd 100644 --- a/src/SFML.Graphics/RenderStates.cs +++ b/src/SFML.Graphics/RenderStates.cs @@ -17,7 +17,18 @@ public struct RenderStates /// Blend mode to use //////////////////////////////////////////////////////////// public RenderStates(BlendMode blendMode) : - this(blendMode, Transform.Identity, null, null) + this(blendMode, StencilMode.Default, Transform.Identity, CoordinateType.Pixels, null, null) + { + } + + //////////////////////////////////////////////////////////// + /// + /// Construct a default set of render states with a custom stencil mode + /// + /// Stencil mode to use + //////////////////////////////////////////////////////////// + public RenderStates(StencilMode stencilMode) : + this(BlendMode.Alpha, stencilMode, Transform.Identity, CoordinateType.Pixels, null, null) { } @@ -28,7 +39,7 @@ public RenderStates(BlendMode blendMode) : /// Transform to use //////////////////////////////////////////////////////////// public RenderStates(Transform transform) : - this(BlendMode.Alpha, transform, null, null) + this(BlendMode.Alpha, StencilMode.Default, transform, CoordinateType.Pixels, null, null) { } @@ -39,7 +50,7 @@ public RenderStates(Transform transform) : /// Texture to use //////////////////////////////////////////////////////////// public RenderStates(Texture texture) : - this(BlendMode.Alpha, Transform.Identity, texture, null) + this(BlendMode.Alpha, StencilMode.Default, Transform.Identity, CoordinateType.Pixels, texture, null) { } @@ -50,7 +61,7 @@ public RenderStates(Texture texture) : /// Shader to use //////////////////////////////////////////////////////////// public RenderStates(Shader shader) : - this(BlendMode.Alpha, Transform.Identity, null, shader) + this(BlendMode.Alpha, StencilMode.Default, Transform.Identity, CoordinateType.Pixels, null, shader) { } @@ -59,14 +70,18 @@ public RenderStates(Shader shader) : /// Construct a set of render states with all its attributes /// /// Blend mode to use + /// Stencil mode to use /// Transform to use + /// Coordinate type to use /// Texture to use /// Shader to use //////////////////////////////////////////////////////////// - public RenderStates(BlendMode blendMode, Transform transform, Texture texture, Shader shader) + public RenderStates(BlendMode blendMode, StencilMode stencilMode, Transform transform, CoordinateType coordinateType, Texture texture, Shader shader) { BlendMode = blendMode; + StencilMode = stencilMode; Transform = transform; + CoordinateType = coordinateType; Texture = texture; Shader = shader; } @@ -80,7 +95,9 @@ public RenderStates(BlendMode blendMode, Transform transform, Texture texture, S public RenderStates(RenderStates copy) { BlendMode = copy.BlendMode; + StencilMode = copy.StencilMode; Transform = copy.Transform; + CoordinateType = copy.CoordinateType; Texture = copy.Texture; Shader = copy.Shader; } @@ -88,14 +105,20 @@ public RenderStates(RenderStates copy) //////////////////////////////////////////////////////////// /// Special instance holding the default render states //////////////////////////////////////////////////////////// - public static RenderStates Default => new RenderStates(BlendMode.Alpha, Transform.Identity, null, null); + public static RenderStates Default => new RenderStates(BlendMode.Alpha, StencilMode.Default, Transform.Identity, CoordinateType.Pixels, null, null); /// Blending mode public BlendMode BlendMode; + /// Stencil mode + public StencilMode StencilMode; + /// Transform public Transform Transform; + /// Texture coordinate type + public CoordinateType CoordinateType; + /// Texture public Texture Texture; @@ -108,7 +131,9 @@ internal MarshalData Marshal() var data = new MarshalData { BlendMode = BlendMode, + StencilMode = StencilMode, Transform = Transform, + CoordinateType = CoordinateType, Texture = Texture != null ? Texture.CPointer : IntPtr.Zero, Shader = Shader != null ? Shader.CPointer : IntPtr.Zero }; @@ -120,7 +145,9 @@ internal MarshalData Marshal() internal struct MarshalData { public BlendMode BlendMode; + public StencilMode StencilMode; public Transform Transform; + public CoordinateType CoordinateType; public IntPtr Texture; public IntPtr Shader; } diff --git a/src/SFML.Graphics/RenderTexture.cs b/src/SFML.Graphics/RenderTexture.cs index 98dd0500..15e814b0 100644 --- a/src/SFML.Graphics/RenderTexture.cs +++ b/src/SFML.Graphics/RenderTexture.cs @@ -17,44 +17,23 @@ public class RenderTexture : ObjectBase, IRenderTarget /// /// Create the render-texture with the given dimensions /// - /// Width of the render-texture - /// Height of the render-texture + /// Width and height of the render-texture //////////////////////////////////////////////////////////// - public RenderTexture(uint width, uint height) : - this(width, height, default(ContextSettings)) + public RenderTexture(Vector2u size) : + this(size, default) { } - //////////////////////////////////////////////////////////// - /// - /// Create the render-texture with the given dimensions and - /// an optional depth-buffer attached - /// - /// Width of the render-texture - /// Height of the render-texture - /// Do you want a depth-buffer attached? - //////////////////////////////////////////////////////////// - [Obsolete("Use RenderTexture(width, height, contextSettings)")] - public RenderTexture(uint width, uint height, bool depthBuffer) : - base(sfRenderTexture_create(width, height, depthBuffer)) - { - _defaultView = new View(sfRenderTexture_getDefaultView(CPointer)); - Texture = new Texture(sfRenderTexture_getTexture(CPointer)); - GC.SuppressFinalize(_defaultView); - GC.SuppressFinalize(Texture); - } - //////////////////////////////////////////////////////////// /// /// Create the render-texture with the given dimensions and /// a ContextSettings. /// - /// Width of the render-texture - /// Height of the render-texture + /// Width and height of the render-texture /// A ContextSettings struct representing settings for the RenderTexture //////////////////////////////////////////////////////////// - public RenderTexture(uint width, uint height, ContextSettings contextSettings) : - base(sfRenderTexture_createWithSettings(width, height, ref contextSettings)) + public RenderTexture(Vector2u size, ContextSettings contextSettings) : + base(sfRenderTexture_create(size, ref contextSettings)) { _defaultView = new View(sfRenderTexture_getDefaultView(CPointer)); Texture = new Texture(sfRenderTexture_getTexture(CPointer)); @@ -134,6 +113,20 @@ public bool Repeated //////////////////////////////////////////////////////////// public IntRect GetViewport(View view) => sfRenderTexture_getViewport(CPointer, view.CPointer); + //////////////////////////////////////////////////////////// + /// + /// Get the scissor rectangle of a view, applied to this render target + /// + /// The scissor rectangle is defined in the view as a ratio. This + /// function simply applies this ratio to the current dimensions + /// of the render target to calculate the pixels rectangle + /// that the scissor rectangle actually covers in the target. + /// + /// The view for which we want to compute the scissor rectangle + /// Scissor rectangle, expressed in pixels + //////////////////////////////////////////////////////////// + public IntRect GetScissor(View view) => sfRenderTexture_getScissor(CPointer, view.CPointer); + //////////////////////////////////////////////////////////// /// /// Convert a point from target coordinates to world @@ -250,6 +243,29 @@ public bool Repeated //////////////////////////////////////////////////////////// public void Clear(Color color) => sfRenderTexture_clear(CPointer, color); + //////////////////////////////////////////////////////////// + /// + /// Clear the entire target with a single color and stencil value + /// + /// The specified stencil value is truncated to the bit + /// width of the current stencil buffer. + /// + /// Fill color to use to clear the render target + /// Stencil value to clear to + //////////////////////////////////////////////////////////// + public void Clear(Color color, StencilValue stencilValue) => sfRenderTexture_clearColorAndStencil(CPointer, color, stencilValue); + + //////////////////////////////////////////////////////////// + /// + /// Clear the stencil buffer to a specific value + /// + /// The specified value is truncated to the bit width of + /// the current stencil buffer. + /// + /// Stencil value to clear to + //////////////////////////////////////////////////////////// + public void ClearStencil(StencilValue stencilValue) => sfRenderTexture_clearStencil(CPointer, stencilValue); + //////////////////////////////////////////////////////////// /// /// Update the contents of the target texture @@ -269,7 +285,7 @@ public bool Repeated /// The maximum anti-aliasing level supported by the system /// //////////////////////////////////////////////////////////// - public static uint MaximumAntialiasingLevel => sfRenderTexture_getMaximumAntialiasingLevel(); + public static uint MaximumAntiAliasingLevel => sfRenderTexture_getMaximumAntiAliasingLevel(); //////////////////////////////////////////////////////////// /// @@ -467,11 +483,8 @@ protected override void Destroy(bool disposing) private readonly View _defaultView; #region Imports - [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity, Obsolete] - private static extern IntPtr sfRenderTexture_create(uint width, uint height, bool depthBuffer); - [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern IntPtr sfRenderTexture_createWithSettings(uint width, uint height, ref ContextSettings settings); + private static extern IntPtr sfRenderTexture_create(Vector2u size, ref ContextSettings settings); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfRenderTexture_destroy(IntPtr cPointer); @@ -479,13 +492,21 @@ protected override void Destroy(bool disposing) [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfRenderTexture_clear(IntPtr cPointer, Color clearColor); + [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfRenderTexture_clearStencil(IntPtr cPointer, StencilValue stencilValue); + + [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfRenderTexture_clearColorAndStencil(IntPtr cPointer, Color clearColor, StencilValue stencilValue); + [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern Vector2u sfRenderTexture_getSize(IntPtr cPointer); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfRenderTexture_isSrgb(IntPtr cPointer); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfRenderTexture_setActive(IntPtr cPointer, bool active); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] @@ -503,6 +524,9 @@ protected override void Destroy(bool disposing) [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern IntRect sfRenderTexture_getViewport(IntPtr cPointer, IntPtr targetView); + [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern IntRect sfRenderTexture_getScissor(IntPtr cPointer, IntPtr targetView); + [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern Vector2i sfRenderTexture_mapCoordsToPixel(IntPtr cPointer, Vector2f point, IntPtr view); @@ -513,21 +537,24 @@ protected override void Destroy(bool disposing) private static extern IntPtr sfRenderTexture_getTexture(IntPtr cPointer); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern uint sfRenderTexture_getMaximumAntialiasingLevel(); + private static extern uint sfRenderTexture_getMaximumAntiAliasingLevel(); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfRenderTexture_setSmooth(IntPtr cPointer, bool smooth); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfRenderTexture_isSmooth(IntPtr cPointer); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfRenderTexture_setRepeated(IntPtr cPointer, bool repeated); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfRenderTexture_isRepeated(IntPtr cPointer); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfRenderTexture_generateMipmap(IntPtr cPointer); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] diff --git a/src/SFML.Graphics/RenderWindow.cs b/src/SFML.Graphics/RenderWindow.cs index 838db6c1..1f56bd51 100644 --- a/src/SFML.Graphics/RenderWindow.cs +++ b/src/SFML.Graphics/RenderWindow.cs @@ -17,13 +17,13 @@ public class RenderWindow : Window.Window, IRenderTarget { //////////////////////////////////////////////////////////// /// - /// Create the window with default style and creation settings + /// Create the window with default style, state and creation settings /// /// Video mode to use /// Title of the window //////////////////////////////////////////////////////////// public RenderWindow(VideoMode mode, string title) : - this(mode, title, Styles.Default, new ContextSettings(0, 0)) + this(mode, title, Styles.Default, State.Windowed, new ContextSettings(0, 0)) { } @@ -34,9 +34,10 @@ public RenderWindow(VideoMode mode, string title) : /// Video mode to use /// Title of the window /// Window style (Resize | Close by default) + /// Window state //////////////////////////////////////////////////////////// - public RenderWindow(VideoMode mode, string title, Styles style) : - this(mode, title, style, new ContextSettings(0, 0)) + public RenderWindow(VideoMode mode, string title, Styles style, State state) : + this(mode, title, style, state, new ContextSettings(0, 0)) { } @@ -47,9 +48,10 @@ public RenderWindow(VideoMode mode, string title, Styles style) : /// Video mode to use /// Title of the window /// Window style (Resize | Close by default) + /// Window state /// Creation parameters //////////////////////////////////////////////////////////// - public RenderWindow(VideoMode mode, string title, Styles style, ContextSettings settings) : + public RenderWindow(VideoMode mode, string title, Styles style, State state, ContextSettings settings) : base(IntPtr.Zero, 0) { // Copy the string to a null-terminated UTF-32 byte array @@ -59,7 +61,7 @@ public RenderWindow(VideoMode mode, string title, Styles style, ContextSettings { fixed (byte* titlePtr = titleAsUtf32) { - CPointer = sfRenderWindow_createUnicode(mode, (IntPtr)titlePtr, style, ref settings); + CPointer = sfRenderWindow_createUnicode(mode, (IntPtr)titlePtr, style, state, ref settings); } } Initialize(); @@ -165,17 +167,16 @@ public override void SetTitle(string title) /// /// Change the window's icon /// - /// Icon's width, in pixels - /// Icon's height, in pixels + /// Icon's width and height, in pixels /// Array of pixels, format must be RGBA 32 bits //////////////////////////////////////////////////////////// - public override void SetIcon(uint width, uint height, byte[] pixels) + public override void SetIcon(Vector2u size, byte[] pixels) { unsafe { fixed (byte* pixelsPtr = pixels) { - sfRenderWindow_setIcon(CPointer, width, height, pixelsPtr); + sfRenderWindow_setIcon(CPointer, size, pixelsPtr); } } } @@ -304,7 +305,7 @@ public override void SetIcon(uint width, uint height, byte[] pixels) /// OS-specific handle of the window /// //////////////////////////////////////////////////////////// - public override IntPtr SystemHandle => sfRenderWindow_getSystemHandle(CPointer); + public override IntPtr NativeHandle => sfRenderWindow_getNativeHandle(CPointer); //////////////////////////////////////////////////////////// /// @@ -321,6 +322,30 @@ public override void SetIcon(uint width, uint height, byte[] pixels) //////////////////////////////////////////////////////////// public void Clear(Color color) => sfRenderWindow_clear(CPointer, color); + //////////////////////////////////////////////////////////// + /// + /// Clear the entire target with a single color and stencil value + /// + /// The specified stencil value is truncated to the bit + /// width of the current stencil buffer. + /// + /// Fill color to use to clear the render target + /// Stencil value to clear to + //////////////////////////////////////////////////////////// + public void Clear(Color color, StencilValue stencilValue) => sfRenderWindow_clearColorAndStencil(CPointer, color, stencilValue); + + //////////////////////////////////////////////////////////// + /// + /// Clear the stencil buffer to a specific value + /// + /// The specified value is truncated to the bit width of + /// the current stencil buffer. + /// + /// Stencil value to clear to + //////////////////////////////////////////////////////////// + public void ClearStencil(StencilValue stencilValue) => sfRenderWindow_clearStencil(CPointer, stencilValue); + + //////////////////////////////////////////////////////////// /// /// Change the current active view @@ -353,6 +378,20 @@ public override void SetIcon(uint width, uint height, byte[] pixels) //////////////////////////////////////////////////////////// public IntRect GetViewport(View view) => sfRenderWindow_getViewport(CPointer, view.CPointer); + //////////////////////////////////////////////////////////// + /// + /// Get the scissor rectangle of a view, applied to this render target + /// + /// The scissor rectangle is defined in the view as a ratio. This + /// function simply applies this ratio to the current dimensions + /// of the render target to calculate the pixels rectangle + /// that the scissor rectangle actually covers in the target. + /// + /// The view for which we want to compute the scissor rectangle + /// Scissor rectangle, expressed in pixels + //////////////////////////////////////////////////////////// + public IntRect GetScissor(View view) => sfRenderWindow_getScissor(CPointer, view.CPointer); + //////////////////////////////////////////////////////////// /// /// Convert a point from target coordinates to world @@ -576,24 +615,6 @@ public void Draw(Vertex[] vertices, uint start, uint count, PrimitiveType type, //////////////////////////////////////////////////////////// public void ResetGLStates() => sfRenderWindow_resetGLStates(CPointer); - //////////////////////////////////////////////////////////// - /// - /// Capture the current contents of the window into an image. - /// - /// - /// - /// Deprecated. Use and - /// instead: - /// - /// Texture texture = new Texture(window.Size); - /// texture.update(window); - /// Image img = texture.CopyToImage(); - /// - /// - //////////////////////////////////////////////////////////// - [Obsolete("Use Texture and Texture.Update(RenderWindow)")] - public Image Capture() => new Image(sfRenderWindow_capture(CPointer)); - //////////////////////////////////////////////////////////// /// /// Provide a string describing the object @@ -628,10 +649,11 @@ public override string ToString() /// /// Internal function to get the next event (blocking) /// + /// Maximum time to wait ( for infinite) /// Variable to fill with the raw pointer to the event structure /// False if any error occurred //////////////////////////////////////////////////////////// - protected override bool WaitEvent(out Event eventToFill) => sfRenderWindow_waitEvent(CPointer, out eventToFill); + protected override bool WaitEvent(Time timeout, out Event eventToFill) => sfRenderWindow_waitEvent(CPointer, timeout, out eventToFill); //////////////////////////////////////////////////////////// /// @@ -697,7 +719,7 @@ private void Initialize() #region Imports [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern IntPtr sfRenderWindow_createUnicode(VideoMode mode, IntPtr title, Styles style, ref ContextSettings settings); + private static extern IntPtr sfRenderWindow_createUnicode(VideoMode mode, IntPtr title, Styles style, State state, ref ContextSettings settings); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern IntPtr sfRenderWindow_createFromHandle(IntPtr handle, ref ContextSettings settings); @@ -709,16 +731,19 @@ private void Initialize() private static extern void sfRenderWindow_close(IntPtr cPointer); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfRenderWindow_isOpen(IntPtr cPointer); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern ContextSettings sfRenderWindow_getSettings(IntPtr cPointer); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfRenderWindow_pollEvent(IntPtr cPointer, out Event evt); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern bool sfRenderWindow_waitEvent(IntPtr cPointer, out Event evt); + [return: MarshalAs(UnmanagedType.I1)] + private static extern bool sfRenderWindow_waitEvent(IntPtr cPointer, Time timeout, out Event evt); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern Vector2i sfRenderWindow_getPosition(IntPtr cPointer); @@ -730,6 +755,7 @@ private void Initialize() private static extern Vector2u sfRenderWindow_getSize(IntPtr cPointer); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfRenderWindow_isSrgb(IntPtr cPointer); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] @@ -739,7 +765,7 @@ private void Initialize() private static extern void sfRenderWindow_setUnicodeTitle(IntPtr cPointer, IntPtr title); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern unsafe void sfRenderWindow_setIcon(IntPtr cPointer, uint width, uint height, byte* pixels); + private static extern unsafe void sfRenderWindow_setIcon(IntPtr cPointer, Vector2u size, byte* pixels); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfRenderWindow_setVisible(IntPtr cPointer, bool visible); @@ -766,23 +792,31 @@ private void Initialize() private static extern void sfRenderWindow_setJoystickThreshold(IntPtr cPointer, float threshold); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfRenderWindow_setActive(IntPtr cPointer, bool active); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfRenderWindow_requestFocus(IntPtr cPointer); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfRenderWindow_hasFocus(IntPtr cPointer); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfRenderWindow_display(IntPtr cPointer); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern IntPtr sfRenderWindow_getSystemHandle(IntPtr cPointer); + private static extern IntPtr sfRenderWindow_getNativeHandle(IntPtr cPointer); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfRenderWindow_clear(IntPtr cPointer, Color clearColor); + [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfRenderWindow_clearStencil(IntPtr cPointer, StencilValue stencilValue); + + [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfRenderWindow_clearColorAndStencil(IntPtr cPointer, Color clearColor, StencilValue stencilValue); + [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfRenderWindow_setView(IntPtr cPointer, IntPtr view); @@ -795,6 +829,9 @@ private void Initialize() [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern IntRect sfRenderWindow_getViewport(IntPtr cPointer, IntPtr targetView); + [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern IntRect sfRenderWindow_getScissor(IntPtr cPointer, IntPtr targetView); + [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern Vector2f sfRenderWindow_mapPixelToCoords(IntPtr cPointer, Vector2i point, IntPtr view); @@ -813,9 +850,6 @@ private void Initialize() [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfRenderWindow_resetGLStates(IntPtr cPointer); - [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern IntPtr sfRenderWindow_capture(IntPtr cPointer); - [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern Vector2i sfMouse_getPositionRenderWindow(IntPtr cPointer); @@ -826,6 +860,7 @@ private void Initialize() private static extern Vector2i sfTouch_getPositionRenderWindow(uint finger, IntPtr relativeTo); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfRenderWindow_createVulkanSurface(IntPtr cPointer, IntPtr vkInstance, out IntPtr surface, IntPtr vkAllocator); #endregion } diff --git a/src/SFML.Graphics/Shader.cs b/src/SFML.Graphics/Shader.cs index 08d83032..78627bb2 100644 --- a/src/SFML.Graphics/Shader.cs +++ b/src/SFML.Graphics/Shader.cs @@ -434,155 +434,6 @@ public unsafe void SetUniformArray(string name, Glsl.Mat4[] array) } } - //////////////////////////////////////////////////////////// - /// - /// Change a float parameter of the shader - /// - /// "name" is the name of the variable to change in the shader. - /// The corresponding parameter in the shader must be a float - /// (float GLSL type). - /// - /// - /// Name of the parameter in the shader - /// Value to assign - /// - //////////////////////////////////////////////////////////// - [Obsolete("Use SetUniform()")] - public void SetParameter(string name, float x) => sfShader_setFloatParameter(CPointer, name, x); - - //////////////////////////////////////////////////////////// - /// - /// Change a 2-components vector parameter of the shader - /// - /// "name" is the name of the variable to change in the shader. - /// The corresponding parameter in the shader must be a 2x1 vector - /// (vec2 GLSL type). - /// - /// Name of the parameter in the shader - /// First component of the value to assign - /// Second component of the value to assign - //////////////////////////////////////////////////////////// - [Obsolete("Use SetUniform()")] - public void SetParameter(string name, float x, float y) => sfShader_setFloat2Parameter(CPointer, name, x, y); - - //////////////////////////////////////////////////////////// - /// - /// Change a 3-components vector parameter of the shader - /// - /// "name" is the name of the variable to change in the shader. - /// The corresponding parameter in the shader must be a 3x1 vector - /// (vec3 GLSL type). - /// - /// Name of the parameter in the shader - /// First component of the value to assign - /// Second component of the value to assign - /// Third component of the value to assign - //////////////////////////////////////////////////////////// - [Obsolete("Use SetUniform()")] - public void SetParameter(string name, float x, float y, float z) => sfShader_setFloat3Parameter(CPointer, name, x, y, z); - - //////////////////////////////////////////////////////////// - /// - /// Change a 4-components vector parameter of the shader - /// - /// "name" is the name of the variable to change in the shader. - /// The corresponding parameter in the shader must be a 4x1 vector - /// (vec4 GLSL type). - /// - /// Name of the parameter in the shader - /// First component of the value to assign - /// Second component of the value to assign - /// Third component of the value to assign - /// Fourth component of the value to assign - //////////////////////////////////////////////////////////// - [Obsolete("Use SetUniform()")] - public void SetParameter(string name, float x, float y, float z, float w) => sfShader_setFloat4Parameter(CPointer, name, x, y, z, w); - - //////////////////////////////////////////////////////////// - /// - /// Change a 2-components vector parameter of the shader - /// - /// "name" is the name of the variable to change in the shader. - /// The corresponding parameter in the shader must be a 2x1 vector - /// (vec2 GLSL type). - /// - /// Name of the parameter in the shader - /// Vector to assign - //////////////////////////////////////////////////////////// - [Obsolete("Use SetUniform()")] - public void SetParameter(string name, Vector2f vector) => SetParameter(name, vector.X, vector.Y); - - //////////////////////////////////////////////////////////// - /// - /// Change a color parameter of the shader - /// - /// "name" is the name of the variable to change in the shader. - /// The corresponding parameter in the shader must be a 4x1 vector - /// (vec4 GLSL type). - /// - /// Name of the parameter in the shader - /// Color to assign - //////////////////////////////////////////////////////////// - [Obsolete("Use SetUniform()")] - public void SetParameter(string name, Color color) => sfShader_setColorParameter(CPointer, name, color); - - //////////////////////////////////////////////////////////// - /// - /// Change a matrix parameter of the shader - /// - /// "name" is the name of the variable to change in the shader. - /// The corresponding parameter in the shader must be a 4x4 matrix - /// (mat4 GLSL type). - /// - /// Name of the parameter in the shader - /// Transform to assign - //////////////////////////////////////////////////////////// - [Obsolete("Use SetUniform()")] - public void SetParameter(string name, Transform transform) => sfShader_setTransformParameter(CPointer, name, transform); - - //////////////////////////////////////////////////////////// - /// - /// Change a texture parameter of the shader - /// - /// "name" is the name of the variable to change in the shader. - /// The corresponding parameter in the shader must be a 2D texture - /// (sampler2D GLSL type). - /// - /// It is important to note that \a texture must remain alive as long - /// as the shader uses it, no copy is made internally. - /// - /// To use the texture of the object being draw, which cannot be - /// known in advance, you can pass the special value - /// Shader.CurrentTexture. - /// - /// Name of the texture in the shader - /// Texture to assign - //////////////////////////////////////////////////////////// - [Obsolete("Use SetUniform()")] - public void SetParameter(string name, Texture texture) - { - // Keep a reference to the Texture so it doesn't get GC'd - _textures[name] = texture; - sfShader_setTextureParameter(CPointer, name, texture.CPointer); - } - - //////////////////////////////////////////////////////////// - /// - /// Change a texture parameter of the shader - /// - /// This overload maps a shader texture variable to the - /// texture of the object being drawn, which cannot be - /// known in advance. The second argument must be - /// sf::Shader::CurrentTexture. - /// The corresponding parameter in the shader must be a 2D texture - /// (sampler2D GLSL type). - /// - /// Name of the texture in the shader - /// Always pass the special value Shader.CurrentTexture - //////////////////////////////////////////////////////////// - [Obsolete("Use SetUniform()")] - public void SetParameter(string name, CurrentTextureType current) => sfShader_setCurrentTextureParameter(CPointer, name); - //////////////////////////////////////////////////////////// /// /// Bind a shader for rendering @@ -754,30 +605,6 @@ public Shader(IntPtr ptr) : [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern unsafe void sfShader_setMat4UniformArray(IntPtr shader, string name, Glsl.Mat4* data, UIntPtr length); - [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity, Obsolete] - private static extern void sfShader_setFloatParameter(IntPtr shader, string name, float x); - - [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity, Obsolete] - private static extern void sfShader_setFloat2Parameter(IntPtr shader, string name, float x, float y); - - [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity, Obsolete] - private static extern void sfShader_setFloat3Parameter(IntPtr shader, string name, float x, float y, float z); - - [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity, Obsolete] - private static extern void sfShader_setFloat4Parameter(IntPtr shader, string name, float x, float y, float z, float w); - - [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity, Obsolete] - private static extern void sfShader_setColorParameter(IntPtr shader, string name, Color color); - - [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity, Obsolete] - private static extern void sfShader_setTransformParameter(IntPtr shader, string name, Transform transform); - - [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity, Obsolete] - private static extern void sfShader_setTextureParameter(IntPtr shader, string name, IntPtr texture); - - [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity, Obsolete] - private static extern void sfShader_setCurrentTextureParameter(IntPtr shader, string name); - [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern uint sfShader_getNativeHandle(IntPtr shader); @@ -785,9 +612,11 @@ public Shader(IntPtr ptr) : private static extern void sfShader_bind(IntPtr shader); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfShader_isAvailable(); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfShader_isGeometryAvailable(); #endregion } diff --git a/src/SFML.Graphics/Shape.cs b/src/SFML.Graphics/Shape.cs index a0429839..5558aba6 100644 --- a/src/SFML.Graphics/Shape.cs +++ b/src/SFML.Graphics/Shape.cs @@ -89,6 +89,19 @@ public float OutlineThickness //////////////////////////////////////////////////////////// public abstract Vector2f GetPoint(uint index); + //////////////////////////////////////////////////////////// + /// + /// Get the geometric center of the shape + /// + /// The returned point is in local coordinates, that is, + /// the shape's transforms (position, rotation, scale) are + /// not taken into account. + /// + /// + /// The geometric center of the shape + //////////////////////////////////////////////////////////// + public virtual Vector2f GetGeometricCenter() => sfShape_getGeometricCenter(CPointer); + //////////////////////////////////////////////////////////// /// /// Get the local bounding rectangle of the entity. @@ -258,6 +271,9 @@ public Shape(Shape copy) : [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern float sfShape_getOutlineThickness(IntPtr cPointer); + [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern Vector2f sfShape_getGeometricCenter(IntPtr cPointer); + [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern FloatRect sfShape_getLocalBounds(IntPtr cPointer); diff --git a/src/SFML.Graphics/Sprite.cs b/src/SFML.Graphics/Sprite.cs index c74cf4a0..a64c4383 100644 --- a/src/SFML.Graphics/Sprite.cs +++ b/src/SFML.Graphics/Sprite.cs @@ -16,16 +16,6 @@ namespace SFML.Graphics //////////////////////////////////////////////////////////// public class Sprite : Transformable, IDrawable { - //////////////////////////////////////////////////////////// - /// - /// Default constructor - /// - //////////////////////////////////////////////////////////// - public Sprite() : - base(sfSprite_create()) - { - } - //////////////////////////////////////////////////////////// /// /// Construct the sprite from a source texture @@ -33,7 +23,7 @@ public Sprite() : /// Source texture to assign to the sprite //////////////////////////////////////////////////////////// public Sprite(Texture texture) : - base(sfSprite_create()) => Texture = texture; + base(sfSprite_create(texture.CPointer)) => Texture = texture; //////////////////////////////////////////////////////////// /// @@ -43,7 +33,7 @@ public Sprite(Texture texture) : /// Sub-rectangle of the texture to assign to the sprite //////////////////////////////////////////////////////////// public Sprite(Texture texture, IntRect rectangle) : - base(sfSprite_create()) + base(sfSprite_create(texture.CPointer)) { Texture = texture; TextureRect = rectangle; @@ -180,7 +170,7 @@ public void Draw(IRenderTarget target, RenderStates states) #region Imports [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern IntPtr sfSprite_create(); + private static extern IntPtr sfSprite_create(IntPtr texture); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern IntPtr sfSprite_copy(IntPtr sprite); diff --git a/src/SFML.Graphics/StencilMode.cs b/src/SFML.Graphics/StencilMode.cs new file mode 100644 index 00000000..a0a40c9a --- /dev/null +++ b/src/SFML.Graphics/StencilMode.cs @@ -0,0 +1,197 @@ +using System.Runtime.InteropServices; + +namespace SFML.Graphics +{ + //////////////////////////////////////////////////////// + /// + /// Enumeration of the stencil test comparisons that can be performed + /// + /// The comparisons are mapped directly to their OpenGL equivalents, + /// specified by glStencilFunc(). + /// + //////////////////////////////////////////////////////// + public enum StencilComparison + { + /// The stencil test never passes + Never, + + /// The stencil test passes if the new value is less than the value in the stencil buffer + Less, + + /// The stencil test passes if the new value is less than or equal to the value in the stencil buffer + LessEqual, + + /// The stencil test passes if the new value is greater than the value in the stencil buffer + Greater, + + /// The stencil test passes if the new value is greater than or equal to the value in the stencil buffer + GreaterEqual, + + /// The stencil test passes if the new value is strictly equal to the value in the stencil buffer + Equal, + + /// The stencil test passes if the new value is strictly inequal to the value in the stencil buffer + NotEqual, + + /// The stencil test always passes + Always + } + + //////////////////////////////////////////////////////// + /// + /// Enumeration of the stencil buffer update operations + /// + /// The update operations are mapped directly to their OpenGL equivalents, + /// specified by glStencilOp(). + /// + //////////////////////////////////////////////////////// + public enum StencilUpdateOperation + { + /// If the stencil test passes, the value in the stencil buffer is not modified + Keep, + + /// If the stencil test passes, the value in the stencil buffer is set to zero + Zero, + + /// If the stencil test passes, the value in the stencil buffer is set to the new value + Replace, + + /// If the stencil test passes, the value in the stencil buffer is incremented and if required clamped + Increment, + + /// If the stencil test passes, the value in the stencil buffer is decremented and if required clamped + Decrement, + + /// If the stencil test passes, the value in the stencil buffer is bitwise inverted + Invert + } + + //////////////////////////////////////////////////////// + /// + /// Stencil value type (also used as a mask) + /// + //////////////////////////////////////////////////////// + [StructLayout(LayoutKind.Sequential)] + public struct StencilValue + { + //////////////////////////////////////////////////////////// + /// + /// The stored stencil value + /// + //////////////////////////////////////////////////////////// + public uint Value; + + //////////////////////////////////////////////////////////// + /// + /// Convert a signed integer to a stencil value + /// + //////////////////////////////////////////////////////////// + public static explicit operator StencilValue(int value) => new StencilValue() { Value = (uint)value }; + + //////////////////////////////////////////////////////////// + /// + /// Convert an unsigned integer to a stencil value + /// + //////////////////////////////////////////////////////////// + public static explicit operator StencilValue(uint value) => new StencilValue() { Value = value }; + } + + //////////////////////////////////////////////////////////// + /// + /// Stencil modes for drawing + /// + //////////////////////////////////////////////////////////// + [StructLayout(LayoutKind.Sequential)] + public struct StencilMode + { + /// + /// The comparison we're performing the stencil test with + /// + public StencilComparison StencilComparison; + + /// + /// The update operation to perform if the stencil test passes + /// + public StencilUpdateOperation StencilUpdateOperation; + + /// + /// The reference value we're performing the stencil test with + /// + public uint StencilReference; + + /// + /// The mask to apply to both the reference value and the value in the stencil buffer + /// + public uint StencilMask; + + /// + /// Whether we should update the color buffer in addition to the stencil buffer + /// + [MarshalAs(UnmanagedType.I1)] + public bool StencilOnly; + + /// + /// Default values for stencil mode + /// + public static readonly StencilMode Default = new StencilMode() + { + StencilComparison = StencilComparison.Always, + StencilUpdateOperation = StencilUpdateOperation.Keep, + StencilReference = 0, + StencilMask = ~0u, + StencilOnly = false + }; + + //////////////////////////////////////////////////////////// + /// + /// Compare two stencil modes and checks if they are equal + /// + /// Stencil Modes are equal + //////////////////////////////////////////////////////////// + public static bool operator ==(StencilMode left, StencilMode right) => left.Equals(right); + + //////////////////////////////////////////////////////////// + /// + /// Compare two stencil modes and checks if they are not equal + /// + /// Stencil Modes are not equal + //////////////////////////////////////////////////////////// + public static bool operator !=(StencilMode left, StencilMode right) => !left.Equals(right); + + //////////////////////////////////////////////////////////// + /// + /// Compare stencil mode and object and checks if they are equal + /// + /// Object to check + /// Object and stencil mode are equal + //////////////////////////////////////////////////////////// + public override bool Equals(object obj) => obj is StencilMode mode && Equals(mode); + + //////////////////////////////////////////////////////////// + /// + /// Provide a integer describing the object + /// + /// Integer description of the object + //////////////////////////////////////////////////////////// + public override int GetHashCode() => + StencilComparison.GetHashCode() ^ + StencilUpdateOperation.GetHashCode() ^ + StencilReference.GetHashCode() ^ + StencilMask.GetHashCode() ^ + StencilOnly.GetHashCode(); + + /////////////////////////////////////////////////////////// + /// + /// Compare two stencil modes and checks if they are equal + /// + /// Stencil mode to check + /// Stencil modes are equal + //////////////////////////////////////////////////////////// + public bool Equals(StencilMode other) => + StencilComparison == other.StencilComparison && + StencilUpdateOperation == other.StencilUpdateOperation && + StencilReference == other.StencilReference && + StencilMask == other.StencilMask && + StencilOnly == other.StencilOnly; + } +} diff --git a/src/SFML.Graphics/Text.cs b/src/SFML.Graphics/Text.cs index b6e2f612..3e869f7c 100644 --- a/src/SFML.Graphics/Text.cs +++ b/src/SFML.Graphics/Text.cs @@ -42,11 +42,12 @@ public enum Styles //////////////////////////////////////////////////////////// /// - /// Default constructor + /// Construct the text from a /// + /// Font to use //////////////////////////////////////////////////////////// - public Text() : - this("", null) + public Text(Font font) : + this(font, "", 30) { } @@ -54,11 +55,11 @@ public Text() : /// /// Construct the text from a string and a /// - /// String to display /// Font to use + /// String to display //////////////////////////////////////////////////////////// - public Text(string str, Font font) : - this(str, font, 30) + public Text(Font font, string str) : + this(font, str, 30) { } @@ -66,12 +67,12 @@ public Text(string str, Font font) : /// /// Construct the text from a string, and size /// - /// String to display /// Font to use + /// String to display /// Font size //////////////////////////////////////////////////////////// - public Text(string str, Font font, uint characterSize) : - base(sfText_create()) + public Text(Font font, string str, uint characterSize) : + base(sfText_create(font.CPointer)) { DisplayedString = str; Font = font; @@ -95,28 +96,6 @@ public Text(Text copy) : Font = copy.Font; } - //////////////////////////////////////////////////////////// - /// - /// DEPRECATED Fill of the - /// - /// - /// - /// Use instead. - /// - /// By default, the text's fill is opaque White. - /// - /// Setting the fill color to a transparent with an outline - /// will cause the outline to be displayed in the fill area of the text. - /// - /// - //////////////////////////////////////////////////////////// - [Obsolete("Use FillColor and OutlineColor")] - public Color Color - { - get => sfText_getFillColor(CPointer); - set => sfText_setFillColor(CPointer, value); - } - //////////////////////////////////////////////////////////// /// /// Fill of the @@ -370,7 +349,7 @@ public void Draw(IRenderTarget target, RenderStates states) #region Imports [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern IntPtr sfText_create(); + private static extern IntPtr sfText_create(IntPtr font); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern IntPtr sfText_copy(IntPtr text); diff --git a/src/SFML.Graphics/Texture.cs b/src/SFML.Graphics/Texture.cs index ce9f2e7f..76991460 100644 --- a/src/SFML.Graphics/Texture.cs +++ b/src/SFML.Graphics/Texture.cs @@ -26,17 +26,6 @@ namespace SFML.Graphics //////////////////////////////////////////////////////////// public class Texture : ObjectBase { - /// - /// Types of texture coordinates that can be used for rendering. - /// - public enum TextureCoordinateType - { - /// Texture coordinates in range [0 .. 1]. - Normalized, - /// Texture coordinates in range [0 .. size]. - Pixels - } - //////////////////////////////////////////////////////////// /// /// Construct the texture @@ -44,13 +33,22 @@ public enum TextureCoordinateType /// /// Textures created this way are uninitialized and have indeterminate contents. /// - /// Texture width - /// Texture height + /// Width and height of the texture + /// True to convert the texture source from sRGB, false otherwise /// //////////////////////////////////////////////////////////// - public Texture(uint width, uint height) : - base(sfTexture_create(width, height)) + public Texture(Vector2u size, bool srgb = false) : + base(IntPtr.Zero) { + if (srgb) + { + CPointer = sfTexture_createSrgb(size); + } + else + { + CPointer = sfTexture_create(size); + } + if (IsInvalid) { throw new LoadingFailedException("texture"); @@ -66,7 +64,7 @@ public Texture(uint width, uint height) : /// //////////////////////////////////////////////////////////// public Texture(string filename, bool srgb = false) : - this(filename, new IntRect(0, 0, 0, 0), srgb) + this(filename, new IntRect((0, 0), (0, 0)), srgb) { } @@ -106,7 +104,7 @@ public Texture(string filename, IntRect area, bool srgb = false) : /// //////////////////////////////////////////////////////////// public Texture(Stream stream, bool srgb = false) : - this(stream, new IntRect(0, 0, 0, 0), srgb) + this(stream, new IntRect((0, 0), (0, 0)), srgb) { } @@ -149,7 +147,7 @@ public Texture(Stream stream, IntRect area, bool srgb = false) : /// //////////////////////////////////////////////////////////// public Texture(Image image, bool srgb = false) : - this(image, new IntRect(0, 0, 0, 0), srgb) + this(image, new IntRect((0, 0), (0, 0)), srgb) { } @@ -189,7 +187,7 @@ public Texture(Image image, IntRect area, bool srgb = false) : /// //////////////////////////////////////////////////////////// public Texture(byte[] bytes, bool srgb = false) : - this(bytes, new IntRect(0, 0, 0, 0), srgb) + this(bytes, new IntRect((0, 0), (0, 0)), srgb) { } @@ -263,29 +261,23 @@ public Texture(Texture copy) : /// /// Array of pixels to copy to the texture //////////////////////////////////////////////////////////// - public void Update(byte[] pixels) - { - var size = Size; - Update(pixels, size.X, size.Y, 0, 0); - } + public void Update(byte[] pixels) => Update(pixels, Size, new Vector2u()); //////////////////////////////////////////////////////////// /// /// Update a texture from an array of pixels /// /// Array of pixels to copy to the texture - /// Width of the pixel region contained in pixels - /// Height of the pixel region contained in pixels - /// X offset in the texture where to copy the source pixels - /// Y offset in the texture where to copy the source pixels + /// Width and height of the pixel region contained in pixels + /// Coordinates of the destination position //////////////////////////////////////////////////////////// - public void Update(byte[] pixels, uint width, uint height, uint x, uint y) + public void Update(byte[] pixels, Vector2u size, Vector2u dest) { unsafe { fixed (byte* ptr = pixels) { - sfTexture_updateFromPixels(CPointer, ptr, width, height, x, y); + sfTexture_updateFromPixels(CPointer, ptr, size, dest); } } } @@ -295,10 +287,9 @@ public void Update(byte[] pixels, uint width, uint height, uint x, uint y) /// Update a part of this texture from another texture /// /// Source texture to copy to destination texture - /// X offset in this texture where to copy the source texture - /// Y offset in this texture where to copy the source texture + /// Coordinates of the destination position //////////////////////////////////////////////////////////// - public void Update(Texture texture, uint x, uint y) => sfTexture_updateFromTexture(CPointer, texture.CPointer, x, y); + public void Update(Texture texture, Vector2u dest) => sfTexture_updateFromTexture(CPointer, texture.CPointer, dest); //////////////////////////////////////////////////////////// /// @@ -306,17 +297,16 @@ public void Update(byte[] pixels, uint width, uint height, uint x, uint y) /// /// Image to copy to the texture //////////////////////////////////////////////////////////// - public void Update(Image image) => Update(image, 0, 0); + public void Update(Image image) => Update(image, new Vector2u()); //////////////////////////////////////////////////////////// /// /// Update a texture from an image /// /// Image to copy to the texture - /// X offset in the texture where to copy the source pixels - /// Y offset in the texture where to copy the source pixels + /// Coordinates of the destination position //////////////////////////////////////////////////////////// - public void Update(Image image, uint x, uint y) => sfTexture_updateFromImage(CPointer, image.CPointer, x, y); + public void Update(Image image, Vector2u dest) => sfTexture_updateFromImage(CPointer, image.CPointer, dest); //////////////////////////////////////////////////////////// /// @@ -324,17 +314,16 @@ public void Update(byte[] pixels, uint width, uint height, uint x, uint y) /// /// Window to copy to the texture //////////////////////////////////////////////////////////// - public void Update(SFML.Window.Window window) => Update(window, 0, 0); + public void Update(SFML.Window.Window window) => Update(window, new Vector2u()); //////////////////////////////////////////////////////////// /// /// Update a texture from the contents of a window /// /// Window to copy to the texture - /// X offset in the texture where to copy the source pixels - /// Y offset in the texture where to copy the source pixels + /// Coordinates of the destination position //////////////////////////////////////////////////////////// - public void Update(SFML.Window.Window window, uint x, uint y) => sfTexture_updateFromWindow(CPointer, window.CPointer, x, y); + public void Update(SFML.Window.Window window, Vector2u dest) => sfTexture_updateFromWindow(CPointer, window.CPointer, dest); //////////////////////////////////////////////////////////// /// @@ -342,17 +331,16 @@ public void Update(byte[] pixels, uint width, uint height, uint x, uint y) /// /// Render-window to copy to the texture //////////////////////////////////////////////////////////// - public void Update(RenderWindow window) => Update(window, 0, 0); + public void Update(RenderWindow window) => Update(window, new Vector2u()); //////////////////////////////////////////////////////////// /// /// Update a texture from the contents of a render-window /// /// Render-window to copy to the texture - /// X offset in the texture where to copy the source pixels - /// Y offset in the texture where to copy the source pixels + /// Coordinates of the destination position //////////////////////////////////////////////////////////// - public void Update(RenderWindow window, uint x, uint y) => sfTexture_updateFromRenderWindow(CPointer, window.CPointer, x, y); + public void Update(RenderWindow window, Vector2u dest) => sfTexture_updateFromRenderWindow(CPointer, window.CPointer, dest); //////////////////////////////////////////////////////////// /// @@ -423,7 +411,7 @@ public bool Smooth /// framebuffer. This can be requested during window creation. /// //////////////////////////////////////////////////////////// - public bool Srgb => sfTexture_isSrgb(CPointer); + public bool IsSrgb => sfTexture_isSrgb(CPointer); //////////////////////////////////////////////////////////// /// @@ -450,7 +438,7 @@ public bool Repeated /// Shader to bind (can be null to use no texture) /// Type of texture coordinates to use //////////////////////////////////////////////////////////// - public static void Bind(Texture texture, TextureCoordinateType type) => sfTexture_bind(texture != null ? texture.CPointer : IntPtr.Zero, type); + public static void Bind(Texture texture, CoordinateType type) => sfTexture_bind(texture != null ? texture.CPointer : IntPtr.Zero, type); //////////////////////////////////////////////////////////// /// @@ -515,7 +503,10 @@ protected override void Destroy(bool disposing) #region Imports [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern IntPtr sfTexture_create(uint width, uint height); + private static extern IntPtr sfTexture_create(Vector2u size); + + [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern IntPtr sfTexture_createSrgb(Vector2u size); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern IntPtr sfTexture_createFromFile(string filename, ref IntRect area); @@ -554,39 +545,43 @@ protected override void Destroy(bool disposing) private static extern IntPtr sfTexture_copyToImage(IntPtr texture); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern unsafe void sfTexture_updateFromPixels(IntPtr texture, byte* pixels, uint width, uint height, uint x, uint y); + private static extern unsafe void sfTexture_updateFromPixels(IntPtr texture, byte* pixels, Vector2u size, Vector2u offset); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern void sfTexture_updateFromTexture(IntPtr cPointer, IntPtr texture, uint x, uint y); + private static extern void sfTexture_updateFromTexture(IntPtr cPointer, IntPtr texture, Vector2u offset); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern void sfTexture_updateFromImage(IntPtr texture, IntPtr image, uint x, uint y); + private static extern void sfTexture_updateFromImage(IntPtr texture, IntPtr image, Vector2u offset); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern void sfTexture_updateFromWindow(IntPtr texture, IntPtr window, uint x, uint y); + private static extern void sfTexture_updateFromWindow(IntPtr texture, IntPtr window, Vector2u offset); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern void sfTexture_updateFromRenderWindow(IntPtr texture, IntPtr renderWindow, uint x, uint y); + private static extern void sfTexture_updateFromRenderWindow(IntPtr texture, IntPtr renderWindow, Vector2u offset); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern void sfTexture_bind(IntPtr texture, TextureCoordinateType type); + private static extern void sfTexture_bind(IntPtr texture, CoordinateType type); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfTexture_setSmooth(IntPtr texture, bool smooth); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfTexture_isSmooth(IntPtr texture); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfTexture_isSrgb(IntPtr texture); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfTexture_setRepeated(IntPtr texture, bool repeated); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfTexture_isRepeated(IntPtr texture); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfTexture_generateMipmap(IntPtr texture); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] diff --git a/src/SFML.Graphics/Transform.cs b/src/SFML.Graphics/Transform.cs index b54a68b5..8ba54836 100644 --- a/src/SFML.Graphics/Transform.cs +++ b/src/SFML.Graphics/Transform.cs @@ -2,6 +2,8 @@ using System.Security; using SFML.System; +// TODO REIMPLEMENT WITH 4x4 MATRIX + namespace SFML.Graphics { //////////////////////////////////////////////////////////// @@ -52,16 +54,6 @@ public Transform(float a00, float a01, float a02, //////////////////////////////////////////////////////////// public Transform GetInverse() => sfTransform_getInverse(ref this); - //////////////////////////////////////////////////////////// - /// - /// Transform a 2D point. - /// - /// X coordinate of the point to transform - /// Y coordinate of the point to transform - /// Transformed point - //////////////////////////////////////////////////////////// - public Vector2f TransformPoint(float x, float y) => TransformPoint(new Vector2f(x, y)); - //////////////////////////////////////////////////////////// /// /// Transform a 2D point. @@ -98,45 +90,21 @@ public Transform(float a00, float a01, float a02, //////////////////////////////////////////////////////////// public void Combine(Transform transform) => sfTransform_combine(ref this, ref transform); - //////////////////////////////////////////////////////////// - /// - /// Combine the current transform with a translation. - /// - /// Offset to apply on X axis - /// Offset to apply on Y axis - //////////////////////////////////////////////////////////// - public void Translate(float x, float y) => sfTransform_translate(ref this, x, y); - //////////////////////////////////////////////////////////// /// /// Combine the current transform with a translation. /// /// Translation offset to apply //////////////////////////////////////////////////////////// - public void Translate(Vector2f offset) => Translate(offset.X, offset.Y); - - //////////////////////////////////////////////////////////// - /// - /// Combine the current transform with a rotation. - /// - /// Rotation angle, in degrees - //////////////////////////////////////////////////////////// - public void Rotate(float angle) => sfTransform_rotate(ref this, angle); + public void Translate(Vector2f offset) => sfTransform_translate(ref this, offset); //////////////////////////////////////////////////////////// /// /// Combine the current transform with a rotation. - /// - /// The center of rotation is provided for convenience as a second - /// argument, so that you can build rotations around arbitrary points - /// more easily (and efficiently) than the usual - /// Translate(-center); Rotate(angle); Translate(center). /// - /// Rotation angle, in degrees - /// X coordinate of the center of rotation - /// Y coordinate of the center of rotation + /// Rotation angle //////////////////////////////////////////////////////////// - public void Rotate(float angle, float centerX, float centerY) => sfTransform_rotateWithCenter(ref this, angle, centerX, centerY); + public void Rotate(Angle angle) => sfTransform_rotate(ref this, angle.Degrees); //////////////////////////////////////////////////////////// /// @@ -147,35 +115,10 @@ public Transform(float a00, float a01, float a02, /// more easily (and efficiently) than the usual /// Translate(-center); Rotate(angle); Translate(center). /// - /// Rotation angle, in degrees + /// Rotation angle /// Center of rotation //////////////////////////////////////////////////////////// - public void Rotate(float angle, Vector2f center) => Rotate(angle, center.X, center.Y); - - //////////////////////////////////////////////////////////// - /// - /// Combine the current transform with a scaling. - /// - /// Scaling factor on the X axis - /// Scaling factor on the Y axis - //////////////////////////////////////////////////////////// - public void Scale(float scaleX, float scaleY) => sfTransform_scale(ref this, scaleX, scaleY); - - //////////////////////////////////////////////////////////// - /// - /// Combine the current transform with a scaling. - /// - /// The center of scaling is provided for convenience as a second - /// argument, so that you can build scaling around arbitrary points - /// more easily (and efficiently) than the usual - /// Translate(-center); Scale(factors); Translate(center). - /// - /// Scaling factor on X axis - /// Scaling factor on Y axis - /// X coordinate of the center of scaling - /// Y coordinate of the center of scaling - //////////////////////////////////////////////////////////// - public void Scale(float scaleX, float scaleY, float centerX, float centerY) => sfTransform_scaleWithCenter(ref this, scaleX, scaleY, centerX, centerY); + public void Rotate(Angle angle, Vector2f center) => sfTransform_rotateWithCenter(ref this, angle.Degrees, center); //////////////////////////////////////////////////////////// /// @@ -183,7 +126,7 @@ public Transform(float a00, float a01, float a02, /// /// Scaling factors //////////////////////////////////////////////////////////// - public void Scale(Vector2f factors) => Scale(factors.X, factors.Y); + public void Scale(Vector2f factors) => sfTransform_scale(ref this, factors); //////////////////////////////////////////////////////////// /// @@ -197,7 +140,7 @@ public Transform(float a00, float a01, float a02, /// Scaling factors /// Center of scaling //////////////////////////////////////////////////////////// - public void Scale(Vector2f factors, Vector2f center) => Scale(factors.X, factors.Y, center.X, center.Y); + public void Scale(Vector2f factors, Vector2f center) => sfTransform_scaleWithCenter(ref this, factors, center); //////////////////////////////////////////////////////////// /// @@ -206,7 +149,7 @@ public Transform(float a00, float a01, float a02, /// Object to check /// Object and transform are equal //////////////////////////////////////////////////////////// - public override bool Equals(object obj) => (obj is Transform) && Equals((Transform)obj); + public override bool Equals(object obj) => (obj is Transform transform) && Equals(transform); //////////////////////////////////////////////////////////// /// @@ -300,21 +243,22 @@ public override string ToString() => string.Format("[Transform]" + private static extern void sfTransform_combine(ref Transform transform, ref Transform other); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern void sfTransform_translate(ref Transform transform, float x, float y); + private static extern void sfTransform_translate(ref Transform transform, Vector2f offset); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfTransform_rotate(ref Transform transform, float angle); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern void sfTransform_rotateWithCenter(ref Transform transform, float angle, float centerX, float centerY); + private static extern void sfTransform_rotateWithCenter(ref Transform transform, float angle, Vector2f center); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern void sfTransform_scale(ref Transform transform, float scaleX, float scaleY); + private static extern void sfTransform_scale(ref Transform transform, Vector2f scale); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern void sfTransform_scaleWithCenter(ref Transform transform, float scaleX, float scaleY, float centerX, float centerY); + private static extern void sfTransform_scaleWithCenter(ref Transform transform, Vector2f scale, Vector2f center); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfTransform_equal(ref Transform left, ref Transform right); #endregion } diff --git a/src/SFML.Graphics/VertexBuffer.cs b/src/SFML.Graphics/VertexBuffer.cs index 0a269615..1047672e 100644 --- a/src/SFML.Graphics/VertexBuffer.cs +++ b/src/SFML.Graphics/VertexBuffer.cs @@ -331,9 +331,11 @@ public void Draw(IRenderTarget target, uint firstVertex, uint vertexCount, Rende private static extern uint sfVertexBuffer_getVertexCount(IntPtr cPointer); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern unsafe bool sfVertexBuffer_update(IntPtr cPointer, Vertex* vertices, uint vertexCount, uint offset); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfVertexBuffer_updateFromVertexBuffer(IntPtr cPointer, IntPtr other); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] @@ -358,6 +360,7 @@ public void Draw(IRenderTarget target, uint firstVertex, uint vertexCount, Rende private static extern void sfVertexBuffer_bind(IntPtr cPointer); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfVertexBuffer_isAvailable(); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] diff --git a/src/SFML.Graphics/View.cs b/src/SFML.Graphics/View.cs index d01e2588..600512ee 100644 --- a/src/SFML.Graphics/View.cs +++ b/src/SFML.Graphics/View.cs @@ -89,10 +89,10 @@ public Vector2f Size /// Rotation of the view, in degrees /// //////////////////////////////////////////////////////////// - public float Rotation + public Angle Rotation { - get => sfView_getRotation(CPointer); - set => sfView_setRotation(CPointer, value); + get => Angle.FromDegrees(sfView_getRotation(CPointer)); + set => sfView_setRotation(CPointer, value.Degrees); } //////////////////////////////////////////////////////////// @@ -109,11 +109,26 @@ public FloatRect Viewport //////////////////////////////////////////////////////////// /// - /// Rebuild the view from a rectangle + /// The scissor rectangle, expressed as a factor (between 0 and 1) of + /// the RenderTarget, specifies the region of the RenderTarget whose + /// pixels are able to be modified by draw or clear operations. + /// Any pixels which lie outside of the scissor rectangle will + /// not be modified by draw or clear operations. + /// For example, a scissor rectangle which only allows modifications + /// to the right side of the target would be defined + /// with View.setScissor(sf::FloatRect({0.5f, 0.f}, {0.5f, 1.f})). + /// By default, a view has a scissor rectangle which allows + /// modifications to the entire target. This is equivalent to + /// disabling the scissor test entirely. Passing the default + /// scissor rectangle to this function will also disable + /// scissor testing. /// - /// Rectangle defining the position and size of the view //////////////////////////////////////////////////////////// - public void Reset(FloatRect rectangle) => sfView_reset(CPointer, rectangle); + public FloatRect Scissor + { + get => sfView_getScissor(CPointer); + set => sfView_setScissor(CPointer, value); + } //////////////////////////////////////////////////////////// /// @@ -210,7 +225,7 @@ protected override void Destroy(bool disposing) private static extern void sfView_setViewport(IntPtr view, FloatRect viewport); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern void sfView_reset(IntPtr view, FloatRect rectangle); + private static extern void sfView_setScissor(IntPtr view, FloatRect viewport); [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern Vector2f sfView_getCenter(IntPtr view); @@ -224,6 +239,9 @@ protected override void Destroy(bool disposing) [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern FloatRect sfView_getViewport(IntPtr view); + [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern FloatRect sfView_getScissor(IntPtr view); + [DllImport(CSFML.Graphics, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfView_move(IntPtr view, Vector2f offset); diff --git a/src/SFML.System/Angle.cs b/src/SFML.System/Angle.cs new file mode 100644 index 00000000..67e11ebb --- /dev/null +++ b/src/SFML.System/Angle.cs @@ -0,0 +1,335 @@ +using System; +using System.Diagnostics; + +namespace SFML.System +{ + //////////////////////////////////////////////////////////// + /// + /// Represents an angle value + /// + //////////////////////////////////////////////////////////// + public readonly struct Angle + { + private const float Tau = (float)(2 * Math.PI); + private const float Pi = (float)Math.PI; + private const double DegreesInRadian = 180.0 / Math.PI; + private const double RadiansInDegree = Math.PI / 180.0; + + //////////////////////////////////////////////////////////// + /// + /// Construct an angle value from a number of radians + /// + /// Number of radians + /// Angle value constructed from the number of radians + //////////////////////////////////////////////////////////// + public static Angle FromRadians(float radians) => new Angle(radians); + + //////////////////////////////////////////////////////////// + /// + /// Construct an angle value from a number of degrees + /// + /// Number of degrees + /// Angle value constructed from the number of degrees + //////////////////////////////////////////////////////////// + public static Angle FromDegrees(float degrees) => new Angle((float)(degrees * RadiansInDegree)); + + //////////////////////////////////////////////////////////// + /// + /// Predefined 0 degree angle value + /// + //////////////////////////////////////////////////////////// + public static readonly Angle Zero; + + //////////////////////////////////////////////////////////// + /// + /// Gets the angle's value in radians + /// + //////////////////////////////////////////////////////////// + public float Radians { get; } + + //////////////////////////////////////////////////////////// + /// + /// Gets the angle's value in degrees + /// + //////////////////////////////////////////////////////////// + public float Degrees => (float)(Radians * DegreesInRadian); + + //////////////////////////////////////////////////////////// + /// + /// Wrap to a range such that -180° <= angle < 180° + /// + /// Similar to a modulo operation, this returns a copy of the angle + /// constrained to the range [-180°, 180°) == [-Pi, Pi). + /// The resulting angle represents a rotation which is equivalent to *this. + /// + /// The name "signed" originates from the similarity to signed integers: + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + ///
signedunsigned
char[-128, 128)[0, 256)
Angle[-180°, 180°)[0°, 360°)
+ ///
+ /// Signed angle, wrapped to [-180°, 180°) + //////////////////////////////////////////////////////////// + public Angle WrapSigned() => FromRadians(PositiveRemainder(Radians + Pi, Tau) - Pi); + + //////////////////////////////////////////////////////////// + /// + /// Wrap to a range such that 0° <= angle < 360° + /// + /// Similar to a modulo operation, this returns a copy of the angle + /// constrained to the range [0°, 360°) == [0, Tau) == [0, 2*Pi). + /// The resulting angle represents a rotation which is equivalent to this + /// + /// The name "unsigned" originates from the similarity to unsigned integers: + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + ///
signedunsigned
char[-128, 128)[0, 256)
Angle[-180°, 180°)[0°, 360°)
+ ///
+ /// Unsigned angle, wrapped to [0°, 360°) + //////////////////////////////////////////////////////////// + public Angle WrapUnsigned() => FromRadians((float)PositiveRemainder(Radians, Tau)); + + //////////////////////////////////////////////////////////// + /// + /// Overload of == operator to compare two angle values + /// + /// Does not automatically wrap the angle value + /// Left operand (an angle) + /// Right operand (an angle) + /// True if both angle values are equal + //////////////////////////////////////////////////////////// + public static bool operator ==(Angle left, Angle right) => left.Radians == right.Radians; + + //////////////////////////////////////////////////////////// + /// + /// Overload of != operator to compare two angle values + /// + /// Does not automatically wrap the angle value + /// Left operand (an angle) + /// Right operand (an angle) + /// True if both angle values are different + //////////////////////////////////////////////////////////// + public static bool operator !=(Angle left, Angle right) => left.Radians != right.Radians; + + //////////////////////////////////////////////////////////// + /// + /// Overload of < operator to compare two angle values + /// + /// Does not automatically wrap the angle value + /// Left operand (an angle) + /// Right operand (an angle) + /// True if is less than + //////////////////////////////////////////////////////////// + public static bool operator <(Angle left, Angle right) => left.Radians < right.Radians; + + //////////////////////////////////////////////////////////// + /// + /// Overload of <= operator to compare two angle values + /// + /// Does not automatically wrap the angle value + /// Left operand (an angle) + /// Right operand (an angle) + /// True if is less than or equal to + //////////////////////////////////////////////////////////// + public static bool operator <=(Angle left, Angle right) => left.Radians <= right.Radians; + + //////////////////////////////////////////////////////////// + /// + /// Overload of > operator to compare two angle values + /// + /// Does not automatically wrap the angle value + /// Left operand (an angle) + /// Right operand (an angle) + /// True if is greater than + //////////////////////////////////////////////////////////// + public static bool operator >(Angle left, Angle right) => left.Radians > right.Radians; + + //////////////////////////////////////////////////////////// + /// + /// Overload of >= operator to compare two angle values + /// + /// Does not automatically wrap the angle value + /// Left operand (an angle) + /// Right operand (an angle) + /// True if is greater than or equal to + //////////////////////////////////////////////////////////// + public static bool operator >=(Angle left, Angle right) => left.Radians >= right.Radians; + + //////////////////////////////////////////////////////////// + /// + /// Overload of binary + operator to add two angle values + /// + /// Left operand (an angle) + /// Right operand (an angle) + /// Sum of the two angle values + //////////////////////////////////////////////////////////// + public static Angle operator +(Angle left, Angle right) => FromRadians(left.Radians + right.Radians); + + //////////////////////////////////////////////////////////// + /// + /// Overload of unary - operator to negate an angle value + /// + /// Represents a rotation in the opposite direction. + /// + /// Right operand (an angle) + /// Negative of the angle value + //////////////////////////////////////////////////////////// + public static Angle operator -(Angle right) => FromRadians(-right.Radians); + + //////////////////////////////////////////////////////////// + /// + /// Overload of binary - operator to substract two angle values + /// + /// Left operand (an angle) + /// Right operand (an angle) + /// Difference of the two angle values + //////////////////////////////////////////////////////////// + public static Angle operator -(Angle left, Angle right) => FromRadians(left.Radians - right.Radians); + + //////////////////////////////////////////////////////////// + /// + /// Overload of binary * operator to scale an angle value + /// + /// Left operand (an angle) + /// Right operand (a number) + /// multiplied by + //////////////////////////////////////////////////////////// + public static Angle operator *(Angle left, float right) => FromRadians(left.Radians * right); + + //////////////////////////////////////////////////////////// + /// + /// Overload of binary * operator to scale an angle value + /// + /// Left operand (a number) + /// Right operand (an angle) + /// multiplied by + //////////////////////////////////////////////////////////// + public static Angle operator *(float left, Angle right) => FromRadians(left * right.Radians); + + //////////////////////////////////////////////////////////// + /// + /// Overload of binary / operator to compute the ratio of two angle values + /// + /// Left operand (an angle) + /// Right operand (an angle) + /// divided by + //////////////////////////////////////////////////////////// + public static float operator /(Angle left, Angle right) + { + Debug.Assert(right.Radians != 0f, "Angle.operator/ cannot divide by 0"); + + return left.Radians / right.Radians; + } + + //////////////////////////////////////////////////////////// + /// + /// Overload of binary / operator to scale an angle value + /// + /// Left operand (an angle) + /// Right operand (a number) + /// divided by + //////////////////////////////////////////////////////////// + public static Angle operator /(Angle left, float right) + { + Debug.Assert(right != 0f, "Angle.operator/ cannot divide by 0"); + + return FromRadians(left.Radians / right); + } + + //////////////////////////////////////////////////////////// + /// + /// Overload of binary % operator to compute modulo of an angle value + /// + /// Right hand angle must be greater than zero. + /// + /// Examples: + /// + /// sf::degrees(90) % sf::degrees(40) // 10 degrees + /// sf::degrees(-90) % sf::degrees(40) // 30 degrees (not -10) + /// + /// + /// Left operand (an angle) + /// Right operand (an angle) + /// modulo + //////////////////////////////////////////////////////////// + public static Angle operator %(Angle left, Angle right) + { + Debug.Assert(right.Radians != 0f, "Angle.operator% cannot modulus by 0"); + + return FromRadians((float)PositiveRemainder(left.Radians, right.Radians)); + } + + //////////////////////////////////////////////////////////// + /// + /// Compare angle and object and checks if they are equal + /// + /// Object to check + /// Object and angle are equal + //////////////////////////////////////////////////////////// + public override bool Equals(object obj) => (obj is Angle angle) && Equals(angle); + + /////////////////////////////////////////////////////////// + /// + /// Compare two angles and checks if they are equal + /// + /// Angle to check + /// Angles are equal + //////////////////////////////////////////////////////////// + public bool Equals(Angle other) => Radians == other.Radians; + + //////////////////////////////////////////////////////////// + /// + /// Provide a integer describing the object + /// + /// Integer description of the object + //////////////////////////////////////////////////////////// + public override int GetHashCode() => Radians.GetHashCode(); + + //////////////////////////////////////////////////////////// + /// + /// Provide a string describing the object + /// + /// String description of the object + //////////////////////////////////////////////////////////// + public override string ToString() => "[Angle]" + + " Radians(" + Radians + ")" + + " Degrees(" + Degrees + ")"; + + private Angle(float radians) => Radians = radians; + + private static float PositiveRemainder(float a, float b) + { + Debug.Assert(b > 0, "Cannot calculate remainder with non-positive divisor"); + + var val = a - ((int)(a / b) * b); + return val >= 0.0 ? val : val + b; + } + } +} diff --git a/src/SFML.System/Clock.cs b/src/SFML.System/Clock.cs index cce8b5cd..c9a17ba9 100644 --- a/src/SFML.System/Clock.cs +++ b/src/SFML.System/Clock.cs @@ -14,6 +14,8 @@ public class Clock : ObjectBase //////////////////////////////////////////////////////////// /// /// Default Constructor + /// + /// The clock starts automatically after being constructed. /// //////////////////////////////////////////////////////////// public Clock() : base(sfClock_create()) { } @@ -29,10 +31,33 @@ public Clock() : base(sfClock_create()) { } //////////////////////////////////////////////////////////// /// /// Gets the time elapsed since the last call to Restart + /// (or the construction of the instance if Restart + /// has not been called). /// //////////////////////////////////////////////////////////// public Time ElapsedTime => sfClock_getElapsedTime(CPointer); + //////////////////////////////////////////////////////////// + /// + /// Check whether the clock is running + /// + //////////////////////////////////////////////////////////// + public bool IsRunning => sfClock_isRunning(CPointer); + + //////////////////////////////////////////////////////////// + /// + /// Start the clock + /// + //////////////////////////////////////////////////////////// + public void Start() => sfClock_start(CPointer); + + //////////////////////////////////////////////////////////// + /// + /// Stop the clock + /// + //////////////////////////////////////////////////////////// + public void Stop() => sfClock_stop(CPointer); + //////////////////////////////////////////////////////////// /// /// This function puts the time counter back to zero. @@ -41,6 +66,16 @@ public Clock() : base(sfClock_create()) { } //////////////////////////////////////////////////////////// public Time Restart() => sfClock_restart(CPointer); + //////////////////////////////////////////////////////////// + /// + /// Reset the clock + /// + /// This function puts the time counter back to zero, returns + /// the elapsed time, and leaves the clock in a paused state. + /// + //////////////////////////////////////////////////////////// + public Time Reset() => sfClock_reset(CPointer); + #region Imports [DllImport(CSFML.System, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern IntPtr sfClock_create(); @@ -51,8 +86,21 @@ public Clock() : base(sfClock_create()) { } [DllImport(CSFML.System, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern Time sfClock_getElapsedTime(IntPtr clock); + [DllImport(CSFML.System, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] + private static extern bool sfClock_isRunning(IntPtr clock); + + [DllImport(CSFML.System, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfClock_start(IntPtr clock); + + [DllImport(CSFML.System, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern void sfClock_stop(IntPtr clock); + [DllImport(CSFML.System, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern Time sfClock_restart(IntPtr clock); + + [DllImport(CSFML.System, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + private static extern Time sfClock_reset(IntPtr clock); #endregion } } diff --git a/src/SFML.System/SharedLibName.cs b/src/SFML.System/SharedLibName.cs index 1c24b354..d07dcb3a 100644 --- a/src/SFML.System/SharedLibName.cs +++ b/src/SFML.System/SharedLibName.cs @@ -5,12 +5,14 @@ public static class CSFML { /// CSFML Audio Lib Name public const string Audio = "csfml-audio"; + /// CSFML Graphics Lib Name public const string Graphics = "csfml-graphics"; + /// CSFML System Lib Name public const string System = "csfml-system"; + /// CSFML Window Lib Name public const string Window = "csfml-window"; - } } diff --git a/src/SFML.System/StreamAdaptor.cs b/src/SFML.System/StreamAdaptor.cs index 284ec043..3dddd884 100644 --- a/src/SFML.System/StreamAdaptor.cs +++ b/src/SFML.System/StreamAdaptor.cs @@ -19,7 +19,7 @@ public struct InputStream /// //////////////////////////////////////////////////////////// [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate long ReadCallbackType(IntPtr data, long size, IntPtr userData); + public delegate long ReadCallbackType(IntPtr data, UIntPtr size, IntPtr userData); //////////////////////////////////////////////////////////// /// @@ -27,7 +27,7 @@ public struct InputStream /// //////////////////////////////////////////////////////////// [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate long SeekCallbackType(long position, IntPtr userData); + public delegate long SeekCallbackType(UIntPtr position, IntPtr userData); //////////////////////////////////////////////////////////// /// @@ -148,9 +148,9 @@ public void Dispose() /// User data -- unused /// Number of bytes read //////////////////////////////////////////////////////////// - private long Read(IntPtr data, long size, IntPtr userData) + private long Read(IntPtr data, UIntPtr size, IntPtr userData) { - var buffer = new byte[size]; + var buffer = new byte[(int)size]; var count = _stream.Read(buffer, 0, (int)size); Marshal.Copy(buffer, 0, data, count); return count; @@ -164,7 +164,7 @@ private long Read(IntPtr data, long size, IntPtr userData) /// User data -- unused /// Actual position //////////////////////////////////////////////////////////// - private long Seek(long position, IntPtr userData) => _stream.Seek(position, SeekOrigin.Begin); + private long Seek(UIntPtr position, IntPtr userData) => _stream.Seek((long)position, SeekOrigin.Begin); //////////////////////////////////////////////////////////// /// diff --git a/src/SFML.System/Time.cs b/src/SFML.System/Time.cs index c6e41c5b..7c2a832a 100644 --- a/src/SFML.System/Time.cs +++ b/src/SFML.System/Time.cs @@ -119,7 +119,7 @@ namespace SFML.System /// Object to check /// Object and time are equal //////////////////////////////////////////////////////////// - public override bool Equals(object obj) => (obj is Time) && Equals((Time)obj); + public override bool Equals(object obj) => (obj is Time time) && Equals(time); /////////////////////////////////////////////////////////// /// diff --git a/src/SFML.System/Vector2.cs b/src/SFML.System/Vector2.cs index 94cd8e5a..f9f9fb52 100644 --- a/src/SFML.System/Vector2.cs +++ b/src/SFML.System/Vector2.cs @@ -25,11 +25,181 @@ public Vector2f(float x, float y) Y = y; } + //////////////////////////////////////////////////////////// + /// + /// Construct the vector from its coordinates + /// + /// Note that this constructor is lossy: calling Length and Angle + /// may return values different to those provided in this constructor. + /// + /// In particular, these transforms can be applied: + /// * Vector2(r, phi) == Vector2(-r, phi + 180_deg) + /// * Vector2(r, phi) == Vector2(r, phi + n * 360_deg) + /// + /// Length of vector (can be negative) + /// Angle from X axis + //////////////////////////////////////////////////////////// + public Vector2f(float r, Angle phi) + { + X = (float)(r * Math.Cos(phi.Radians)); + Y = (float)(r * Math.Sin(phi.Radians)); + } + + //////////////////////////////////////////////////////////// + /// + /// Length of the vector + /// + /// If you are not interested in the actual length, but only in comparisons, consider using + /// + //////////////////////////////////////////////////////////// + public float Length => (float)Math.Sqrt(((double)X * X) + ((double)Y * Y)); + + //////////////////////////////////////////////////////////// + /// + /// Square of vector's length + /// + /// Suitable for comparisons, more efficient than + /// + //////////////////////////////////////////////////////////// + public float LengthSquared => Dot(this); + + //////////////////////////////////////////////////////////// + /// + /// Vector with same direction but length 1 + /// + /// should not be a zero vector + /// + //////////////////////////////////////////////////////////// + public Vector2f Normalized() => this / Length; + + //////////////////////////////////////////////////////////// + /// + /// Signed angle from to + /// + /// Neither nor should be a zero vector. + /// + /// + /// The smallest angle which rotates `*this` in positive + /// or negative direction, until it has the same direction as \a `rhs`. + /// The result has a sign and lies in the range [-180, 180) degrees. + /// + //////////////////////////////////////////////////////////// + public Angle AngleTo(Vector2f rhs) => SFML.System.Angle.FromRadians((float)Math.Atan2(Cross(rhs), Dot(rhs))); + + //////////////////////////////////////////////////////////// + /// + /// Signed angle from +X or (1,0) vector + /// + /// For example, the vector (1,0) corresponds to 0 degrees, (0,1) corresponds to 90 degrees. + /// + /// should not be a zero vector + /// + /// Angle in the range [-180, 180) degrees + //////////////////////////////////////////////////////////// + public Angle Angle() => SFML.System.Angle.FromRadians((float)Math.Atan2(Y, X)); + + //////////////////////////////////////////////////////////// + /// + /// Rotate by angle + /// + /// Returns a vector with same length but different direction. + /// + /// In SFML's default coordinate system with +X right and +Y down, + /// this amounts to a clockwise rotation by . + /// + //////////////////////////////////////////////////////////// + public Vector2f RotatedBy(Angle phi) + { + var cos = Math.Cos(phi.Radians); + var sin = Math.Sin(phi.Radians); + + return new Vector2f((float)((cos * X) - (sin * Y)), (float)((sin * X) + (cos * Y))); + } + + //////////////////////////////////////////////////////////// + /// + /// Projection of this vector onto + /// + /// must not have length zero + /// + /// Vector being projected onto. Need not be normalized + //////////////////////////////////////////////////////////// + public Vector2f ProjectedOnto(Vector2f axis) => Dot(axis) / axis.LengthSquared * axis; + + //////////////////////////////////////////////////////////// + /// + /// Returns a perpendicular vector. + /// + /// Returns rotated by +90 degrees; (x,y) becomes (-y,x). + /// For example, the vector (1,0) is transformed to (0,1). + /// + /// In SFML's default coordinate system with +X right and +Y down, + /// this amounts to a clockwise rotation. + /// + //////////////////////////////////////////////////////////// + public Vector2f Perpendicular() => new Vector2f(-Y, X); + + //////////////////////////////////////////////////////////// + /// + /// Dot product of two 2D vectors. + /// + //////////////////////////////////////////////////////////// + public float Dot(Vector2f rhs) => (X * rhs.X) + (Y * rhs.Y); + + //////////////////////////////////////////////////////////// + /// + /// Z component of the cross product of two 2D vectors. + /// + /// Treats the operands as 3D vectors, computes their cross product + /// and returns the result's Z component (X and Y components are always zero). + /// + //////////////////////////////////////////////////////////// + public float Cross(Vector2f rhs) => (X * rhs.Y) - (Y * rhs.X); + + //////////////////////////////////////////////////////////// + /// + /// Component-wise multiplication of and + /// + /// Computes (this.X * rhs.X, this.Y * rhs.Y) + /// + /// Scaling is the most common use case for component-wise multiplication/division. + /// This operation is also known as the Hadamard or Schur product. + /// + //////////////////////////////////////////////////////////// + public Vector2f ComponentWiseMul(Vector2f rhs) => new Vector2f(X * rhs.X, Y * rhs.Y); + + //////////////////////////////////////////////////////////// + /// + /// Component-wise division of and + /// + /// Computes (this.X / rhs.X, this.Y / rhs.Y) + /// + /// Scaling is the most common use case for component-wise multiplication/division. + /// + //////////////////////////////////////////////////////////// + public Vector2f ComponentWiseDiv(Vector2f rhs) => new Vector2f(X / rhs.X, Y / rhs.Y); + + //////////////////////////////////////////////////////////// + /// + /// The X unit vector (1, 0), usually facing right + /// + //////////////////////////////////////////////////////////// + public static readonly Vector2f UnitX = new Vector2f(1, 0); + + //////////////////////////////////////////////////////////// + /// + /// The Y unit vector (0, 1), usually facing down + /// + //////////////////////////////////////////////////////////// + public static readonly Vector2f UnitY = new Vector2f(0, 1); + + //////////////////////////////////////////////////////////// /// /// Deconstructs a Vector2f into a tuple of floats /// /// X coordinate /// Y coordinate + //////////////////////////////////////////////////////////// public void Deconstruct(out float x, out float y) { x = X; @@ -130,7 +300,7 @@ public void Deconstruct(out float x, out float y) /// Object to check /// Object and vector are equal //////////////////////////////////////////////////////////// - public override bool Equals(object obj) => (obj is Vector2f) && Equals((Vector2f)obj); + public override bool Equals(object obj) => (obj is Vector2f vec) && Equals(vec); /////////////////////////////////////////////////////////// /// @@ -202,11 +372,87 @@ public Vector2i(int x, int y) Y = y; } + //////////////////////////////////////////////////////////// + /// + /// Square of vector's length + /// + //////////////////////////////////////////////////////////// + public float LengthSquared => Dot(this); + + //////////////////////////////////////////////////////////// + /// + /// Returns a perpendicular vector. + /// + /// Returns rotated by +90 degrees; (x,y) becomes (-y,x). + /// For example, the vector (1,0) is transformed to (0,1). + /// + /// In SFML's default coordinate system with +X right and +Y down, + /// this amounts to a clockwise rotation. + /// + //////////////////////////////////////////////////////////// + public Vector2i Perpendicular() => new Vector2i(-Y, X); + + //////////////////////////////////////////////////////////// + /// + /// Dot product of two 2D vectors. + /// + //////////////////////////////////////////////////////////// + public float Dot(Vector2i rhs) => (X * rhs.X) + (Y * rhs.Y); + + //////////////////////////////////////////////////////////// + /// + /// Z component of the cross product of two 2D vectors. + /// + /// Treats the operands as 3D vectors, computes their cross product + /// and returns the result's Z component (X and Y components are always zero). + /// + //////////////////////////////////////////////////////////// + public float Cross(Vector2i rhs) => (X * rhs.Y) - (Y * rhs.X); + + //////////////////////////////////////////////////////////// + /// + /// Component-wise multiplication of and + /// + /// Computes (this.X * rhs.X, this.Y * rhs.Y) + /// + /// Scaling is the most common use case for component-wise multiplication/division. + /// This operation is also known as the Hadamard or Schur product. + /// + //////////////////////////////////////////////////////////// + public Vector2i ComponentWiseMul(Vector2i rhs) => new Vector2i(X * rhs.X, Y * rhs.Y); + + //////////////////////////////////////////////////////////// + /// + /// Component-wise division of and + /// + /// Computes (this.X / rhs.X, this.Y / rhs.Y) + /// + /// Scaling is the most common use case for component-wise multiplication/division. + /// + //////////////////////////////////////////////////////////// + public Vector2i ComponentWiseDiv(Vector2i rhs) => new Vector2i(X / rhs.X, Y / rhs.Y); + + //////////////////////////////////////////////////////////// + /// + /// The X unit vector (1, 0), usually facing right + /// + //////////////////////////////////////////////////////////// + public static readonly Vector2i UnitX = new Vector2i(1, 0); + + //////////////////////////////////////////////////////////// + /// + /// The Y unit vector (0, 1), usually facing down + /// + //////////////////////////////////////////////////////////// + public static readonly Vector2i UnitY = new Vector2i(0, 1); + + //////////////////////////////////////////////////////////// /// /// Deconstructs a Vector2i into a tuple of ints /// /// X coordinate /// Y coordinate + //////////////////////////////////////////////////////////// public void Deconstruct(out int x, out int y) { x = X; @@ -307,7 +553,7 @@ public void Deconstruct(out int x, out int y) /// Object to check /// Object and vector are equal //////////////////////////////////////////////////////////// - public override bool Equals(object obj) => (obj is Vector2i) && Equals((Vector2i)obj); + public override bool Equals(object obj) => (obj is Vector2i vec) && Equals(vec); /////////////////////////////////////////////////////////// /// @@ -344,10 +590,12 @@ public void Deconstruct(out int x, out int y) //////////////////////////////////////////////////////////// public static explicit operator Vector2u(Vector2i v) => new Vector2u((uint)v.X, (uint)v.Y); + //////////////////////////////////////////////////////////// /// /// Converts a tuple of ints to a Vector2i /// /// The tuple to convert + //////////////////////////////////////////////////////////// public static implicit operator Vector2i((int X, int Y) tuple) => new Vector2i(tuple.X, tuple.Y); /// X (horizontal) component of the vector @@ -379,11 +627,74 @@ public Vector2u(uint x, uint y) Y = y; } + //////////////////////////////////////////////////////////// + /// + /// Square of vector's length + /// + //////////////////////////////////////////////////////////// + public float LengthSquared => Dot(this); + + //////////////////////////////////////////////////////////// + /// + /// Dot product of two 2D vectors. + /// + //////////////////////////////////////////////////////////// + public float Dot(Vector2u rhs) => (X * rhs.X) + (Y * rhs.Y); + + //////////////////////////////////////////////////////////// + /// + /// Z component of the cross product of two 2D vectors. + /// + /// Treats the operands as 3D vectors, computes their cross product + /// and returns the result's Z component (X and Y components are always zero). + /// + //////////////////////////////////////////////////////////// + public float Cross(Vector2u rhs) => (X * rhs.Y) - (Y * rhs.X); + + //////////////////////////////////////////////////////////// + /// + /// Component-wise multiplication of and + /// + /// Computes (this.X * rhs.X, this.Y * rhs.Y) + /// + /// Scaling is the most common use case for component-wise multiplication/division. + /// This operation is also known as the Hadamard or Schur product. + /// + //////////////////////////////////////////////////////////// + public Vector2u ComponentWiseMul(Vector2u rhs) => new Vector2u(X * rhs.X, Y * rhs.Y); + + //////////////////////////////////////////////////////////// + /// + /// Component-wise division of and + /// + /// Computes (this.X / rhs.X, this.Y / rhs.Y) + /// + /// Scaling is the most common use case for component-wise multiplication/division. + /// + //////////////////////////////////////////////////////////// + public Vector2u ComponentWiseDiv(Vector2u rhs) => new Vector2u(X / rhs.X, Y / rhs.Y); + + //////////////////////////////////////////////////////////// + /// + /// The X unit vector (1, 0), usually facing right + /// + //////////////////////////////////////////////////////////// + public static readonly Vector2u UnitX = new Vector2u(1, 0); + + //////////////////////////////////////////////////////////// + /// + /// The Y unit vector (0, 1), usually facing down + /// + //////////////////////////////////////////////////////////// + public static readonly Vector2u UnitY = new Vector2u(0, 1); + + //////////////////////////////////////////////////////////// /// /// Deconstructs a Vector2u into a tuple of uints /// /// X coordinate /// Y coordinate + //////////////////////////////////////////////////////////// public void Deconstruct(out uint x, out uint y) { x = X; @@ -475,7 +786,7 @@ public void Deconstruct(out uint x, out uint y) /// Object to check /// Object and vector are equal //////////////////////////////////////////////////////////// - public override bool Equals(object obj) => (obj is Vector2u) && Equals((Vector2u)obj); + public override bool Equals(object obj) => (obj is Vector2u vec) && Equals(vec); /////////////////////////////////////////////////////////// /// @@ -512,10 +823,12 @@ public void Deconstruct(out uint x, out uint y) //////////////////////////////////////////////////////////// public static explicit operator Vector2f(Vector2u v) => new Vector2f(v.X, v.Y); + //////////////////////////////////////////////////////////// /// /// Converts a tuple of uints to a Vector2u /// /// The tuple to convert + //////////////////////////////////////////////////////////// public static implicit operator Vector2u((uint X, uint Y) tuple) => new Vector2u(tuple.X, tuple.Y); /// X (horizontal) component of the vector diff --git a/src/SFML.System/Vector3.cs b/src/SFML.System/Vector3.cs index 5037ee4c..9d069015 100644 --- a/src/SFML.System/Vector3.cs +++ b/src/SFML.System/Vector3.cs @@ -27,12 +27,78 @@ public Vector3f(float x, float y, float z) Z = z; } + //////////////////////////////////////////////////////////// + /// + /// Length of the vector + /// + /// If you are not interested in the actual length, but only in comparisons, consider using . + /// + //////////////////////////////////////////////////////////// + public float Length => (float)Math.Sqrt((X * X) + (Y * Y) + (Z * Z)); + + //////////////////////////////////////////////////////////// + /// + /// Square of vector's length + /// + /// Suitable for comparisons, more efficient than + /// + //////////////////////////////////////////////////////////// + public float LengthSquared => Dot(this); + + //////////////////////////////////////////////////////////// + /// + /// Vector with same direction but length 1 + /// + /// should not be a zero vector + /// + //////////////////////////////////////////////////////////// + public Vector3f Normalized() => this / Length; + + //////////////////////////////////////////////////////////// + /// + /// Dot product of two 3D vectors. + /// + //////////////////////////////////////////////////////////// + public float Dot(Vector3f rhs) => (X * rhs.X) + (Y * rhs.Y) + (Z * rhs.Z); + + //////////////////////////////////////////////////////////// + /// + /// Cross product of two 3D vectors + /// + //////////////////////////////////////////////////////////// + public Vector3f Cross(Vector3f rhs) => new Vector3f((Y * rhs.Z) - (Z * rhs.Y), (Z * rhs.X) - (X * rhs.Z), (X * rhs.Y) - (Y * rhs.X)); + + //////////////////////////////////////////////////////////// + /// + /// Component-wise multiplication of and + /// + /// Computes (this.X * rhs.X, this.Y * rhs.Y, this.Z * rhs.Z) + /// + /// Scaling is the most common use case for component-wise multiplication/division. + /// This operation is also known as the Hadamard or Schur product. + /// + //////////////////////////////////////////////////////////// + public Vector3f ComponentWiseMul(Vector3f rhs) => new Vector3f(X * rhs.X, Y * rhs.Y, Z * rhs.Z); + + //////////////////////////////////////////////////////////// + /// + /// Component-wise division of and + /// + /// Computes (this.X / rhs.X, this.Y / rhs.Y, this.Z * rhs.Z) + /// + /// Scaling is the most common use case for component-wise multiplication/division. + /// + //////////////////////////////////////////////////////////// + public Vector3f ComponentWiseDiv(Vector3f rhs) => new Vector3f(X / rhs.X, Y / rhs.Y, Z / rhs.Z); + + //////////////////////////////////////////////////////////// /// /// Deconstructs a Vector3f into a tuple of floats /// /// X coordinate /// Y coordinate /// Z coordinate + //////////////////////////////////////////////////////////// public void Deconstruct(out float x, out float y, out float z) { x = X; @@ -134,7 +200,7 @@ public void Deconstruct(out float x, out float y, out float z) /// Object to check /// Object and vector are equal //////////////////////////////////////////////////////////// - public override bool Equals(object obj) => (obj is Vector3f) && Equals((Vector3f)obj); + public override bool Equals(object obj) => (obj is Vector3f vec) && Equals(vec); /////////////////////////////////////////////////////////// /// @@ -153,10 +219,12 @@ public void Deconstruct(out float x, out float y, out float z) //////////////////////////////////////////////////////////// public override int GetHashCode() => X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode(); + //////////////////////////////////////////////////////////// /// /// Converts a tuple of floats to a Vector3f /// /// The tuple to convert + //////////////////////////////////////////////////////////// public static implicit operator Vector3f((float X, float Y, float Z) tuple) => new Vector3f(tuple.X, tuple.Y, tuple.Z); /// X (horizontal) component of the vector diff --git a/src/SFML.Window/Context.cs b/src/SFML.Window/Context.cs index 8739a662..a31a6ae4 100644 --- a/src/SFML.Window/Context.cs +++ b/src/SFML.Window/Context.cs @@ -4,6 +4,9 @@ using System.Security; using SFML.System; +// TODO getActiveContext +// TODO getActiveContextId + namespace SFML.Window { ////////////////////////////////////////////////////////////////// @@ -102,9 +105,11 @@ public static Context Global private static extern void sfContext_destroy(IntPtr view); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfContext_isExtensionAvailable(string name); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfContext_setActive(IntPtr view, bool active); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] diff --git a/src/SFML.Window/ContextSettings.cs b/src/SFML.Window/ContextSettings.cs index 9cfe9029..aea2c397 100644 --- a/src/SFML.Window/ContextSettings.cs +++ b/src/SFML.Window/ContextSettings.cs @@ -74,7 +74,7 @@ public ContextSettings(uint depthBits, uint stencilBits, uint antialiasingLevel, MajorVersion = majorVersion; MinorVersion = minorVersion; AttributeFlags = attributes; - _srgbCapable = sRgbCapable ? 1 : 0; + SRgbCapable = sRgbCapable; } //////////////////////////////////////////////////////////// @@ -84,13 +84,13 @@ public ContextSettings(uint depthBits, uint stencilBits, uint antialiasingLevel, /// String description of the object //////////////////////////////////////////////////////////// public override string ToString() => "[ContextSettings]" + - " DepthBits(" + DepthBits + ")" + - " StencilBits(" + StencilBits + ")" + - " AntialiasingLevel(" + AntialiasingLevel + ")" + - " MajorVersion(" + MajorVersion + ")" + - " MinorVersion(" + MinorVersion + ")" + - " AttributeFlags(" + AttributeFlags + ")" + - " SRgbCapable(" + SRgbCapable + ")"; + $" DepthBits({DepthBits})" + + $" StencilBits({StencilBits})" + + $" AntialiasingLevel({AntialiasingLevel})" + + $" MajorVersion({MajorVersion})" + + $" MinorVersion({MinorVersion})" + + $" AttributeFlags({AttributeFlags})" + + $" SRgbCapable({SRgbCapable})"; /// Depth buffer bits (0 is disabled) public uint DepthBits; @@ -110,14 +110,8 @@ public override string ToString() => "[ContextSettings]" + /// The attribute flags to create the context with public Attribute AttributeFlags; - /// Internal Representation - private int _srgbCapable; - /// Whether the context framebuffer is sRGB capable - public bool SRgbCapable - { - get => _srgbCapable == 1; - set => _srgbCapable = value ? 1 : 0; - } + [MarshalAs(UnmanagedType.I1)] + public bool SRgbCapable; } } diff --git a/src/SFML.Window/Cursor.cs b/src/SFML.Window/Cursor.cs index fbb33a7d..c8787a77 100644 --- a/src/SFML.Window/Cursor.cs +++ b/src/SFML.Window/Cursor.cs @@ -5,9 +5,11 @@ namespace SFML.Window { + //////////////////////////////////////////////////////////// /// /// Cursor defines the appearance of a system cursor. /// + //////////////////////////////////////////////////////////// public class Cursor : ObjectBase { /// @@ -164,6 +166,7 @@ public enum CursorType NotAllowed } + //////////////////////////////////////////////////////////// /// /// Create a native system cursor /// @@ -174,6 +177,7 @@ public enum CursorType /// /// System cursor type /// + //////////////////////////////////////////////////////////// public Cursor(CursorType type) : base(sfCursor_createFromSystem(type)) { @@ -183,6 +187,7 @@ public Cursor(CursorType type) } } + //////////////////////////////////////////////////////////// /// /// Create a cursor with the provided image /// @@ -209,7 +214,8 @@ public Cursor(CursorType type) /// Width and height of the image /// (x,y) location of the hotspot /// - public Cursor(byte[] pixels, SFML.System.Vector2u size, SFML.System.Vector2u hotspot) + //////////////////////////////////////////////////////////// + public Cursor(byte[] pixels, Vector2u size, Vector2u hotspot) : base((IntPtr)0) { unsafe diff --git a/src/SFML.Window/Event.cs b/src/SFML.Window/Event.cs index affc0c00..7efe5bd4 100644 --- a/src/SFML.Window/Event.cs +++ b/src/SFML.Window/Event.cs @@ -1,5 +1,5 @@ -using System; using System.Runtime.InteropServices; +using SFML.System; namespace SFML.Window { @@ -31,10 +31,6 @@ public enum EventType /// Event triggered when a keyboard key is released KeyReleased, - /// Event triggered when the mouse wheel is scrolled - [Obsolete("Use MouseWheelScrolled")] - MouseWheelMoved, - /// Event triggered when a mouse wheel is scrolled MouseWheelScrolled, @@ -47,6 +43,9 @@ public enum EventType /// Event triggered when the mouse moves within the area of a window MouseMoved, + /// Event triggered when the mouse moves within the area of a window, as raw mouse movement data + MouseMovedRaw, + /// Event triggered when the mouse enters the area of a window MouseEntered, @@ -96,16 +95,20 @@ public struct KeyEvent public Keyboard.Scancode Scancode; /// Is the Alt modifier pressed? - public int Alt; + [MarshalAs(UnmanagedType.I1)] + public bool Alt; /// Is the Control modifier pressed? - public int Control; + [MarshalAs(UnmanagedType.I1)] + public bool Control; /// Is the Shift modifier pressed? - public int Shift; + [MarshalAs(UnmanagedType.I1)] + public bool Shift; /// Is the System modifier pressed? - public int System; + [MarshalAs(UnmanagedType.I1)] + public bool System; } //////////////////////////////////////////////////////////// @@ -128,48 +131,35 @@ public struct TextEvent [StructLayout(LayoutKind.Sequential)] public struct MouseMoveEvent { - /// X coordinate of the mouse cursor - public int X; - - /// Y coordinate of the mouse cursor - public int Y; + /// Coordinates of the mouse cursor + public Vector2i Position; } //////////////////////////////////////////////////////////// /// - /// Mouse buttons event parameters + /// Mouse move event parameters /// //////////////////////////////////////////////////////////// [StructLayout(LayoutKind.Sequential)] - public struct MouseButtonEvent + public struct MouseMoveRawEvent { - /// Code of the button (see MouseButton enum) - public Mouse.Button Button; - - /// X coordinate of the mouse cursor - public int X; - - /// Y coordinate of the mouse cursor - public int Y; + /// Delta movement of the mouse since the last event + public Vector2i Delta; } //////////////////////////////////////////////////////////// /// - /// Mouse wheel move event parameters + /// Mouse buttons event parameters /// //////////////////////////////////////////////////////////// [StructLayout(LayoutKind.Sequential)] - [Obsolete("Use MouseWheelScrollEvent")] - public struct MouseWheelEvent + public struct MouseButtonEvent { - /// Scroll amount - public int Delta; - - /// X coordinate of the mouse cursor - public int X; + /// Code of the button (see MouseButton enum) + public Mouse.Button Button; - /// Y coordinate of the mouse cursor - public int Y; + /// Coordinates of the mouse cursor + public Vector2i Position; } //////////////////////////////////////////////////////////// @@ -186,11 +176,8 @@ public struct MouseWheelScrollEvent /// Scroll amount public float Delta; - /// X coordinate of the mouse cursor - public int X; - - /// Y coordinate of the mouse cursor - public int Y; + /// Coordinates of the mouse cursor + public Vector2i Position; } //////////////////////////////////////////////////////////// @@ -246,11 +233,8 @@ public struct JoystickConnectEvent [StructLayout(LayoutKind.Sequential)] public struct SizeEvent { - /// New width of the window - public uint Width; - - /// New height of the window - public uint Height; + /// New size of the window + public Vector2u Size; } //////////////////////////////////////////////////////////// @@ -264,11 +248,8 @@ public struct TouchEvent /// Index of the finger in case of multi-touch events public uint Finger; - /// X position of the touch, relative to the left of the owner window - public int X; - - /// Y position of the touch, relative to the top of the owner window - public int Y; + /// Position of the touch, relative to the left of the owner window + public Vector2i Position; } //////////////////////////////////////////////////////////// @@ -282,14 +263,8 @@ public struct SensorEvent /// Type of the sensor public Sensor.Type Type; - /// Current value of the sensor on X axis - public float X; - - /// Current value of the sensor on Y axis - public float Y; - - /// Current value of the sensor on Z axis - public float Z; + /// Current value of the sensor on X, Y and Z axes + public Vector3f Value; } //////////////////////////////////////////////////////////// @@ -297,7 +272,7 @@ public struct SensorEvent /// Event defines a system event and its parameters /// //////////////////////////////////////////////////////////// - [StructLayout(LayoutKind.Explicit, Size = 28)] + [StructLayout(LayoutKind.Explicit, Size = 20)] public struct Event { /// Type of event (see EventType enum) @@ -320,14 +295,13 @@ public struct Event [FieldOffset(4)] public MouseMoveEvent MouseMove; - /// Arguments for mouse button events (MouseButtonPressed, MouseButtonReleased) + /// Arguments for mouse move raw events (MouseMovedRaw) [FieldOffset(4)] - public MouseButtonEvent MouseButton; + public MouseMoveRawEvent MouseMoveRaw; - /// Arguments for mouse wheel events (MouseWheelMoved) + /// Arguments for mouse button events (MouseButtonPressed, MouseButtonReleased) [FieldOffset(4)] - [Obsolete("Use MouseWheelScroll")] - public MouseWheelEvent MouseWheel; + public MouseButtonEvent MouseButton; /// Arguments for mouse wheel scroll events (MouseWheelScrolled) [FieldOffset(4)] diff --git a/src/SFML.Window/EventArgs.cs b/src/SFML.Window/EventArgs.cs index 1fc4b7fc..72ed2f7e 100644 --- a/src/SFML.Window/EventArgs.cs +++ b/src/SFML.Window/EventArgs.cs @@ -1,4 +1,5 @@ using System; +using SFML.System; namespace SFML.Window { @@ -19,10 +20,10 @@ public KeyEventArgs(KeyEvent e) { Code = e.Code; Scancode = e.Scancode; - Alt = e.Alt != 0; - Control = e.Control != 0; - Shift = e.Shift != 0; - System = e.System != 0; + Alt = e.Alt; + Control = e.Control; + Shift = e.Shift; + System = e.System; } //////////////////////////////////////////////////////////// @@ -71,7 +72,7 @@ public class TextEventArgs : EventArgs /// /// Text event //////////////////////////////////////////////////////////// - public TextEventArgs(TextEvent e) => Unicode = Char.ConvertFromUtf32((int)e.Unicode); + public TextEventArgs(TextEvent e) => Unicode = char.ConvertFromUtf32((int)e.Unicode); //////////////////////////////////////////////////////////// /// @@ -99,11 +100,7 @@ public class MouseMoveEventArgs : EventArgs /// /// Mouse move event //////////////////////////////////////////////////////////// - public MouseMoveEventArgs(MouseMoveEvent e) - { - X = e.X; - Y = e.Y; - } + public MouseMoveEventArgs(MouseMoveEvent e) => Position = e.Position; //////////////////////////////////////////////////////////// /// @@ -112,35 +109,26 @@ public MouseMoveEventArgs(MouseMoveEvent e) /// String description of the object //////////////////////////////////////////////////////////// public override string ToString() => "[MouseMoveEventArgs]" + - " X(" + X + ")" + - " Y(" + Y + ")"; - - /// X coordinate of the mouse cursor - public int X; + $" Position({Position.X}, {Position.Y})"; - /// Y coordinate of the mouse cursor - public int Y; + /// Coordinates of the mouse cursor + public Vector2i Position; } //////////////////////////////////////////////////////////// /// - /// Mouse buttons event parameters + /// Mouse move raw event parameters /// //////////////////////////////////////////////////////////// - public class MouseButtonEventArgs : EventArgs + public class MouseMoveRawEventArgs : EventArgs { //////////////////////////////////////////////////////////// /// - /// Construct the mouse button arguments from a mouse button event + /// Construct the mouse move raw arguments from a mouse move raw event /// - /// Mouse button event + /// Mouse move event //////////////////////////////////////////////////////////// - public MouseButtonEventArgs(MouseButtonEvent e) - { - Button = e.Button; - X = e.X; - Y = e.Y; - } + public MouseMoveRawEventArgs(MouseMoveRawEvent e) => Delta = e.Delta; //////////////////////////////////////////////////////////// /// @@ -148,40 +136,30 @@ public MouseButtonEventArgs(MouseButtonEvent e) /// /// String description of the object //////////////////////////////////////////////////////////// - public override string ToString() => "[MouseButtonEventArgs]" + - " Button(" + Button + ")" + - " X(" + X + ")" + - " Y(" + Y + ")"; - - /// Code of the button (see MouseButton enum) - public Mouse.Button Button; - - /// X coordinate of the mouse cursor - public int X; + public override string ToString() => "[MouseMoveRawEventArgs]" + + $" Delta({Delta.X}, {Delta.Y})"; - /// Y coordinate of the mouse cursor - public int Y; + /// Delta movement of the mouse since last event + public Vector2i Delta; } //////////////////////////////////////////////////////////// /// - /// Mouse wheel event parameters + /// Mouse buttons event parameters /// //////////////////////////////////////////////////////////// - [Obsolete("Use MouseWheelScrollEventArgs")] - public class MouseWheelEventArgs : EventArgs + public class MouseButtonEventArgs : EventArgs { //////////////////////////////////////////////////////////// /// - /// Construct the mouse wheel arguments from a mouse wheel event + /// Construct the mouse button arguments from a mouse button event /// - /// Mouse wheel event + /// Mouse button event //////////////////////////////////////////////////////////// - public MouseWheelEventArgs(MouseWheelEvent e) + public MouseButtonEventArgs(MouseButtonEvent e) { - Delta = e.Delta; - X = e.X; - Y = e.Y; + Button = e.Button; + Position = e.Position; } //////////////////////////////////////////////////////////// @@ -190,19 +168,15 @@ public MouseWheelEventArgs(MouseWheelEvent e) /// /// String description of the object //////////////////////////////////////////////////////////// - public override string ToString() => "[MouseWheelEventArgs]" + - " Delta(" + Delta + ")" + - " X(" + X + ")" + - " Y(" + Y + ")"; - - /// Scroll amount - public int Delta; + public override string ToString() => "[MouseButtonEventArgs]" + + $" Button({Button})" + + $" Position({Position.X}, {Position.Y})"; - /// X coordinate of the mouse cursor - public int X; + /// Code of the button (see MouseButton enum) + public Mouse.Button Button; - /// Y coordinate of the mouse cursor - public int Y; + /// Coordinates of the mouse cursor + public Vector2i Position; } //////////////////////////////////////////////////////////// @@ -222,8 +196,7 @@ public MouseWheelScrollEventArgs(MouseWheelScrollEvent e) { Delta = e.Delta; Wheel = e.Wheel; - X = e.X; - Y = e.Y; + Position = e.Position; } //////////////////////////////////////////////////////////// @@ -233,10 +206,9 @@ public MouseWheelScrollEventArgs(MouseWheelScrollEvent e) /// String description of the object //////////////////////////////////////////////////////////// public override string ToString() => "[MouseWheelScrollEventArgs]" + - " Wheel(" + Wheel + ")" + - " Delta(" + Delta + ")" + - " X(" + X + ")" + - " Y(" + Y + ")"; + $" Wheel({Wheel})" + + $" Delta({Delta})" + + $" Position(${Position.X}, {Position.Y})"; /// Mouse Wheel which triggered the event public Mouse.Wheel Wheel; @@ -244,11 +216,8 @@ public override string ToString() => "[MouseWheelScrollEventArgs]" + /// Scroll amount public float Delta; - /// X coordinate of the mouse cursor - public int X; - - /// Y coordinate of the mouse cursor - public int Y; + /// Coordinate of the mouse cursor + public Vector2i Position; } //////////////////////////////////////////////////////////// @@ -278,9 +247,9 @@ public JoystickMoveEventArgs(JoystickMoveEvent e) /// String description of the object //////////////////////////////////////////////////////////// public override string ToString() => "[JoystickMoveEventArgs]" + - " JoystickId(" + JoystickId + ")" + - " Axis(" + Axis + ")" + - " Position(" + Position + ")"; + $" JoystickId({JoystickId})" + + $" Axis({Axis})" + + $" Position({Position})"; /// Index of the joystick which triggered the event public uint JoystickId; @@ -318,8 +287,8 @@ public JoystickButtonEventArgs(JoystickButtonEvent e) /// String description of the object //////////////////////////////////////////////////////////// public override string ToString() => "[JoystickButtonEventArgs]" + - " JoystickId(" + JoystickId + ")" + - " Button(" + Button + ")"; + $" JoystickId({JoystickId})" + + $" Button({Button})"; /// Index of the joystick which triggered the event public uint JoystickId; @@ -350,7 +319,7 @@ public class JoystickConnectEventArgs : EventArgs /// String description of the object //////////////////////////////////////////////////////////// public override string ToString() => "[JoystickConnectEventArgs]" + - " JoystickId(" + JoystickId + ")"; + $" JoystickId({JoystickId})"; /// Index of the joystick which triggered the event public uint JoystickId; @@ -369,11 +338,7 @@ public class SizeEventArgs : EventArgs /// /// Size event //////////////////////////////////////////////////////////// - public SizeEventArgs(SizeEvent e) - { - Width = e.Width; - Height = e.Height; - } + public SizeEventArgs(SizeEvent e) => Size = e.Size; //////////////////////////////////////////////////////////// /// @@ -382,14 +347,10 @@ public SizeEventArgs(SizeEvent e) /// String description of the object //////////////////////////////////////////////////////////// public override string ToString() => "[SizeEventArgs]" + - " Width(" + Width + ")" + - " Height(" + Height + ")"; - - /// New width of the window - public uint Width; + $" Size({Size.X}, {Size.Y})"; - /// New height of the window - public uint Height; + /// New size of the window + public Vector2u Size; } //////////////////////////////////////////////////////////// @@ -408,8 +369,7 @@ public class TouchEventArgs : EventArgs public TouchEventArgs(TouchEvent e) { Finger = e.Finger; - X = e.X; - Y = e.Y; + Position = e.Position; } //////////////////////////////////////////////////////////// @@ -419,18 +379,14 @@ public TouchEventArgs(TouchEvent e) /// String description of the object //////////////////////////////////////////////////////////// public override string ToString() => "[TouchEventArgs]" + - " Finger(" + Finger + ")" + - " X(" + X + ")" + - " Y(" + Y + ")"; + $" Finger({Finger})" + + $" Position({Position.X}, {Position.Y})"; /// Index of the finger in case of multi-touch events public uint Finger; - /// X position of the touch, relative to the left of the owner window - public int X; - - /// Y position of the touch, relative to the top of the owner window - public int Y; + /// Position of the touch, relative to the left of the owner window + public Vector2i Position; } //////////////////////////////////////////////////////////// @@ -449,9 +405,7 @@ public class SensorEventArgs : EventArgs public SensorEventArgs(SensorEvent e) { Type = e.Type; - X = e.X; - Y = e.Y; - Z = e.Z; + Value = e.Value; } //////////////////////////////////////////////////////////// @@ -461,21 +415,13 @@ public SensorEventArgs(SensorEvent e) /// String description of the object //////////////////////////////////////////////////////////// public override string ToString() => "[SensorEventArgs]" + - " Type(" + Type + ")" + - " X(" + X + ")" + - " Y(" + Y + ")" + - " Z(" + Z + ")"; + $" Type({Type})" + + $" Value({Value.X}, {Value.Y}, {Value.Z})"; /// Type of the sensor public Sensor.Type Type; - /// Current value of the sensor on X axis - public float X; - - /// Current value of the sensor on Y axis - public float Y; - - /// Current value of the sensor on Z axis - public float Z; + /// Current value of the sensor on X, Y and Z axes + public Vector3f Value; } } diff --git a/src/SFML.Window/Joystick.cs b/src/SFML.Window/Joystick.cs index 140975ae..058c6189 100644 --- a/src/SFML.Window/Joystick.cs +++ b/src/SFML.Window/Joystick.cs @@ -169,15 +169,18 @@ internal struct IdentificationMarshalData #region Imports [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfJoystick_isConnected(uint joystick); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern uint sfJoystick_getButtonCount(uint joystick); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfJoystick_hasAxis(uint joystick, Axis axis); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfJoystick_isButtonPressed(uint joystick, uint button); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] diff --git a/src/SFML.Window/Keyboard.cs b/src/SFML.Window/Keyboard.cs index 49918b0e..1885cca0 100644 --- a/src/SFML.Window/Keyboard.cs +++ b/src/SFML.Window/Keyboard.cs @@ -226,35 +226,16 @@ public enum Key /// The F15 key F15, /// The Pause key - Pause, - - /// The total number of keyboard keys - KeyCount, // Keep last - - // Deprecated backwards compatible stuff - /// DEPRECATED: Use Grave - [Obsolete("Replace with Grave")] - Tilde = Grave, - /// DEPRECATED: Use Hyphen - [Obsolete("Replace with Hyphen")] - Dash = Hyphen, - /// DEPRECATED: Use Backspace - [Obsolete("Replace with Backspace")] - BackSpace = Backspace, - /// DEPRECATED: Use Enter - [Obsolete("Replace with Enter")] - Return = Enter, - /// DEPRECATED: Use Backslash - [Obsolete("Replace with Backslash")] - BackSlash = Backslash, - /// DEPRECATED: Use Semicolon - [Obsolete("Replace with Semicolon")] - SemiColon = Semicolon, - /// DEPRECATED: Use Apostrophe - [Obsolete("Replace with Apostrophe")] - Quote = Apostrophe + Pause }; + //////////////////////////////////////////////////////////// + /// + /// The total number of keyboard keys, ignoring + /// + //////////////////////////////////////////////////////////// + public static readonly uint KeyCount = (uint)Key.Pause + 1; + //////////////////////////////////////////////////////////// /// /// Scancodes @@ -566,11 +547,15 @@ public enum Scancode LaunchMail, /// Keyboard Launch Media Select key LaunchMediaSelect, - - /// Keep last -- the total number of scancodes - ScancodeCount } + //////////////////////////////////////////////////////////// + /// + /// The total number of scancodes, ignoring + /// + //////////////////////////////////////////////////////////// + public static readonly uint ScancodeCount = (uint)Scancode.LaunchMediaSelect + 1; + //////////////////////////////////////////////////////////// /// /// Check if a key is pressed @@ -659,9 +644,11 @@ public static string GetDescription(Scancode code) #region Imports [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfKeyboard_isKeyPressed(Key key); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfKeyboard_isScancodePressed(Scancode code); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] diff --git a/src/SFML.Window/Mouse.cs b/src/SFML.Window/Mouse.cs index b1b0ca07..b04ec03c 100644 --- a/src/SFML.Window/Mouse.cs +++ b/src/SFML.Window/Mouse.cs @@ -29,15 +29,19 @@ public enum Button Middle, /// The first extra mouse button - XButton1, + Extra1, /// The second extra mouse button - XButton2, - - /// Keep last -- the total number of mouse buttons - ButtonCount + Extra2 }; + //////////////////////////////////////////////////////////// + /// + /// The total number of mouse buttons + /// + //////////////////////////////////////////////////////////// + public static readonly uint ButtonCount = (uint)Button.Extra2 + 1; + //////////////////////////////////////////////////////////// /// /// Mouse wheels @@ -46,10 +50,10 @@ public enum Button public enum Wheel { /// The vertical mouse wheel - VerticalWheel, + Vertical, /// The horizontal mouse wheel - HorizontalWheel + Horizontal }; //////////////////////////////////////////////////////////// @@ -125,6 +129,7 @@ public static void SetPosition(Vector2i position, WindowBase relativeTo) #region Imports [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfMouse_isButtonPressed(Button button); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] diff --git a/src/SFML.Window/Sensor.cs b/src/SFML.Window/Sensor.cs index e004fb8a..fb8f61e7 100644 --- a/src/SFML.Window/Sensor.cs +++ b/src/SFML.Window/Sensor.cs @@ -34,12 +34,16 @@ public enum Type UserAcceleration, /// Measures the absolute 3D orientation (degrees) - Orientation, - - /// Keep last -- the total number of sensor types - TypeCount + Orientation }; + //////////////////////////////////////////////////////////// + /// + /// The total number of sensor types + /// + //////////////////////////////////////////////////////////// + public static readonly uint Count = (uint)Type.Orientation + 1; + //////////////////////////////////////////////////////////// /// /// Check if a sensor is available on the underlying platform @@ -69,6 +73,7 @@ public enum Type #region Imports [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfSensor_isAvailable(Type sensor); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] diff --git a/src/SFML.Window/Touch.cs b/src/SFML.Window/Touch.cs index 164aaa94..f23863a9 100644 --- a/src/SFML.Window/Touch.cs +++ b/src/SFML.Window/Touch.cs @@ -53,6 +53,7 @@ public static Vector2i GetPosition(uint finger, WindowBase relativeTo) #region Imports [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfTouch_isDown(uint finger); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] diff --git a/src/SFML.Window/VideoMode.cs b/src/SFML.Window/VideoMode.cs index ca374b0a..0dffccbd 100644 --- a/src/SFML.Window/VideoMode.cs +++ b/src/SFML.Window/VideoMode.cs @@ -19,11 +19,10 @@ public struct VideoMode /// /// Construct the video mode with its width and height /// - /// Video mode width - /// Video mode height + /// Video mode size //////////////////////////////////////////////////////////// - public VideoMode(uint width, uint height) : - this(width, height, 32) + public VideoMode(Vector2u size) : + this(size, 32) { } @@ -31,14 +30,12 @@ public VideoMode(uint width, uint height) : /// /// Construct the video mode with its width, height and depth /// - /// Video mode width - /// Video mode height + /// Video mode size /// Video mode depth (bits per pixel) //////////////////////////////////////////////////////////// - public VideoMode(uint width, uint height, uint bpp) + public VideoMode(Vector2u size, uint bpp) { - Width = width; - Height = height; + Size = size; BitsPerPixel = bpp; } @@ -87,19 +84,115 @@ public static VideoMode[] FullscreenModes /// String description of the object //////////////////////////////////////////////////////////// public override string ToString() => "[VideoMode]" + - " Width(" + Width + ")" + - " Height(" + Height + ")" + - " BitsPerPixel(" + BitsPerPixel + ")"; + $" Size({Size})" + + $" BitsPerPixel({BitsPerPixel})"; - /// Video mode width, in pixels - public uint Width; + //////////////////////////////////////////////////////////// + /// + /// Compare video mode and object and checks if they are equal + /// + /// Object to check + /// Object and video mode are equal + //////////////////////////////////////////////////////////// + public override bool Equals(object obj) => (obj is VideoMode mode) && Equals(mode); + + /////////////////////////////////////////////////////////// + /// + /// Compare two video modes and checks if they are equal + /// + /// Video modes to check + /// Video modes are equal + //////////////////////////////////////////////////////////// + public bool Equals(VideoMode other) => (Size == other.Size) && + (BitsPerPixel == other.BitsPerPixel); + + //////////////////////////////////////////////////////////// + /// + /// Provide a integer describing the object + /// + /// Integer description of the object + //////////////////////////////////////////////////////////// + public override int GetHashCode() => Size.GetHashCode() ^ BitsPerPixel.GetHashCode(); - /// Video mode height, in pixels - public uint Height; + /// Video mode width and height, in pixels + public Vector2u Size; /// Video mode depth, in bits per pixel public uint BitsPerPixel; + //////////////////////////////////////////////////////////// + /// + /// Overload of == operator to compare two video modes + /// + /// Left operand (a video mode) + /// Right operand (a video mode) + /// True if modes are equal + //////////////////////////////////////////////////////////// + public static bool operator ==(VideoMode left, VideoMode right) => left.Equals(right); + + //////////////////////////////////////////////////////////// + /// + /// Overload of != operator to compare two video modes + /// + /// Left operand (a video mode) + /// Right operand (a video mode) + /// True if modes are different + //////////////////////////////////////////////////////////// + public static bool operator !=(VideoMode left, VideoMode right) => !left.Equals(right); + + //////////////////////////////////////////////////////////// + /// + /// Overload of < operator to compare two video modes + /// + /// Left operand (a video mode) + /// Right operand (a video mode) + /// True if is less than + //////////////////////////////////////////////////////////// + public static bool operator <(VideoMode left, VideoMode right) + { + if (left.BitsPerPixel == right.BitsPerPixel) + { + if (left.Size.X == right.Size.X) + { + return left.Size.Y < right.Size.Y; + } + + return left.Size.X < right.Size.X; + } + + return left.BitsPerPixel < right.BitsPerPixel; + } + + //////////////////////////////////////////////////////////// + /// + /// Overload of <= operator to compare two video modes + /// + /// Left operand (a video mode) + /// Right operand (a video mode) + /// True if is less than or equal to + //////////////////////////////////////////////////////////// + public static bool operator <=(VideoMode left, VideoMode right) => !(right < left); + + //////////////////////////////////////////////////////////// + /// + /// Overload of > operator to compare two video modes + /// + /// Left operand (a video mode) + /// Right operand (a video mode) + /// True if is greater than + //////////////////////////////////////////////////////////// + public static bool operator >(VideoMode left, VideoMode right) => right < left; + + //////////////////////////////////////////////////////////// + /// + /// Overload of >= operator to compare two video modes + /// + /// Left operand (a video mode) + /// Right operand (a video mode) + /// True if is greater than or equal to + //////////////////////////////////////////////////////////// + public static bool operator >=(VideoMode left, VideoMode right) => !(left < right); + #region Imports [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern VideoMode sfVideoMode_getDesktopMode(); @@ -108,6 +201,7 @@ public override string ToString() => "[VideoMode]" + private static extern unsafe VideoMode* sfVideoMode_getFullscreenModes(out UIntPtr count); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfVideoMode_isValid(VideoMode mode); #endregion } diff --git a/src/SFML.Window/Vulkan.cs b/src/SFML.Window/Vulkan.cs index 190c9194..79c0c0d4 100644 --- a/src/SFML.Window/Vulkan.cs +++ b/src/SFML.Window/Vulkan.cs @@ -58,6 +58,7 @@ public static string[] GetGraphicsRequiredInstanceExtensions() #region Imports [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfVulkan_isAvailable(bool requireGraphics); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] diff --git a/src/SFML.Window/Window.cs b/src/SFML.Window/Window.cs index 13209bbd..264c52d5 100644 --- a/src/SFML.Window/Window.cs +++ b/src/SFML.Window/Window.cs @@ -21,7 +21,7 @@ public class Window : WindowBase /// Title of the window //////////////////////////////////////////////////////////// public Window(VideoMode mode, string title) : - this(mode, title, Styles.Default, new ContextSettings(0, 0)) + this(mode, title, Styles.Default, State.Windowed, new ContextSettings(0, 0)) { } @@ -32,9 +32,10 @@ public Window(VideoMode mode, string title) : /// Video mode to use /// Title of the window /// Window style (Resize | Close by default) + /// Window state //////////////////////////////////////////////////////////// - public Window(VideoMode mode, string title, Styles style) : - this(mode, title, style, new ContextSettings(0, 0)) + public Window(VideoMode mode, string title, Styles style, State state) : + this(mode, title, style, state, new ContextSettings(0, 0)) { } @@ -45,9 +46,10 @@ public Window(VideoMode mode, string title, Styles style) : /// Video mode to use /// Title of the window /// Window style (Resize | Close by default) + /// Window state /// Creation parameters //////////////////////////////////////////////////////////// - public Window(VideoMode mode, string title, Styles style, ContextSettings settings) : + public Window(VideoMode mode, string title, Styles style, State state, ContextSettings settings) : base(IntPtr.Zero) { // Copy the title to a null-terminated UTF-32 byte array @@ -57,7 +59,7 @@ public Window(VideoMode mode, string title, Styles style, ContextSettings settin { fixed (byte* titlePtr = titleAsUtf32) { - CPointer = sfWindow_createUnicode(mode, (IntPtr)titlePtr, style, ref settings); + CPointer = sfWindow_createUnicode(mode, (IntPtr)titlePtr, style, state, ref settings); } } } @@ -164,17 +166,16 @@ public override void SetTitle(string title) /// /// Change the window's icon /// - /// Icon's width, in pixels - /// Icon's height, in pixels + /// Icon's width and height, in pixels /// Array of pixels, format must be RGBA 32 bits //////////////////////////////////////////////////////////// - public override void SetIcon(uint width, uint height, byte[] pixels) + public override void SetIcon(Vector2u size, byte[] pixels) { unsafe { fixed (byte* pixelsPtr = pixels) { - sfWindow_setIcon(CPointer, width, height, pixelsPtr); + sfWindow_setIcon(CPointer, size, pixelsPtr); } } } @@ -278,7 +279,7 @@ public override void SetIcon(uint width, uint height, byte[] pixels) /// OS-specific handle of the window /// //////////////////////////////////////////////////////////// - public override IntPtr SystemHandle => sfWindow_getSystemHandle(CPointer); + public override IntPtr NativeHandle => sfWindow_getNativeHandle(CPointer); //////////////////////////////////////////////////////////// /// @@ -383,10 +384,11 @@ protected Window(IntPtr cPointer, int dummy) : /// /// Internal function to get the next event (blocking) /// + /// Maximum time to wait ( for infinite) /// Variable to fill with the raw pointer to the event structure /// False if any error occurred //////////////////////////////////////////////////////////// - protected override bool WaitEvent(out Event eventToFill) => sfWindow_waitEvent(CPointer, out eventToFill); + protected override bool WaitEvent(Time timeout, out Event eventToFill) => sfWindow_waitEvent(CPointer, timeout, out eventToFill); //////////////////////////////////////////////////////////// /// @@ -398,7 +400,7 @@ protected Window(IntPtr cPointer, int dummy) : #region Imports [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern IntPtr sfWindow_createUnicode(VideoMode mode, IntPtr title, Styles style, ref ContextSettings settings); + private static extern IntPtr sfWindow_createUnicode(VideoMode mode, IntPtr title, Styles style, State state, ref ContextSettings settings); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern IntPtr sfWindow_createFromHandle(IntPtr handle, ref ContextSettings settings); @@ -407,16 +409,19 @@ protected Window(IntPtr cPointer, int dummy) : private static extern void sfWindow_destroy(IntPtr cPointer); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfWindow_isOpen(IntPtr cPointer); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfWindow_close(IntPtr cPointer); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfWindow_pollEvent(IntPtr cPointer, out Event evt); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern bool sfWindow_waitEvent(IntPtr cPointer, out Event evt); + [return: MarshalAs(UnmanagedType.I1)] + private static extern bool sfWindow_waitEvent(IntPtr cPointer, Time timeout, out Event evt); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfWindow_display(IntPtr cPointer); @@ -440,7 +445,7 @@ protected Window(IntPtr cPointer, int dummy) : private static extern void sfWindow_setUnicodeTitle(IntPtr cPointer, IntPtr title); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern unsafe void sfWindow_setIcon(IntPtr cPointer, uint width, uint height, byte* pixels); + private static extern unsafe void sfWindow_setIcon(IntPtr cPointer, Vector2u size, byte* pixels); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfWindow_setVisible(IntPtr cPointer, bool visible); @@ -461,6 +466,7 @@ protected Window(IntPtr cPointer, int dummy) : private static extern void sfWindow_setKeyRepeatEnabled(IntPtr cPointer, bool enable); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfWindow_setActive(IntPtr cPointer, bool active); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] @@ -470,12 +476,13 @@ protected Window(IntPtr cPointer, int dummy) : private static extern void sfWindow_setJoystickThreshold(IntPtr cPointer, float threshold); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern IntPtr sfWindow_getSystemHandle(IntPtr cPointer); + private static extern IntPtr sfWindow_getNativeHandle(IntPtr cPointer); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfWindow_requestFocus(IntPtr cPointer); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfWindow_hasFocus(IntPtr cPointer); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] @@ -488,6 +495,7 @@ protected Window(IntPtr cPointer, int dummy) : private static extern Vector2i sfTouch_getPosition(uint finger, IntPtr relativeTo); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfWindow_createVulkanSurface(IntPtr cPointer, IntPtr vkInstance, out IntPtr surface, IntPtr vkAllocator); #endregion } diff --git a/src/SFML.Window/WindowBase.cs b/src/SFML.Window/WindowBase.cs index ddf6794f..2e3bca1e 100644 --- a/src/SFML.Window/WindowBase.cs +++ b/src/SFML.Window/WindowBase.cs @@ -26,13 +26,23 @@ public enum Styles /// Titlebar + close button Close = 1 << 2, - /// Fullscreen mode (this flag and all others are mutually exclusive)) - Fullscreen = 1 << 3, - /// Default window style (titlebar + resize + close) Default = Titlebar | Resize | Close } + //////////////////////////////////////////////////////////// + /// + /// Enumeration of window states + /// + //////////////////////////////////////////////////////////// + public enum State + { + /// Floating window + Windowed, + + /// Fullscreen window + Fullscreen + } //////////////////////////////////////////////////////////// /// @@ -49,7 +59,7 @@ public class WindowBase : ObjectBase /// Title of the window //////////////////////////////////////////////////////////// public WindowBase(VideoMode mode, string title) : - this(mode, title, Styles.Default) + this(mode, title, Styles.Default, State.Windowed) { } @@ -60,8 +70,9 @@ public WindowBase(VideoMode mode, string title) : /// Video mode to use /// Title of the window /// Window style (Resize | Close by default) + /// Window state //////////////////////////////////////////////////////////// - public WindowBase(VideoMode mode, string title, Styles style) : + public WindowBase(VideoMode mode, string title, Styles style, State state) : base(IntPtr.Zero) { // Copy the title to a null-terminated UTF-32 byte array @@ -71,7 +82,7 @@ public WindowBase(VideoMode mode, string title, Styles style) : { fixed (byte* titlePtr = titleAsUtf32) { - CPointer = sfWindowBase_createUnicode(mode, (IntPtr)titlePtr, style); + CPointer = sfWindowBase_createUnicode(mode, (IntPtr)titlePtr, style, state); } } } @@ -152,17 +163,16 @@ public virtual void SetTitle(string title) /// /// Change the window's icon /// - /// Icon's width, in pixels - /// Icon's height, in pixels + /// Icon's width and height, in pixels /// Array of pixels, format must be RGBA 32 bits //////////////////////////////////////////////////////////// - public virtual void SetIcon(uint width, uint height, byte[] pixels) + public virtual void SetIcon(Vector2u size, byte[] pixels) { unsafe { fixed (byte* pixelsPtr = pixels) { - sfWindowBase_setIcon(CPointer, width, height, pixelsPtr); + sfWindowBase_setIcon(CPointer, size, pixelsPtr); } } } @@ -238,7 +248,7 @@ public virtual void SetIcon(uint width, uint height, byte[] pixels) /// OS-specific handle of the window /// //////////////////////////////////////////////////////////// - public virtual IntPtr SystemHandle => sfWindowBase_getSystemHandle(CPointer); + public virtual IntPtr NativeHandle => sfWindowBase_getNativeHandle(CPointer); //////////////////////////////////////////////////////////// /// @@ -246,9 +256,18 @@ public virtual void SetIcon(uint width, uint height, byte[] pixels) /// event handler /// //////////////////////////////////////////////////////////// - public void WaitAndDispatchEvents() + public void WaitAndDispatchEvents() => WaitAndDispatchEvents(Time.Zero); + + //////////////////////////////////////////////////////////// + /// + /// Wait for a new event and dispatch it to the corresponding + /// event handler + /// + /// Maximum time to wait ( for infinite) + //////////////////////////////////////////////////////////// + public void WaitAndDispatchEvents(Time timeout) { - if (WaitEvent(out var e)) + if (WaitEvent(timeout, out var e)) { CallEventHandler(e); } @@ -338,10 +357,11 @@ protected WindowBase(IntPtr cPointer, int dummy) : /// /// Internal function to get the next event (blocking) /// + /// Maximum time to wait ( for infinite) /// Variable to fill with the raw pointer to the event structure /// False if any error occurred //////////////////////////////////////////////////////////// - protected virtual bool WaitEvent(out Event eventToFill) => sfWindowBase_waitEvent(CPointer, out eventToFill); + protected virtual bool WaitEvent(Time timeout, out Event eventToFill) => sfWindowBase_waitEvent(CPointer, timeout, out eventToFill); //////////////////////////////////////////////////////////// /// @@ -452,13 +472,9 @@ private void CallEventHandler(Event e) MouseMoved?.Invoke(this, new MouseMoveEventArgs(e.MouseMove)); break; - // Disable CS0618 (Obsolete Warning). This Event will be removed in SFML.NET 3.0, but should remain supported until then. -#pragma warning disable CS0618 - case EventType.MouseWheelMoved: - MouseWheelMoved?.Invoke(this, new MouseWheelEventArgs(e.MouseWheel)); + case EventType.MouseMovedRaw: + MouseMovedRaw?.Invoke(this, new MouseMoveRawEventArgs(e.MouseMoveRaw)); break; - // restore CS0618 -#pragma warning restore CS0618 case EventType.MouseWheelScrolled: MouseWheelScrolled?.Invoke(this, new MouseWheelScrollEventArgs(e.MouseWheelScroll)); @@ -514,10 +530,6 @@ private void CallEventHandler(Event e) /// Event handler for the KeyReleased event public event EventHandler KeyReleased; - /// Event handler for the MouseWheelMoved event - [Obsolete("Use MouseWheelScrolled")] - public event EventHandler MouseWheelMoved; - /// Event handler for the MouseWheelScrolled event public event EventHandler MouseWheelScrolled; @@ -530,6 +542,9 @@ private void CallEventHandler(Event e) /// Event handler for the MouseMoved event public event EventHandler MouseMoved; + /// Event handler for the MouseMovedRaw event + public event EventHandler MouseMovedRaw; + /// Event handler for the MouseEntered event public event EventHandler MouseEntered; @@ -565,7 +580,7 @@ private void CallEventHandler(Event e) #region Imports [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern IntPtr sfWindowBase_createUnicode(VideoMode mode, IntPtr title, Styles style); + private static extern IntPtr sfWindowBase_createUnicode(VideoMode mode, IntPtr title, Styles style, State state); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern IntPtr sfWindowBase_createFromHandle(IntPtr handle); @@ -577,13 +592,16 @@ private void CallEventHandler(Event e) private static extern void sfWindowBase_close(IntPtr cPointer); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfWindowBase_isOpen(IntPtr cPointer); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfWindowBase_pollEvent(IntPtr cPointer, out Event evt); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern bool sfWindowBase_waitEvent(IntPtr cPointer, out Event evt); + [return: MarshalAs(UnmanagedType.I1)] + private static extern bool sfWindowBase_waitEvent(IntPtr cPointer, Time timeout, out Event evt); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern Vector2i sfWindowBase_getPosition(IntPtr cPointer); @@ -601,7 +619,7 @@ private void CallEventHandler(Event e) private static extern void sfWindowBase_setUnicodeTitle(IntPtr cPointer, IntPtr title); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern unsafe void sfWindowBase_setIcon(IntPtr cPointer, uint width, uint height, byte* pixels); + private static extern unsafe void sfWindowBase_setIcon(IntPtr cPointer, Vector2u size, byte* pixels); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] private static extern void sfWindowBase_setVisible(IntPtr cPointer, bool visible); @@ -625,12 +643,14 @@ private void CallEventHandler(Event e) private static extern void sfWindowBase_requestFocus(IntPtr cPointer); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfWindowBase_hasFocus(IntPtr cPointer); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] - private static extern IntPtr sfWindowBase_getSystemHandle(IntPtr cPointer); + private static extern IntPtr sfWindowBase_getNativeHandle(IntPtr cPointer); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.I1)] private static extern bool sfWindowBase_createVulkanSurface(IntPtr cPointer, IntPtr vkInstance, out IntPtr surface, IntPtr vkAllocator); [DllImport(CSFML.Window, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] diff --git a/test/SFML.System.Test/Angle.test.cs b/test/SFML.System.Test/Angle.test.cs new file mode 100644 index 00000000..b61111ff --- /dev/null +++ b/test/SFML.System.Test/Angle.test.cs @@ -0,0 +1,266 @@ +namespace SFML.System.Test; + +public class AngleTests +{ + private const float Eps = 1e-5f; + + [Fact] + public void Construction() + { + var angle = new Angle(); + Assert.Equal(0f, angle.Degrees); + Assert.Equal(0f, angle.Radians); + } + + [Fact] + public void WrapSigned_WithZero() + => Assert.Equal(Angle.Zero, Angle.Zero.WrapSigned()); + + [Theory] + [InlineData(0, 0)] + [InlineData(1, 1)] + [InlineData(-1, -1)] + [InlineData(90, 90)] + [InlineData(-90, -90)] + [InlineData(180, -180)] + [InlineData(-180, -180)] + [InlineData(360, 0)] + [InlineData(-360, 0)] + [InlineData(720, 0)] + [InlineData(-720, 0)] + public void WrapSigned(float input, float expectedWrap) + => Assert.Equal(Angle.FromDegrees(expectedWrap).Radians, Angle.FromDegrees(input).WrapSigned().Radians, Eps); + + [Fact] + public void WrapUnsigned_WithZero() + => Assert.Equal(Angle.Zero, Angle.Zero.WrapUnsigned()); + + [Theory] + [InlineData(0, 0)] + [InlineData(1, 1)] + [InlineData(-1, 359)] + [InlineData(90, 90)] + [InlineData(-90, 270)] + [InlineData(180, 180)] + [InlineData(-180, 180)] + [InlineData(360, 0)] + [InlineData(-360, 0)] + [InlineData(720, 0)] + [InlineData(-720, 0)] + public void WrapUnsigned(float input, float expectedWrap) + => Assert.Equal(Angle.FromDegrees(expectedWrap).Radians, Angle.FromDegrees(input).WrapUnsigned().Radians, Eps); + + [Theory] + [InlineData(15, 0.26179939f)] + [InlineData(1000, 17.453293f)] + [InlineData(-4321, -75.415677f)] + public void Degrees(float degrees, float expectedRadians) + { + var angle = Angle.FromDegrees(degrees); + Assert.Equal(degrees, angle.Degrees, Eps); + Assert.Equal(expectedRadians, angle.Radians, Eps); + } + + [Theory] + [InlineData(1, 57.2957795f)] + [InlineData(72, 4125.29612f)] + [InlineData(-200, -11459.1559f)] + public void Radians(float radians, float expectedDegrees) + { + var angle = Angle.FromRadians(radians); + Assert.Equal(radians, angle.Radians, Eps); + Assert.Equal(expectedDegrees, angle.Degrees, Eps); + } + + [Fact] + public void Constants() + { + Assert.Equal(0f, Angle.Zero.Degrees); + Assert.Equal(0f, Angle.Zero.Radians); + } + + [Fact] + public void OperatorEq() + { + Assert.True(new Angle() == new Angle()); + Assert.True(new Angle() == Angle.Zero); + Assert.True(new Angle() == Angle.FromDegrees(0)); + Assert.True(new Angle() == Angle.FromRadians(0)); + Assert.True(Angle.FromDegrees(0) == Angle.FromRadians(0)); + Assert.True(Angle.FromDegrees(15) == Angle.FromDegrees(15)); + Assert.True(Angle.FromRadians(15) == Angle.FromRadians(15)); + Assert.True(Angle.FromDegrees(360) == Angle.FromDegrees(360)); + Assert.True(Angle.FromDegrees(720) == Angle.FromDegrees(720)); + } + + [Fact] + public void OperatorNotEq() + { + Assert.True(new Angle() != Angle.FromRadians(2)); + Assert.True(Angle.FromDegrees(1) != Angle.FromRadians(1)); + Assert.True(Angle.FromRadians(0) != Angle.FromRadians(0.1f)); + } + + [Fact] + public void OperatorLt() + { + Assert.True(Angle.FromRadians(0) < Angle.FromDegrees(0.1f)); + Assert.True(Angle.FromDegrees(0) < Angle.FromRadians(0.1f)); + Assert.True(Angle.FromRadians(-0.1f) < Angle.FromRadians(0f)); + Assert.True(Angle.FromDegrees(-0.1f) < Angle.FromDegrees(0f)); + } + + [Fact] + public void OperatorGt() + { + Assert.True(Angle.FromRadians(0.1f) > Angle.FromDegrees(0)); + Assert.True(Angle.FromDegrees(0.1f) > Angle.FromRadians(0)); + Assert.True(Angle.FromRadians(0) > Angle.FromRadians(-0.1f)); + Assert.True(Angle.FromDegrees(0) > Angle.FromDegrees(-0.1f)); + } + + [Fact] + public void OperatorLte() + { + Assert.True(Angle.FromRadians(0) <= Angle.FromDegrees(0.1f)); + Assert.True(Angle.FromDegrees(0) <= Angle.FromRadians(0.1f)); + Assert.True(Angle.FromRadians(-0.1f) <= Angle.FromRadians(0f)); + Assert.True(Angle.FromDegrees(-0.1f) <= Angle.FromDegrees(0f)); + + Assert.True(new Angle() <= new Angle()); + Assert.True(new Angle() <= Angle.Zero); + Assert.True(new Angle() <= Angle.FromDegrees(0)); + Assert.True(new Angle() <= Angle.FromRadians(0)); + Assert.True(Angle.FromDegrees(0) <= Angle.FromRadians(0)); + Assert.True(Angle.FromDegrees(15) <= Angle.FromDegrees(15)); + Assert.True(Angle.FromRadians(15) <= Angle.FromRadians(15)); + Assert.True(Angle.FromDegrees(360) <= Angle.FromDegrees(360)); + Assert.True(Angle.FromDegrees(720) <= Angle.FromDegrees(720)); + } + + [Fact] + public void OperatorGte() + { + Assert.True(Angle.FromRadians(0.1f) > Angle.FromDegrees(0)); + Assert.True(Angle.FromDegrees(0.1f) > Angle.FromRadians(0)); + Assert.True(Angle.FromRadians(0) > Angle.FromRadians(-0.1f)); + Assert.True(Angle.FromDegrees(0) > Angle.FromDegrees(-0.1f)); + + Assert.True(new Angle() >= new Angle()); + Assert.True(new Angle() >= Angle.Zero); + Assert.True(new Angle() >= Angle.FromDegrees(0)); + Assert.True(new Angle() >= Angle.FromRadians(0)); + Assert.True(Angle.FromDegrees(0) >= Angle.FromRadians(0)); + Assert.True(Angle.FromDegrees(15) >= Angle.FromDegrees(15)); + Assert.True(Angle.FromRadians(15) >= Angle.FromRadians(15)); + Assert.True(Angle.FromDegrees(360) >= Angle.FromDegrees(360)); + Assert.True(Angle.FromDegrees(720) >= Angle.FromDegrees(720)); + } + + [Fact] + public void OperatorNeg() + { + Assert.Equal(-new Angle(), new Angle()); + Assert.Equal(-Angle.FromRadians(-1), Angle.FromRadians(1)); + Assert.Equal(-Angle.FromDegrees(15), Angle.FromDegrees(-15)); + Assert.Equal(-Angle.FromRadians(1), Angle.FromRadians(-1)); + } + + [Fact] + public void OperatorAdd() + { + Assert.Equal(new Angle() + new Angle(), new Angle()); + Assert.Equal(Angle.Zero + Angle.FromRadians(0.5f), Angle.FromRadians(0.5f)); + Assert.Equal(Angle.FromRadians(6) + Angle.FromRadians(0.5f), Angle.FromRadians(6.5f)); + Assert.Equal(Angle.FromRadians(10) + Angle.FromRadians(0.5f), Angle.FromRadians(10.5f)); + Assert.Equal(Angle.FromDegrees(360) + Angle.FromDegrees(360), Angle.FromDegrees(720)); + } + + [Fact] + public void OperatorAddAssign() + { + var angle = Angle.FromDegrees(-15); + angle += Angle.FromDegrees(15); + Assert.Equal(Angle.FromDegrees(0).Degrees, angle.Degrees, Eps); + angle += Angle.FromDegrees(10); + Assert.Equal(Angle.FromDegrees(10).Degrees, angle.Degrees, Eps); + } + + [Fact] + public void OperatorSub() + { + Assert.Equal(new Angle() - new Angle(), new Angle()); + Assert.Equal(Angle.FromRadians(1) - Angle.FromRadians(0.5f), Angle.FromRadians(0.5f)); + Assert.Equal(Angle.Zero - Angle.FromRadians(0.5f), Angle.FromRadians(-0.5f)); + Assert.Equal(Angle.FromDegrees(900) - Angle.FromDegrees(1), Angle.FromDegrees(899)); + } + + [Fact] + public void OperatorSubAssign() + { + var angle = Angle.FromDegrees(15); + angle -= Angle.FromDegrees(15); + Assert.Equal(Angle.FromDegrees(0).Degrees, angle.Degrees, Eps); + angle -= Angle.FromDegrees(10); + Assert.Equal(Angle.FromDegrees(-10).Degrees, angle.Degrees, Eps); + } + + [Fact] + public void OperatorMul() + { + Assert.Equal(10 * Angle.FromRadians(0), Angle.Zero); + Assert.Equal((2.5f * Angle.FromDegrees(10)).Degrees, Angle.FromDegrees(25).Degrees, Eps); + Assert.Equal((10f * Angle.FromDegrees(100)).Degrees, Angle.FromDegrees(1000).Degrees, Eps); + + Assert.Equal(10 * Angle.FromRadians(0), Angle.Zero); + Assert.Equal((2.5f * Angle.FromDegrees(10)).Degrees, Angle.FromDegrees(25).Degrees, Eps); + Assert.Equal((10f * Angle.FromDegrees(100)).Degrees, Angle.FromDegrees(1000).Degrees, Eps); + } + + [Fact] + public void OperatorMulAssign() + { + var angle = Angle.FromDegrees(1); + angle *= 10; + Assert.Equal(Angle.FromDegrees(10).Degrees, angle.Degrees, Eps); + } + + [Fact] + public void OperatorDiv() + { + Assert.Equal(Angle.Zero / 10, Angle.Zero); + Assert.Equal(Angle.FromDegrees(10) / 2.5f, Angle.FromDegrees(4)); + Assert.Equal(Angle.FromRadians(12) / 3, Angle.FromRadians(4)); + + Assert.Equal(0f, Angle.Zero / Angle.FromDegrees(1)); + Assert.Equal(1f, Angle.FromDegrees(10) / Angle.FromDegrees(10)); + Assert.Equal(5f, Angle.FromRadians(10) / Angle.FromRadians(2), Eps); + } + + [Fact] + public void OperatorDivAssign() + { + var angle = Angle.FromDegrees(60); + angle /= 5; + Assert.Equal(Angle.FromDegrees(12).Degrees, angle.Degrees, Eps); + } + + [Fact] + public void OperatorMod() + { + Assert.Equal(Angle.Zero % Angle.FromRadians(0.5f), Angle.Zero); + Assert.Equal(Angle.FromRadians(10) % Angle.FromRadians(1), Angle.FromRadians(0)); + Assert.Equal((Angle.FromDegrees(90) % Angle.FromDegrees(30)).Degrees, Angle.FromDegrees(0).Degrees, Eps); + Assert.Equal((Angle.FromDegrees(90) % Angle.FromDegrees(40)).Degrees, Angle.FromDegrees(10).Degrees, Eps); + Assert.Equal((Angle.FromDegrees(-90) % Angle.FromDegrees(30)).Degrees, Angle.FromDegrees(0).Degrees, Eps); + Assert.Equal((Angle.FromDegrees(-90) % Angle.FromDegrees(40)).Degrees, Angle.FromDegrees(30).Degrees, Eps); + } + + [Fact] + public void OperatorModAssign() + { + var angle = Angle.FromDegrees(59); + angle %= Angle.FromDegrees(10); + Assert.Equal(Angle.FromDegrees(9).Degrees, angle.Degrees, Eps); + } +} \ No newline at end of file diff --git a/test/SFML.System.Test/SFML.System.Test.csproj b/test/SFML.System.Test/SFML.System.Test.csproj new file mode 100644 index 00000000..1dc76e09 --- /dev/null +++ b/test/SFML.System.Test/SFML.System.Test.csproj @@ -0,0 +1,34 @@ + + + + net6.0 + enable + enable + + false + true + AnyCPU;x86;x64 + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + diff --git a/test/SFML.System.Test/Vector2f.test.cs b/test/SFML.System.Test/Vector2f.test.cs new file mode 100644 index 00000000..60e5d9f6 --- /dev/null +++ b/test/SFML.System.Test/Vector2f.test.cs @@ -0,0 +1,282 @@ +namespace SFML.System.Test; + +public class Vector2fTests +{ + private const float Eps = 1e-5f; + private const float EpsRelaxed = 15e-5f; + private const float Sqrt2Div2 = 0.7071067811865475f; + + private static void AssertVectorEqual(Vector2f lhs, Vector2f rhs, float tolerance = 0f) + { + Assert.Equal(lhs.X, rhs.X, tolerance); + Assert.Equal(lhs.Y, rhs.Y, tolerance); + } + + [Fact] + public void DefaultConstructor() + { + var vec = new Vector2f(); + Assert.Equal(0, vec.X); + Assert.Equal(0, vec.Y); + } + + [Fact] + public void CoordinateConstructor() + { + var vec = new Vector2f(1, 2); + Assert.Equal(1, vec.X); + Assert.Equal(2, vec.Y); + } + + // TODO Conversion methods + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(0, 45, 0, 0)] + [InlineData(0, 90, 0, 0)] + [InlineData(0, 135, 0, 0)] + [InlineData(0, 180, 0, 0)] + [InlineData(0, 270, 0, 0)] + [InlineData(0, 360, 0, 0)] + [InlineData(0, -90, 0, 0)] + [InlineData(0, -180, 0, 0)] + [InlineData(0, -270, 0, 0)] + [InlineData(0, -360, 0, 0)] + [InlineData(1, 0, 1, 0)] + [InlineData(1, 45, Sqrt2Div2, Sqrt2Div2)] + [InlineData(1, 90, 0, 1)] + [InlineData(1, 135, -Sqrt2Div2, Sqrt2Div2)] + [InlineData(1, 180, -1, 0)] + [InlineData(1, 270, 0, -1)] + [InlineData(1, 360, 1, 0)] + [InlineData(1, -90, 0, -1)] + [InlineData(1, -180, -1, 0)] + [InlineData(1, -270, 0, 1)] + [InlineData(1, -360, 1, 0)] + [InlineData(-1, 0, -1, 0)] + [InlineData(-1, 45, -Sqrt2Div2, -Sqrt2Div2)] + [InlineData(-1, 90, 0, -1)] + [InlineData(-1, 135, Sqrt2Div2, -Sqrt2Div2)] + [InlineData(-1, 180, 1, 0)] + [InlineData(-1, 270, 0, 1)] + [InlineData(-1, 360, -1, 0)] + [InlineData(-1, -90, 0, 1)] + [InlineData(-1, -180, 1, 0)] + [InlineData(-1, -270, 0, -1)] + [InlineData(-1, -360, -1, 0)] + [InlineData(4.2f, 0, 4.2f, 0)] + [InlineData(4.2f, 45, 4.2f * Sqrt2Div2, 4.2f * Sqrt2Div2)] + [InlineData(4.2f, 90, 0, 4.2f)] + [InlineData(4.2f, 135, -4.2f * Sqrt2Div2, 4.2f * Sqrt2Div2)] + [InlineData(4.2f, 180, -4.2f, 0)] + [InlineData(4.2f, 270, 0, -4.2f)] + [InlineData(4.2f, 360, 4.2f, 0)] + [InlineData(4.2f, -90, 0, -4.2f)] + [InlineData(4.2f, -180, -4.2f, 0)] + [InlineData(4.2f, -270, 0, 4.2f)] + [InlineData(4.2f, -360, 4.2f, 0)] + public void LengthAndAngleConstructor(float radius, float degrees, float expectedX, float expectedY) + { + var expected = new Vector2f(expectedX, expectedY); + var actual = new Vector2f(radius, Angle.FromDegrees(degrees)); + AssertVectorEqual(expected, actual, Eps); + } + + [Fact] + public void OperatorNeg() + { + var vec = new Vector2f(1, 2); + var negatedVec = -vec; + + Assert.Equal(-1, negatedVec.X); + Assert.Equal(-2, negatedVec.Y); + } + + private static readonly Vector2f _lhs = new Vector2f(2, 5); + private static readonly Vector2f _rhs = new Vector2f(8, 3); + + [Fact] + public void OperatorAddAssign() + { + var vec = _lhs; + vec += _rhs; + + Assert.Equal(10, vec.X); + Assert.Equal(8, vec.Y); + } + + [Fact] + public void OperatorSubAssign() + { + var vec = _lhs; + vec -= _rhs; + + Assert.Equal(-6, vec.X); + Assert.Equal(2, vec.Y); + } + + [Fact] + public void OperatorAdd() + { + var vec = _lhs + _rhs; + + Assert.Equal(10, vec.X); + Assert.Equal(8, vec.Y); + } + + [Fact] + public void OperatorSub() + { + var vec = _lhs - _rhs; + + Assert.Equal(-6, vec.X); + Assert.Equal(2, vec.Y); + } + + private static readonly Vector2f _vecMul = new Vector2f(26, 12); + private static readonly float _floatMul = 2; + + [Fact] + public void OperatorMul() + { + var vecFloat = _vecMul * _floatMul; + + Assert.Equal(52, vecFloat.X); + Assert.Equal(24, vecFloat.Y); + + var floatVec = _floatMul * _vecMul; + + Assert.Equal(52, floatVec.X); + Assert.Equal(24, floatVec.Y); + } + + [Fact] + public void OperatorMulAssign() + { + var vecFloat = _vecMul; + vecFloat *= _floatMul; + + Assert.Equal(52, vecFloat.X); + Assert.Equal(24, vecFloat.Y); + } + + [Fact] + public void OperatorDiv() + { + var vec = _vecMul / _floatMul; + + Assert.Equal(13, vec.X); + Assert.Equal(6, vec.Y); + } + + [Fact] + public void OperatorDivAssign() + { + var vec = _vecMul; + vec /= _floatMul; + + Assert.Equal(13, vec.X); + Assert.Equal(6, vec.Y); + } + + private static readonly Vector2f _equalFirst = new Vector2f(1, 5); + private static readonly Vector2f _equalSecond = new Vector2f(1, 5); + private static readonly Vector2f _different = new Vector2f(6, 9); + + [Fact] + public void OperatorEq() + { + Assert.True(_equalFirst == _equalSecond); + Assert.False(_equalFirst == _different); + } + + [Fact] + public void OperatorNotEq() + { + Assert.True(_equalFirst != _different); + Assert.False(_equalFirst != _equalSecond); + } + + // TODO Structured bindigns (tuples?) + + [Theory] + [InlineData(2.4f, 3.0f, 3.84187f, 14.7599650969f, 0.624695f, 0.780869f)] + [InlineData(-0.7f, -2.2f, 2.30868f, 5.3300033f, -0.303204f, -0.952926f)] + public void LengthAndNormalization(float x, float y, float len, float lenSq, float normX, float normY) + { + var vec = new Vector2f(x, y); + + Assert.Equal(len, vec.Length, Eps); + Assert.Equal(lenSq, vec.LengthSquared, EpsRelaxed); + + var normalized = vec.Normalized(); + var vec2 = new Vector2f(normX, normY); + + AssertVectorEqual(normalized, vec2, Eps); + } + + [Fact] + public void RotationsAndAngles() + { + var v = new Vector2f(2.4f, 3.0f); + + Assert.Equal(51.3402f, v.Angle().Degrees, Eps); + Assert.Equal(51.3402f, Vector2f.UnitX.AngleTo(v).Degrees, Eps); + Assert.Equal(-38.6598f, Vector2f.UnitY.AngleTo(v).Degrees, Eps); + + var w = new Vector2f(-0.7f, -2.2f); + + Assert.Equal(-107.65f, w.Angle().Degrees, EpsRelaxed); + Assert.Equal(-107.65f, Vector2f.UnitX.AngleTo(w).Degrees, EpsRelaxed); + Assert.Equal(162.35f, Vector2f.UnitY.AngleTo(w).Degrees, EpsRelaxed); + + Assert.Equal(-158.9902f, v.AngleTo(w).Degrees, EpsRelaxed); + Assert.Equal(158.9902f, w.AngleTo(v).Degrees, EpsRelaxed); + + var ratio = w.Length / v.Length; + + AssertVectorEqual(v.RotatedBy(Angle.FromDegrees(-158.9902f)) * ratio, w, Eps); + AssertVectorEqual(w.RotatedBy(Angle.FromDegrees(158.9902f)) / ratio, v, Eps); + + AssertVectorEqual(v.Perpendicular(), new Vector2f(-3.0f, 2.4f), Eps); + AssertVectorEqual(v.Perpendicular().Perpendicular().Perpendicular().Perpendicular(), v, Eps); + + AssertVectorEqual(v.RotatedBy(Angle.FromDegrees(90)), new Vector2f(-3.0f, 2.4f), Eps); + AssertVectorEqual(v.RotatedBy(Angle.FromDegrees(27.14f)), new Vector2f(0.767248f, 3.76448f), Eps); + AssertVectorEqual(v.RotatedBy(Angle.FromDegrees(-36.11f)), new Vector2f(3.70694f, 1.00925f), Eps); + } + + [Fact] + public void ProductsAndQuotinents() + { + var v = new Vector2f(2.4f, 3.0f); + var w = new Vector2f(-0.7f, -2.2f); + + Assert.Equal(-8.28f, v.Dot(w), Eps); + Assert.Equal(-8.28f, w.Dot(v), Eps); + + Assert.Equal(-3.18f, v.Cross(w), Eps); + Assert.Equal(3.18f, w.Cross(v), Eps); + + AssertVectorEqual(v.ComponentWiseMul(w), new Vector2f(-1.68f, -6.6f), Eps); + AssertVectorEqual(w.ComponentWiseMul(v), new Vector2f(-1.68f, -6.6f), Eps); + AssertVectorEqual(v.ComponentWiseDiv(w), new Vector2f(-3.428571f, -1.363636f), Eps); + AssertVectorEqual(w.ComponentWiseDiv(v), new Vector2f(-0.291666f, -0.733333f), Eps); + } + + [Fact] + public void Projection() + { + var v = new Vector2f(2.4f, 3.0f); + var w = new Vector2f(-0.7f, -2.2f); + + AssertVectorEqual(v.ProjectedOnto(w), new Vector2f(1.087430f, 3.417636f), Eps); + AssertVectorEqual(v.ProjectedOnto(w), -1.55347f * w, Eps); + + AssertVectorEqual(w.ProjectedOnto(v), new Vector2f(-1.346342f, -1.682927f), Eps); + AssertVectorEqual(w.ProjectedOnto(v), -0.560976f * v, Eps); + + AssertVectorEqual(v.ProjectedOnto(Vector2f.UnitX), new Vector2f(2.4f, 0.0f), Eps); + AssertVectorEqual(v.ProjectedOnto(Vector2f.UnitY), new Vector2f(0.0f, 3.0f), Eps); + } +} diff --git a/test/SFML.System.Test/Vector2i.test.cs b/test/SFML.System.Test/Vector2i.test.cs new file mode 100644 index 00000000..717089fb --- /dev/null +++ b/test/SFML.System.Test/Vector2i.test.cs @@ -0,0 +1,141 @@ +namespace SFML.System.Test; + +public class Vector2iTests +{ + private const float Eps = 1e-5f; + private const float EpsRelaxed = 15e-5f; + private const float Sqrt2Div2 = 0.7071067811865475f; + + [Fact] + public void DefaultConstructor() + { + var vec = new Vector2i(); + Assert.Equal(0, vec.X); + Assert.Equal(0, vec.Y); + } + + [Fact] + public void CoordinateConstructor() + { + var vec = new Vector2i(1, 2); + Assert.Equal(1, vec.X); + Assert.Equal(2, vec.Y); + } + + [Fact] + public void OperatorNeg() + { + var vec = new Vector2i(1, 2); + var negatedVec = -vec; + + Assert.Equal(-1, negatedVec.X); + Assert.Equal(-2, negatedVec.Y); + } + + private static readonly Vector2i _lhs = new Vector2i(2, 5); + private static readonly Vector2i _rhs = new Vector2i(8, 3); + + [Fact] + public void OperatorAddAssign() + { + var vec = _lhs; + vec += _rhs; + + Assert.Equal(10, vec.X); + Assert.Equal(8, vec.Y); + } + + [Fact] + public void OperatorSubAssign() + { + var vec = _lhs; + vec -= _rhs; + + Assert.Equal(-6, vec.X); + Assert.Equal(2, vec.Y); + } + + [Fact] + public void OperatorAdd() + { + var vec = _lhs + _rhs; + + Assert.Equal(10, vec.X); + Assert.Equal(8, vec.Y); + } + + [Fact] + public void OperatorSub() + { + var vec = _lhs - _rhs; + + Assert.Equal(-6, vec.X); + Assert.Equal(2, vec.Y); + } + + private static readonly Vector2i _vecMul = new Vector2i(26, 12); + private static readonly int _intMul = 2; + + [Fact] + public void OperatorMul() + { + var vecInt = _vecMul * _intMul; + + Assert.Equal(52, vecInt.X); + Assert.Equal(24, vecInt.Y); + + var floatVec = _intMul * _vecMul; + + Assert.Equal(52, floatVec.X); + Assert.Equal(24, floatVec.Y); + } + + [Fact] + public void OperatorMulAssign() + { + var vecInt = _vecMul; + vecInt *= _intMul; + + Assert.Equal(52, vecInt.X); + Assert.Equal(24, vecInt.Y); + } + + [Fact] + public void OperatorDiv() + { + var vec = _vecMul / _intMul; + + Assert.Equal(13, vec.X); + Assert.Equal(6, vec.Y); + } + + [Fact] + public void OperatorDivAssign() + { + var vec = _vecMul; + vec /= _intMul; + + Assert.Equal(13, vec.X); + Assert.Equal(6, vec.Y); + } + + private static readonly Vector2i _equalFirst = new Vector2i(1, 5); + private static readonly Vector2i _equalSecond = new Vector2i(1, 5); + private static readonly Vector2i _different = new Vector2i(6, 9); + + [Fact] + public void OperatorEq() + { + Assert.True(_equalFirst == _equalSecond); + Assert.False(_equalFirst == _different); + } + + [Fact] + public void OperatorNotEq() + { + Assert.True(_equalFirst != _different); + Assert.False(_equalFirst != _equalSecond); + } + + // TODO Structured bindigns (tuples?) +} diff --git a/test/SFML.System.Test/Vector2u.test.cs b/test/SFML.System.Test/Vector2u.test.cs new file mode 100644 index 00000000..c6157912 --- /dev/null +++ b/test/SFML.System.Test/Vector2u.test.cs @@ -0,0 +1,131 @@ +namespace SFML.System.Test; + +public class Vector2uTests +{ + private const float Eps = 1e-5f; + private const float EpsRelaxed = 15e-5f; + private const float Sqrt2Div2 = 0.7071067811865475f; + + [Fact] + public void DefaultConstructor() + { + var vec = new Vector2u(); + Assert.Equal(0u, vec.X); + Assert.Equal(0u, vec.Y); + } + + [Fact] + public void CoordinateConstructor() + { + var vec = new Vector2u(1, 2); + Assert.Equal(1u, vec.X); + Assert.Equal(2u, vec.Y); + } + + private static readonly Vector2u _lhs = new Vector2u(2, 5); + private static readonly Vector2u _rhs = new Vector2u(8, 3); + + [Fact] + public void OperatorAddAssign() + { + var vec = _lhs; + vec += _rhs; + + Assert.Equal(10u, vec.X); + Assert.Equal(8u, vec.Y); + } + + [Fact] + public void OperatorSubAssign() + { + var vec = new Vector2u(10, 5); + vec -= new Vector2u(8, 2); + + Assert.Equal(2u, vec.X); + Assert.Equal(3u, vec.Y); + } + + [Fact] + public void OperatorAdd() + { + var vec = _lhs + _rhs; + + Assert.Equal(10u, vec.X); + Assert.Equal(8u, vec.Y); + } + + [Fact] + public void OperatorSub() + { + var vec = new Vector2u(10, 5) - new Vector2u(8, 2); + + Assert.Equal(2u, vec.X); + Assert.Equal(3u, vec.Y); + } + + private static readonly Vector2u _vecMul = new Vector2u(26, 12); + private static readonly uint _uintMul = 2; + + [Fact] + public void OperatorMul() + { + var vecUint = _vecMul * _uintMul; + + Assert.Equal(52u, vecUint.X); + Assert.Equal(24u, vecUint.Y); + + var floatVec = _uintMul * _vecMul; + + Assert.Equal(52u, floatVec.X); + Assert.Equal(24u, floatVec.Y); + } + + [Fact] + public void OperatorMulAssign() + { + var vecUint = _vecMul; + vecUint *= _uintMul; + + Assert.Equal(52u, vecUint.X); + Assert.Equal(24u, vecUint.Y); + } + + [Fact] + public void OperatorDiv() + { + var vec = _vecMul / _uintMul; + + Assert.Equal(13u, vec.X); + Assert.Equal(6u, vec.Y); + } + + [Fact] + public void OperatorDivAssign() + { + var vec = _vecMul; + vec /= _uintMul; + + Assert.Equal(13u, vec.X); + Assert.Equal(6u, vec.Y); + } + + private static readonly Vector2u _equalFirst = new Vector2u(1, 5); + private static readonly Vector2u _equalSecond = new Vector2u(1, 5); + private static readonly Vector2u _different = new Vector2u(6, 9); + + [Fact] + public void OperatorEq() + { + Assert.True(_equalFirst == _equalSecond); + Assert.False(_equalFirst == _different); + } + + [Fact] + public void OperatorNotEq() + { + Assert.True(_equalFirst != _different); + Assert.False(_equalFirst != _equalSecond); + } + + // TODO Structured bindigns (tuples?) +}