diff --git a/Nu/Nu.Gaia/Gaia.fs b/Nu/Nu.Gaia/Gaia.fs index 081ca026b9..82462bcbb9 100644 --- a/Nu/Nu.Gaia/Gaia.fs +++ b/Nu/Nu.Gaia/Gaia.fs @@ -1276,7 +1276,11 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1920,1080 Split """""" """""" """""" - """"""|] + """""" + """""" + """""" + """""" + """"""|] |> Array.append (File.ReadAllLines fsprojFilePath) let fsprojNugetPaths = fsprojFileLines @@ -3273,7 +3277,10 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=1920,1080 Split "#r \"Magick.NET-Q8-AnyCPU.dll\"\n" + "#r \"OpenGL.Net.dll\"\n" + "#r \"Pfim.dll\"\n" + - "#r \"SDL2-CS.dll\"\n" + + "#r \"SDL3-CS.dll\"\n" + + "#r \"SDL3_image-CS.dll\"\n" + + "#r \"SDL3_mixer-CS.dll\"\n" + + "#r \"SDL3_ttf-CS.dll\"\n" + "#r \"TiledSharp.dll\"\n" + "#r \"Twizzle.ImGui-Bundle.NET.dll\"\n" + "#r \"Prime.dll\"\n" + diff --git a/Nu/Nu.Gaia/Nu.Gaia.fsproj b/Nu/Nu.Gaia/Nu.Gaia.fsproj index 82a8093efb..ac4522d923 100644 --- a/Nu/Nu.Gaia/Nu.Gaia.fsproj +++ b/Nu/Nu.Gaia/Nu.Gaia.fsproj @@ -62,9 +62,6 @@ ..\..\Nu\Nu.Dependencies\OpenGL.NET\lib\netcoreapp2.2\OpenGL.Net.dll - - ..\..\Nu\Nu.Dependencies\SDL2-CS\netstandard2.0\SDL2-CS.dll - ..\..\Nu\Nu.Dependencies\TiledSharp\lib\netstandard2.0\TiledSharp.dll diff --git a/Nu/Nu.Pipe/Nu.Pipe.fsproj b/Nu/Nu.Pipe/Nu.Pipe.fsproj index aa387fae1a..f8814a69f2 100644 --- a/Nu/Nu.Pipe/Nu.Pipe.fsproj +++ b/Nu/Nu.Pipe/Nu.Pipe.fsproj @@ -53,9 +53,6 @@ ..\Nu.Dependencies\OpenGL.NET\lib\netcoreapp2.2\OpenGL.Net.dll - - ..\Nu.Dependencies\SDL2-CS\netstandard2.0\SDL2-CS.dll - ..\Nu.Dependencies\TiledSharp\lib\netstandard2.0\TiledSharp.dll diff --git a/Nu/Nu.Template.ImSim.Empty/Nu.Template.ImSim.Empty.fsproj b/Nu/Nu.Template.ImSim.Empty/Nu.Template.ImSim.Empty.fsproj index c59ed61945..bd919e3a55 100644 --- a/Nu/Nu.Template.ImSim.Empty/Nu.Template.ImSim.Empty.fsproj +++ b/Nu/Nu.Template.ImSim.Empty/Nu.Template.ImSim.Empty.fsproj @@ -66,9 +66,6 @@ ..\..\Nu\Nu.Dependencies\OpenGL.NET\lib\netcoreapp2.2\OpenGL.Net.dll - - ..\..\Nu\Nu.Dependencies\SDL2-CS\netstandard2.0\SDL2-CS.dll - ..\..\Nu\Nu.Dependencies\TiledSharp\lib\netstandard2.0\TiledSharp.dll diff --git a/Nu/Nu.Template.ImSim.Game/Nu.Template.ImSim.Game.fsproj b/Nu/Nu.Template.ImSim.Game/Nu.Template.ImSim.Game.fsproj index eaae74f05d..8889a302e1 100644 --- a/Nu/Nu.Template.ImSim.Game/Nu.Template.ImSim.Game.fsproj +++ b/Nu/Nu.Template.ImSim.Game/Nu.Template.ImSim.Game.fsproj @@ -69,9 +69,6 @@ ..\..\Nu\Nu.Dependencies\OpenGL.NET\lib\netcoreapp2.2\OpenGL.Net.dll - - ..\..\Nu\Nu.Dependencies\SDL2-CS\netstandard2.0\SDL2-CS.dll - ..\..\Nu\Nu.Dependencies\TiledSharp\lib\netstandard2.0\TiledSharp.dll diff --git a/Nu/Nu.Template.Mmcc.Empty/Nu.Template.Mmcc.Empty.fsproj b/Nu/Nu.Template.Mmcc.Empty/Nu.Template.Mmcc.Empty.fsproj index 73fca2ca5e..3001f7deb3 100644 --- a/Nu/Nu.Template.Mmcc.Empty/Nu.Template.Mmcc.Empty.fsproj +++ b/Nu/Nu.Template.Mmcc.Empty/Nu.Template.Mmcc.Empty.fsproj @@ -66,9 +66,6 @@ ..\..\Nu\Nu.Dependencies\OpenGL.NET\lib\netcoreapp2.2\OpenGL.Net.dll - - ..\..\Nu\Nu.Dependencies\SDL2-CS\netstandard2.0\SDL2-CS.dll - ..\..\Nu\Nu.Dependencies\TiledSharp\lib\netstandard2.0\TiledSharp.dll diff --git a/Nu/Nu.Template.Mmcc.Game/Nu.Template.Mmcc.Game.fsproj b/Nu/Nu.Template.Mmcc.Game/Nu.Template.Mmcc.Game.fsproj index af5e3747e7..e7dccf5516 100644 --- a/Nu/Nu.Template.Mmcc.Game/Nu.Template.Mmcc.Game.fsproj +++ b/Nu/Nu.Template.Mmcc.Game/Nu.Template.Mmcc.Game.fsproj @@ -70,9 +70,6 @@ ..\..\Nu\Nu.Dependencies\OpenGL.NET\lib\netcoreapp2.2\OpenGL.Net.dll - - ..\..\Nu\Nu.Dependencies\SDL2-CS\netstandard2.0\SDL2-CS.dll - ..\..\Nu\Nu.Dependencies\TiledSharp\lib\netstandard2.0\TiledSharp.dll diff --git a/Nu/Nu.Tests/Nu.Tests.fsproj b/Nu/Nu.Tests/Nu.Tests.fsproj index 8969e48698..73636b52d6 100644 --- a/Nu/Nu.Tests/Nu.Tests.fsproj +++ b/Nu/Nu.Tests/Nu.Tests.fsproj @@ -71,9 +71,6 @@ ..\Nu.Dependencies\OpenGL.NET\lib\netcoreapp2.2\OpenGL.Net.dll - - ..\Nu.Dependencies\SDL2-CS\netstandard2.0\SDL2-CS.dll - ..\Nu.Dependencies\TiledSharp\lib\netstandard2.0\TiledSharp.dll diff --git a/Nu/Nu/Audio/AudioPlayer.fs b/Nu/Nu/Audio/AudioPlayer.fs index 861acf3807..2fda678855 100644 --- a/Nu/Nu/Audio/AudioPlayer.fs +++ b/Nu/Nu/Audio/AudioPlayer.fs @@ -8,7 +8,8 @@ namespace Nu open System open System.Collections.Generic open System.IO -open SDL2 +open FSharp.NativeInterop +open SDL open Prime /// Describes a sound. @@ -54,11 +55,6 @@ type AudioMessage = | StopSongMessage | ReloadAudioAssetsMessage -/// An audio asset used by the audio system. -type internal AudioAsset = - | WavAsset of nativeint - | MusAsset of nativeint - /// The audio player. Represents the audio subsystem of Nu generally. type AudioPlayer = @@ -81,10 +77,10 @@ type AudioPlayer = abstract EnqueueMessage : message : AudioMessage -> unit /// Get the current optionally-playing song. - abstract SongOpt : SongDescriptor option + abstract SongOpt : SongDescriptor option // TODO: We can support multiple tracks for multiple songs. /// Get the current song's position or 0.0 if one isn't playing. - abstract SongPosition : double + abstract SongPosition : GameTime /// Get the current song's volume or 0.0f if one isn't playing. abstract SongVolume : single @@ -114,7 +110,7 @@ type [] StubAudioPlayer = member audioPlayer.ClearMessages () = () member audioPlayer.EnqueueMessage _ = () member audioPlayer.SongOpt = None - member audioPlayer.SongPosition = 0.0 + member audioPlayer.SongPosition = GameTime.zero member audioPlayer.SongVolume = 0.0f member audioPlayer.SongFadingIn = false member audioPlayer.SongFadingOut = false @@ -127,57 +123,34 @@ type [] StubAudioPlayer = /// The SDL implementation of AudioPlayer. type [] SdlAudioPlayer = private - { AudioContext : unit // audio context, interestingly, is global. Good luck encapsulating that! - AudioPackages : Packages + { AudioMixer : MIX_Mixer nativeptr + SoundTrack : MIX_Track nativeptr // One track for all sounds for now. We can add more tracks later. + SongTrack : MIX_Track nativeptr // One track for all songs for now. We can add more tracks later. + SongTrackPropertiesId : SDL_PropertiesID // reused instance across PlayTrack calls. + AudioPackages : Packages mutable AudioMessages : AudioMessage List mutable MasterAudioVolume : single mutable MasterSoundVolume : single mutable MasterSongVolume : single - mutable SongOpt : (SongDescriptor * nativeint) option } - - static member private tryFreeAudioAsset (audioAsset : AudioAsset) (audioPlayer : SdlAudioPlayer) = - match audioAsset with - | WavAsset wav -> - match audioPlayer.SongOpt with - | Some (_, wavPlaying) -> - let freeing = wav <> wavPlaying - if freeing then SDL_mixer.Mix_FreeChunk wav - freeing - | None -> - SDL_mixer.Mix_FreeChunk wav - true - | MusAsset mus -> - match audioPlayer.SongOpt with - | Some (_, musPlaying) -> - let freeing = mus <> musPlaying - if freeing then SDL_mixer.Mix_FreeMusic mus - freeing - | None -> - SDL_mixer.Mix_FreeMusic mus - true - - static member private haltAudio () = - SDL_mixer.Mix_HaltMusic () |> ignore - let (_, _, _, channelCount) = SDL_mixer.Mix_QuerySpec () - for i in [0 .. channelCount - 1] do - SDL_mixer.Mix_HaltChannel i |> ignore - - static member private tryLoadAudioAsset (asset : Asset) = + mutable SongOpt : (SongDescriptor * MIX_Audio nativeptr) option } + + static member private haltAudio audioPlayer = + if not (SDL3_mixer.MIX_StopAllTracks (audioPlayer.AudioMixer, 0L)) then + Log.error ("Could not halt audio due to '" + SDL3.SDL_GetError () + "'.") + + static member private tryLoadAudioAsset (asset : Asset) audioPlayer = + // Set predecode to true: https://github.com/libsdl-org/SDL_mixer/issues/662#issuecomment-2626072254 + // "There's also the need to decode the data in advance because some formats are expensive to decode + // and can't be done just in time to feed the audio device. I'm operating under the assumption that for + // the most part games want the minimum possible latency so will be feeding the output small chunks at a high rate." match PathF.GetExtensionLower asset.FilePath with - | SoundExtension _ -> - let wavOpt = SDL_mixer.Mix_LoadWAV asset.FilePath - if wavOpt <> IntPtr.Zero then Some (WavAsset wavOpt) - else - let errorMsg = SDL.SDL_GetError () - Log.info ("Could not load wav '" + asset.FilePath + "' due to '" + errorMsg + "'.") - None - | SongExtension _ -> - let musOpt = SDL_mixer.Mix_LoadMUS asset.FilePath - if musOpt <> IntPtr.Zero then Some (MusAsset musOpt) - else - let errorMsg = SDL.SDL_GetError () - Log.info ("Could not load song asset '" + asset.FilePath + "' due to '" + errorMsg + "'.") + | SoundExtension _ | SongExtension _ -> + let musOpt = SDL3_mixer.MIX_LoadAudio (audioPlayer.AudioMixer, asset.FilePath, true) + if NativePtr.isNullPtr musOpt then + let errorMsg = SDL3.SDL_GetError () + Log.info ("Could not load sound or song asset '" + asset.FilePath + "' due to '" + errorMsg + "'.") None + else Some musOpt | _ -> None static member private tryLoadAudioPackage packageName audioPlayer = @@ -210,12 +183,9 @@ type [] SdlAudioPlayer = then assetsToFree.Add (asset, audioAsset) else assetsToKeep.Add (assetName, (lastWriteTime, asset, audioAsset)) - // attempt to free assets - for assetEntry in assetsToFree do - let asset = assetEntry.Key - let audioAsset = assetEntry.Value - if not (SdlAudioPlayer.tryFreeAudioAsset audioAsset audioPlayer) then - assetsToKeep.Add (asset.FilePath, (DateTimeOffset.MinValue.DateTime, asset, audioAsset)) + // free assets + for asset in assetsToFree do + SDL3_mixer.MIX_DestroyAudio asset.Value // Audio assets are reference counted. If a track is still using it, the actual deallocation will happen when the track stops using it. // categorize assets to load let assetsToLoad = HashSet () @@ -226,7 +196,7 @@ type [] SdlAudioPlayer = // load assets let assetsLoaded = Dictionary () for asset in assetsToLoad do - match SdlAudioPlayer.tryLoadAudioAsset asset with + match SdlAudioPlayer.tryLoadAudioAsset asset audioPlayer with | Some audioAsset -> let lastWriteTime = try DateTimeOffset (File.GetLastWriteTime asset.FilePath) @@ -255,75 +225,57 @@ type [] SdlAudioPlayer = | None -> None static member private playSong songDescriptor audioPlayer = - let song = songDescriptor.Song - match SdlAudioPlayer.tryGetAudioAsset song audioPlayer with - | Some audioAsset -> - match audioAsset with - | WavAsset _ -> - Log.info ("Cannot play wav file as song '" + scstring song + "'.") - | MusAsset musAsset -> - let loops = match songDescriptor.RepeatLimitOpt with Some repeatLimit -> max 0 (int repeatLimit) | None -> -1 - SDL_mixer.Mix_HaltMusic () |> ignore // NOTE: have to stop current song in case it is still fading out, causing the next song not to play. - SDL_mixer.Mix_VolumeMusic (int (songDescriptor.Volume * audioPlayer.MasterAudioVolume * audioPlayer.MasterSongVolume * single SDL_mixer.MIX_MAX_VOLUME)) |> ignore - match SDL_mixer.Mix_FadeInMusicPos (musAsset, loops, int (max Constants.Audio.FadeInSecondsMin songDescriptor.FadeInTime.Seconds * 1000.0), double songDescriptor.StartTime.Seconds) with - | -1 -> - // HACK: start time exceeded length of track, so starting over. - SDL_mixer.Mix_FadeInMusicPos (musAsset, loops, int (max Constants.Audio.FadeInSecondsMin songDescriptor.FadeInTime.Seconds * 1000.0), 0.0) |> ignore - | _ -> () - audioPlayer.SongOpt <- Some (songDescriptor, musAsset) - | None -> - Log.info ("PlaySongMessage failed due to unloadable assets for '" + scstring song + "'.") + if songDescriptor.Volume > 0.0f then + let song = songDescriptor.Song + match SdlAudioPlayer.tryGetAudioAsset song audioPlayer with + | Some audioAsset -> + let loops = match songDescriptor.RepeatLimitOpt with Some repeatLimit -> max 0L (int64 repeatLimit) | None -> -1L + SDL3_mixer.MIX_SetTrackAudio (audioPlayer.SongTrack, audioAsset) |> ignore + SDL3_mixer.MIX_SetTrackGain (audioPlayer.SongTrack, songDescriptor.Volume * audioPlayer.MasterAudioVolume * audioPlayer.MasterSongVolume) |> ignore + SDL3.SDL_SetNumberProperty (audioPlayer.SongTrackPropertiesId, SDL3_mixer.MIX_PROP_PLAY_LOOPS_NUMBER, loops) |> ignore + SDL3.SDL_SetNumberProperty (audioPlayer.SongTrackPropertiesId, SDL3_mixer.MIX_PROP_PLAY_FADE_IN_MILLISECONDS_NUMBER, int64 (max Constants.Audio.FadeInSecondsMin songDescriptor.FadeInTime.Seconds * 1000.0)) |> ignore + SDL3.SDL_SetNumberProperty (audioPlayer.SongTrackPropertiesId, SDL3_mixer.MIX_PROP_PLAY_START_MILLISECOND_NUMBER, int64 (songDescriptor.StartTime.Seconds * 1000.0)) |> ignore + if not (SDL3_mixer.MIX_PlayTrack (audioPlayer.SongTrack, audioPlayer.SongTrackPropertiesId)) then + Log.info ("Could not play song asset '" + scstring song + "' due to '" + SDL3.SDL_GetError () + "'.") + audioPlayer.SongOpt <- Some (songDescriptor, audioAsset) + | None -> + Log.info ("PlaySongMessage failed due to unloadable assets for '" + scstring song + "'.") static member private handleLoadAudioPackage packageName audioPlayer = SdlAudioPlayer.tryLoadAudioPackage packageName audioPlayer static member private handleUnloadAudioPackage packageName audioPlayer = - match Dictionary.tryFind packageName audioPlayer.AudioPackages with + match Dictionary.tryFind packageName audioPlayer.AudioPackages with | Some package -> - // all sounds / music must be halted because one of them might be playing during unload - // (which is very bad according to the API docs). - SdlAudioPlayer.haltAudio () for asset in package.Assets do - match __c asset.Value with - | WavAsset wavAsset -> SDL_mixer.Mix_FreeChunk wavAsset - | MusAsset musAsset -> SDL_mixer.Mix_FreeMusic musAsset + SDL3_mixer.MIX_DestroyAudio (__c asset.Value) audioPlayer.AudioPackages.Remove packageName |> ignore | None -> () static member private handlePlaySound (soundDescriptor : SoundDescriptor) audioPlayer = if soundDescriptor.Volume > 0.0f then - let sound = soundDescriptor.Sound - match SdlAudioPlayer.tryGetAudioAsset sound audioPlayer with + match SdlAudioPlayer.tryGetAudioAsset soundDescriptor.Sound audioPlayer with | Some audioAsset -> - match audioAsset with - | WavAsset wavAsset -> - let channel = SDL_mixer.Mix_PlayChannel (-1, wavAsset, 0) - if channel > -1 then - SDL_mixer.Mix_Volume (channel, int (soundDescriptor.Volume * audioPlayer.MasterSoundVolume * single SDL_mixer.MIX_MAX_VOLUME)) |> ignore - let pan = soundDescriptor.Panning |> max -1.0f |> min 1.0f - let left = byte (255.0f * (1.0f - max 0.0f pan)) - let right = byte (255.0f * (1.0f + min 0.0f pan)) - SDL_mixer.Mix_SetPanning (channel, left, right) |> ignore - let distance = soundDescriptor.Distance |> max 0.0f |> min 1.0f |> (*) 255.0f |> byte - SDL_mixer.Mix_SetDistance (channel, distance) |> ignore - | MusAsset _ -> Log.info ("Cannot play song asset as sound '" + scstring sound + "'.") + SDL3_mixer.MIX_SetTrackAudio (audioPlayer.SoundTrack, audioAsset) |> ignore + SDL3_mixer.MIX_SetTrackGain (audioPlayer.SoundTrack, soundDescriptor.Volume * audioPlayer.MasterAudioVolume * audioPlayer.MasterSoundVolume) |> ignore + let pan = soundDescriptor.Panning |> max -1.0f |> min 1.0f // TODO: support 3D sound via SetTrack3DPosition. It is not optimal to force stereo for surround sound setups. + let mutable stereoGains = MIX_StereoGains (left = 1.0f - max 0.0f pan, right = 1.0f + min 0.0f pan) + SDL3_mixer.MIX_SetTrackStereo (audioPlayer.SoundTrack, &&stereoGains) |> ignore + // TODO: Distance is not supported with stereo gains! We need to use 3D sound. + SDL3_mixer.MIX_PlayTrack (audioPlayer.SoundTrack, Unchecked.defaultof) |> ignore | None -> - Log.info ("PlaySoundMessage failed due to unloadable assets for '" + scstring sound + "'.") + Log.info ("PlaySoundMessage failed due to unloadable assets for '" + scstring soundDescriptor.Sound + "'.") static member private handlePlaySong songDescriptor audioPlayer = SdlAudioPlayer.playSong songDescriptor audioPlayer - static member private handleFadeOutSong (fadeOutTime : GameTime) = - if SDL_mixer.Mix_PlayingMusic () = 1 then - if fadeOutTime <> GameTime.zero && - SDL_mixer.Mix_FadingMusic () <> SDL_mixer.Mix_Fading.MIX_FADING_OUT then - SDL_mixer.Mix_FadeOutMusic (int (fadeOutTime.Seconds * 1000.0)) |> ignore - else - SDL_mixer.Mix_HaltMusic () |> ignore + static member private handleFadeOutSong (fadeOutTime : GameTime) audioPlayer = + if not (SDL3_mixer.MIX_StopTrack (audioPlayer.SongTrack, SDL3_mixer.MIX_TrackFramesToMS (audioPlayer.SongTrack, int64 (fadeOutTime.Seconds * 1000.0)))) then + Log.info ("Could not fade out song due to '" + SDL3.SDL_GetError () + "'.") - static member private handleStopSong = - if SDL_mixer.Mix_PlayingMusic () = 1 then - SDL_mixer.Mix_HaltMusic () |> ignore + static member private handleStopSong audioPlayer = + if not (SDL3_mixer.MIX_StopTrack (audioPlayer.SongTrack, 0)) then + Log.info ("Could not stop song due to '" + SDL3.SDL_GetError () + "'.") static member private handleReloadAudioAssets audioPlayer = for packageName in audioPlayer.AudioPackages |> Seq.map (fun entry -> entry.Key) |> Array.ofSeq do @@ -336,8 +288,8 @@ type [] SdlAudioPlayer = | PlaySoundMessage soundDescriptor -> SdlAudioPlayer.handlePlaySound soundDescriptor audioPlayer | PlaySongMessage songDescriptor -> SdlAudioPlayer.handlePlaySong songDescriptor audioPlayer | SetSongVolumeMessage volume -> SdlAudioPlayer.setSongVolume volume audioPlayer - | FadeOutSongMessage fadeOutTime -> SdlAudioPlayer.handleFadeOutSong fadeOutTime - | StopSongMessage -> SdlAudioPlayer.handleStopSong + | FadeOutSongMessage fadeOutTime -> SdlAudioPlayer.handleFadeOutSong fadeOutTime audioPlayer + | StopSongMessage -> SdlAudioPlayer.handleStopSong audioPlayer | ReloadAudioAssetsMessage -> SdlAudioPlayer.handleReloadAudioAssets audioPlayer static member private handleAudioMessages audioMessages audioPlayer = @@ -346,33 +298,48 @@ type [] SdlAudioPlayer = static member private updateSongVolume audioPlayer = match audioPlayer.SongOpt with - | Some (currentSong, _) -> SDL_mixer.Mix_VolumeMusic (int (currentSong.Volume * audioPlayer.MasterAudioVolume * audioPlayer.MasterSongVolume * single SDL_mixer.MIX_MAX_VOLUME)) |> ignore + | Some (currentSong, _) -> SDL3_mixer.MIX_SetTrackGain (audioPlayer.SongTrack, currentSong.Volume * audioPlayer.MasterAudioVolume * audioPlayer.MasterSongVolume) |> ignore | None -> () static member private setSongVolume volume audioPlayer = match audioPlayer.SongOpt with | Some (currentSong, musPlaying) -> if currentSong.Volume <> volume then - SDL_mixer.Mix_VolumeMusic (int (volume * audioPlayer.MasterAudioVolume * audioPlayer.MasterSongVolume * single SDL_mixer.MIX_MAX_VOLUME)) |> ignore + SDL3_mixer.MIX_SetTrackGain (audioPlayer.SongTrack, volume * audioPlayer.MasterAudioVolume * audioPlayer.MasterSongVolume) |> ignore audioPlayer.SongOpt <- Some ({ currentSong with Volume = volume }, musPlaying) | None -> () static member private updateSong audioPlayer = - if SDL_mixer.Mix_PlayingMusic () = 0 then + if not (SDL3_mixer.MIX_TrackPlaying audioPlayer.SongTrack) then audioPlayer.SongOpt <- None - static member private getSongFadingIn () = - SDL_mixer.Mix_FadingMusic () = SDL_mixer.Mix_Fading.MIX_FADING_IN + static member private getSongFadingIn audioPlayer = + SDL3_mixer.MIX_GetTrackFadeFrames audioPlayer.SongTrack > 0L - static member private getSongFadingOut () = - SDL_mixer.Mix_FadingMusic () = SDL_mixer.Mix_Fading.MIX_FADING_OUT + static member private getSongFadingOut audioPlayer = + SDL3_mixer.MIX_GetTrackFadeFrames audioPlayer.SongTrack < 0L /// Make an SdlAudioPlayer. static member make () = - if SDL.SDL_WasInit SDL.SDL_INIT_AUDIO = 0u then + if SDL3.SDL_WasInit SDL_InitFlags.SDL_INIT_AUDIO = LanguagePrimitives.EnumOfValue 0u then failwith "Cannot create an AudioPlayer without SDL audio initialized." + let mixer = SDL3_mixer.MIX_CreateMixerDevice (SDL3.SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, NativePtr.nullPtr) + if NativePtr.isNullPtr mixer then + Log.info ("Mixer could not initialize audio due to '" + SDL3.SDL_GetError () + "'.") + let soundTrack = SDL3_mixer.MIX_CreateTrack mixer + if NativePtr.isNullPtr soundTrack then + Log.info ("Sound track could not be created due to '" + SDL3.SDL_GetError () + "'.") + let songTrack = SDL3_mixer.MIX_CreateTrack mixer + if NativePtr.isNullPtr songTrack then + Log.info ("Song track could not be created due to '" + SDL3.SDL_GetError () + "'.") + let props = SDL3.SDL_CreateProperties () + if props = Unchecked.defaultof<_> then + Log.info ("SDL properties could not be created due to '" + SDL3.SDL_GetError () + "'.") let audioPlayer = - { AudioContext = () + { AudioMixer = mixer + SoundTrack = soundTrack + SongTrack = songTrack + SongTrackPropertiesId = props AudioPackages = dictPlus StringComparer.Ordinal [] AudioMessages = List () MasterAudioVolume = Constants.Audio.MasterAudioVolumeDefault @@ -415,8 +382,8 @@ type [] SdlAudioPlayer = member audioPlayer.SongPosition = match audioPlayer.SongOpt with - | Some (_, musAsset) -> ignore musAsset; failwithnie () // SDL_mixer.Mix_GetMusicPosition musAsset - | None -> failwithnie () // 0.0 + | Some (_, musAsset) -> SDL3_mixer.MIX_AudioFramesToMS (musAsset, SDL3_mixer.MIX_GetTrackPlaybackPosition audioPlayer.SongTrack) |> double |> (*) 0.001 |> GameTime.ofSeconds + | None -> GameTime.zero member audioPlayer.SongVolume = match audioPlayer.SongOpt with @@ -424,18 +391,19 @@ type [] SdlAudioPlayer = | None -> 0.0f member audioPlayer.SongFadingIn = - SdlAudioPlayer.getSongFadingIn () + SdlAudioPlayer.getSongFadingIn audioPlayer member audioPlayer.SongFadingOut = - SdlAudioPlayer.getSongFadingOut () + SdlAudioPlayer.getSongFadingOut audioPlayer member audioPlayer.Play audioMessages = SdlAudioPlayer.handleAudioMessages audioMessages audioPlayer SdlAudioPlayer.updateSong audioPlayer member audioPlayer.CleanUp () = - SdlAudioPlayer.haltAudio () + SDL3_mixer.MIX_DestroyMixer audioPlayer.AudioMixer // also destroys tracks let audioPackages = audioPlayer.AudioPackages |> Seq.map (fun entry -> entry.Value) let audioAssets = audioPackages |> Seq.map (fun package -> package.Assets.Values) |> Seq.concat - for (_, _, audioAsset) in audioAssets do SdlAudioPlayer.tryFreeAudioAsset audioAsset audioPlayer |> ignore - audioPlayer.AudioPackages.Clear () \ No newline at end of file + for (_, _, audioAsset) in audioAssets do SDL3_mixer.MIX_DestroyAudio audioAsset + audioPlayer.AudioPackages.Clear () + SDL3.SDL_DestroyProperties audioPlayer.SongTrackPropertiesId \ No newline at end of file diff --git a/Nu/Nu/Core/Constants.fs b/Nu/Nu/Core/Constants.fs index e10d95cd35..1e46972835 100644 --- a/Nu/Nu/Core/Constants.fs +++ b/Nu/Nu/Core/Constants.fs @@ -10,7 +10,7 @@ open System.Collections.Frozen open System.Configuration open System.Diagnostics open System.Numerics -open SDL2 +open SDL open Prime open Nu @@ -45,7 +45,7 @@ module OpenGL = let [] VersionMajor = 4 let [] VersionMinor = 6 - let [] Profile = SDL.SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_CORE + let [] Profile = SDL3.SDL_GL_CONTEXT_PROFILE_CORE let [] GlslVersionPragma = "#version " + string VersionMajor + string VersionMinor + "0" + " core" let [] TextureImageUnitsRequired = 32 let [] mutable HlDebug = match ConfigurationManager.AppSettings.["HlDebug"] with null -> false | value -> scvalue value @@ -346,7 +346,7 @@ module Render = let [] RefractiveIndexDefault = 1.0f let [] ClearCoatDefault = 1.0f let [] ClearCoatRoughnessDefault = 1.0f - let [] FontSizeDefault = 14 + let [] FontSizeDefault = 14.0f let [] Body3dSegmentRenderMagnitudeMax = 48.0f let [] Body3dSegmentRenderDistanceMax = 40.0f let [] Body3dRenderDistanceMax = 32.0f @@ -361,8 +361,6 @@ module Audio = let [] SongVolumeDefault = 1.0f let [] FadeOutTimeDefault = GameTime.ofSeconds 0.5 let [] SongResumptionMax = GameTime.ofSeconds 90.0 // HACK: prevents songs from starting over too often due to hack in SdlAudioPlayer.playSong. - let [] Frequency = 44100 - let [] BufferSize = 1024 let [] FadeInSecondsMin = 0.1 // NOTE: Mix_FadeInMusicPos seems to sometimes cause audio 'popping' when starting a song, so a minimum fade is used instead. [] diff --git a/Nu/Nu/Cursor/CursorClient.fs b/Nu/Nu/Cursor/CursorClient.fs index 03076a068e..a7770cc7bf 100644 --- a/Nu/Nu/Cursor/CursorClient.fs +++ b/Nu/Nu/Cursor/CursorClient.fs @@ -2,25 +2,54 @@ open System open System.Collections.Generic open System.IO -open SDL2 +open FSharp.NativeInterop +open SDL open Prime open Nu /// The type of a system cursor. type CursorType = + /// Default cursor. Usually an arrow. | DefaultCursor - | ArrowCursor - | IBeamCursor + /// Text selection. Usually an I-beam. + | TextCursor + /// Wait. Usually an hourglass or watch or spinning ball. | WaitCursor + /// Crosshair. | CrosshairCursor - | WaitArrowCursor - | SizeNwseCursor - | SizeNeswCursor - | SizeWestEastCursor - | SizeNorthSouthCursor - | SizeAllCursor - | NoCursor - | HandCursor + /// Program is busy but still interactive. Usually it's WaitCursor with an arrow. + | ProgressCursor + /// Double arrow pointing northwest and southeast. + | ResizeNwseCursor + /// Double arrow pointing northeast and southwest. + | ResizeNeswCursor + /// Double arrow pointing west and east. + | ResizeEastWestCursor + /// Double arrow pointing north and south. + | ResizeNorthSouthCursor + /// Four pointed arrow pointing north, south, east, and west. + | MoveCursor + /// Not permitted. Usually a slashed circle or crossbones. + | NotAllowedCursor + /// Pointer that indicates a link. Usually a pointing hand. + | PointerCursor + /// Window resize top-left. This may be a single arrow or a double arrow like ResizeNwseCursor. + | ResizeNorthWestCursor + /// Window resize top. This may be a single arrow or a double arrow like ResizeNorthSouthCursor. + | ResizeNorthCursor + /// Window resize top-right. This may be a single arrow or a double arrow like ResizeNeswCursor. + | ResizeNorthEastCursor + /// Window resize right. This may be a single arrow or a double arrow like ResizeEastWestCursor. + | ResizeEastCursor + /// Window resize bottom-right. This may be a single arrow or a double arrow like ResizeNwseCursor. + | ResizeSouthEastCursor + /// Window resize bottom. This may be a single arrow or a double arrow like ResizeNorthSouthCursor. + | ResizeSouthCursor + /// Window resize bottom-left. This may be a single arrow or a double arrow like ResizeNeswCursor. + | ResizeSouthWestCursor + /// Window resize left. This may be a single arrow or a double arrow like ResizeEastWestCursor. + | ResizeWestCursor + /// User-defined cursor loaded from a cursor asset. | UserDefinedCursor of AssetTag : Cursor AssetTag /// Instructs the system cursor display behavior. @@ -62,8 +91,8 @@ type [] StubCursorClient = /// The SDL implementation of CursorClient. type [] SdlCursorClient = private - { SystemCursors : Dictionary - CursorPackages : Packages + { SystemCursors : Dictionary + CursorPackages : Packages mutable CursorType : CursorType } /// Make an SdlCursorClient. @@ -75,30 +104,21 @@ type [] SdlCursorClient = static member private tryLoadCursorAsset (asset : Asset) = match PathF.GetExtensionLower asset.FilePath with | CursorExtension _ -> - let surface = SDL_image.IMG_Load asset.FilePath - if surface <> 0n then - - // load hotspot from .cur file. NOTE: SDL3 reads cursor hotspots as SDLSurface properties - // (https://github.com/libsdl-org/SDL_image/pull/519/files), however SDL2 does not do that. Therefore, - // we have to read the hotspot properties manually. - // NOTE: reference for .cur file format here - https://www.daubnet.com/en/file-format-cur - use fileStream = new FileStream (asset.FilePath, FileMode.Open, FileAccess.Read, FileShare.None, 0) // 0 = disable buffering - use binaryReader = new BinaryReader (fileStream) - fileStream.Seek (10, SeekOrigin.Begin) |> ignore - let hotspotX = binaryReader.ReadUInt16 () // hotspotX of first cursor is bytes 10 to 11 (little endian) - let hotspotY = binaryReader.ReadUInt16 () // hotspotY of first cursor is bytes 12 to 13 (little endian) - - // create cursor and free the surface that the cursor copied from - let cursor = SDL.SDL_CreateColorCursor (surface, int hotspotX, int hotspotY) - SDL.SDL_FreeSurface surface - if cursor <> 0n + let surface = SDL3_image.IMG_Load asset.FilePath + if not (NativePtr.isNullPtr surface) then + + // create cursor. hotspot parameters (0, 0) here are overridden by the surface properties + // SDL_PROP_SURFACE_HOTSPOT_X_NUMBER and SDL_PROP_SURFACE_HOTSPOT_Y_NUMBER set by Image.Load + let cursor = SDL3.SDL_CreateColorCursor (surface, 0, 0) + SDL3.SDL_DestroySurface surface // the cursor stores a copy of the frame data, the surface can be destroyed here + if not (NativePtr.isNullPtr cursor) then Some cursor else - Log.warn ("Could not create cursor for '" + asset.FilePath + "' due to: '" + SDL.SDL_GetError ()) + Log.warn ("Could not create cursor for '" + asset.FilePath + "' due to: '" + SDL3.SDL_GetError ()) None else - Log.warn ("Could not load cursor for '" + asset.FilePath + "' due to: '" + SDL.SDL_GetError ()) + Log.warn ("Could not load cursor for '" + asset.FilePath + "' due to: '" + SDL3.SDL_GetError ()) None | _ -> None @@ -125,7 +145,7 @@ type [] SdlCursorClient = with exn -> Log.info ("Asset file write time read error due to: " + scstring exn); DateTimeOffset.MinValue.DateTime if lastWriteTime = lastWriteTime' then assetsToKeep.Add assetName |> ignore - else SDL.SDL_FreeCursor cursor + else SDL3.SDL_DestroyCursor cursor // load newly found assets that are not kept for asset in assetsCollected do @@ -152,46 +172,48 @@ type [] SdlCursorClient = | Some package -> package.Assets |> Dictionary.tryFind assetTag.AssetName |> Option.map __c | None -> None - static member private setSystemCursor (systemCursor : SDL.SDL_SystemCursor) cursorClient = + static member private setSystemCursor (systemCursor : SDL_SystemCursor) cursorClient = match Dictionary.tryFind systemCursor cursorClient.SystemCursors with - | Some cursor -> SDL.SDL_SetCursor cursor + | Some cursor -> SDL3.SDL_SetCursor cursor | None -> - let cursor = SDL.SDL_CreateSystemCursor systemCursor - if cursor <> nativeint 0 then + let cursor = SDL3.SDL_CreateSystemCursor systemCursor + if not (NativePtr.isNullPtr cursor) then cursorClient.SystemCursors.[systemCursor] <- cursor - SDL.SDL_SetCursor cursor - else Log.warn ("Failed to create system cursor '" + scstring systemCursor + "' due to: " + SDL.SDL_GetError ()) + SDL3.SDL_SetCursor cursor + else Log.warn ("Failed to create system cursor '" + scstring systemCursor + "' due to: " + SDL3.SDL_GetError ()); true static member private getCursorType cursorClient = cursorClient.CursorType static member private setCursorType cursorType (cursorClient : SdlCursorClient) = match cursorType with - | DefaultCursor -> SDL.SDL_SetCursor (SDL.SDL_GetDefaultCursor ()) - | ArrowCursor -> SdlCursorClient.setSystemCursor SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_ARROW cursorClient - | IBeamCursor -> SdlCursorClient.setSystemCursor SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_IBEAM cursorClient - | WaitCursor -> SdlCursorClient.setSystemCursor SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_WAIT cursorClient - | CrosshairCursor -> SdlCursorClient.setSystemCursor SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_CROSSHAIR cursorClient - | WaitArrowCursor -> SdlCursorClient.setSystemCursor SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_WAITARROW cursorClient - | SizeNwseCursor -> SdlCursorClient.setSystemCursor SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENWSE cursorClient - | SizeNeswCursor -> SdlCursorClient.setSystemCursor SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENESW cursorClient - | SizeWestEastCursor -> SdlCursorClient.setSystemCursor SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZEWE cursorClient - | SizeNorthSouthCursor -> SdlCursorClient.setSystemCursor SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENS cursorClient - | SizeAllCursor -> SdlCursorClient.setSystemCursor SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZEALL cursorClient - | NoCursor -> SdlCursorClient.setSystemCursor SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_NO cursorClient - | HandCursor -> SdlCursorClient.setSystemCursor SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_HAND cursorClient + | DefaultCursor -> SDL3.SDL_SetCursor (SDL3.SDL_GetDefaultCursor ()) // is a shared system cursor instance of SDL_SystemCursor.SDL_SYSTEM_CURSOR_DEFAULT unless SDL is initialized with the SDL_HINT_MOUSE_DEFAULT_SYSTEM_CURSOR hint, which we don't use + | TextCursor -> SdlCursorClient.setSystemCursor SDL_SystemCursor.SDL_SYSTEM_CURSOR_TEXT cursorClient + | WaitCursor -> SdlCursorClient.setSystemCursor SDL_SystemCursor.SDL_SYSTEM_CURSOR_WAIT cursorClient + | CrosshairCursor -> SdlCursorClient.setSystemCursor SDL_SystemCursor.SDL_SYSTEM_CURSOR_CROSSHAIR cursorClient + | ProgressCursor -> SdlCursorClient.setSystemCursor SDL_SystemCursor.SDL_SYSTEM_CURSOR_PROGRESS cursorClient + | ResizeNwseCursor -> SdlCursorClient.setSystemCursor SDL_SystemCursor.SDL_SYSTEM_CURSOR_NWSE_RESIZE cursorClient + | ResizeNeswCursor -> SdlCursorClient.setSystemCursor SDL_SystemCursor.SDL_SYSTEM_CURSOR_NESW_RESIZE cursorClient + | ResizeEastWestCursor -> SdlCursorClient.setSystemCursor SDL_SystemCursor.SDL_SYSTEM_CURSOR_EW_RESIZE cursorClient + | ResizeNorthSouthCursor -> SdlCursorClient.setSystemCursor SDL_SystemCursor.SDL_SYSTEM_CURSOR_NS_RESIZE cursorClient + | MoveCursor -> SdlCursorClient.setSystemCursor SDL_SystemCursor.SDL_SYSTEM_CURSOR_MOVE cursorClient + | NotAllowedCursor -> SdlCursorClient.setSystemCursor SDL_SystemCursor.SDL_SYSTEM_CURSOR_NOT_ALLOWED cursorClient + | PointerCursor -> SdlCursorClient.setSystemCursor SDL_SystemCursor.SDL_SYSTEM_CURSOR_POINTER cursorClient + | ResizeNorthWestCursor -> SdlCursorClient.setSystemCursor SDL_SystemCursor.SDL_SYSTEM_CURSOR_NW_RESIZE cursorClient + | ResizeNorthCursor -> SdlCursorClient.setSystemCursor SDL_SystemCursor.SDL_SYSTEM_CURSOR_N_RESIZE cursorClient + | ResizeNorthEastCursor -> SdlCursorClient.setSystemCursor SDL_SystemCursor.SDL_SYSTEM_CURSOR_NE_RESIZE cursorClient + | ResizeEastCursor -> SdlCursorClient.setSystemCursor SDL_SystemCursor.SDL_SYSTEM_CURSOR_E_RESIZE cursorClient + | ResizeSouthEastCursor -> SdlCursorClient.setSystemCursor SDL_SystemCursor.SDL_SYSTEM_CURSOR_SE_RESIZE cursorClient + | ResizeSouthCursor -> SdlCursorClient.setSystemCursor SDL_SystemCursor.SDL_SYSTEM_CURSOR_S_RESIZE cursorClient + | ResizeSouthWestCursor -> SdlCursorClient.setSystemCursor SDL_SystemCursor.SDL_SYSTEM_CURSOR_SW_RESIZE cursorClient + | ResizeWestCursor -> SdlCursorClient.setSystemCursor SDL_SystemCursor.SDL_SYSTEM_CURSOR_W_RESIZE cursorClient | UserDefinedCursor assetTag -> match SdlCursorClient.tryGetCursorAsset assetTag cursorClient with - | Some cursor -> SDL.SDL_SetCursor cursor - | None -> Log.info ("UserDefinedCursor message failed due to unloadable assets for '" + scstring assetTag + "'.") + | Some cursor -> SDL3.SDL_SetCursor cursor + | None -> Log.warn ("UserDefinedCursor message failed due to unloadable assets for '" + scstring assetTag + "'."); true + |> fun result -> if not result then Log.warn ("Failed to set cursor type '" + scstring cursorType + "' due to: " + SDL3.SDL_GetError ()) cursorClient.CursorType <- cursorType - static member private getCursorVisible = - SDL.SDL_ShowCursor SDL.SDL_QUERY = SDL.SDL_ENABLE - - static member private setCursorVisible visible = - SDL.SDL_ShowCursor (if visible then SDL.SDL_ENABLE else SDL.SDL_DISABLE) |> ignore - interface CursorClient with member cursorClient.CursorType @@ -199,8 +221,10 @@ type [] SdlCursorClient = and set value = SdlCursorClient.setCursorType value cursorClient member cursorClient.CursorVisible - with get () = SdlCursorClient.getCursorVisible - and set value = SdlCursorClient.setCursorVisible value + with get () = SDL3.SDL_CursorVisible () + and set value = + if not (if value then SDL3.SDL_ShowCursor () else SDL3.SDL_HideCursor ()) then + Log.warn ("Failed to set cursor visibility to '" + scstring value + "' due to: " + SDL3.SDL_GetError ()) member cursorClient.LoadCursorPackage packageName = SdlCursorClient.tryLoadCursorPackage packageName cursorClient @@ -208,13 +232,13 @@ type [] SdlCursorClient = member cursorClient.UnloadCursorPackage packageName = match Dictionary.tryFind packageName cursorClient.CursorPackages with | Some package -> - for asset in package.Assets do SDL.SDL_FreeCursor (__c asset.Value) + for asset in package.Assets do SDL3.SDL_DestroyCursor (__c asset.Value) cursorClient.CursorPackages.Remove packageName |> ignore | None -> () member cursorClient.ReloadCursorAssets () = for systemCursor in cursorClient.SystemCursors.Values do - SDL.SDL_FreeCursor systemCursor + SDL3.SDL_DestroyCursor systemCursor cursorClient.SystemCursors.Clear () for packageName in cursorClient.CursorPackages |> Seq.map (fun entry -> entry.Key) |> Array.ofSeq do SdlCursorClient.tryLoadCursorPackage packageName cursorClient @@ -222,7 +246,7 @@ type [] SdlCursorClient = member cursorClient.CleanUp () = for systemCursor in cursorClient.SystemCursors.Values do - SDL.SDL_FreeCursor systemCursor + SDL3.SDL_DestroyCursor systemCursor for package in cursorClient.CursorPackages.Values do for (_, _, cursor) in package.Assets.Values do - SDL.SDL_FreeCursor cursor \ No newline at end of file + SDL3.SDL_DestroyCursor cursor \ No newline at end of file diff --git a/Nu/Nu/Effects/EffectDescriptor.fs b/Nu/Nu/Effects/EffectDescriptor.fs index dfbf8c3c3d..01586cc880 100644 --- a/Nu/Nu/Effects/EffectDescriptor.fs +++ b/Nu/Nu/Effects/EffectDescriptor.fs @@ -194,7 +194,7 @@ and Content = | Nil // first to make default value when missing | StaticSprite of Image : Resource * Aspects : Aspect array * Content : Content | AnimatedSprite of Image : Resource * CelSize : Vector2i * CelCount : int * CelRun : int * CelDelay : GameTime * Playback : Playback * Aspects : Aspect array * Content : Content - | TextSprite of Font : Resource * Text : string * FontSizing : int option * FontStyling : FontStyle Set * Aspects : Aspect array * Content : Content + | TextSprite of Font : Resource * Text : string * FontSizing : single option * FontStyling : FontStyle Set * Aspects : Aspect array * Content : Content | Billboard of Albedo : Resource * Roughness : Resource * Metallic : Resource * AmbientOcclusion : Resource * Emission : Resource * Normal : Resource * HeightMap : Resource * TwoSided : bool * Clipped : bool * Aspects : Aspect array * Content : Content | StaticModel of Resource : Resource * Clipped : bool * Aspects : Aspect array * Content : Content | Light3d of LightType : LightType * Aspects : Aspect array * Content : Content diff --git a/Nu/Nu/ImGui/ImGui.fs b/Nu/Nu/ImGui/ImGui.fs index ee6477622f..16265b0f46 100644 --- a/Nu/Nu/ImGui/ImGui.fs +++ b/Nu/Nu/ImGui/ImGui.fs @@ -25,8 +25,8 @@ type ImGui (stub : bool, displaySize : Vector2i) = static let mutable Font = Unchecked.defaultof static let mutable MouseLeftIdInternal = 0L - let charsPressed = - List () + let inputs = + List () let context = ImGui.CreateContext () @@ -89,8 +89,8 @@ type ImGui (stub : bool, displaySize : Vector2i) = let io = ImGui.GetIO () io.MouseWheel <- io.MouseWheel + change - member this.HandleKeyChar (keyChar : char) = - charsPressed.Add keyChar + member this.HandleTextInput (input : string) = + inputs.Add input member this.BeginFrame deltaTime = if not stub then @@ -108,9 +108,9 @@ type ImGui (stub : bool, displaySize : Vector2i) = member this.InputFrame () = let io = ImGui.GetIO () - for c in charsPressed do - io.AddInputCharacter (uint32 c) - charsPressed.Clear () + for i in inputs do + io.AddInputCharactersUTF8 i + inputs.Clear () member this.RenderFrame () = if not stub then ImGui.Render () diff --git a/Nu/Nu/Nu.fsproj b/Nu/Nu/Nu.fsproj index ff314b5418..85ce6c5a73 100644 --- a/Nu/Nu/Nu.fsproj +++ b/Nu/Nu/Nu.fsproj @@ -65,7 +65,6 @@ - @@ -141,66 +140,6 @@ - - PreserveNewest - False - - - PreserveNewest - False - - - PreserveNewest - False - - - PreserveNewest - False - - - PreserveNewest - False - - - PreserveNewest - False - - - PreserveNewest - False - - - PreserveNewest - False - - - PreserveNewest - False - - - PreserveNewest - False - - - PreserveNewest - False - - - PreserveNewest - False - - - PreserveNewest - False - - - PreserveNewest - False - - - PreserveNewest - False - PreserveNewest False @@ -233,9 +172,6 @@ ..\Nu.Dependencies\OpenGL.NET\lib\netcoreapp2.2\OpenGL.Net.dll - - ..\Nu.Dependencies\SDL2-CS\netstandard2.0\SDL2-CS.dll - ..\Nu.Dependencies\TiledSharp\lib\netstandard2.0\TiledSharp.dll @@ -250,6 +186,10 @@ + + + + diff --git a/Nu/Nu/OpenGL/OpenGL.Hl.fs b/Nu/Nu/OpenGL/OpenGL.Hl.fs index f0e5edc6a4..5e4bb6726f 100644 --- a/Nu/Nu/OpenGL/OpenGL.Hl.fs +++ b/Nu/Nu/OpenGL/OpenGL.Hl.fs @@ -15,7 +15,8 @@ open System open System.Runtime.InteropServices open System.Numerics open System.Text -open SDL2 +open FSharp.NativeInterop +open SDL open Prime open Nu @@ -133,10 +134,10 @@ module Hl = let CreateSglContextInitial window = Log.info "Initializing OpenGL 4.6..." Gl.Initialize () - let glContext = SDL.SDL_GL_CreateContext window + let glContext = SDL3.SDL_GL_CreateContext window let swapInterval = if Constants.Render.Vsync then 1 else 0 - SDL.SDL_GL_SetSwapInterval swapInterval |> ignore - if SDL.SDL_GL_MakeCurrent (window, glContext) <> 0 then Log.error "Could not make OpenGL context current when required." + SDL3.SDL_GL_SetSwapInterval swapInterval |> ignore + if not (SDL3.SDL_GL_MakeCurrent (window, glContext)) then Log.error "Could not make OpenGL context current when required." Gl.BindAPI () let vendorName = Gl.GetString StringName.Vendor let versionStr = Gl.GetString StringName.Version @@ -149,17 +150,17 @@ module Hl = /// Create a SDL OpenGL context with the given window that shares the current context. Originating thread must wait /// on the given WaitOnce object before continuing processing. let CreateSglContextSharedWithCurrentContext (window, sharedContext) = - if SDL.SDL_GL_MakeCurrent (window, sharedContext) <> 0 then Log.error "Could not make OpenGL context current when required." - SDL.SDL_GL_SetAttribute (SDL.SDL_GLattr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1) |> ignore - let glContext = SDL.SDL_GL_CreateContext window - if SDL.SDL_GL_MakeCurrent (window, glContext) <> 0 then Log.error "Could not make OpenGL context current when required." + if not (SDL3.SDL_GL_MakeCurrent (window, sharedContext)) then Log.error "Could not make OpenGL context current when required." + SDL3.SDL_GL_SetAttribute (SDL_GLAttr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1) |> ignore + let glContext = SDL3.SDL_GL_CreateContext window + if not (SDL3.SDL_GL_MakeCurrent (window, glContext)) then Log.error "Could not make OpenGL context current when required." Gl.BindAPI () glContext /// Delete an SDL-created OpenGL context. let DestroySglContext (glContext, sglWindow) = - if SDL.SDL_GL_MakeCurrent (sglWindow, IntPtr.Zero) <> 0 then Log.error "Could not clear OpenGL context current when desired." - SDL.SDL_GL_DeleteContext glContext + if not (SDL3.SDL_GL_MakeCurrent (sglWindow, NativePtr.nullPtr)) then Log.error "Could not clear OpenGL context current when desired." + if not (SDL3.SDL_GL_DestroyContext glContext) then Log.error "Failed to destroy OpenGL context when desired." /// Initialize OpenGL context once created. let InitContext attach = diff --git a/Nu/Nu/OpenGL/OpenGL.Texture.fs b/Nu/Nu/OpenGL/OpenGL.Texture.fs index e4a0b0556e..9cd6ce376d 100644 --- a/Nu/Nu/OpenGL/OpenGL.Texture.fs +++ b/Nu/Nu/OpenGL/OpenGL.Texture.fs @@ -13,7 +13,8 @@ open System.IO open System.Numerics open System.Runtime.InteropServices open System.Threading -open SDL2 +open FSharp.NativeInterop +open SDL open Pfim open Prime open Nu @@ -391,21 +392,20 @@ module Texture = Some (TextureDataNative (metadata, scan0, { new IDisposable with member this.Dispose () = bitmap.UnlockBits data; bitmap.Dispose () })) // NOTE: calling UnlockBits explicitly since I can't figure out if Dispose does. with _ -> None | _ -> None - + // attempt to load data as any format supported by SDL_image on any device else - let format = SDL.SDL_PIXELFORMAT_ARGB8888 // seems to be the right format on Ubuntu... - let unconvertedPtr = SDL_image.IMG_Load filePath - if unconvertedPtr <> nativeint 0 then - let unconverted = Marshal.PtrToStructure unconvertedPtr + let format = SDL_PixelFormat.SDL_PIXELFORMAT_ARGB8888 // seems to be the right format on Ubuntu... + let unconvertedPtr = SDL3_image.IMG_Load filePath + if not (NativePtr.isNullPtr unconvertedPtr) then + let unconverted = NativePtr.toByRef unconvertedPtr let metadata = TextureMetadata.make unconverted.w unconverted.h - let unconvertedFormat = Marshal.PtrToStructure unconverted.format - if unconvertedFormat.format <> format then - let convertedPtr = SDL.SDL_ConvertSurfaceFormat (unconvertedPtr, format, 0u) - let converted = Marshal.PtrToStructure convertedPtr - SDL.SDL_FreeSurface unconvertedPtr // no longer need this - Some (TextureDataNative (metadata, converted.pixels, { new IDisposable with member this.Dispose () = SDL.SDL_FreeSurface convertedPtr })) - else Some (TextureDataNative (metadata, unconverted.pixels, { new IDisposable with member this.Dispose () = SDL.SDL_FreeSurface unconvertedPtr })) + if unconverted.format <> format then + let convertedPtr = SDL3.SDL_ConvertSurface (unconvertedPtr, format) + let converted = NativePtr.toByRef convertedPtr + SDL3.SDL_DestroySurface unconvertedPtr // no longer need this + Some (TextureDataNative (metadata, converted.pixels, { new IDisposable with member this.Dispose () = SDL3.SDL_DestroySurface convertedPtr })) + else Some (TextureDataNative (metadata, unconverted.pixels, { new IDisposable with member this.Dispose () = SDL3.SDL_DestroySurface unconvertedPtr })) else None else None diff --git a/Nu/Nu/Render/Renderer2d.fs b/Nu/Nu/Render/Renderer2d.fs index 3d93381101..9f563d1091 100644 --- a/Nu/Nu/Render/Renderer2d.fs +++ b/Nu/Nu/Render/Renderer2d.fs @@ -9,8 +9,8 @@ open System open System.Collections.Generic open System.IO open System.Numerics -open System.Runtime.InteropServices -open SDL2 +open FSharp.NativeInterop +open SDL open TiledSharp open Prime @@ -31,7 +31,7 @@ type [] TextValue = mutable ClipOpt : Box2 voption mutable Text : string mutable Font : Font AssetTag - mutable FontSizing : int option + mutable FontSizing : single option mutable FontStyling : FontStyle Set mutable Color : Color mutable Justification : Justification @@ -95,7 +95,7 @@ type TextDescriptor = ClipOpt : Box2 voption Text : string Font : Font AssetTag - FontSizing : int option + FontSizing : single option FontStyling : FontStyle Set Color : Color Justification : Justification @@ -207,7 +207,7 @@ type [] GlRenderer2d = match renderAsset with | RawAsset -> () | TextureAsset texture -> texture.Destroy () - | FontAsset (_, font) -> SDL_ttf.TTF_CloseFont font + | FontAsset (_, font) -> SDL3_ttf.TTF_CloseFont font | CubeMapAsset _ -> () | StaticModelAsset _ -> () | AnimatedModelAsset _ -> () @@ -233,15 +233,15 @@ type [] GlRenderer2d = let fontSizeDefault = if fileFirstNameLength >= 3 then let fontSizeText = fileFirstName.Substring (fileFirstNameLength - 3, 3) - match Int32.TryParse fontSizeText with + match Single.TryParse fontSizeText with | (true, fontSize) -> fontSize | (false, _) -> Constants.Render.FontSizeDefault else Constants.Render.FontSizeDefault - let fontSize = fontSizeDefault * renderer.Viewport.DisplayScalar - let fontOpt = SDL_ttf.TTF_OpenFont (asset.FilePath, fontSize) - if fontOpt <> IntPtr.Zero + let fontSize = fontSizeDefault * single renderer.Viewport.DisplayScalar + let fontOpt = SDL3_ttf.TTF_OpenFont (asset.FilePath, fontSize) + if fontOpt <> NativePtr.nullPtr then Some (FontAsset (fontSizeDefault, fontOpt)) - else Log.info ("Could not load font due to '" + SDL_ttf.TTF_GetError () + "'."); None + else Log.info ("Could not load font due to '" + SDL3.SDL_GetError () + "'."); None | _ -> None static member private tryLoadRenderPackage packageName renderer = @@ -618,6 +618,7 @@ type [] GlRenderer2d = // fin tileIndex <- inc tileIndex tileMin.X <- tileMin.X + tileSize.X + else Log.infoOnce ("TileLayerDescriptor failed due to unloadable or non-texture assets for one or more of '" + scstring tileAssets + "'.") /// Render Spine skeleton. @@ -658,7 +659,7 @@ type [] GlRenderer2d = clipOpt : Box2 voption inref, text : string, font : Font AssetTag, - fontSizing : int option, + fontSizing : single option, fontStyling : FontStyle Set, color : Color inref, justification : Justification, @@ -702,8 +703,8 @@ type [] GlRenderer2d = // determine font size let fontSize = match fontSizing with - | Some fontSize -> fontSize * renderer.Viewport.DisplayScalar - | None -> fontSizeDefault * renderer.Viewport.DisplayScalar + | Some fontSize -> fontSize * single renderer.Viewport.DisplayScalar + | None -> fontSizeDefault * single renderer.Viewport.DisplayScalar // attempt to find or create text texture // NOTE: because of the hacky way the caret is shown, texture is recreated every blink on / off. @@ -713,48 +714,47 @@ type [] GlRenderer2d = | (false, _) -> // gather rendering resources - let (offset, textSurface, textSurfacePtr) = + let (offset, textSurfacePtr) = // create sdl color - let mutable colorSdl = SDL.SDL_Color () + let mutable colorSdl = SDL_Color () colorSdl.r <- color.R8 colorSdl.g <- color.G8 colorSdl.b <- color.B8 colorSdl.a <- color.A8 // attempt to configure sdl font size - if SDL_ttf.TTF_SetFontSize (font, fontSize) <> 0 then - let error = SDL_ttf.TTF_GetError () + if not (SDL3_ttf.TTF_SetFontSize (font, fontSize)) then + let error = SDL3.SDL_GetError () Log.infoOnce ("Failed to set font size for font '" + scstring font + "' due to: " + error) - SDL_ttf.TTF_SetFontSize (font, fontSizeDefault * renderer.Viewport.DisplayScalar) |> ignore + SDL3_ttf.TTF_SetFontSize (font, fontSizeDefault * single renderer.Viewport.DisplayScalar) |> ignore // configure sdl font style let styleSdl = if fontStyling.Count > 0 then // OPTIMIZATION: avoid set queries where possible. - (if fontStyling.Contains Bold then SDL_ttf.TTF_STYLE_BOLD else 0) ||| - (if fontStyling.Contains Italic then SDL_ttf.TTF_STYLE_ITALIC else 0) ||| - (if fontStyling.Contains Underline then SDL_ttf.TTF_STYLE_UNDERLINE else 0) ||| - (if fontStyling.Contains Strikethrough then SDL_ttf.TTF_STYLE_STRIKETHROUGH else 0) - else 0 - SDL_ttf.TTF_SetFontStyle (font, styleSdl) + (if fontStyling.Contains Bold then TTF_FontStyleFlags.TTF_STYLE_BOLD else TTF_FontStyleFlags.TTF_STYLE_NORMAL) ||| + (if fontStyling.Contains Italic then TTF_FontStyleFlags.TTF_STYLE_ITALIC else TTF_FontStyleFlags.TTF_STYLE_NORMAL) ||| + (if fontStyling.Contains Underline then TTF_FontStyleFlags.TTF_STYLE_UNDERLINE else TTF_FontStyleFlags.TTF_STYLE_NORMAL) ||| + (if fontStyling.Contains Strikethrough then TTF_FontStyleFlags.TTF_STYLE_STRIKETHROUGH else TTF_FontStyleFlags.TTF_STYLE_NORMAL) + else TTF_FontStyleFlags.TTF_STYLE_NORMAL + SDL3_ttf.TTF_SetFontStyle (font, styleSdl) // render text to surface match justification with | Unjustified wrapped -> let textSurfacePtr = if wrapped - then SDL_ttf.TTF_RenderUNICODE_Blended_Wrapped (font, text, colorSdl, uint32 size.X) - else SDL_ttf.TTF_RenderUNICODE_Blended (font, text, colorSdl) - let textSurface = Marshal.PtrToStructure textSurfacePtr + then SDL3_ttf.TTF_RenderText_Blended_Wrapped (font, text, 0un, colorSdl, int size.X) + else SDL3_ttf.TTF_RenderText_Blended (font, text, 0un, colorSdl) + let textSurface = NativePtr.toByRef textSurfacePtr let textSurfaceHeight = single textSurface.h let offsetY = size.Y - textSurfaceHeight - (v2 0.0f offsetY, textSurface, textSurfacePtr) + (v2 0.0f offsetY, textSurfacePtr) | Justified (h, v) -> let mutable width = 0 let mutable height = 0 - SDL_ttf.TTF_SizeUNICODE (font, text, &width, &height) |> ignore - let textSurfacePtr = SDL_ttf.TTF_RenderUNICODE_Blended (font, text, colorSdl) - let textSurface = Marshal.PtrToStructure textSurfacePtr + SDL3_ttf.TTF_GetStringSize (font, text, 0un, &&width, &&height) |> ignore + let textSurfacePtr = SDL3_ttf.TTF_RenderText_Blended (font, text, 0un, colorSdl) let offsetX = match h with | JustifyLeft -> 0.0f @@ -766,13 +766,14 @@ type [] GlRenderer2d = | JustifyMiddle -> floor ((size.Y - single height) * 0.5f) | JustifyBottom -> 0.0f let offset = v2 offsetX offsetY - (offset, textSurface, textSurfacePtr) + (offset, textSurfacePtr) // render only when a valid surface was created - if textSurfacePtr <> IntPtr.Zero then + if not (NativePtr.isNullPtr textSurfacePtr) then + let textSurface = NativePtr.toByRef textSurfacePtr // construct mvp matrix - let textSurfaceWidth = textSurface.pitch / 4 + let textSurfaceWidth = textSurface.pitch / 4 // NOTE: textSurface.w may be an innacurate representation of texture width in SDL2_ttf versions beyond v2.0.15 because... I don't know why. let textSurfaceHeight = textSurface.h let translation = (position + offset).V3 let scale = v3 (single textSurfaceWidth) (single textSurfaceHeight) 1.0f @@ -800,8 +801,8 @@ type [] GlRenderer2d = let textTexture = OpenGL.Texture.EagerTexture { TextureMetadata = textTextureMetadata; TextureId = textTextureId } OpenGL.Hl.Assert () - // free sdl surface - SDL.SDL_FreeSurface textSurfacePtr + // destroy sdl surface + SDL3.SDL_DestroySurface textSurfacePtr // register texture for reuse renderer.TextTextures.Add (textTextureKey, (ref true, (textSurfaceWidth, textSurfaceHeight, modelViewProjection, textTexture))) diff --git a/Nu/Nu/Render/Renderer3d.fs b/Nu/Nu/Render/Renderer3d.fs index 55a0f1298c..b0d8b869e4 100644 --- a/Nu/Nu/Render/Renderer3d.fs +++ b/Nu/Nu/Render/Renderer3d.fs @@ -11,7 +11,8 @@ open System.Collections.Generic open System.IO open System.Numerics open System.Runtime.InteropServices -open SDL2 +open FSharp.NativeInterop +open SDL open Prime /// A layer from which a 3d terrain's material is composed. @@ -1434,7 +1435,7 @@ type [] GlRenderer3d = match renderAsset with | RawAsset -> () // nothing to do | TextureAsset texture -> texture.Destroy () - | FontAsset (_, font) -> SDL_ttf.TTF_CloseFont font + | FontAsset (_, font) -> SDL3_ttf.TTF_CloseFont font | CubeMapAsset (_, cubeMap, _) -> cubeMap.Destroy () | StaticModelAsset (_, model) -> OpenGL.PhysicallyBased.DestroyPhysicallyBasedModel model | AnimatedModelAsset model -> OpenGL.PhysicallyBased.DestroyPhysicallyBasedModel model @@ -4747,12 +4748,11 @@ type [] GlRenderer3d = static member make glContext window geometryViewport windowViewport = // start lazy texture server - let sglWindow = match window with SglWindow sglWindow -> sglWindow.SglWindow - if SDL.SDL_GL_MakeCurrent (sglWindow, IntPtr.Zero) <> 0 then Log.error "Could not clear OpenGL context current when desired." + if not (SDL3.SDL_GL_MakeCurrent (window, NativePtr.nullPtr)) then Log.error "Could not clear OpenGL context current when desired." let lazyTextureQueues = ConcurrentDictionary HashIdentity.Reference - let textureServer = OpenGL.Texture.TextureServer (lazyTextureQueues, glContext, sglWindow) + let textureServer = OpenGL.Texture.TextureServer (lazyTextureQueues, glContext, window) textureServer.Start () - if SDL.SDL_GL_MakeCurrent (sglWindow, glContext) <> 0 then Log.error "Could not make OpenGL context current when required." + if not (SDL3.SDL_GL_MakeCurrent (window, glContext)) then Log.error "Could not make OpenGL context current when required." OpenGL.Hl.Assert () // create cube map vao diff --git a/Nu/Nu/Render/RendererPrelude.fs b/Nu/Nu/Render/RendererPrelude.fs index 0898049a51..74fc74aacf 100644 --- a/Nu/Nu/Render/RendererPrelude.fs +++ b/Nu/Nu/Render/RendererPrelude.fs @@ -8,6 +8,7 @@ namespace Nu open System open System.Numerics open Prime +open SDL /// The blend mode of a sprite. type [] Blend = @@ -129,7 +130,7 @@ type [] RenderPass = type RenderAsset = | RawAsset | TextureAsset of Texture : OpenGL.Texture.Texture - | FontAsset of FontSizeDefault : int * Font : nativeint + | FontAsset of FontSizeDefault : single * Font : TTF_Font nativeptr | CubeMapAsset of FilePaths : OpenGL.CubeMap.CubeMapKey * CubeMap : OpenGL.Texture.Texture * IrradianceAndEnvironmentMapOptRef : (OpenGL.Texture.Texture * OpenGL.Texture.Texture) option ref | StaticModelAsset of UserDefined : bool * StaticModel : OpenGL.PhysicallyBased.PhysicallyBasedModel | AnimatedModelAsset of AnimatedModel : OpenGL.PhysicallyBased.PhysicallyBasedModel \ No newline at end of file diff --git a/Nu/Nu/Render/RendererProcess.fs b/Nu/Nu/Render/RendererProcess.fs index a19d771c9c..e27178a3b6 100644 --- a/Nu/Nu/Render/RendererProcess.fs +++ b/Nu/Nu/Render/RendererProcess.fs @@ -10,7 +10,7 @@ open System.Collections.Concurrent open System.Collections.Generic open System.Numerics open System.Threading -open SDL2 +open SDL open ImGuiNET open Prime @@ -20,7 +20,7 @@ type RendererProcess = interface /// Start the rendering process. - abstract Start : ImFontAtlasPtr -> Window option -> Viewport -> Viewport -> unit + abstract Start : ImFontAtlasPtr -> SDL_Window nativeptr option -> Viewport -> Viewport -> unit /// The current configuration of the 3d renderer. abstract Renderer3dConfig : Renderer3dConfig @@ -67,11 +67,11 @@ type RendererInline () = let mutable started = false let mutable terminated = false - let mutable windowOpt = Option.None + let mutable windowOpt = Option.None let mutable messages3d = List () let mutable messages2d = List () let mutable messagesImGui = List () - let mutable dependenciesOpt = Option.None + let mutable dependenciesOpt = Option.None let assetTextureRequests = ConcurrentDictionary HashIdentity.Structural let assetTextureOpts = ConcurrentDictionary HashIdentity.Structural @@ -91,7 +91,7 @@ type RendererInline () = | Some window -> // create gl context - let glContext = match window with SglWindow window -> OpenGL.Hl.CreateSglContextInitial window.SglWindow + let glContext = OpenGL.Hl.CreateSglContextInitial window OpenGL.Hl.Assert () // initialize gl context @@ -204,7 +204,7 @@ type RendererInline () = member ri.RequestSwap () = match windowOpt with - | Some (SglWindow window) -> SDL.SDL_GL_SwapWindow window.SglWindow + | Some window -> SDL3.SDL_GL_SwapWindow window |> ignore | None -> () member ri.Terminate () = @@ -226,7 +226,7 @@ type RendererInline () = // clean up gl dependenciesOpt <- None match windowOpt with - | Some (SglWindow window) -> OpenGL.Hl.DestroySglContext (glContext, window.SglWindow) + | Some window -> OpenGL.Hl.DestroySglContext (glContext, window) | None -> () // fin @@ -374,7 +374,7 @@ type RendererThread () = member private rt.Run fonts window geometryViewport windowViewport = // create gl context - let glContext = match window with SglWindow window -> OpenGL.Hl.CreateSglContextInitial window.SglWindow + let glContext = OpenGL.Hl.CreateSglContextInitial window OpenGL.Hl.Assert () // initialize gl context @@ -445,7 +445,7 @@ type RendererThread () = swapRequestAcknowledged <- true // swap - match window with SglWindow window -> SDL.SDL_GL_SwapWindow window.SglWindow + SDL3.SDL_GL_SwapWindow window |> ignore // clean up 3d renderer3d.CleanUp () @@ -460,7 +460,7 @@ type RendererThread () = OpenGL.Hl.Assert () // clean up gl - OpenGL.Hl.DestroySglContext (glContext, match window with SglWindow window -> window.SglWindow) + OpenGL.Hl.DestroySglContext (glContext, window) interface RendererProcess with diff --git a/Nu/Nu/SDL2.dll b/Nu/Nu/SDL2.dll deleted file mode 100644 index e26bcb1c3b..0000000000 Binary files a/Nu/Nu/SDL2.dll and /dev/null differ diff --git a/Nu/Nu/SDL2_image.dll b/Nu/Nu/SDL2_image.dll deleted file mode 100644 index d90de2a301..0000000000 Binary files a/Nu/Nu/SDL2_image.dll and /dev/null differ diff --git a/Nu/Nu/SDL2_mixer.dll b/Nu/Nu/SDL2_mixer.dll deleted file mode 100644 index 5e4fef32f9..0000000000 Binary files a/Nu/Nu/SDL2_mixer.dll and /dev/null differ diff --git a/Nu/Nu/SDL2_ttf.dll b/Nu/Nu/SDL2_ttf.dll deleted file mode 100644 index dbbf385119..0000000000 Binary files a/Nu/Nu/SDL2_ttf.dll and /dev/null differ diff --git a/Nu/Nu/Scripts/GenerateDependencyGraph.fsx b/Nu/Nu/Scripts/GenerateDependencyGraph.fsx index 22130b24f1..3782e93411 100644 --- a/Nu/Nu/Scripts/GenerateDependencyGraph.fsx +++ b/Nu/Nu/Scripts/GenerateDependencyGraph.fsx @@ -2,6 +2,7 @@ #r "FSharp.Compiler.Service" #r "nuget: MSBuild.StructuredLogger" #r "nuget: GiGraph.Dot, 4.1.0" +#r "nuget: Prime" open System open System.IO diff --git a/Nu/Nu/Scripts/GenerateInputBindings.fsx b/Nu/Nu/Scripts/GenerateInputBindings.fsx index d61a67b58c..6f3bb99c6b 100644 --- a/Nu/Nu/Scripts/GenerateInputBindings.fsx +++ b/Nu/Nu/Scripts/GenerateInputBindings.fsx @@ -4,32 +4,14 @@ // Nu Game Engine is licensed under the Nu Game Engine Noncommercial License. // See https://github.com/bryanedds/Nu/blob/master/License.md. -#I __SOURCE_DIRECTORY__ -#r "nuget: Aether.Physics2D, 2.2.0" -#r "nuget: Box2D.NET, 3.1.1.557" -#r "nuget: BCnEncoder.Net, 2.2.1" -#r "nuget: DotRecast.Recast.Toolset, 2026.1.1" -#r "nuget: JoltPhysicsSharp, 2.19.5" -#r "nuget: Magick.NET-Q8-AnyCpu, 14.10.3" -#r "nuget: Pfim, 0.11.4" -#r "nuget: Prime, 11.4.1" -#r "nuget: System.Configuration.ConfigurationManager, 10.0.1" -#r "nuget: System.Drawing.Common, 10.0.1" -#r "nuget: Twizzle.ImGui-Bundle.NET, 1.91.5.2" -#r "../../../Nu/Nu.Dependencies/AssimpNet/netstandard2.1/AssimpNet.dll" -#r "../../../Nu/Nu.Dependencies/BulletSharpPInvoke/netstandard2.1/BulletSharp.dll" -#r "../../../Nu/Nu.Dependencies/OpenGL.NET/lib/netcoreapp2.2/OpenGL.Net.dll" -#r "../../../Nu/Nu.Dependencies/SDL2-CS/netstandard2.0/SDL2-CS.dll" -#r "../../../Nu/Nu.Dependencies/TiledSharp/lib/netstandard2.0/TiledSharp.dll" -#r "../../../Nu/Nu.Math/bin/Debug/netstandard2.1/Nu.Math.dll" -#r "../../../Nu/Nu/bin/Debug/net10.0/Nu.dll" +#r "nuget: Prime" +#r "nuget: ppy.SDL3-CS" open System -open System.Text.RegularExpressions -open System.Linq open System.IO +open System.Text.RegularExpressions open Prime -open SDL2 +open SDL // this function was copied and converted from - https://stackoverflow.com/a/46095771 let upperCaseToPascalCase (original : string) = @@ -43,39 +25,17 @@ let upperCaseToPascalCase (original : string) = // replace white spaces with undescore, then replace all invalid chars with empty string invalidCharsRgx.Replace(whiteSpace.Replace(original, "_"), "") // split by underscores - |> (fun (str : string) -> str.Split ([|'_'|], StringSplitOptions.RemoveEmptyEntries)) + |> fun str -> str.Split ([|'_'|], StringSplitOptions.RemoveEmptyEntries) // set first letter to uppercase - |> (fun (strs : string array) -> strs.Select(fun w -> startsWithLowerCaseChar.Replace (w, fun m -> m.Value.ToUpperInvariant ()))) + |> Array.map (fun w -> startsWithLowerCaseChar.Replace (w, _.Value.ToUpperInvariant())) // replace second and all following upper case letters to lower if there is no next lower (ABC -> Abc) - |> (fun (strs : string seq) -> strs.Select (fun w -> firstCharFollowedByUpperCasesOnly.Replace (w, fun m -> m.Value.ToLowerInvariant ()))) + |> Array.map (fun w -> firstCharFollowedByUpperCasesOnly.Replace (w, _.Value.ToLowerInvariant())) // set upper case the first lower case following a number (Ab9cd -> Ab9Cd) - |> (fun (strs : string seq) -> strs.Select(fun w -> lowerCaseNextToNumber.Replace (w, fun m -> m.Value.ToUpperInvariant ()))) + |> Array.map (fun w -> lowerCaseNextToNumber.Replace (w, _.Value.ToUpperInvariant())) // lower second and next upper case letters except the last if it follows by any lower (ABcDEf -> AbcDef) - |> (fun (strs : string seq) -> strs.Select(fun w -> upperCaseInside.Replace (w, fun m -> m.Value.ToLowerInvariant ()))) + |> Array.map (fun w -> upperCaseInside.Replace (w, _.Value.ToLowerInvariant())) String.Concat pascalCase -let enumEntries (ty : Type) = - ty.GetEnumNames () - |> enumerable - |> Seq.map (fun (name : string) -> - let name = name.Replace ("SDL_SCANCODE_", "") - let firstChar = name.[0] // NOTE: elided bounds check because I presume no case where this is possible - if firstChar >= '0' && firstChar <= '9' - then "Num" + name - else name) - |> flip Seq.zip (ty.GetEnumValues () |> enumerable) - |> Seq.filter (fun (name, _) -> name <> "SDL_NUM_SCANCODES") - |> Seq.map (mapFst (fun (name : string) -> name.Replace ("RETURN", "ENTER"))) // NOTE: ImGui calls this the 'enter' key, so I choose that. - |> Seq.map (mapFst upperCaseToPascalCase) - |> List.ofSeq - -let enumEntryToCode (entryName : string, entryValue : int) = - " | " + entryName + " = " + scstring entryValue - -let enumEntriesToCode entries = - let codes = List.map enumEntryToCode entries - String.Join ("\n", codes) - let generateBindingsCode codesStr = "// Nu Game Engine.\n" + "// Required Notice:\n" + @@ -83,12 +43,12 @@ let generateBindingsCode codesStr = "// Nu Game Engine is licensed under the Nu Game Engine Noncommercial License.\n" + "// See https://github.com/bryanedds/Nu/blob/master/License.md.\n" + "\n" + - "//*****************************************************************************************//\n" + - "// //\n" + - "// NOTE: This code is GENERATED by 'GenerateInputBindings.fsx' and then manually modified! //\n" + - "// Do NOT edit this code by hand! //\n" + - "// //\n" + - "//*****************************************************************************************//\n" + + "//*********************************************************************************************//\n" + + "// //\n" + + "// NOTE: This code is GENERATED by 'Scripts/GenerateInputBindings.fsx' as this is a one-to-one //\n" + + "// correspondence to SDL3.SDL_Scancode with cleaner names. Do NOT edit this code by hand! //\n" + + "// //\n" + + "//*********************************************************************************************//\n" + "\n" + "namespace Nu\n" + "open System\n" + @@ -97,11 +57,32 @@ let generateBindingsCode codesStr = "type KeyboardKey =\n" + codesStr -do - Directory.SetCurrentDirectory (__SOURCE_DIRECTORY__ + "/../bin/Debug") - let code = - typeof |> - enumEntries |> - enumEntriesToCode |> - generateBindingsCode - File.WriteAllText ("../../Sdl/SdlInputBindings.fs", code) \ No newline at end of file +let code = + Enum.GetValues () + |> Array.filter (function SDL_Scancode.SDL_SCANCODE_COUNT | SDL_Scancode.SDL_SCANCODE_RESERVED -> false | _ -> true) + |> Array.map (fun value -> + let name = + match value with + | SDL_Scancode.SDL_SCANCODE_0 -> "Num0" + | SDL_Scancode.SDL_SCANCODE_1 -> "Num1" + | SDL_Scancode.SDL_SCANCODE_2 -> "Num2" + | SDL_Scancode.SDL_SCANCODE_3 -> "Num3" + | SDL_Scancode.SDL_SCANCODE_4 -> "Num4" + | SDL_Scancode.SDL_SCANCODE_5 -> "Num5" + | SDL_Scancode.SDL_SCANCODE_6 -> "Num6" + | SDL_Scancode.SDL_SCANCODE_7 -> "Num7" + | SDL_Scancode.SDL_SCANCODE_8 -> "Num8" + | SDL_Scancode.SDL_SCANCODE_9 -> "Num9" + | SDL_Scancode.SDL_SCANCODE_RETURN -> "Enter" // NOTE: ImGui calls this the 'enter' key, so I choose that. + | SDL_Scancode.SDL_SCANCODE_RETURN2 -> "Enter2" + | SDL_Scancode.SDL_SCANCODE_NONUSHASH -> "NonUsHash" + | SDL_Scancode.SDL_SCANCODE_NONUSBACKSLASH -> "NonUsBackslash" + | _ -> + let name = (string value).Replace ("SDL_SCANCODE_", "") + let name = Regex.Replace (name, "LEFT|RIGHT|UP|DOWN|LOCK|SCREEN|GUI|SHIFT|CTRL|ALT|BRACE|BRACKET|PAREN|SEL|SEPARATOR|MINUS|UNIT|REQ", "_$0") + let name = Regex.Replace (name, "MEM|EQUALS|END|DBL|VERTICAL|CURRENCY|CLEAR|ALT|LOCK", "$0_") + upperCaseToPascalCase name + " | " + name + " = " + string (int value)) + |> String.concat "\n" + |> generateBindingsCode +File.WriteAllText (__SOURCE_DIRECTORY__ + "/../Sdl/SdlInputBindings.fs", code) \ No newline at end of file diff --git a/Nu/Nu/Sdl/Sdl.fs b/Nu/Nu/Sdl/Sdl.fs index b18b8d85a2..0102f9404e 100644 --- a/Nu/Nu/Sdl/Sdl.fs +++ b/Nu/Nu/Sdl/Sdl.fs @@ -9,30 +9,23 @@ open System open System.Collections.Generic open System.Numerics open System.Runtime.InteropServices -open SDL2 +open FSharp.NativeInterop +open SDL open Prime -/// A window for rendering in SDL OpenGL. -type [] SglWindow = - { SglWindow : nativeint } - -/// A window for rendering. -type [] Window = - | SglWindow of SglWindow - /// Describes the initial configuration of a window created via SDL. type SdlWindowConfig = { WindowTitle : string WindowX : int WindowY : int - WindowFlags : SDL.SDL_WindowFlags } + WindowFlags : SDL_WindowFlags } /// A default SdlWindowConfig. static member val defaultConfig = { WindowTitle = "Nu Game" - WindowX = SDL.SDL_WINDOWPOS_UNDEFINED - WindowY = SDL.SDL_WINDOWPOS_UNDEFINED - WindowFlags = SDL.SDL_WindowFlags.SDL_WINDOW_SHOWN ||| SDL.SDL_WindowFlags.SDL_WINDOW_RESIZABLE ||| SDL.SDL_WindowFlags.SDL_WINDOW_OPENGL } + WindowX = int SDL3.SDL_WINDOWPOS_UNDEFINED + WindowY = int SDL3.SDL_WINDOWPOS_UNDEFINED + WindowFlags = SDL_WindowFlags.SDL_WINDOW_RESIZABLE ||| SDL_WindowFlags.SDL_WINDOW_OPENGL } /// Describes the general configuration of SDL. type [] SdlConfig = @@ -51,13 +44,13 @@ module SdlEvents = /// like Windows from eco-hanging the application when it sees user input not getting processed in a timely /// fashion. let poll () = - let mutable polledEvent = SDL2.SDL.SDL_Event () - while SDL2.SDL.SDL_PollEvent &polledEvent <> 0 do + let mutable polledEvent = SDL_Event () + while (SDL3.SDL_PollEvent &&polledEvent : bool) do PolledEvents.Enqueue polledEvent /// Attempt to consume an SDL event. Usually only the engine should call this, but there might be cases where the /// user needs to utilize it to cancel a long-running process or something. - let tryConsume (event : SDL2.SDL.SDL_Event outref) = + let tryConsume (event : SDL_Event outref) = PolledEvents.TryDequeue &event [] @@ -66,7 +59,7 @@ module SdlDeps = /// The dependencies needed to initialize SDL. type [] SdlDeps = private - { WindowOpt : Window option + { WindowOpt : SDL_Window nativeptr option Config : SdlConfig Destroy : unit -> unit } @@ -88,53 +81,63 @@ module SdlDeps = let getConfig sdlDeps = sdlDeps.Config + /// Get the desktop display mode. + let getDesktopDisplayMode () = + let display = SDL3.SDL_GetPrimaryDisplay () + let displayMode = SDL3.SDL_GetDesktopDisplayMode display + if NativePtr.isNullPtr displayMode then + Log.error ("Failed to get desktop display mode: " + SDL3.SDL_GetError ()) + Unchecked.defaultof<_> + else NativePtr.read displayMode + /// Attempt to set the window's full screen state. let trySetWindowFullScreen fullScreen sdlDeps = match sdlDeps.WindowOpt with - | Some (SglWindow window) -> + | Some window -> // get a snapshot of whether screen was full - let (width, height) = (ref 0, ref 0) - SDL.SDL_GetWindowSize (window.SglWindow, width, height) |> ignore - let mutable displayMode = Unchecked.defaultof<_> - SDL.SDL_GetDesktopDisplayMode (0, &displayMode) |> ignore - let wasFullScreen = width.Value = displayMode.w || height.Value = displayMode.h + let mutable width, height = 0, 0 + SDL3.SDL_GetWindowSize (window, &&width, &&height) |> ignore + let displayMode = getDesktopDisplayMode () + let wasFullScreen = width = displayMode.w || height = displayMode.h // change full screen status via flags - let flags = if fullScreen then uint SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP else 0u - SDL.SDL_SetWindowFullscreen (window.SglWindow, flags) |> ignore + SDL3.SDL_SetWindowFullscreen (window, fullScreen) |> ignore // when changing from full screen, set window to windowed size and make sure its title bar is visible if wasFullScreen && not fullScreen then let windowSizeWindowed = Constants.Render.DisplayVirtualResolution * 2 - SDL.SDL_RestoreWindow window.SglWindow - SDL.SDL_SetWindowSize (window.SglWindow, windowSizeWindowed.X, windowSizeWindowed.Y) - SDL.SDL_SetWindowPosition (window.SglWindow, 100, 100) + SDL3.SDL_RestoreWindow window |> ignore + SDL3.SDL_SetWindowSize (window, windowSizeWindowed.X, windowSizeWindowed.Y) |> ignore + SDL3.SDL_SetWindowPosition (window, 100, 100) |> ignore - | _ -> () + | None -> () sdlDeps /// Attempt to initalize an SDL module. let internal attemptPerformSdlInit create destroy = let initResult = create () - let error = SDL.SDL_GetError () - if initResult = 0 + let error = SDL3.SDL_GetError () + if initResult then Right ((), destroy) else Left error /// Attempt to initalize an SDL resource. let internal tryMakeSdlResource create destroy = let resource = create () - if resource <> IntPtr.Zero - then Right (resource, destroy) - else Left ("SDL2# resource creation failed due to '" + SDL.SDL_GetError () + "'.") + if NativePtr.isNullPtr resource + then Left ("SDL3# resource creation failed due to '" + SDL3.SDL_GetError () + "'.") + else Right (resource, destroy) /// Attempt to initalize a global SDL resource. let internal tryMakeSdlGlobalResource create destroy = - let resource = create () - if resource = 0 + let resource : SDLBool = create () + if SDLBool.op_Implicit resource then Right ((), destroy) - else Left ("SDL2# global resource creation failed due to '" + SDL.SDL_GetError () + "'.") + else Left ("SDL3# global resource creation failed due to '" + SDL3.SDL_GetError () + "'.") + + type private LogOutputDelegate = + delegate of nativeint * int * SDL_LogPriority * nativeptr -> unit /// Attempt to make an SdlDeps instance. let tryMake sdlConfig accompanied (windowSize : Vector2i) = @@ -142,88 +145,76 @@ module SdlDeps = (fun () -> // setup SDL logging - SDL.SDL_LogSetOutputFunction - ((fun _ category priority message -> + SDL3.SDL_SetLogOutputFunction + (Marshal.GetFunctionPointerForDelegate(fun _ category priority message -> + let message = SDL3.PtrToStringUTF8 message match priority with - | SDL.SDL_LogPriority.SDL_LOG_PRIORITY_VERBOSE - | SDL.SDL_LogPriority.SDL_LOG_PRIORITY_DEBUG - | SDL.SDL_LogPriority.SDL_LOG_PRIORITY_INFO -> Log.info (Marshal.PtrToStringUTF8 message + " (Category " + string category + ")") - | SDL.SDL_LogPriority.SDL_LOG_PRIORITY_WARN -> Log.warn (Marshal.PtrToStringUTF8 message + " (Category " + string category + ")") - | SDL.SDL_LogPriority.SDL_LOG_PRIORITY_ERROR -> Log.error (Marshal.PtrToStringUTF8 message + " (Category " + string category + ")") - | SDL.SDL_LogPriority.SDL_LOG_PRIORITY_CRITICAL -> Log.fail (Marshal.PtrToStringUTF8 message + " (Category " + string category + ")") + | SDL_LogPriority.SDL_LOG_PRIORITY_VERBOSE + | SDL_LogPriority.SDL_LOG_PRIORITY_DEBUG + | SDL_LogPriority.SDL_LOG_PRIORITY_INFO -> Log.info (message + " (Category " + string category + ")") + | SDL_LogPriority.SDL_LOG_PRIORITY_WARN -> Log.warn (message + " (Category " + string category + ")") + | SDL_LogPriority.SDL_LOG_PRIORITY_ERROR -> Log.error (message + " (Category " + string category + ")") + | SDL_LogPriority.SDL_LOG_PRIORITY_CRITICAL -> Log.fail (message + " (Category " + string category + ")") | _ -> ()), - nativeint 0) + 0n) // attempt to initialize sdl - Log.info "Initializing SDL 2..." - SDL.SDL_SetHint ("SDL_WINDOWS_DPI_AWARENESS", "permonitorv2") |> ignore - SDL.SDL_SetHint (SDL.SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4, "1") |> ignore + Log.info "Initializing SDL 3..." + SDL3.SDL_SetHint (SDL3.SDL_HINT_WINDOWS_CLOSE_ON_ALT_F4, "0") |> ignore let initConfig = - SDL.SDL_INIT_TIMER ||| - SDL.SDL_INIT_AUDIO ||| - SDL.SDL_INIT_VIDEO ||| - SDL.SDL_INIT_JOYSTICK ||| - SDL.SDL_INIT_HAPTIC ||| - SDL.SDL_INIT_GAMECONTROLLER ||| - SDL.SDL_INIT_EVENTS - let result = SDL.SDL_Init initConfig - - // verify initialization - if result = 0 then - let mutable sdlVersion = Unchecked.defaultof<_> - SDL.SDL_GetVersion &sdlVersion - Log.info ("Initialized SDL " + string sdlVersion.major + "." + string sdlVersion.minor + "." + string sdlVersion.patch + ".") - result) - - (fun () -> SDL.SDL_Quit ()) with + SDL_InitFlags.SDL_INIT_AUDIO ||| + SDL_InitFlags.SDL_INIT_VIDEO ||| + SDL_InitFlags.SDL_INIT_JOYSTICK ||| + SDL_InitFlags.SDL_INIT_HAPTIC ||| + SDL_InitFlags.SDL_INIT_GAMEPAD ||| + SDL_InitFlags.SDL_INIT_EVENTS + SDL3.SDL_Init initConfig) + + (fun () -> SDL3.SDL_Quit ()) with | Left error -> Left error | Right ((), destroy) -> match tryMakeSdlResource (fun () -> - + // create window let windowConfig = sdlConfig.WindowConfig - SDL.SDL_GL_SetAttribute (SDL.SDL_GLattr.SDL_GL_ACCELERATED_VISUAL, 1) |> ignore - SDL.SDL_GL_SetAttribute (SDL.SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, Constants.OpenGL.VersionMajor) |> ignore - SDL.SDL_GL_SetAttribute (SDL.SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, Constants.OpenGL.VersionMinor) |> ignore - SDL.SDL_GL_SetAttribute (SDL.SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, Constants.OpenGL.Profile) |> ignore + SDL3.SDL_GL_SetAttribute (SDL_GLAttr.SDL_GL_ACCELERATED_VISUAL, 1) |> ignore + SDL3.SDL_GL_SetAttribute (SDL_GLAttr.SDL_GL_CONTEXT_MAJOR_VERSION, Constants.OpenGL.VersionMajor) |> ignore + SDL3.SDL_GL_SetAttribute (SDL_GLAttr.SDL_GL_CONTEXT_MINOR_VERSION, Constants.OpenGL.VersionMinor) |> ignore + SDL3.SDL_GL_SetAttribute (SDL_GLAttr.SDL_GL_CONTEXT_PROFILE_MASK, Constants.OpenGL.Profile) |> ignore #if DEBUG - SDL.SDL_GL_SetAttribute (SDL.SDL_GLattr.SDL_GL_CONTEXT_FLAGS, int SDL.SDL_GLcontext.SDL_GL_CONTEXT_DEBUG_FLAG) |> ignore - SDL.SDL_GL_SetAttribute (SDL.SDL_GLattr.SDL_GL_CONTEXT_FLAGS, int SDL.SDL_GLcontext.SDL_GL_CONTEXT_ROBUST_ACCESS_FLAG) |> ignore + SDL3.SDL_GL_SetAttribute (SDL_GLAttr.SDL_GL_CONTEXT_FLAGS, int SDL_GLContextFlag.SDL_GL_CONTEXT_DEBUG_FLAG) |> ignore + SDL3.SDL_GL_SetAttribute (SDL_GLAttr.SDL_GL_CONTEXT_FLAGS, int SDL_GLContextFlag.SDL_GL_CONTEXT_ROBUST_ACCESS_FLAG) |> ignore #endif - SDL.SDL_GL_SetAttribute (SDL.SDL_GLattr.SDL_GL_DOUBLEBUFFER, 1) |> ignore - SDL.SDL_GL_SetAttribute (SDL.SDL_GLattr.SDL_GL_DEPTH_SIZE, 24) |> ignore - SDL.SDL_GL_SetAttribute (SDL.SDL_GLattr.SDL_GL_STENCIL_SIZE, 8) |> ignore - let window = SDL.SDL_CreateWindow (windowConfig.WindowTitle, windowConfig.WindowX, windowConfig.WindowY, windowSize.X, windowSize.Y, windowConfig.WindowFlags) - - // set to full screen when window taking up entire screen and unaccompanied - let mutable displayMode = Unchecked.defaultof<_> - SDL.SDL_GetDesktopDisplayMode (0, &displayMode) |> ignore - if (windowSize.X = displayMode.w || windowSize.Y = displayMode.h) && not accompanied then - SDL.SDL_SetWindowFullscreen (window, uint SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP) |> ignore + SDL3.SDL_GL_SetAttribute (SDL_GLAttr.SDL_GL_DOUBLEBUFFER, 1) |> ignore + SDL3.SDL_GL_SetAttribute (SDL_GLAttr.SDL_GL_DEPTH_SIZE, 24) |> ignore + SDL3.SDL_GL_SetAttribute (SDL_GLAttr.SDL_GL_STENCIL_SIZE, 8) |> ignore + let window = SDL3.SDL_CreateWindow (windowConfig.WindowTitle, windowSize.X, windowSize.Y, windowConfig.WindowFlags) + if not (NativePtr.isNullPtr window) then + SDL3.SDL_StartTextInput window |> ignore // TODO: This would activate an IME! We need this for receiving text input events at all, but we should only show an IME when the a text input field is focused. + SDL3.SDL_SetWindowPosition (window, windowConfig.WindowX, windowConfig.WindowY) |> ignore + + // set to full screen when window taking up entire screen and unaccompanied + let mutable displayMode = getDesktopDisplayMode () + if (windowSize.X = displayMode.w || windowSize.Y = displayMode.h) && not accompanied then + SDL3.SDL_SetWindowFullscreen (window, true) |> ignore window) - (fun window -> SDL.SDL_DestroyWindow window; destroy ()) with + (fun window -> SDL3.SDL_DestroyWindow window; destroy ()) with | Left error -> Left error | Right (window, destroy) -> - match tryMakeSdlGlobalResource - (fun () -> SDL_ttf.TTF_Init ()) - (fun () -> SDL_ttf.TTF_Quit (); destroy window) with + match tryMakeSdlGlobalResource SDL3_ttf.TTF_Init (fun () -> SDL3_ttf.TTF_Quit (); destroy window) with | Left error -> Left error | Right ((), destroy) -> - match tryMakeSdlGlobalResource - (fun () -> SDL_mixer.Mix_Init (enum 0)) - (fun () -> SDL_mixer.Mix_Quit (); destroy ()) with + match tryMakeSdlGlobalResource SDL3_mixer.MIX_Init (fun () -> SDL3_mixer.MIX_Quit (); destroy ()) with | Left error -> Left error | Right ((), destroy) -> - match tryMakeSdlGlobalResource - (fun () -> SDL_mixer.Mix_OpenAudio (Constants.Audio.Frequency, SDL_mixer.MIX_DEFAULT_FORMAT, SDL_mixer.MIX_DEFAULT_CHANNELS, Constants.Audio.BufferSize)) - (fun () -> SDL_mixer.Mix_CloseAudio (); destroy ()) with - | Left error -> Left error - | Right ((), destroy) -> - GamepadState.init () - let context = SglWindow { SglWindow = window } - Right { WindowOpt = Some context; Config = sdlConfig; Destroy = destroy } + let versionToString version = + let version = version () + $"{SDL3.SDL_VERSIONNUM_MAJOR version}.{SDL3.SDL_VERSIONNUM_MINOR version}.{SDL3.SDL_VERSIONNUM_MICRO version}" + Log.info $"Initialized SDL {versionToString SDL3.SDL_GetVersion}, SDL_ttf {versionToString SDL3_ttf.TTF_Version}, SDL_mixer {versionToString SDL3_mixer.MIX_Version}, SDL_image {versionToString SDL3_image.IMG_Version}." + GamepadState.init () + Right { WindowOpt = Some window; Config = sdlConfig; Destroy = destroy } /// The dependencies needed to initialize SDL. type SdlDeps = SdlDeps.SdlDeps \ No newline at end of file diff --git a/Nu/Nu/Sdl/SdlInput.fs b/Nu/Nu/Sdl/SdlInput.fs index 5672a91eb7..676afb146d 100644 --- a/Nu/Nu/Sdl/SdlInput.fs +++ b/Nu/Nu/Sdl/SdlInput.fs @@ -7,8 +7,8 @@ namespace Nu open System open System.Numerics -open System.Runtime.InteropServices -open SDL2 +open FSharp.NativeInterop +open SDL open Prime /// Describes a mouse button. @@ -81,6 +81,7 @@ type GamepadDirection = | DirectionCentered /// Describes a gamepad button. +// TODO: The actual button and the button name should be decoupled! See https://wiki.libsdl.org/SDL3/SDL_GamepadButton#remarks type GamepadButton = | ButtonA | ButtonB @@ -95,36 +96,45 @@ type GamepadButton = [] module internal MouseState = - let mutable private MouseButtonStatePrevious = 0u - let mutable private MouseButtonStateCurrent = 0u + let mutable private MouseButtonStatePrevious : SDL_MouseButtonFlags = LanguagePrimitives.EnumOfValue 0u + let mutable private MouseButtonStateCurrent : SDL_MouseButtonFlags = LanguagePrimitives.EnumOfValue 0u let mutable internal MouseScrollStatePrevious = 0.0f let mutable internal MouseScrollStateCurrent = 0.0f /// Convert a MouseButton to SDL's representation. let internal toSdlButton mouseButton = match mouseButton with - | MouseLeft -> SDL.SDL_BUTTON_LEFT - | MouseMiddle -> SDL.SDL_BUTTON_MIDDLE - | MouseRight -> SDL.SDL_BUTTON_RIGHT - | MouseX1 -> SDL.SDL_BUTTON_X1 - | MouseX2 -> SDL.SDL_BUTTON_X2 + | MouseLeft -> SDLButton.SDL_BUTTON_LEFT + | MouseMiddle -> SDLButton.SDL_BUTTON_MIDDLE + | MouseRight -> SDLButton.SDL_BUTTON_RIGHT + | MouseX1 -> SDLButton.SDL_BUTTON_X1 + | MouseX2 -> SDLButton.SDL_BUTTON_X2 /// Convert SDL's representation of a mouse button to a MouseButton. let internal toNuButton mouseButton = match mouseButton with - | SDL.SDL_BUTTON_LEFT -> MouseLeft - | SDL.SDL_BUTTON_MIDDLE -> MouseMiddle - | SDL.SDL_BUTTON_RIGHT -> MouseRight - | SDL.SDL_BUTTON_X1 -> MouseX1 - | SDL.SDL_BUTTON_X2 -> MouseX2 + | SDLButton.SDL_BUTTON_LEFT -> MouseLeft + | SDLButton.SDL_BUTTON_MIDDLE -> MouseMiddle + | SDLButton.SDL_BUTTON_RIGHT -> MouseRight + | SDLButton.SDL_BUTTON_X1 -> MouseX1 + | SDLButton.SDL_BUTTON_X2 -> MouseX2 | _ -> failwith "Invalid SDL mouse button." + /// Convert a MouseButton to SDL's representation. + let private toSdlButtonFlags mouseButton = + match mouseButton with + | MouseLeft -> SDL_MouseButtonFlags.SDL_BUTTON_LMASK + | MouseMiddle -> SDL_MouseButtonFlags.SDL_BUTTON_MMASK + | MouseRight -> SDL_MouseButtonFlags.SDL_BUTTON_RMASK + | MouseX1 -> SDL_MouseButtonFlags.SDL_BUTTON_X1MASK + | MouseX2 -> SDL_MouseButtonFlags.SDL_BUTTON_X2MASK + /// Update the current mouse state from SDL. let internal update () = // update button state MouseButtonStatePrevious <- MouseButtonStateCurrent - let (sdlMouseButtonState, _, _) = SDL.SDL_GetMouseState () + let sdlMouseButtonState = SDL3.SDL_GetMouseState (NativePtr.nullPtr, NativePtr.nullPtr) MouseButtonStateCurrent <- sdlMouseButtonState // update scroll state @@ -132,8 +142,9 @@ module internal MouseState = /// Get the position of the mouse. let internal getPosition () = - let (_, x, y) = SDL.SDL_GetMouseState () - v2 (single x) (single y) + let mutable x, y = 0.0f, 0.0f + SDL3.SDL_GetMouseState (&&x, &&y) |> ignore + v2 x y /// Get the scroll of the mouse. let internal getScroll () = @@ -141,9 +152,8 @@ module internal MouseState = /// Check that the given mouse button is down. let internal isButtonDown mouseButton = - let sdlMouseButton = toSdlButton mouseButton - let sdlMouseButtonMask = SDL.SDL_BUTTON sdlMouseButton - MouseButtonStateCurrent &&& sdlMouseButtonMask <> 0u + let sdlMouseButton = toSdlButtonFlags mouseButton + MouseButtonStateCurrent &&& sdlMouseButton <> LanguagePrimitives.EnumOfValue 0u /// Check that the given mouse button is up. let internal isButtonUp mouseButton = @@ -151,17 +161,15 @@ module internal MouseState = /// Check that the given mouse button was just pressed. let internal isButtonPressed mouseButton = - let sdlMouseButton = toSdlButton mouseButton - let sdlMouseButtonMask = SDL.SDL_BUTTON sdlMouseButton - (MouseButtonStatePrevious &&& sdlMouseButtonMask = 0u) && - (MouseButtonStateCurrent &&& sdlMouseButtonMask <> 0u) + let sdlMouseButton = toSdlButtonFlags mouseButton + (MouseButtonStatePrevious &&& sdlMouseButton = LanguagePrimitives.EnumOfValue 0u) && + (MouseButtonStateCurrent &&& sdlMouseButton <> LanguagePrimitives.EnumOfValue 0u) /// Check that the given mouse button was just released. let internal isButtonReleased mouseButton = - let sdlMouseButton = toSdlButton mouseButton - let sdlMouseButtonMask = SDL.SDL_BUTTON sdlMouseButton - (MouseButtonStatePrevious &&& sdlMouseButtonMask <> 0u) && - (MouseButtonStateCurrent &&& sdlMouseButtonMask = 0u) + let sdlMouseButton = toSdlButtonFlags mouseButton + (MouseButtonStatePrevious &&& sdlMouseButton <> LanguagePrimitives.EnumOfValue 0u) && + (MouseButtonStateCurrent &&& sdlMouseButton = LanguagePrimitives.EnumOfValue 0u) /// Get how much the mouse has just scrolled. let internal getScrolled () = @@ -178,50 +186,55 @@ module internal MouseState = /// Exposes the ongoing state of the keyboard. [] module internal KeyboardState = - - let mutable private KeyboardStatePreviousOpt = None - let mutable private KeyboardStateCurrentOpt = None + + let mutable private keysCount = 0 + let mutable private KeyboardStatePrevious = Array.empty + let mutable private KeyboardStateCurrent = Array.empty /// Update the current keyboard state from SDL. let internal update () = - let mutable keysCount = 0 - let keyboardStatePtr = SDL.SDL_GetKeyboardState &keysCount - let keyboardState = Array.zeroCreate keysCount - Marshal.Copy(keyboardStatePtr, keyboardState, 0, keysCount) - KeyboardStatePreviousOpt <- KeyboardStateCurrentOpt - KeyboardStateCurrentOpt <- Some keyboardState + // move keyboard state current to previous + if KeyboardStatePrevious.Length <> KeyboardStateCurrent.Length + then KeyboardStatePrevious <- Array.copy KeyboardStateCurrent + else KeyboardStateCurrent.CopyTo (KeyboardStatePrevious.AsSpan ()) + + // get current keyboard state + let oldKeysCount = keysCount + let keyboardStatePtr = SDL3.SDL_GetKeyboardState &&keysCount + if oldKeysCount <> keysCount then KeyboardStateCurrent <- Array.zeroCreate keysCount + Span(NativePtr.toVoidPtr keyboardStatePtr, keysCount).CopyTo (KeyboardStateCurrent.AsSpan ()) /// Check that the given keyboard key is down. let internal isKeyDown (key : KeyboardKey) = - match KeyboardStateCurrentOpt with - | Some keyboardState -> keyboardState.[int key] = byte 1 - | None -> false + match KeyboardStateCurrent with + | [||] -> false + | keyboardState -> keyboardState.[int key] /// Check that the given keyboard key is up. let internal isKeyUp (key : KeyboardKey) = - match KeyboardStateCurrentOpt with - | Some keyboardState -> keyboardState.[int key] = byte 0 - | None -> false + match KeyboardStateCurrent with + | [||] -> false + | keyboardState -> not keyboardState.[int key] /// Check that the given keyboard key was just pressed. let internal isKeyPressed key = - match KeyboardStateCurrentOpt with - | Some keyboardState -> - keyboardState.[int key] = byte 1 && - match KeyboardStatePreviousOpt with - | Some keyboardState -> keyboardState.[int key] = byte 0 - | None -> false - | None -> false + match KeyboardStateCurrent with + | [||] -> false + | keyboardState -> + keyboardState.[int key] && + match KeyboardStatePrevious with + | [||] -> false + | keyboardState -> not keyboardState.[int key] /// Check that the given keyboard key was just released. let internal isKeyReleased key = - match KeyboardStateCurrentOpt with - | Some keyboardState -> - keyboardState.[int key] = byte 0 && - match KeyboardStatePreviousOpt with - | Some keyboardState -> keyboardState.[int key] = byte 1 - | None -> false - | None -> false + match KeyboardStateCurrent with + | [||] -> false + | keyboardState -> + not keyboardState.[int key] && + match KeyboardStatePrevious with + | [||] -> false + | keyboardState -> keyboardState.[int key] /// Check that either enter key is down. let internal isEnterDown () = @@ -245,7 +258,7 @@ module internal KeyboardState = /// Check that either ctrl key is down. let internal isCtrlDown () = - int (SDL.SDL_GetModState ()) &&& int SDL.SDL_Keymod.KMOD_CTRL <> 0 + SDL3.SDL_GetModState () &&& SDL_Keymod.SDL_KMOD_CTRL <> SDL_Keymod.SDL_KMOD_NONE /// Check that both ctrl keys are up. let internal isCtrlUp () = @@ -253,7 +266,7 @@ module internal KeyboardState = /// Check that either alt key is down. let internal isAltDown () = - int (SDL.SDL_GetModState ()) &&& int SDL.SDL_Keymod.KMOD_ALT <> 0 + SDL3.SDL_GetModState () &&& SDL_Keymod.SDL_KMOD_ALT <> SDL_Keymod.SDL_KMOD_NONE /// Check that both alt keys are up. let internal isAltUp () = @@ -261,7 +274,7 @@ module internal KeyboardState = /// Check that either shift key is down. let internal isShiftDown () = - int (SDL.SDL_GetModState ()) &&& int SDL.SDL_Keymod.KMOD_SHIFT <> 0 + SDL3.SDL_GetModState () &&& SDL_Keymod.SDL_KMOD_SHIFT <> SDL_Keymod.SDL_KMOD_NONE /// Check that both shift keys are up. let internal isShiftUp () = @@ -275,16 +288,9 @@ module GamepadState = /// Initialize gamepad state. let internal init () = - let indices = SDL.SDL_NumJoysticks () - Joysticks <- - Array.map (fun joystick -> - // NOTE: we don't have a matching call to SDL.SDL_JoystickClose, but it may not be necessary - SDL.SDL_JoystickOpen joystick) - [|0 .. indices|] - - /// Check that an SDL gamepad button is supported. - let internal isSdlButtonSupported button = - button < 8 + use joysticks = SDL3.SDL_GetJoysticks () + // NOTE: we don't have a matching call to SDL3.SDL_CloseJoystick, but it may not be necessary + Joysticks <- Array.init joysticks.Count (fun i -> SDL3.SDL_OpenJoystick joysticks.[i]) /// Get the number of open gamepad. let internal getGamepadCount () = @@ -293,74 +299,74 @@ module GamepadState = /// Convert an SDL joystick axis to a GamepadAxis. let internal toNuAxis axis = match axis with - | SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTX -> StickLeftX - | SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTY -> StickLeftY - | SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_RIGHTX -> StickRightX - | SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_RIGHTY -> StickRightY - | SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERLEFT -> TriggerLeft - | SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERRIGHT -> TriggerRight + | SDL_GamepadAxis.SDL_GAMEPAD_AXIS_LEFTX -> StickLeftX + | SDL_GamepadAxis.SDL_GAMEPAD_AXIS_LEFTY -> StickLeftY + | SDL_GamepadAxis.SDL_GAMEPAD_AXIS_RIGHTX -> StickRightX + | SDL_GamepadAxis.SDL_GAMEPAD_AXIS_RIGHTY -> StickRightY + | SDL_GamepadAxis.SDL_GAMEPAD_AXIS_LEFT_TRIGGER -> TriggerLeft + | SDL_GamepadAxis.SDL_GAMEPAD_AXIS_RIGHT_TRIGGER -> TriggerRight | _ -> failwith "Invalid SDL joystick axis." /// Convert a GamepadAxis to SDL's representation. let internal toSdlAxis axis = match axis with - | StickLeftX -> SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTX - | StickLeftY -> SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTY - | StickRightX -> SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_RIGHTX - | StickRightY -> SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_RIGHTY - | TriggerLeft -> SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERLEFT - | TriggerRight -> SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERRIGHT + | StickLeftX -> SDL_GamepadAxis.SDL_GAMEPAD_AXIS_LEFTX + | StickLeftY -> SDL_GamepadAxis.SDL_GAMEPAD_AXIS_LEFTY + | StickRightX -> SDL_GamepadAxis.SDL_GAMEPAD_AXIS_RIGHTX + | StickRightY -> SDL_GamepadAxis.SDL_GAMEPAD_AXIS_RIGHTY + | TriggerLeft -> SDL_GamepadAxis.SDL_GAMEPAD_AXIS_LEFT_TRIGGER + | TriggerRight -> SDL_GamepadAxis.SDL_GAMEPAD_AXIS_RIGHT_TRIGGER /// Convert a GamepadButton to SDL's representation. let internal toSdlButton gamepadButton = match gamepadButton with - | ButtonA -> 0 - | ButtonB -> 1 - | ButtonX -> 2 - | ButtonY -> 3 - | ButtonL -> 4 - | ButtonR -> 5 - | ButtonSelect -> 6 - | ButtonStart -> 7 - - /// Convert SDL's representation of a joystick button to a GamepadButton. - let internal toNuButton gamepadButton = + | ButtonA -> SDL_GamepadButton.SDL_GAMEPAD_BUTTON_SOUTH + | ButtonB -> SDL_GamepadButton.SDL_GAMEPAD_BUTTON_EAST + | ButtonX -> SDL_GamepadButton.SDL_GAMEPAD_BUTTON_WEST + | ButtonY -> SDL_GamepadButton.SDL_GAMEPAD_BUTTON_NORTH + | ButtonL -> SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_SHOULDER + | ButtonR -> SDL_GamepadButton.SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER + | ButtonSelect -> SDL_GamepadButton.SDL_GAMEPAD_BUTTON_BACK + | ButtonStart -> SDL_GamepadButton.SDL_GAMEPAD_BUTTON_START + + /// Try to convert SDL's representation of a joystick button to a GamepadButton. + let internal tryToNuButton gamepadButton = match gamepadButton with - | 0 -> ButtonA - | 1 -> ButtonB - | 2 -> ButtonX - | 3 -> ButtonY - | 4 -> ButtonL - | 5 -> ButtonR - | 6 -> ButtonSelect - | 7 -> ButtonStart - | _ -> failwith "Invalid SDL joystick button." + | SDL_GamepadButton.SDL_GAMEPAD_BUTTON_SOUTH -> Some ButtonA + | SDL_GamepadButton.SDL_GAMEPAD_BUTTON_EAST -> Some ButtonB + | SDL_GamepadButton.SDL_GAMEPAD_BUTTON_WEST -> Some ButtonX + | SDL_GamepadButton.SDL_GAMEPAD_BUTTON_NORTH -> Some ButtonY + | SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_SHOULDER -> Some ButtonL + | SDL_GamepadButton.SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER -> Some ButtonR + | SDL_GamepadButton.SDL_GAMEPAD_BUTTON_BACK -> Some ButtonSelect + | SDL_GamepadButton.SDL_GAMEPAD_BUTTON_START -> Some ButtonStart + | _ -> None /// Convert a GamepadDirection to SDL's representation. let internal toSdlDirection gamepadDirection = match gamepadDirection with - | DirectionUp -> SDL.SDL_HAT_UP - | DirectionUpLeft -> SDL.SDL_HAT_LEFTUP - | DirectionLeft -> SDL.SDL_HAT_LEFT - | DirectionDownLeft -> SDL.SDL_HAT_LEFTDOWN - | DirectionDown -> SDL.SDL_HAT_DOWN - | DirectionDownRight -> SDL.SDL_HAT_RIGHTDOWN - | DirectionRight -> SDL.SDL_HAT_RIGHT - | DirectionUpRight -> SDL.SDL_HAT_RIGHTUP - | DirectionCentered -> SDL.SDL_HAT_CENTERED + | DirectionUp -> SDL3.SDL_HAT_UP + | DirectionUpLeft -> SDL3.SDL_HAT_LEFTUP + | DirectionLeft -> SDL3.SDL_HAT_LEFT + | DirectionDownLeft -> SDL3.SDL_HAT_LEFTDOWN + | DirectionDown -> SDL3.SDL_HAT_DOWN + | DirectionDownRight -> SDL3.SDL_HAT_RIGHTDOWN + | DirectionRight -> SDL3.SDL_HAT_RIGHT + | DirectionUpRight -> SDL3.SDL_HAT_RIGHTUP + | DirectionCentered -> SDL3.SDL_HAT_CENTERED /// Convert SDL's representation of a hat direction to a GamepadDirection. let internal toNuDirection gamepadDirection = - match gamepadDirection with - | SDL.SDL_HAT_UP -> DirectionUp - | SDL.SDL_HAT_LEFTUP -> DirectionUpLeft - | SDL.SDL_HAT_LEFT -> DirectionLeft - | SDL.SDL_HAT_LEFTDOWN -> DirectionDownLeft - | SDL.SDL_HAT_DOWN -> DirectionDown - | SDL.SDL_HAT_RIGHTDOWN -> DirectionDownRight - | SDL.SDL_HAT_RIGHT -> DirectionRight - | SDL.SDL_HAT_RIGHTUP -> DirectionUpRight - | SDL.SDL_HAT_CENTERED -> DirectionCentered + match uint32 gamepadDirection with + | SDL3.SDL_HAT_UP -> DirectionUp + | SDL3.SDL_HAT_LEFTUP -> DirectionUpLeft + | SDL3.SDL_HAT_LEFT -> DirectionLeft + | SDL3.SDL_HAT_LEFTDOWN -> DirectionDownLeft + | SDL3.SDL_HAT_DOWN -> DirectionDown + | SDL3.SDL_HAT_RIGHTDOWN -> DirectionDownRight + | SDL3.SDL_HAT_RIGHT -> DirectionRight + | SDL3.SDL_HAT_RIGHTUP -> DirectionUpRight + | SDL3.SDL_HAT_CENTERED -> DirectionCentered | _ -> failwith "Invalid SDL hat direction." /// Convert an SDL joystick axis value to a float in the range -1.0f to 1.0f. @@ -373,8 +379,8 @@ module GamepadState = let internal getStickLeft index = match Array.tryItem index Joysticks with | Some joystick -> - let x = SDL.SDL_GameControllerGetAxis (joystick, SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTX) - let y = SDL.SDL_GameControllerGetAxis (joystick, SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTY) + let x = SDL3.SDL_GetJoystickAxis (joystick, int SDL_GamepadAxis.SDL_GAMEPAD_AXIS_LEFTX) + let y = SDL3.SDL_GetJoystickAxis (joystick, int SDL_GamepadAxis.SDL_GAMEPAD_AXIS_LEFTY) v2 (toNuAxisValue x) (toNuAxisValue y) | None -> v2Zero @@ -382,8 +388,8 @@ module GamepadState = let internal getStickRight index = match Array.tryItem index Joysticks with | Some joystick -> - let x = SDL.SDL_GameControllerGetAxis (joystick, SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_RIGHTX) - let y = SDL.SDL_GameControllerGetAxis (joystick, SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_RIGHTY) + let x = SDL3.SDL_GetJoystickAxis (joystick, int SDL_GamepadAxis.SDL_GAMEPAD_AXIS_RIGHTX) + let y = SDL3.SDL_GetJoystickAxis (joystick, int SDL_GamepadAxis.SDL_GAMEPAD_AXIS_RIGHTY) v2 (toNuAxisValue x) (toNuAxisValue y) | None -> v2Zero @@ -391,7 +397,7 @@ module GamepadState = let internal getTriggerLeft index = match Array.tryItem index Joysticks with | Some joystick -> - let value = SDL.SDL_GameControllerGetAxis (joystick, SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERLEFT) + let value = SDL3.SDL_GetJoystickAxis (joystick, int SDL_GamepadAxis.SDL_GAMEPAD_AXIS_LEFT_TRIGGER) toNuAxisValue value | None -> 0.0f @@ -399,15 +405,15 @@ module GamepadState = let internal getTriggerRight index = match Array.tryItem index Joysticks with | Some joystick -> - let value = SDL.SDL_GameControllerGetAxis (joystick, SDL.SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERRIGHT) + let value = SDL3.SDL_GetJoystickAxis (joystick, int SDL_GamepadAxis.SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) toNuAxisValue value | None -> 0.0f /// Get the given gamepad's current direction. let internal getDirection index = match Array.tryItem index Joysticks with - | Some joystick -> - let hat = SDL.SDL_JoystickGetHat (joystick, 0) + | Some gamepad -> + let hat = SDL3.SDL_GetJoystickHat (gamepad, 0) toNuDirection hat | None -> DirectionCentered @@ -415,5 +421,5 @@ module GamepadState = let internal isButtonDown index button = let sdlButton = toSdlButton button match Array.tryItem index Joysticks with - | Some joystick -> SDL.SDL_JoystickGetButton (joystick, sdlButton) = byte 1 + | Some joystick -> SDL3.SDL_GetJoystickButton (joystick, int sdlButton) | None -> false \ No newline at end of file diff --git a/Nu/Nu/Sdl/SdlInputBindings.fs b/Nu/Nu/Sdl/SdlInputBindings.fs index f123cae2b1..f3d48521ac 100644 --- a/Nu/Nu/Sdl/SdlInputBindings.fs +++ b/Nu/Nu/Sdl/SdlInputBindings.fs @@ -4,12 +4,12 @@ // Nu Game Engine is licensed under the Nu Game Engine Noncommercial License. // See https://github.com/bryanedds/Nu/blob/master/License.md. -//*****************************************************************************************// -// // -// NOTE: This code is GENERATED by 'GenerateInputBindings.fsx' and then manually modified! // -// Do NOT edit this code by hand! // -// // -//*****************************************************************************************// +//*********************************************************************************************// +// // +// NOTE: This code is GENERATED by 'Scripts/GenerateInputBindings.fsx' as this is a one-to-one // +// correspondence to SDL3.SDL_Scancode with cleaner names. Do NOT edit this code by hand! // +// // +//*********************************************************************************************// namespace Nu open System @@ -173,14 +173,14 @@ type KeyboardKey = | Out = 160 | Oper = 161 | ClearAgain = 162 - | Crsel = 163 - | Exsel = 164 + | CrSel = 163 + | ExSel = 164 | Kp00 = 176 | Kp000 = 177 | ThousandsSeparator = 178 | DecimalSeparator = 179 | CurrencyUnit = 180 - | CurrencySubunit = 181 + | CurrencySubUnit = 181 | KpLeftParen = 182 | KpRightParen = 183 | KpLeftBrace = 184 @@ -230,32 +230,36 @@ type KeyboardKey = | RAlt = 230 | RGui = 231 | Mode = 257 - | AudioNext = 258 - | AudioPrev = 259 - | AudioStop = 260 - | AudioPlay = 261 - | AudioMute = 262 - | MediaSelect = 263 - | Www = 264 - | Mail = 265 - | Calculator = 266 - | Computer = 267 - | AcSearch = 268 - | AcHome = 269 - | AcBack = 270 - | AcForward = 271 - | AcStop = 272 - | AcRefresh = 273 - | AcBookmarks = 274 - | BrightnessDown = 275 - | BrightnessUp = 276 - | DisplaySwitch = 277 - | KbdIllumToggle = 278 - | KbdIllumDown = 279 - | KbdIllumUp = 280 - | Eject = 281 - | Sleep = 282 - | App1 = 283 - | App2 = 284 - | AudioRewind = 285 - | AudioFastForward = 286 \ No newline at end of file + | Sleep = 258 + | Wake = 259 + | ChannelIncrement = 260 + | ChannelDecrement = 261 + | MediaPlay = 262 + | MediaPause = 263 + | MediaRecord = 264 + | MediaFastForward = 265 + | MediaRewind = 266 + | MediaNextTrack = 267 + | MediaPreviousTrack = 268 + | MediaStop = 269 + | MediaEject = 270 + | MediaPlayPause = 271 + | MediaSelect = 272 + | AcNew = 273 + | AcOpen = 274 + | AcClose = 275 + | AcExit = 276 + | AcSave = 277 + | AcPrint = 278 + | AcProperties = 279 + | AcSearch = 280 + | AcHome = 281 + | AcBack = 282 + | AcForward = 283 + | AcStop = 284 + | AcRefresh = 285 + | AcBookmarks = 286 + | SoftLeft = 287 + | SoftRight = 288 + | Call = 289 + | EndCall = 290 \ No newline at end of file diff --git a/Nu/Nu/Sdl/SdlInterop.fs b/Nu/Nu/Sdl/SdlInterop.fs deleted file mode 100644 index 9421b8a3c0..0000000000 --- a/Nu/Nu/Sdl/SdlInterop.fs +++ /dev/null @@ -1,16 +0,0 @@ -// Nu Game Engine. -// Required Notice: -// Copyright (C) Bryan Edds. -// Nu Game Engine is licensed under the Nu Game Engine Noncommercial License. -// See https://github.com/bryanedds/Nu/blob/master/License.md. - -namespace SDL2 -open System -open System.Runtime.InteropServices - -[] -module SDL = - - // Missing in SDL2#? https://wiki.libsdl.org/SDL2/SDL_GetDefaultCursor? - [] - extern nativeint SDL_GetDefaultCursor () \ No newline at end of file diff --git a/Nu/Nu/World/World.fs b/Nu/Nu/World/World.fs index 0822b8e1f1..c2b295e711 100644 --- a/Nu/Nu/World/World.fs +++ b/Nu/Nu/World/World.fs @@ -12,7 +12,7 @@ open System.Diagnostics.Tracing open System.Numerics open System.Reflection open System.Threading -open SDL2 +open SDL open Prime /// GC event listener. Currently just logs whenever an object larger than 85k is allocated to notify user of possible @@ -396,7 +396,7 @@ module WorldModule4 = assemblyName.Name <> "Prime" && assemblyName.Name <> "Nu" && assemblyName.Name <> "netstandard" && - assemblyName.Name <> "SDL2-CS" + not (assemblyName.Name.StartsWith "ppy.SDL3") let pluginAssembly = plugin.GetType().Assembly let pluginAssembliesReferenced = Reflection.loadReferencedAssembliesTransitively pluginAssemblyNamePredicate pluginAssembly let pluginAssemblies = Array.cons pluginAssembly pluginAssembliesReferenced @@ -446,7 +446,7 @@ module WorldModule4 = for package in initialPackages do rendererProcess.EnqueueMessage3d (LoadRenderPackage3d package) let audioPlayer = - if SDL.SDL_WasInit SDL.SDL_INIT_AUDIO <> 0u + if SDL3.SDL_WasInit SDL_InitFlags.SDL_INIT_AUDIO <> LanguagePrimitives.EnumOfValue 0u then SdlAudioPlayer.make () :> AudioPlayer else StubAudioPlayer.make () :> AudioPlayer for package in initialPackages do diff --git a/Nu/Nu/World/WorldEvents.fs b/Nu/Nu/World/WorldEvents.fs index b01d22749f..b89a2a3357 100644 --- a/Nu/Nu/World/WorldEvents.fs +++ b/Nu/Nu/World/WorldEvents.fs @@ -63,9 +63,9 @@ type GamepadButtonData = { GamepadButton : GamepadButton Down : bool } -/// The data for a text input event. +/// The data for a text input event for one character that may be represented as multiple Unicode code points for non-English languages. type TextInputData = - { TextInput : char } + { TextInput : string } /// The data for a text edit event. type TextEditData = @@ -342,19 +342,19 @@ module Events = let KeyboardKeyUpEvent = stoa "KeyboardKey/Up/Event" /// Raised when a gamepad axis is changed. - let GamepadAxisChangeEvent axis (index : int) = rtoa [|"Gamepad"; GamepadAxis.toEventName axis + string index + "Change"; "Event"|] + let GamepadAxisChangeEvent axis (index : uint) = rtoa [|"Gamepad"; GamepadAxis.toEventName axis + string index + "Change"; "Event"|] /// Raised when a gamepad direction is changed. - let GamepadDirectionChangeEvent (index : int) = rtoa [|"Gamepad"; "Direction" + string index + "Change"; "Event"|] + let GamepadDirectionChangeEvent (index : uint) = rtoa [|"Gamepad"; "Direction" + string index + "Change"; "Event"|] /// Raised when a gamepad button is pressed or released. - let GamepadButtonChangeEvent (index : int) = rtoa [|"Gamepad"; "Button" + string index + "Change"; "Event"|] + let GamepadButtonChangeEvent (index : uint) = rtoa [|"Gamepad"; "Button" + string index + "Change"; "Event"|] /// Raised when a gamepad direction is pressed. - let GamepadButtonDownEvent (index : int) = rtoa [|"Gamepad"; "Button" + string index + "Down"; "Event"|] + let GamepadButtonDownEvent (index : uint) = rtoa [|"Gamepad"; "Button" + string index + "Down"; "Event"|] /// Raised when a gamepad direction is released. - let GamepadButtonUpEvent (index : int) = rtoa [|"Gamepad"; "Button" + string index + "Up"; "Event"|] + let GamepadButtonUpEvent (index : uint) = rtoa [|"Gamepad"; "Button" + string index + "Up"; "Event"|] /// Raised when text input is received from the OS. let TextInputEvent = stoa "TextInput/Event" diff --git a/Nu/Nu/World/WorldFacets.fs b/Nu/Nu/World/WorldFacets.fs index 861c88eff2..7550d74bfd 100644 --- a/Nu/Nu/World/WorldFacets.fs +++ b/Nu/Nu/World/WorldFacets.fs @@ -71,7 +71,7 @@ type StaticSpriteFacet () = override this.Render (_, entity, world) = let mutable transform = entity.GetTransform world let staticImage = entity.GetStaticImage world - let insetOpt = match entity.GetInsetOpt world with Some inset -> ValueSome inset | None -> ValueNone + let insetOpt = entity.GetInsetOpt world |> Option.toValueOption let clipOpt = entity.GetClipOpt world |> Option.toValueOption let color = entity.GetColor world let blend = entity.GetBlend world @@ -152,7 +152,7 @@ type AnimatedSpriteFacet () = override this.Render (_, entity, world) = let mutable transform = entity.GetTransform world let animationSheet = entity.GetAnimationSheet world - let insetOpt = match getSpriteInsetOpt entity world with Some inset -> ValueSome inset | None -> ValueNone + let insetOpt = getSpriteInsetOpt entity world |> Option.toValueOption let clipOpt = entity.GetClipOpt world |> Option.toValueOption let color = entity.GetColor world let blend = entity.GetBlend world @@ -423,8 +423,8 @@ module TextFacetExtensions = member this.GetFont world : Font AssetTag = this.Get (nameof this.Font) world member this.SetFont (value : Font AssetTag) world = this.Set (nameof this.Font) value world member this.Font = lens (nameof this.Font) this this.GetFont this.SetFont - member this.GetFontSizing world : int option = this.Get (nameof this.FontSizing) world - member this.SetFontSizing (value : int option) world = this.Set (nameof this.FontSizing) value world + member this.GetFontSizing world : single option = this.Get (nameof this.FontSizing) world + member this.SetFontSizing (value : single option) world = this.Set (nameof this.FontSizing) value world member this.FontSizing = lens (nameof this.FontSizing) this this.GetFontSizing this.SetFontSizing member this.GetFontStyling world : FontStyle Set = this.Get (nameof this.FontStyling) world member this.SetFontStyling (value : FontStyle Set) world = this.Set (nameof this.FontStyling) value world @@ -869,9 +869,9 @@ type FillBarFacet () = define Entity.ColorDisabled Constants.Gui.ColorDisabledDefault define Entity.Fill 0.0f define Entity.FillInset 0.0f - define Entity.FillColor (Color (1.0f, 0.0f, 0.0f, 1.0f)) + define Entity.FillColor Color.Red define Entity.FillImage Assets.Default.White - define Entity.BorderColor (Color (1.0f, 1.0f, 1.0f, 1.0f)) + define Entity.BorderColor Color.White define Entity.BorderImage Assets.Default.Border] override this.Render (_, entity, world) = diff --git a/Nu/Nu/World/WorldInput.fs b/Nu/Nu/World/WorldInput.fs index bffcc63200..755ae62345 100644 --- a/Nu/Nu/World/WorldInput.fs +++ b/Nu/Nu/World/WorldInput.fs @@ -201,11 +201,6 @@ module WorldInputModule = ignore (world : World) KeyboardState.isShiftUp () - /// Check that an SDL gamepad button is supported. - static member isSdlButtonSupported button world = - ignore (world : World) - GamepadState.isSdlButtonSupported button - /// Get the number of open gamepad. static member getGamepadCount world = ignore (world : World) @@ -226,10 +221,10 @@ module WorldInputModule = ignore (world : World) GamepadState.toSdlButton gamepadButton - /// Convert SDL's representation of a joystick button to a GamepadButton. - static member toNuButton gamepadButton world = + /// Try to convert SDL's representation of a joystick button to a GamepadButton. + static member tryToNuButton gamepadButton world = ignore (world : World) - GamepadState.toNuButton gamepadButton + GamepadState.tryToNuButton gamepadButton /// Convert a GamepadDirection to SDL's representation. static member toSdlDirection gamepadDirection world = diff --git a/Nu/Nu/World/WorldModule2.fs b/Nu/Nu/World/WorldModule2.fs index b6e1339b79..1cc1c94f34 100644 --- a/Nu/Nu/World/WorldModule2.fs +++ b/Nu/Nu/World/WorldModule2.fs @@ -11,7 +11,7 @@ open System.Diagnostics open System.IO open System.Numerics open System.Threading -open SDL2 +open SDL open ImGuiNET open Prime @@ -1097,55 +1097,54 @@ module WorldModule2 = | KeyboardKey.LShift -> [ImGuiKey.LeftShift; ImGuiKey.ModShift] | KeyboardKey.RShift -> [ImGuiKey.RightShift; ImGuiKey.ModShift] | _ -> - if int keyboardKey >= int KeyboardKey.Num1 && int keyboardKey <= int KeyboardKey.Num9 then int ImGuiKey._1 + (int keyboardKey - int KeyboardKey.Num1) |> enum |> List.singleton - elif int keyboardKey >= int KeyboardKey.A && int keyboardKey <= int KeyboardKey.Z then int ImGuiKey.A + (int keyboardKey - int KeyboardKey.A) |> enum |> List.singleton - elif int keyboardKey >= int KeyboardKey.F1 && int keyboardKey <= int KeyboardKey.F12 then int ImGuiKey.F1 + (int keyboardKey - int KeyboardKey.F1) |> enum |> List.singleton + if keyboardKey >= KeyboardKey.Num1 && keyboardKey <= KeyboardKey.Num9 then ImGuiKey._1 + (keyboardKey - KeyboardKey.Num1 |> LanguagePrimitives.EnumToValue |> LanguagePrimitives.EnumOfValue) |> List.singleton + elif keyboardKey >= KeyboardKey.A && keyboardKey <= KeyboardKey.Z then ImGuiKey.A + (keyboardKey - KeyboardKey.A |> LanguagePrimitives.EnumToValue |> LanguagePrimitives.EnumOfValue) |> List.singleton + elif keyboardKey >= KeyboardKey.F1 && keyboardKey <= KeyboardKey.F12 then ImGuiKey.F1 + (keyboardKey - KeyboardKey.F1 |> LanguagePrimitives.EnumToValue |> LanguagePrimitives.EnumOfValue) |> List.singleton else [] - static member private processInput2 (evt : SDL.SDL_Event) (world : World) = - match evt.``type`` with - | SDL.SDL_EventType.SDL_QUIT -> + static member private processInput2 (evt : SDL_Event) (world : World) = + match evt.Type with + | SDL_EventType.SDL_EVENT_QUIT -> if world.Accompanied then let eventTrace = EventTrace.debug "World" "processInput2" "ExitRequest" EventTrace.empty World.publishPlus () Nu.Game.Handle.ExitRequestEvent eventTrace Nu.Game.Handle true true world - | SDL.SDL_EventType.SDL_WINDOWEVENT -> - if evt.window.windowEvent = SDL.SDL_WindowEventID.SDL_WINDOWEVENT_SIZE_CHANGED then - - // ensure window size is a factor of display virtual resolution, going to full screen otherwise - let windowSize = World.getWindowSize world - let windowScalar = - max (single windowSize.X / single Constants.Render.DisplayVirtualResolution.X |> ceil |> int |> max 1) - (single windowSize.Y / single Constants.Render.DisplayVirtualResolution.Y |> ceil |> int |> max 1) - let windowSize' = windowScalar * Constants.Render.DisplayVirtualResolution - World.trySetWindowSize windowSize' world - let windowSize'' = World.getWindowSize world - if windowSize''.X < windowSize'.X || windowSize''.Y < windowSize'.Y then - World.trySetWindowFullScreen true world - - // synchronize display virtual scalar - let windowSize'' = World.getWindowSize world - let xScalar = windowSize''.X / Constants.Render.DisplayVirtualResolution.X - let yScalar = windowSize''.Y / Constants.Render.DisplayVirtualResolution.Y - Globals.Render.DisplayScalar <- min xScalar yScalar - - // synchronize view ports - World.synchronizeViewports world - - | SDL.SDL_EventType.SDL_MOUSEMOTION -> + | SDL_EventType.SDL_EVENT_WINDOW_RESIZED -> + + // ensure window size is a factor of display virtual resolution, going to full screen otherwise + let windowSize = World.getWindowSize world + let windowScalar = + max (single windowSize.X / single Constants.Render.DisplayVirtualResolution.X |> ceil |> int |> max 1) + (single windowSize.Y / single Constants.Render.DisplayVirtualResolution.Y |> ceil |> int |> max 1) + let windowSize' = windowScalar * Constants.Render.DisplayVirtualResolution + World.trySetWindowSize windowSize' world + let windowSize'' = World.getWindowSize world + if windowSize''.X < windowSize'.X || windowSize''.Y < windowSize'.Y then + World.trySetWindowFullScreen true world + + // synchronize display virtual scalar + let windowSize'' = World.getWindowSize world + let xScalar = windowSize''.X / Constants.Render.DisplayVirtualResolution.X + let yScalar = windowSize''.Y / Constants.Render.DisplayVirtualResolution.Y + Globals.Render.DisplayScalar <- min xScalar yScalar + + // synchronize view ports + World.synchronizeViewports world + + | SDL_EventType.SDL_EVENT_MOUSE_MOTION -> let io = ImGui.GetIO () let boundsMin = world.WindowViewport.Bounds.Min - io.AddMousePosEvent (single (evt.button.x - boundsMin.X), single (evt.button.y - boundsMin.Y)) + io.AddMousePosEvent (evt.button.x - single boundsMin.X, evt.button.y - single boundsMin.Y) let mousePosition = v2 (single evt.button.x) (single evt.button.y) if World.isMouseButtonDown MouseLeft world then let eventTrace = EventTrace.debug "World" "processInput2" "MouseDrag" EventTrace.empty World.publishPlus { MouseMoveData.Position = mousePosition } Nu.Game.Handle.MouseDragEvent eventTrace Nu.Game.Handle true true world let eventTrace = EventTrace.debug "World" "processInput2" "MouseMove" EventTrace.empty World.publishPlus { MouseMoveData.Position = mousePosition } Nu.Game.Handle.MouseMoveEvent eventTrace Nu.Game.Handle true true world - | SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN -> + | SDL_EventType.SDL_EVENT_MOUSE_BUTTON_DOWN -> let io = ImGui.GetIO () - let mouseButton = World.toNuMouseButton (uint32 evt.button.button) + let mouseButton = World.toNuMouseButton evt.button.Button io.AddMouseButtonEvent (World.toImGuiMouseButton mouseButton, true) - if not (io.WantCaptureMouseGlobal) then + if not io.WantCaptureMouseGlobal then let mousePosition = World.getMousePosition world let mouseButtonDownEvent = stoa ("Mouse/" + MouseButton.toEventName mouseButton + "/Down/Event/" + Constants.Engine.GameName) let mouseButtonChangeEvent = stoa ("Mouse/" + MouseButton.toEventName mouseButton + "/Change/Event/" + Constants.Engine.GameName) @@ -1154,13 +1153,12 @@ module WorldModule2 = World.publishPlus eventData mouseButtonDownEvent eventTrace Nu.Game.Handle true true world let eventTrace = EventTrace.debug "World" "processInput2" "MouseButtonChange" EventTrace.empty World.publishPlus eventData mouseButtonChangeEvent eventTrace Nu.Game.Handle true true world - | SDL.SDL_EventType.SDL_MOUSEBUTTONUP -> + | SDL_EventType.SDL_EVENT_MOUSE_BUTTON_UP -> let io = ImGui.GetIO () - let mouseButton = World.toNuMouseButton (uint32 evt.button.button) + let mouseButton = World.toNuMouseButton evt.button.Button io.AddMouseButtonEvent (World.toImGuiMouseButton mouseButton, false) - if not (io.WantCaptureMouseGlobal) then + if not io.WantCaptureMouseGlobal then let mousePosition = World.getMousePosition world - let mouseButton = World.toNuMouseButton (uint32 evt.button.button) let mouseButtonUpEvent = stoa ("Mouse/" + MouseButton.toEventName mouseButton + "/Up/Event/" + Constants.Engine.GameName) let mouseButtonChangeEvent = stoa ("Mouse/" + MouseButton.toEventName mouseButton + "/Change/Event/" + Constants.Engine.GameName) let eventData = { Position = mousePosition; Button = mouseButton; Down = false } @@ -1168,82 +1166,84 @@ module WorldModule2 = World.publishPlus eventData mouseButtonUpEvent eventTrace Nu.Game.Handle true true world let eventTrace = EventTrace.debug "World" "processInput2" "MouseButtonChange" EventTrace.empty World.publishPlus eventData mouseButtonChangeEvent eventTrace Nu.Game.Handle true true world - | SDL.SDL_EventType.SDL_MOUSEWHEEL -> + | SDL_EventType.SDL_EVENT_MOUSE_WHEEL -> let imGui = World.getImGui world - if evt.wheel.preciseY <> 0.0f then - let flipped = evt.wheel.direction = uint SDL.SDL_MouseWheelDirection.SDL_MOUSEWHEEL_FLIPPED - let travel = evt.wheel.preciseY * if flipped then -1.0f else 1.0f + if evt.wheel.y <> 0.0f then + let flipped = evt.wheel.direction = SDL_MouseWheelDirection.SDL_MOUSEWHEEL_FLIPPED + let travel = evt.wheel.y * if flipped then -1.0f else 1.0f MouseState.MouseScrollStateCurrent <- MouseState.MouseScrollStateCurrent + travel imGui.HandleMouseScrollChange travel let eventData = { Travel = travel } let eventTrace = EventTrace.debug "World" "processInput2" "MouseScroll" EventTrace.empty World.publishPlus eventData Nu.Game.Handle.MouseScrollEvent eventTrace Nu.Game.Handle true true world - | SDL.SDL_EventType.SDL_TEXTINPUT -> + | SDL_EventType.SDL_EVENT_TEXT_INPUT -> let io = ImGui.GetIO () let imGui = World.getImGui world - let textInput = char evt.text.text.FixedElementField - imGui.HandleKeyChar textInput - if not (io.WantCaptureKeyboardGlobal) then + let textInput = evt.text.GetText () + imGui.HandleTextInput textInput + if not io.WantCaptureKeyboardGlobal then let eventData = { TextInput = textInput } let eventTrace = EventTrace.debug "World" "processInput2" "TextInput" EventTrace.empty World.publishPlus eventData Nu.Game.Handle.TextInputEvent eventTrace Nu.Game.Handle true true world - | SDL.SDL_EventType.SDL_KEYDOWN -> + | SDL_EventType.SDL_EVENT_KEY_DOWN -> let io = ImGui.GetIO () - let keyboard = evt.key - let key = keyboard.keysym - let keyboardKey = key.scancode |> int |> enum + let key = evt.key + let keyboardKey = key.scancode |> LanguagePrimitives.EnumToValue |> LanguagePrimitives.EnumOfValue for imGuiKey in World.toImGuiKeys keyboardKey do io.AddKeyEvent (imGuiKey, true) if not (io.WantCaptureKeyboardGlobal) then - let eventData = { KeyboardKey = keyboardKey; Repeated = keyboard.repeat <> byte 0; Down = true } + let eventData = { KeyboardKey = keyboardKey; Repeated = key.repeat; Down = true } let eventTrace = EventTrace.debug "World" "processInput2" "KeyboardKeyDown" EventTrace.empty World.publishPlus eventData Nu.Game.Handle.KeyboardKeyDownEvent eventTrace Nu.Game.Handle true true world let eventTrace = EventTrace.debug "World" "processInput2" "KeyboardKeyChange" EventTrace.empty World.publishPlus eventData Nu.Game.Handle.KeyboardKeyChangeEvent eventTrace Nu.Game.Handle true true world - | SDL.SDL_EventType.SDL_KEYUP -> + | SDL_EventType.SDL_EVENT_KEY_UP -> let io = ImGui.GetIO () - let keyboard = evt.key - let key = keyboard.keysym - let keyboardKey = key.scancode |> int |> enum + let key = evt.key + let keyboardKey = key.scancode |> LanguagePrimitives.EnumToValue |> LanguagePrimitives.EnumOfValue for imGuiKey in World.toImGuiKeys keyboardKey do io.AddKeyEvent (imGuiKey, false) if not (io.WantCaptureKeyboardGlobal) then - let eventData = { KeyboardKey = key.scancode |> int |> enum; Repeated = keyboard.repeat <> byte 0; Down = false } + let eventData = { KeyboardKey = keyboardKey; Repeated = key.repeat; Down = false } let eventTrace = EventTrace.debug "World" "processInput2" "KeyboardKeyUp" EventTrace.empty World.publishPlus eventData Nu.Game.Handle.KeyboardKeyUpEvent eventTrace Nu.Game.Handle true true world let eventTrace = EventTrace.debug "World" "processInput2" "KeyboardKeyChange" EventTrace.empty World.publishPlus eventData Nu.Game.Handle.KeyboardKeyChangeEvent eventTrace Nu.Game.Handle true true world - | SDL.SDL_EventType.SDL_JOYAXISMOTION -> - let index = evt.jaxis.which - let axis = evt.jaxis.axis |> int |> enum - let value = evt.jaxis.axisValue + | SDL_EventType.SDL_EVENT_JOYSTICK_AXIS_MOTION -> + let index = evt.jaxis.which |> LanguagePrimitives.EnumToValue + let axis = evt.jaxis.axis |> int |> enum + let value = evt.jaxis.value let eventData = { GamepadAxis = GamepadState.toNuAxisValue value } let eventTrace = EventTrace.debug "World" "processInput2" "GamepadAxisChange" EventTrace.empty World.publishPlus eventData (Nu.Game.Handle.GamepadAxisChangeEvent (GamepadState.toNuAxis axis) index) eventTrace Nu.Game.Handle true true world - | SDL.SDL_EventType.SDL_JOYHATMOTION -> - let index = evt.jhat.which - let direction = evt.jhat.hatValue + | SDL_EventType.SDL_EVENT_JOYSTICK_HAT_MOTION -> + let index = evt.jhat.which |> LanguagePrimitives.EnumToValue + let direction = evt.jhat.value let eventData = { GamepadDirection = GamepadState.toNuDirection direction } let eventTrace = EventTrace.debug "World" "processInput2" "GamepadDirectionChange" EventTrace.empty World.publishPlus eventData (Nu.Game.Handle.GamepadDirectionChangeEvent index) eventTrace Nu.Game.Handle true true world - | SDL.SDL_EventType.SDL_JOYBUTTONDOWN -> - let index = evt.jbutton.which - let button = int evt.jbutton.button - if GamepadState.isSdlButtonSupported button then - let eventData = { GamepadButton = GamepadState.toNuButton button; Down = true } + | SDL_EventType.SDL_EVENT_JOYSTICK_BUTTON_DOWN -> + let index = evt.jbutton.which |> LanguagePrimitives.EnumToValue + let button = evt.jbutton.button |> int |> enum + match GamepadState.tryToNuButton button with + | Some button -> + let eventData = { GamepadButton = button; Down = true } let eventTrace = EventTrace.debug "World" "processInput2" "GamepadButtonDown" EventTrace.empty World.publishPlus eventData (Nu.Game.Handle.GamepadButtonDownEvent index) eventTrace Nu.Game.Handle true true world let eventTrace = EventTrace.debug "World" "processInput2" "GamepadButtonChange" EventTrace.empty World.publishPlus eventData (Nu.Game.Handle.GamepadButtonChangeEvent index) eventTrace Nu.Game.Handle true true world - | SDL.SDL_EventType.SDL_JOYBUTTONUP -> - let index = evt.jbutton.which - let button = int evt.jbutton.button - if GamepadState.isSdlButtonSupported button then - let eventData = { GamepadButton = GamepadState.toNuButton button; Down = true } + | None -> () + | SDL_EventType.SDL_EVENT_JOYSTICK_BUTTON_UP -> + let index = evt.jbutton.which |> LanguagePrimitives.EnumToValue + let button = evt.jbutton.button |> int |> enum + match GamepadState.tryToNuButton button with + | Some button -> + let eventData = { GamepadButton = button; Down = true } let eventTrace = EventTrace.debug "World" "processInput2" "GamepadButtonUp" EventTrace.empty World.publishPlus eventData (Nu.Game.Handle.GamepadButtonUpEvent index) eventTrace Nu.Game.Handle true true world let eventTrace = EventTrace.debug "World" "processInput2" "GamepadButtonChange" EventTrace.empty World.publishPlus eventData (Nu.Game.Handle.GamepadButtonChangeEvent index) eventTrace Nu.Game.Handle true true world + | None -> () | _ -> () static member private processIntegrationMessage integrationMessage (world : World) = @@ -1894,7 +1894,7 @@ module WorldModule2 = WorldModuleInternal2.HashSet3dShadowCached.Clear () static member private processInput (world : World) = - if SDL.SDL_WasInit SDL.SDL_INIT_TIMER <> 0u then + if SDL3.SDL_WasInit SDL_InitFlags.SDL_INIT_EVENTS <> LanguagePrimitives.EnumOfValue 0u then SdlEvents.poll () MouseState.update () KeyboardState.update () @@ -2034,7 +2034,7 @@ module WorldModule2 = // process audio world.Timers.AudioTimer.Restart () - if SDL.SDL_WasInit SDL.SDL_INIT_AUDIO <> 0u then + if SDL3.SDL_WasInit SDL_InitFlags.SDL_INIT_AUDIO <> LanguagePrimitives.EnumOfValue 0u then let audioPlayer = World.getAudioPlayer world let audioMessages = audioPlayer.PopMessages () audioPlayer.Play audioMessages diff --git a/Nu/Nu/World/WorldPrelude.fs b/Nu/Nu/World/WorldPrelude.fs index c812e44444..7e408d6e2a 100644 --- a/Nu/Nu/World/WorldPrelude.fs +++ b/Nu/Nu/World/WorldPrelude.fs @@ -9,7 +9,7 @@ open System open System.Collections.Generic open System.Diagnostics open System.Numerics -open SDL2 +open SDL open TiledSharp open DotRecast.Core.Collections open DotRecast.Core.Numerics @@ -651,23 +651,22 @@ module internal AmbientState = let internal tryGetWindowFlags state = match Option.flatten (Option.map SdlDeps.getWindowOpt state.SdlDepsOpt) with - | Some (SglWindow window) -> Some (SDL.SDL_GetWindowFlags window.SglWindow) + | Some window -> Some (SDL3.SDL_GetWindowFlags window) | _ -> None let internal tryGetWindowMinimized state = - Option.map (fun flags -> flags &&& uint32 SDL.SDL_WindowFlags.SDL_WINDOW_MINIMIZED <> 0u) (tryGetWindowFlags state) + Option.map (fun flags -> flags &&& SDL_WindowFlags.SDL_WINDOW_MINIMIZED <> LanguagePrimitives.EnumOfValue 0UL) (tryGetWindowFlags state) let internal tryGetWindowMaximized state = - Option.map (fun flags -> flags &&& uint32 SDL.SDL_WindowFlags.SDL_WINDOW_MAXIMIZED <> 0u) (tryGetWindowFlags state) + Option.map (fun flags -> flags &&& SDL_WindowFlags.SDL_WINDOW_MAXIMIZED <> LanguagePrimitives.EnumOfValue 0UL) (tryGetWindowFlags state) let internal tryGetWindowFullScreen state = match Option.flatten (Option.map SdlDeps.getWindowOpt state.SdlDepsOpt) with - | Some (SglWindow window) -> - let (width, height) = (ref 0, ref 0) - SDL.SDL_GetWindowSize (window.SglWindow, width, height) |> ignore - let mutable displayMode = Unchecked.defaultof<_> - SDL.SDL_GetDesktopDisplayMode (0, &displayMode) |> ignore - Some (width.Value = displayMode.w || height.Value = displayMode.h) + | Some window -> + let mutable width, height = 0, 0 + SDL3.SDL_GetWindowSize (window, &&width, &&height) |> ignore + let displayMode = SdlDeps.getDesktopDisplayMode () + Some (width = displayMode.w || height = displayMode.h) | _ -> None let internal trySetWindowFullScreen fullScreen state = @@ -682,28 +681,28 @@ module internal AmbientState = let internal tryGetWindowPosition state = match Option.flatten (Option.map SdlDeps.getWindowOpt state.SdlDepsOpt) with - | Some (SglWindow window) -> - let (x, y) = (ref 0, ref 0) - SDL.SDL_GetWindowPosition (window.SglWindow, x, y) |> ignore - Some (v2i x.Value y.Value) + | Some window -> + let mutable x, y = 0, 0 + SDL3.SDL_GetWindowPosition (window, &&x, &&y) |> ignore + Some (v2i x y) | _ -> None let internal trySetWindowPosition (position : Vector2i) state = match Option.flatten (Option.map SdlDeps.getWindowOpt state.SdlDepsOpt) with - | Some (SglWindow window) -> SDL.SDL_SetWindowPosition (window.SglWindow, position.X, position.Y) |> ignore + | Some window -> SDL3.SDL_SetWindowPosition (window, position.X, position.Y) |> ignore | None -> () let internal tryGetWindowSize state = match Option.flatten (Option.map SdlDeps.getWindowOpt state.SdlDepsOpt) with - | Some (SglWindow window) -> - let (width, height) = (ref 0, ref 0) - SDL.SDL_GetWindowSize (window.SglWindow, width, height) |> ignore - Some (v2i width.Value height.Value) + | Some window -> + let mutable width, height = 0, 0 + SDL3.SDL_GetWindowSize (window, &&width, &&height) |> ignore + Some (v2i width height) | _ -> None let internal trySetWindowSize (size : Vector2i) state = match Option.flatten (Option.map SdlDeps.getWindowOpt state.SdlDepsOpt) with - | Some (SglWindow window) -> SDL.SDL_SetWindowSize (window.SglWindow, size.X, size.Y) |> ignore + | Some window -> SDL3.SDL_SetWindowSize (window, size.X, size.Y) |> ignore | None -> () let internal getSymbolicsBy by state = diff --git a/Nu/Nu/libFLAC-8.dll b/Nu/Nu/libFLAC-8.dll deleted file mode 100644 index 71f2e19d9c..0000000000 Binary files a/Nu/Nu/libFLAC-8.dll and /dev/null differ diff --git a/Nu/Nu/libfreetype-6.dll b/Nu/Nu/libfreetype-6.dll deleted file mode 100644 index 16ef77711f..0000000000 Binary files a/Nu/Nu/libfreetype-6.dll and /dev/null differ diff --git a/Nu/Nu/libjpeg-9.dll b/Nu/Nu/libjpeg-9.dll deleted file mode 100644 index 9a05528eff..0000000000 Binary files a/Nu/Nu/libjpeg-9.dll and /dev/null differ diff --git a/Nu/Nu/libmodplug-1.dll b/Nu/Nu/libmodplug-1.dll deleted file mode 100644 index 7c0512674e..0000000000 Binary files a/Nu/Nu/libmodplug-1.dll and /dev/null differ diff --git a/Nu/Nu/libmpg123-0.dll b/Nu/Nu/libmpg123-0.dll deleted file mode 100644 index c7809b163f..0000000000 Binary files a/Nu/Nu/libmpg123-0.dll and /dev/null differ diff --git a/Nu/Nu/libogg-0.dll b/Nu/Nu/libogg-0.dll deleted file mode 100644 index 5ec1f1e617..0000000000 Binary files a/Nu/Nu/libogg-0.dll and /dev/null differ diff --git a/Nu/Nu/libopus-0.dll b/Nu/Nu/libopus-0.dll deleted file mode 100644 index 5991eb7e51..0000000000 Binary files a/Nu/Nu/libopus-0.dll and /dev/null differ diff --git a/Nu/Nu/libopusfile-0.dll b/Nu/Nu/libopusfile-0.dll deleted file mode 100644 index 8a5134c2e6..0000000000 Binary files a/Nu/Nu/libopusfile-0.dll and /dev/null differ diff --git a/Nu/Nu/libpng16-16.dll b/Nu/Nu/libpng16-16.dll deleted file mode 100644 index 709f724459..0000000000 Binary files a/Nu/Nu/libpng16-16.dll and /dev/null differ diff --git a/Nu/Nu/libtiff-5.dll b/Nu/Nu/libtiff-5.dll deleted file mode 100644 index 6ef56deaed..0000000000 Binary files a/Nu/Nu/libtiff-5.dll and /dev/null differ diff --git a/Nu/Nu/libwebp-7.dll b/Nu/Nu/libwebp-7.dll deleted file mode 100644 index fad57b229e..0000000000 Binary files a/Nu/Nu/libwebp-7.dll and /dev/null differ diff --git a/Projects/Blaze Vector ImSim/Blaze Vector ImSim.fsproj b/Projects/Blaze Vector ImSim/Blaze Vector ImSim.fsproj index 25e3ff171e..e0a8c438f6 100644 --- a/Projects/Blaze Vector ImSim/Blaze Vector ImSim.fsproj +++ b/Projects/Blaze Vector ImSim/Blaze Vector ImSim.fsproj @@ -74,9 +74,6 @@ ..\..\Nu\Nu.Dependencies\OpenGL.NET\lib\netcoreapp2.2\OpenGL.Net.dll - - ..\..\Nu\Nu.Dependencies\SDL2-CS\netstandard2.0\SDL2-CS.dll - ..\..\Nu\Nu.Dependencies\TiledSharp\lib\netstandard2.0\TiledSharp.dll diff --git a/Projects/Blaze Vector Mmcc/Blaze Vector Mmcc.fsproj b/Projects/Blaze Vector Mmcc/Blaze Vector Mmcc.fsproj index 87f56656c1..701b90c543 100644 --- a/Projects/Blaze Vector Mmcc/Blaze Vector Mmcc.fsproj +++ b/Projects/Blaze Vector Mmcc/Blaze Vector Mmcc.fsproj @@ -74,9 +74,6 @@ ..\..\Nu\Nu.Dependencies\OpenGL.NET\lib\netcoreapp2.2\OpenGL.Net.dll - - ..\..\Nu\Nu.Dependencies\SDL2-CS\netstandard2.0\SDL2-CS.dll - ..\..\Nu\Nu.Dependencies\TiledSharp\lib\netstandard2.0\TiledSharp.dll diff --git a/Projects/Breakout ImSim/Breakout ImSim.fsproj b/Projects/Breakout ImSim/Breakout ImSim.fsproj index 7fd5be608b..af148373a4 100644 --- a/Projects/Breakout ImSim/Breakout ImSim.fsproj +++ b/Projects/Breakout ImSim/Breakout ImSim.fsproj @@ -69,9 +69,6 @@ ..\..\Nu\Nu.Dependencies\OpenGL.NET\lib\netcoreapp2.2\OpenGL.Net.dll - - ..\..\Nu\Nu.Dependencies\SDL2-CS\netstandard2.0\SDL2-CS.dll - ..\..\Nu\Nu.Dependencies\TiledSharp\lib\netstandard2.0\TiledSharp.dll diff --git a/Projects/Breakout Mmcc/Breakout Mmcc.fsproj b/Projects/Breakout Mmcc/Breakout Mmcc.fsproj index a3fe76a148..4c0d1d76bd 100644 --- a/Projects/Breakout Mmcc/Breakout Mmcc.fsproj +++ b/Projects/Breakout Mmcc/Breakout Mmcc.fsproj @@ -70,9 +70,6 @@ ..\..\Nu\Nu.Dependencies\OpenGL.NET\lib\netcoreapp2.2\OpenGL.Net.dll - - ..\..\Nu\Nu.Dependencies\SDL2-CS\netstandard2.0\SDL2-CS.dll - ..\..\Nu\Nu.Dependencies\TiledSharp\lib\netstandard2.0\TiledSharp.dll diff --git a/Projects/Jump Box/Jump Box.fsproj b/Projects/Jump Box/Jump Box.fsproj index 40928151e2..772e5a505e 100644 --- a/Projects/Jump Box/Jump Box.fsproj +++ b/Projects/Jump Box/Jump Box.fsproj @@ -66,9 +66,6 @@ ..\..\Nu\Nu.Dependencies\OpenGL.NET\lib\netcoreapp2.2\OpenGL.Net.dll - - ..\..\Nu\Nu.Dependencies\SDL2-CS\netstandard2.0\SDL2-CS.dll - ..\..\Nu\Nu.Dependencies\TiledSharp\lib\netstandard2.0\TiledSharp.dll diff --git a/Projects/Metrics/Metrics.fsproj b/Projects/Metrics/Metrics.fsproj index 3accf14b25..fb00184cc3 100644 --- a/Projects/Metrics/Metrics.fsproj +++ b/Projects/Metrics/Metrics.fsproj @@ -64,9 +64,6 @@ ..\..\Nu\Nu.Dependencies\OpenGL.NET\lib\netcoreapp2.2\OpenGL.Net.dll - - ..\..\Nu\Nu.Dependencies\SDL2-CS\netstandard2.0\SDL2-CS.dll - ..\..\Nu\Nu.Dependencies\TiledSharp\lib\netstandard2.0\TiledSharp.dll diff --git a/Projects/Nelmish/Nelmish.fsproj b/Projects/Nelmish/Nelmish.fsproj index 82471b83ed..88cd3b5836 100644 --- a/Projects/Nelmish/Nelmish.fsproj +++ b/Projects/Nelmish/Nelmish.fsproj @@ -66,9 +66,6 @@ ..\..\Nu\Nu.Dependencies\OpenGL.NET\lib\netcoreapp2.2\OpenGL.Net.dll - - ..\..\Nu\Nu.Dependencies\SDL2-CS\netstandard2.0\SDL2-CS.dll - ..\..\Nu\Nu.Dependencies\TiledSharp\lib\netstandard2.0\TiledSharp.dll diff --git a/Projects/Sand Box 2d/FluidSim.fs b/Projects/Sand Box 2d/FluidSim.fs index 8077e55b17..1576031e55 100644 --- a/Projects/Sand Box 2d/FluidSim.fs +++ b/Projects/Sand Box 2d/FluidSim.fs @@ -176,7 +176,7 @@ type FluidSimDispatcher () = [Entity.Position .= v3 255f 80f 0f Entity.Text @= $"Particle Sprite: {(fluidEmitter.GetStaticImage world).AssetName}" Entity.Elevation .= 1f - Entity.FontSizing .= Some 8] world then + Entity.FontSizing .= Some 8.f] world then if fluidEmitter.GetStaticImage world = Assets.Default.Ball then // in Paint.NET (canvas size = 50 x 50), use the Brush (size = 50, hardness = 50%, fill = solid color #0094FF) // and click the center once, to generate this Particle image. @@ -199,7 +199,7 @@ type FluidSimDispatcher () = [Entity.Position .= v3 255f 50f 0f Entity.Text @= $"Viscosity: {fluidEmitter.GetViscocity world}" Entity.Elevation .= 1f - Entity.FontSizing .= Some 12] world then + Entity.FontSizing .= Some 12.f] world then fluidEmitter.Viscocity.Map (function | 0.004f -> 0.01f @@ -216,7 +216,7 @@ type FluidSimDispatcher () = [Entity.Position .= v3 255f 20f 0f Entity.Text @= $"Linear Damping: {fluidEmitter.GetLinearDamping world}" Entity.Elevation .= 1f - Entity.FontSizing .= Some 11] world then + Entity.FontSizing .= Some 11.f] world then fluidEmitter.LinearDamping.Map (function | 0f -> 0.2f @@ -232,7 +232,7 @@ type FluidSimDispatcher () = [Entity.Position .= v3 255f -10f 0f Entity.Text @= $"Particle Radius: {fluidEmitter.GetFluidParticleRadius world}" Entity.Elevation .= 1f - Entity.FontSizing .= Some 10] world then + Entity.FontSizing .= Some 10.f] world then fluidEmitter.FluidParticleRadius.Map (function | 28.8f -> fluidEmitter.LinearDamping.Map (max 0.5f) world; 22.2f // Particles would explode when tank is full without damping @@ -299,7 +299,7 @@ type FluidSimDispatcher () = Mouse Left and Right - Summon a giant bubble that collides with particles.\n\ Mouse Middle - Draw contours that collide with particles. \n\ NOTE: Intersecting contours are not supported and will cause tunneling!" - Entity.FontSizing .= Some 10 + Entity.FontSizing .= Some 10.f Entity.TextMargin .= v2 5f 0f] world if World.doButton "Info Close" [Entity.LayoutOrder .= 3 diff --git a/Projects/Sand Box 2d/Sand Box 2d.fsproj b/Projects/Sand Box 2d/Sand Box 2d.fsproj index 9ef3f57236..97a3bfc909 100644 --- a/Projects/Sand Box 2d/Sand Box 2d.fsproj +++ b/Projects/Sand Box 2d/Sand Box 2d.fsproj @@ -72,9 +72,6 @@ ..\..\Nu\Nu.Dependencies\OpenGL.NET\lib\netcoreapp2.2\OpenGL.Net.dll - - ..\..\Nu\Nu.Dependencies\SDL2-CS\netstandard2.0\SDL2-CS.dll - ..\..\Nu\Nu.Dependencies\TiledSharp\lib\netstandard2.0\TiledSharp.dll diff --git a/Projects/Sand Box 2d/ToyBox.fs b/Projects/Sand Box 2d/ToyBox.fs index bde5788d82..620a76bf4a 100644 --- a/Projects/Sand Box 2d/ToyBox.fs +++ b/Projects/Sand Box 2d/ToyBox.fs @@ -1022,7 +1022,7 @@ type ToyBoxDispatcher () = [Entity.Position .= v3 255f -50f 0f Entity.Text @= $"Avatar Gravity: {fst gravity}" Entity.Elevation .= 1f - Entity.FontSizing .= Some 10] world then + Entity.FontSizing .= Some 10.f] world then toyBox.AvatarGravities.Map List.tail world // clear toys button @@ -1083,7 +1083,7 @@ type ToyBoxDispatcher () = Mouse Left - Click button or Drag entity. Mouse Right - Cause an explosion.\n\ Mouse Scroll - Apply rotation to entity.\n\ Alt+F4 - Close game if not in Editor. Read source code for explanations!" - Entity.FontSizing .= Some 10 + Entity.FontSizing .= Some 10.f Entity.TextMargin .= v2 5f 0f] world if World.doButton "Info Close" [Entity.LayoutOrder .= 3 diff --git a/Projects/Sand Box 3d/Sand Box 3d.fsproj b/Projects/Sand Box 3d/Sand Box 3d.fsproj index c1be72d56d..e7f7a79e89 100644 --- a/Projects/Sand Box 3d/Sand Box 3d.fsproj +++ b/Projects/Sand Box 3d/Sand Box 3d.fsproj @@ -69,9 +69,6 @@ ..\..\Nu\Nu.Dependencies\OpenGL.NET\lib\netcoreapp2.2\OpenGL.Net.dll - - ..\..\Nu\Nu.Dependencies\SDL2-CS\netstandard2.0\SDL2-CS.dll - ..\..\Nu\Nu.Dependencies\TiledSharp\lib\netstandard2.0\TiledSharp.dll diff --git a/Projects/Terra Firma/Terra Firma.fsproj b/Projects/Terra Firma/Terra Firma.fsproj index 0546698a16..63e8be7314 100644 --- a/Projects/Terra Firma/Terra Firma.fsproj +++ b/Projects/Terra Firma/Terra Firma.fsproj @@ -72,9 +72,6 @@ ..\..\Nu\Nu.Dependencies\OpenGL.NET\lib\netcoreapp2.2\OpenGL.Net.dll - - ..\..\Nu\Nu.Dependencies\SDL2-CS\netstandard2.0\SDL2-CS.dll - ..\..\Nu\Nu.Dependencies\TiledSharp\lib\netstandard2.0\TiledSharp.dll diff --git a/Projects/Twenty 48/GameplayDispatcher.fs b/Projects/Twenty 48/GameplayDispatcher.fs index 2dcdb817c9..1c3aad3e68 100644 --- a/Projects/Twenty 48/GameplayDispatcher.fs +++ b/Projects/Twenty 48/GameplayDispatcher.fs @@ -105,7 +105,7 @@ type GameplayDispatcher () = Entity.Text := string tile.Value Entity.Justification == Justified (JustifyCenter, JustifyMiddle) Entity.Font == Assets.Gui.ClearSansFont - Entity.FontSizing := if tile.Value < 16384 then Some 12 else Some 8 + Entity.FontSizing := if tile.Value < 16384 then Some 12.f else Some 8.f Entity.TextColor == Color.GhostWhite Entity.BackdropImageOpt := Some (Assets.Gameplay.TileImage tile.Value)]] diff --git a/Projects/Twenty 48/Twenty 48.fsproj b/Projects/Twenty 48/Twenty 48.fsproj index 43c3d9906c..f87b593893 100644 --- a/Projects/Twenty 48/Twenty 48.fsproj +++ b/Projects/Twenty 48/Twenty 48.fsproj @@ -71,9 +71,6 @@ ..\..\Nu\Nu.Dependencies\OpenGL.NET\lib\netcoreapp2.2\OpenGL.Net.dll - - ..\..\Nu\Nu.Dependencies\SDL2-CS\netstandard2.0\SDL2-CS.dll - ..\..\Nu\Nu.Dependencies\TiledSharp\lib\netstandard2.0\TiledSharp.dll