diff --git a/.gitignore b/.gitignore
index d9a0016101c..3cd44f30853 100644
--- a/.gitignore
+++ b/.gitignore
@@ -45,6 +45,9 @@ project.lock.json
/MonoGame.Framework/MonoGame.Framework.WindowsUniversal.project.lock.json
artifacts/
+# JetBrains Rider
+.idea/
+
#Tooling
_ReSharper*/
*.resharper
@@ -93,3 +96,4 @@ Test/*.csproj
*.zip
Installers/MacOS/Scripts/Framework/postinstall
+IDE/MonoDevelop/MonoDevelop.MonoGame/templates/Common/MonoGame.Framework.dll.config
diff --git a/.gitmodules b/.gitmodules
index ad282c1a9e3..b89dc6185d3 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,3 +4,6 @@
[submodule "ThirdParty/Dependencies"]
path = ThirdParty/Dependencies
url = https://github.com/Mono-Game/MonoGame.Dependencies.git
+[submodule "ThirdParty/NVorbis"]
+ path = ThirdParty/NVorbis
+ url = https://github.com/ioctlLR/NVorbis.git
diff --git a/Build/Module.xml b/Build/Module.xml
index 96d93aa22cc..57035b845f1 100644
--- a/Build/Module.xml
+++ b/Build/Module.xml
@@ -1,17 +1,16 @@
-
+
MonoGame.Framework
Generate
Angle,Linux,WindowsGL
Angle,MacOS,iOS,WindowsGL,Android,tvOS,Linux
- Android,Angle,Linux,Ouya,Windows8,Windows,WindowsGL,WindowsPhone,WindowsPhone81,WindowsUniversal,iOS,tvOS
- Android,Angle,Linux,Ouya,Windows8,Windows,WindowsGL,WindowsPhone,WindowsPhone81,WindowsUniversal,iOS,MacOS,Web,tvOS
+ Android,Angle,Linux,Windows8,Windows,WindowsGL,WindowsPhone81,WindowsUniversal,iOS,tvOS
+ Android,Angle,Linux,Windows8,Windows,WindowsGL,WindowsPhone81,WindowsUniversal,iOS,MacOS,Web,tvOS,PSVita,XBoxOne
true
+ false
-
+
+
-
- PackageManagement
-
diff --git a/Build/MonoDevelopPolicies.xml b/Build/MonoDevelopPolicies.xml
index c9a0f40e222..29c5eac6b66 100644
--- a/Build/MonoDevelopPolicies.xml
+++ b/Build/MonoDevelopPolicies.xml
@@ -1,4 +1,4 @@
-
-
+
+
diff --git a/Build/Projects/2MGFX.definition b/Build/Projects/2MGFX.definition
index d2348c38da7..c1bee7a5c5a 100644
--- a/Build/Projects/2MGFX.definition
+++ b/Build/Projects/2MGFX.definition
@@ -106,6 +106,9 @@
MonoGame.Framework\TextureFilter.cs
+
+ MonoGame.Framework\TextureFilterMode.cs
+
MonoGame.Framework\VertexElementUsage.cs
@@ -160,7 +163,15 @@
MonoGame.Framework\Utilities\Hash.cs
-
+
+
+
+ MonoGame.Framework.Content.Pipeline\ExternalTool.cs
+
+
+ MonoGame.Framework.Content.Pipeline\LoadedTypeCollection.c
+
+
@@ -184,6 +195,8 @@
+
+
@@ -200,5 +213,6 @@
+
diff --git a/Build/Projects/FrameworkReferences.Net.definition b/Build/Projects/FrameworkReferences.Net.definition
index 8061512184e..1ca6964a989 100644
--- a/Build/Projects/FrameworkReferences.Net.definition
+++ b/Build/Projects/FrameworkReferences.Net.definition
@@ -36,17 +36,6 @@
-
-
-
-
-
-
-
-
-
@@ -66,17 +55,6 @@
-
-
-
-
- MicrosoftXnaGamerServices
-
-
- MicrosoftXnaFramework
-
-
-
diff --git a/Build/Projects/FrameworkReferences.definition b/Build/Projects/FrameworkReferences.definition
index 793d912f16f..065351d5f95 100644
--- a/Build/Projects/FrameworkReferences.definition
+++ b/Build/Projects/FrameworkReferences.definition
@@ -8,6 +8,9 @@
+
+
+
@@ -16,12 +19,6 @@
-
-
@@ -48,17 +45,6 @@
-
-
-
-
-
-
-
-
-
@@ -103,9 +89,6 @@
-
@@ -147,42 +130,9 @@
-
-
-
-
-
-
-
-
-
-
-
-
- MicrosoftXnaFramework
-
-
- MicrosoftXnaGamerServices
-
-
-
@@ -244,9 +194,6 @@
-
diff --git a/Build/Projects/Lidgren.Network.References.definition b/Build/Projects/Lidgren.Network.References.definition
index 8f617d6b307..a006e8d0835 100644
--- a/Build/Projects/Lidgren.Network.References.definition
+++ b/Build/Projects/Lidgren.Network.References.definition
@@ -25,12 +25,6 @@
-
-
-
-
-
-
diff --git a/Build/Projects/Lidgren.Network.definition b/Build/Projects/Lidgren.Network.definition
index 9da3fa8efe7..e46c2372200 100644
--- a/Build/Projects/Lidgren.Network.definition
+++ b/Build/Projects/Lidgren.Network.definition
@@ -3,7 +3,7 @@
Name="Lidgren.Network"
Path="ThirdParty/Lidgren.Network"
Type="Library"
- Platforms="Android,Linux,MacOS,Ouya,Windows,WindowsGL,iOS">
+ Platforms="Android,Linux,MacOS,Windows,WindowsGL,iOS">
@@ -91,6 +93,7 @@
+
@@ -112,6 +115,7 @@
+
@@ -131,6 +135,7 @@
+
@@ -280,6 +285,14 @@
Windows
Processors\MGFX\ShaderProfile.cs
+
+ Windows
+ Processors\MGFX\ShaderProfile.OpenGL.cs
+
+
+ Windows
+ Processors\MGFX\ShaderProfile.DirectX.cs
+
Windows
Processors\MGFX\TechniqueInfo.cs
@@ -314,6 +327,7 @@
+
@@ -362,6 +376,7 @@
+
@@ -380,6 +395,7 @@
+
@@ -418,6 +434,7 @@
+
diff --git a/Build/Projects/MonoGame.Framework.Net.definition b/Build/Projects/MonoGame.Framework.Net.definition
index feec5c47ce7..da0fc10e541 100644
--- a/Build/Projects/MonoGame.Framework.Net.definition
+++ b/Build/Projects/MonoGame.Framework.Net.definition
@@ -1,5 +1,5 @@
-
+
-
- MonoGameXnaFramework
-
-
+
@@ -47,11 +42,9 @@
IOS;GLES;OPENGL;OPENAL;NET
TRACE;LINUX;OPENGL;OPENAL;NET;DESKTOPGL
MONOMAC;OPENGL;OPENAL;NET
- TRACE;ANDROID;GLES;OPENGL;OUYA;NET
TRACE;WINDOWS;DIRECTX;WINDOWS_MEDIA_SESSION;NET
TRACE;NETFX_CORE;WINRT;WINDOWS_STOREAPP;DIRECTX;DIRECTX11_1;WINDOWS_MEDIA_ENGINE;NET
TRACE;WINDOWS;OPENGL;OPENAL;NET;DESKTOPGL
- TRACE;SILVERLIGHT;WINDOWS_PHONE;WINRT;DIRECTX;NET
TRACE;WINRT;WINDOWS_PHONE81;DIRECTX;WINDOWS_STOREAPP
TRACE;NETFX_CORE;WINDOWS_UAP;WINRT;DIRECTX;DIRECTX11_1;WINDOWS_MEDIA_ENGINE
@@ -69,45 +62,19 @@
-
- WindowsPhone
-
-
- WindowsPhone
-
-
- WindowsPhone
-
-
- WindowsPhone
-
-
- WindowsPhone
-
-
- WindowsPhone
-
-
- WindowsPhone
-
-
- WindowsPhone
-
-
- WindowsPhone
-
-
- WindowsPhone
-
-
- WindowsPhone
-
-
- WindowsPhone
-
-
- WindowsPhone
-
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -115,122 +82,120 @@
-
- WindowsPhone
-
+
- Android,MacOS,Ouya,Windows,WindowsGL,Linux
+ Android,MacOS,Windows,WindowsGL,Linux
MacOS,Windows,WindowsGL,Linux
- Android,iOS,Linux,MacOS,Ouya,Windows,WindowsGL
+ Android,iOS,Linux,MacOS,Windows,WindowsGL
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
- WindowsPhone,WindowsPhone81,Windows8,WindowsUniversal
+ WindowsPhone81,Windows8,WindowsUniversal
-
+
- Android,Ouya
+ Android
- Android,Ouya
+ Android
- Android,Ouya
+ Android
@@ -296,7 +261,7 @@
- Windows8,Windows,WindowsPhone,WindowsPhone81,WindowsUniversal
+ Windows8,Windows,WindowsPhone81,WindowsUniversal
Windows
diff --git a/Build/Projects/MonoGame.Framework.definition b/Build/Projects/MonoGame.Framework.definition
index 2f41d93ca96..2961d08d4ec 100644
--- a/Build/Projects/MonoGame.Framework.definition
+++ b/Build/Projects/MonoGame.Framework.definition
@@ -1,5 +1,5 @@
-
+
- Angle,iOS,Linux,Windows,WindowsGL,MacOS,Android,Ouya,tvOS
+ Angle,iOS,Linux,Windows,WindowsGL,MacOS,Android,tvOS
XAudioAudio,WebAudio
- Windows,Windows8,WindowsPhone,WindowsPhone81,WindowsUniversal
+ Windows,Windows8,WindowsPhone81,WindowsUniversal
OpenALAudio,WebAudio
Web
OpenALAudio,XAudioAudio
+
+ WindowsGL,Linux
+
+ XNADESIGNPROVIDED
-
-
-
-
+
+ Android
+
+
+ WindowsGL,Linux
+
+
+ iOS
+
+
+ MacOS
+
+
+ Angle
+
+
+ tvOS
+
+
+ Windows
+
+
+ WindowsPhone81
+
+
+ Windows8
+
+
+ WindowsUniversal
+
+
+ Web
+
MacOS
@@ -154,25 +186,81 @@
PreserveNewest
- WindowsGL,Linux,MacOS
+ MacOS
OpenTK.dll.config
PreserveNewest
+
+ WindowsGL,Linux
+ MonoGame.Framework.dll.config
+ PreserveNewest
+
+
+
+ WindowsGL,Linux
+ x86\SDL2.dll
+ PreserveNewest
+
+
+ WindowsGL,Linux
+ x64\SDL2.dll
+ PreserveNewest
+
+
+ WindowsGL,Linux
+ x86\soft_oal.dll
+ PreserveNewest
+
+
+ WindowsGL,Linux
+ x64\soft_oal.dll
+ PreserveNewest
+
+
+ WindowsGL,Linux
+ x86\libSDL2-2.0.so.0
+ PreserveNewest
+
+
+ WindowsGL,Linux
+ x64\libSDL2-2.0.so.0
+ PreserveNewest
+
+
+ WindowsGL,Linux
+ x86\libopenal.so.1
+ PreserveNewest
+
+
+ WindowsGL,Linux
+ x64\libopenal.so.1
+ PreserveNewest
+
+
+ WindowsGL,Linux
+ libSDL2-2.0.0.dylib
+ PreserveNewest
+
+
+ WindowsGL,Linux
+ libopenal.1.dylib
+ PreserveNewest
+
- Android,Ouya
+ Android
libs\armeabi-v7a\libopenal32.so
- Android,Ouya
+ Android
libs\arm64-v8a\libopenal32.so
- Android,Ouya
+ Android
libs\x86\libopenal32.so
- Android,Ouya
+ Android
libs\x86_64\libopenal32.so
@@ -190,9 +278,7 @@
-
- _FrameworkDispatcherProvided
-
+
@@ -203,7 +289,7 @@
Angle,Linux,MacOS,Windows,Windows8,WindowsGL,WindowsUniversal
- Android,iOS,Ouya,WindowsPhone,WindowsPhone81,Web
+ Android,iOS,WindowsPhone81,Web,tvOS
@@ -212,7 +298,7 @@
Windows8,WindowsPhone81
- Windows8,WindowsPhone,WindowsPhone81
+ Windows8,WindowsPhone81
@@ -232,7 +318,7 @@
- Android,MacOS,Ouya
+ Android,MacOS
@@ -245,9 +331,21 @@
Angle,Linux,MacOS,Windows,WindowsGL,WindowsUniversal
- Android,Angle,iOS,Linux,MacOS,Ouya,WindowsGL,WindowsPhone,tvOS
+ Android,Angle,iOS,Linux,MacOS,WindowsGL,tvOS
+
+ Android
+
+
+ Angle,Linux,Windows,WindowsGL,Web
+
+
+ iOS,tvOS,MacOS
+
+
+ Windows8,WindowsPhone81,WindowsUniversal
+
@@ -260,11 +358,22 @@
- Linux,WindowsGL,Angle,Android,Ouya
+ Linux,WindowsGL,Angle,Android
+
+
+
+ OpenALAudio
+
+
+ XAudioAudio
+
+
+ WebAudio
+
- Android,Angle,iOS,Linux,MacOS,Ouya,WindowsGL,Web,tvOS
+ Android,Angle,iOS,Linux,MacOS,WindowsGL,Web,tvOS
@@ -273,6 +382,9 @@
WindowsGL,Linux
+
+ WindowsGL,Linux
+
OpenALAudio
@@ -308,10 +420,21 @@
+
+
+
+
+
-
+
+
+
+
+
+
+
@@ -373,13 +496,13 @@
- Angle,Linux,MacOS,Windows8,Windows,WindowsGL,WindowsPhone,WindowsPhone81,WindowsUniversal
+ Angle,Linux,MacOS,Windows8,Windows,WindowsGL,WindowsPhone81,WindowsUniversal
- Android,Angle,iOS,Linux,MacOS,Ouya,Windows8,Windows,WindowsGL,WindowsPhone,WindowsPhone81,WindowsUniversal,tvOS
+ Android,Angle,iOS,Linux,MacOS,Windows8,Windows,WindowsGL,WindowsPhone81,WindowsUniversal,tvOS
- Android,Angle,iOS,Linux,MacOS,Ouya,Windows8,Windows,WindowsGL,WindowsPhone,WindowsPhone81,WindowsUniversal,tvOS
+ Android,Angle,iOS,Linux,MacOS,Windows8,Windows,WindowsGL,WindowsPhone81,WindowsUniversal,tvOS
@@ -391,7 +514,7 @@
- Angle,Linux,WindowsGL,WindowsPhone,Web,tvOS
+ Angle,Linux,WindowsGL,Web,tvOS
@@ -402,7 +525,7 @@
- Angle,Android,iOS,Linux,Ouya,Windows,WindowsGL,tvOS
+ Angle,Android,iOS,Linux,Windows,WindowsGL,tvOS
@@ -413,7 +536,18 @@
-
+
+ Windows,WindowsGL,Linux,Windows8,WindowsPhone81,WindowsUniversal,Web
+
+
+ WindowsGL,Linux
+
+
+ Windows8,WindowsPhone81,WindowsUniversal
+
+
+ Android,Angle,iOS,MacOS,tvOS
+
@@ -454,8 +588,28 @@
Web
-
+
+ Android,Angle,iOS,Linux,MacOS,WindowsGL,tvOS,Web
+
+
+ DirectXGraphics
+
+
+ Android,Angle,iOS,Linux,MacOS,WindowsGL,tvOS,Web
+
+
+ DirectXGraphics
+
+
+ OpenGLGraphics,ANGLEGraphics
+
+
+ WebGraphics
+
+
+ WindowsGL,Linux
+
DirectXGraphics
@@ -473,11 +627,13 @@
+
- Angle,Linux,Windows8,Windows,WindowsGL,WindowsPhone,WindowsPhone81,WindowsUniversal
+ Angle,Linux,Windows8,Windows,WindowsGL,WindowsPhone81,WindowsUniversal
+
@@ -488,18 +644,27 @@
- iOS,Android,Ouya
+ iOS,Android
DirectXGraphics
ANGLEGraphics,OpenGLGraphics
- iOS,Android,Ouya
+ iOS,Android
WebGraphics
+
+ WindowsGL,Linux
+
+
+ WindowsGL,Linux
+
+
+ WindowsGL,Linux
+
@@ -539,6 +704,15 @@
+
+ DirectXGraphics
+
+
+ OpenGLGraphics
+
+
+ WebGraphics
+
@@ -575,7 +749,7 @@
WebGraphics
- Android,Angle,iOS,Linux,MacOS,Ouya,WindowsGL,tvOS
+ Android,Angle,iOS,Linux,MacOS,WindowsGL,tvOS
@@ -634,6 +808,7 @@
+
Windows
@@ -749,14 +924,19 @@
-
-
+
+ WindowsGL,Linux
+
+
- Android,WindowsPhone,WindowsPhone81
+ WindowsPhone81
+
+
+ Android
iOS
@@ -764,11 +944,8 @@
tvOS
-
- Ouya
-
-
- Angle,Linux,MacOS,WindowsGL
+
+ Linux,WindowsGL
MacOS
@@ -795,22 +972,28 @@
- Android,Ouya,Windows8,Windows,WindowsPhone,WindowsPhone81,WindowsUniversal,iOS,tvOS
-
-
- Angle,Linux,MacOS,WindowsGL
+ Angle,Android,MacOS,Windows8,Windows,WindowsPhone81,WindowsUniversal,iOS,tvOS
-
- MacOS
+
+ Linux,WindowsGL
Web
- Android,Ouya
+ Android
+
+
+ Android,Windows,WindowsGL,Linux
+
+
+ WindowsGL,Linux
+
+
+ Windows
- Android,iOS,Windows8,WindowsPhone,WindowsPhone81,WindowsUniversal
+ Android,iOS,Windows8,WindowsPhone81,WindowsUniversal
Android
@@ -818,17 +1001,14 @@
iOS
-
- WindowsPhone
-
Windows8,WindowsPhone81,WindowsUniversal
-
+
- Android,iOS,Windows8,WindowsPhone,WindowsPhone81,WindowsUniversal
+ Android,iOS,Windows8,WindowsPhone81,WindowsUniversal
Android
@@ -836,13 +1016,29 @@
iOS
-
- WindowsPhone
-
Windows8,WindowsPhone81,WindowsUniversal
+
+ WindowsGL,Linux
+
+
+ Windows
+
+
+ MacOS
+
+
+ WindowsGL,Linux,Windows,MacOS
+
+
+
+ Linux,WindowsGL
+
+
+ Linux,WindowsGL
+
@@ -853,9 +1049,9 @@
- Windows8,WindowsPhone,WindowsPhone81,WindowsUniversal
+ Windows8,WindowsPhone81,WindowsUniversal
-
+
Windows8,WindowsPhone81,WindowsUniversal
MSBuild:Compile
@@ -869,10 +1065,10 @@
- iOS,Windows8,WindowsPhone,Android,Ouya,WindowsPhone81,WindowsUniversal
+ iOS,Windows8,Android,WindowsPhone81,WindowsUniversal
- Android,Ouya
+ Android
iOS
@@ -880,9 +1076,6 @@
Windows8,WindowsPhone81,WindowsUniversal
-
- WindowsPhone
-
Windows
@@ -893,11 +1086,8 @@
Windows
-
- WindowsPhone
-
- Angle,WindowsGL,Linux,MacOS,iOS,Android,Ouya,Web,tvOS
+ Angle,WindowsGL,Linux,MacOS,iOS,Android,Web,tvOS
@@ -914,7 +1104,7 @@
Angle,MacOS,Web
- Android,Ouya
+ Android
iOS,tvOS
@@ -925,15 +1115,12 @@
Windows
-
- WindowsPhone
-
Windows8,WindowsPhone81,WindowsUniversal
- Angle,Linux,WindowsGL,WindowsPhone,Web,tvOS
+ Angle,Linux,WindowsGL,Web,tvOS
iOS
@@ -942,13 +1129,13 @@
MacOS
- Android,Ouya
+ Android
Windows
- Angle,Linux,WindowsGL,WindowsPhone,Web,tvOS
+ Angle,Linux,WindowsGL,Web,tvOS
iOS
@@ -960,7 +1147,7 @@
MacOS
- Android,Ouya
+ Android
Windows
@@ -968,39 +1155,31 @@
- Android,iOS,Ouya
+ Android,iOS
- Android,iOS,Ouya
+ Android,iOS
- Android,iOS,Ouya
+ Android,iOS
- Android,iOS,Ouya
+ Android,iOS
- Android,iOS,Ouya
+ Android,iOS
- Android,iOS,Ouya
+ Android,iOS
- Android,iOS,Ouya
+ Android,iOS
- Android,iOS,Ouya
+ Android,iOS
- Android,iOS,Ouya
-
-
-
-
-
-
-
- Android,Angle,iOS,Ouya,Windows8,Windows,WindowsGL,WindowsPhone,Web,WindowsPhone81,WindowsUniversal,Linux,tvOS
+ Android,iOS
@@ -1014,68 +1193,84 @@
+
+ Android,iOS,MacOS,Windows8,WindowsPhone81,WindowsUniversal,tvOS
+
+
+ Angle,Linux,Windows,WindowsGL,WindowsPhone,Web
+
+
+
-
+
- Android,Ouya
+ Android
- Android,Ouya
+ Android
- Android,Ouya
+ Android
- Android,Ouya
+ Android
- Android,Ouya
+ Android
- Android,Ouya
+ Android
- Android,Ouya
+ Android
- Android,Ouya
+ Android
- Android,Ouya
+ Android
- Android,Ouya
+ Android
- Android,Ouya
+ Android
- Android,Ouya
+ Android
- Android,Ouya
+ Android
-
+
Angle,Linux,WindowsGL
-
+
Angle,Linux,WindowsGL
-
+
+ WindowsGL,Linux
+
+
Angle,Linux,WindowsGL
-
+
+ MonoGame.bmp
Angle,Linux,WindowsGL
+
+ Angle,Linux,WindowsGL
+ gamecontrollerdb.txt
+
@@ -1143,9 +1338,6 @@
MacOS
-
- MacOS
-
@@ -1165,16 +1357,16 @@
Windows8,WindowsPhone81
- Windows8,WindowsPhone,WindowsPhone81,WindowsUniversal
+ Windows8,WindowsPhone81,WindowsUniversal
- Windows8,Windows,WindowsPhone,WindowsPhone81,WindowsUniversal
+ Windows8,Windows,WindowsPhone81,WindowsUniversal
Windows8,WindowsPhone81
-
+
WindowsUniversal
@@ -1186,7 +1378,7 @@
WindowsUniversal
-
+
Windows
@@ -1202,26 +1394,6 @@
-
-
- WindowsPhone
-
-
- WindowsPhone
-
-
- WindowsPhone
-
-
- WindowsPhone
-
-
- WindowsPhone
-
-
- WindowsPhone
-
-
Web
@@ -1294,6 +1466,107 @@
DirectXGraphics
+
+
+ Utilities\NVorbis\BufferedReadStream.cs
+ NVorbis
+
+
+ Utilities\NVorbis\DataPacket.cs
+ NVorbis
+
+
+ Utilities\NVorbis\Huffman.cs
+ NVorbis
+
+
+ Utilities\NVorbis\IContainerReader.cs
+ NVorbis
+
+
+ Utilities\NVorbis\IPacketProvider.cs
+ NVorbis
+
+
+ Utilities\NVorbis\IVorbisStreamStatus.cs
+ NVorbis
+
+
+ Utilities\NVorbis\Mdct.cs
+ NVorbis
+
+
+ Utilities\NVorbis\NewStreamEventArgs.cs
+ NVorbis
+
+
+ Utilities\NVorbis\ParameterChangeEventArgs.cs
+ NVorbis
+
+
+ Utilities\NVorbis\RingBuffer.cs
+ NVorbis
+
+
+ Utilities\NVorbis\StreamReadBuffer.cs
+ NVorbis
+
+
+ Utilities\NVorbis\Utils.cs
+ NVorbis
+
+
+ Utilities\NVorbis\VorbisCodebook.cs
+ NVorbis
+
+
+ Utilities\NVorbis\VorbisFloor.cs
+ NVorbis
+
+
+ Utilities\NVorbis\VorbisMapping.cs
+ NVorbis
+
+
+ Utilities\NVorbis\VorbisMode.cs
+ NVorbis
+
+
+ Utilities\NVorbis\VorbisReader.cs
+ NVorbis
+
+
+ Utilities\NVorbis\VorbisResidue.cs
+ NVorbis
+
+
+ Utilities\NVorbis\VorbisStreamDecoder.cs
+ NVorbis
+
+
+ Utilities\NVorbis\VorbisTime.cs
+ NVorbis
+
+
+ Utilities\NVorbis\Ogg\OggContainerReader.cs
+ NVorbis
+
+
+ Utilities\NVorbis\Ogg\OggCrc.cs
+ NVorbis
+
+
+ Utilities\NVorbis\Ogg\OggPacket.cs
+ NVorbis
+
+
+ Utilities\NVorbis\Ogg\OggPacketReader.cs
+ NVorbis
+
+
+ Utilities\NVorbis\Ogg\OggPageFlags.cs
+ NVorbis
+
diff --git a/Build/Projects/MonoGame.Tests.definition b/Build/Projects/MonoGame.Tests.definition
index 4f12279e114..bc321d2793c 100644
--- a/Build/Projects/MonoGame.Tests.definition
+++ b/Build/Projects/MonoGame.Tests.definition
@@ -5,14 +5,14 @@
-
+
-
+
Default
1591,0436
Build/MonoDevelopPolicies.xml
@@ -33,6 +33,33 @@
+
+
+ WindowsGL
+ SDL2.dll
+ PreserveNewest
+
+
+ WindowsGL
+ soft_oal.dll
+ PreserveNewest
+
+
+ Linux
+ x64\libSDL2-2.0.so.0
+ PreserveNewest
+
+
+ Linux
+ x64\libopenal.so.1
+ PreserveNewest
+
+
+
+ Linux
+ PreserveNewest
+
+
@@ -86,15 +113,17 @@
iOS
+
+
-
-
-
+
+
+
@@ -106,9 +135,8 @@
-
-
+
@@ -117,11 +145,19 @@
-
+
+
+
+
+
+ Windows
+
+
+
@@ -130,25 +166,44 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ Windows
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Windows
+
+
+
-
-
-
-
-
+
+
+
+
+
+
@@ -156,6 +211,9 @@
Windows,MacOS,Linux
+
+ Windows,MacOS,Linux
+
Windows,MacOS,Linux
@@ -174,9 +232,21 @@
Windows,MacOS,Linux
+
+ Windows,MacOS,Linux
+
+
+ Windows,MacOS,Linux
+
Windows,MacOS,Linux
+
+ Windows,MacOS,Linux
+
+
+ Windows,MacOS,Linux
+
Windows,MacOS,Linux
@@ -195,6 +265,86 @@
Windows,MacOS,Linux
+
+ Windows,MacOS,Linux
+
+
+ Windows,MacOS,Linux
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
PreserveNewest
@@ -241,35 +391,77 @@
PreserveNewest
-
- PreserveNewest
-
-
- PreserveNewest
-
PreserveNewest
+ Windows
PreserveNewest
+ Windows
PreserveNewest
+ Windows
PreserveNewest
+ Windows
PreserveNewest
+ Windows
PreserveNewest
+ Windows
PreserveNewest
+ Windows
PreserveNewest
+ Windows
+
+
+ PreserveNewest
+ Windows
+
+
+ PreserveNewest
+ Windows
+
+
+ PreserveNewest
+ Windows
+
+
+ PreserveNewest
+ Windows
+
+
+ PreserveNewest
+ Windows
+
+
+ PreserveNewest
+ Windows
+
+
+ PreserveNewest
+ Windows
+
+
+ PreserveNewest
+ Windows
+
+
+ PreserveNewest
+ Windows
+
+
+ PreserveNewest
+ Windows
@@ -290,25 +482,25 @@
PreserveNewest
-
+
PreserveNewest
-
+
PreserveNewest
-
+
PreserveNewest
-
+
PreserveNewest
-
+
PreserveNewest
-
+
PreserveNewest
-
+
PreserveNewest
@@ -365,6 +557,15 @@
PreserveNewest
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
PreserveNewest
@@ -392,6 +593,9 @@
PreserveNewest
+
+ PreserveNewest
+
PreserveNewest
@@ -529,10 +733,22 @@
PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
PreserveNewest
+
+ PreserveNewest
+
+
+ PreserveNewest
+
PreserveNewest
@@ -551,7 +767,16 @@
PreserveNewest
-
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
PreserveNewest
@@ -695,6 +920,9 @@
PreserveNewest
+
+ PreserveNewest
+
PreserveNewest
@@ -705,6 +933,7 @@
PreserveNewest
+
PreserveNewest
Windows
@@ -737,6 +966,66 @@
PreserveNewest
Windows
+
+ PreserveNewest
+ Windows
+
+
+ PreserveNewest
+ Windows
+
+
+ PreserveNewest
+ Windows
+
+
+ PreserveNewest
+ Windows
+
+
+ PreserveNewest
+ Windows
+
+
+
+
+ PreserveNewest
+ WindowsGL,Linux
+
+
+ PreserveNewest
+ WindowsGL,Linux
+
+
+ PreserveNewest
+ WindowsGL,Linux
+
+
+ PreserveNewest
+ WindowsGL,Linux
+
+
+ PreserveNewest
+ WindowsGL,Linux
+
+
+ PreserveNewest
+ WindowsGL,Linux
+
+
+ PreserveNewest
+ WindowsGL,Linux
+
+
+ PreserveNewest
+ WindowsGL,Linux
+
+
+ PreserveNewest
+ WindowsGL,Linux
+
+
+
PreserveNewest
@@ -806,21 +1095,39 @@
PreserveNewest
+
+ PreserveNewest
+
+
+ PreserveNewest
+
PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
PreserveNewest
-
+
PreserveNewest
-
+
PreserveNewest
diff --git a/Build/Projects/Pipeline.definition b/Build/Projects/Pipeline.definition
index 9b1a820ff27..5808ea9aed0 100644
--- a/Build/Projects/Pipeline.definition
+++ b/Build/Projects/Pipeline.definition
@@ -11,7 +11,7 @@
-
+
@@ -28,6 +28,10 @@
TRACE;MONOMAC;GTK2
TRACE;LINUX;GTK3
+
+ v4.6.1
+
+ True
@@ -39,29 +43,29 @@
+
-
-
-
+
-
+
-
-
-
+
+
+
+
@@ -71,309 +75,269 @@
-
-
-
- Windows
-
-
- Windows
-
-
- Windows
-
-
- Windows
-
-
- Windows
- Form
-
-
- Windows
- MainView.cs
+
+
+
+
+
+ MainWindow.cs
-
+
+
+
+ Global.cs
Windows
- Form
-
+
Windows
- NewContentDialog.cs
-
+
Windows
- Component
-
- Windows
- Component
+
+
+
+ MainWindow.cs
+ MainWindow.glade
+ Linux
+
+
+ Global.cs
+ Linux
-
- Windows
- Component
+
+ Linux
-
- Windows
+
+ Linux
-
- Windows
+
+
+
+ Global.cs
+ MacOS
-
- Windows
+
+ MacOS
-
- Windows
-
-
- Windows
- AboutDialog.cs
+
+ MacOS
-
- Windows
-
-
- Windows
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Pad.cs
+
+
+
+ BuildOutput.cs
+
+
+
+ PropertyGridControl.cs
+
+
+
+ PropertyGridTable.cs
+
+
+
+
+
+ AddItemDialog.cs
+
+
+
+ DeleteDialog.cs
+
+
+
+ DialogBase.cs
+
+
+
+ EditDialog.cs
+
+
+
+ NewItemDialog.cs
+
+
+
+ PathDialog.cs
+
+
+
ReferenceDialog.cs
-
- Windows
-
-
- Windows
- AddFileDialog.cs
-
-
- Windows
- AddFileDialog.cs
+
+
+
+
+ Icons.monogame.png
-
- Windows
-
-
- Windows
- AddFolderDialog.cs
-
-
- Windows
- AddFolderDialog.cs
+
+ Icons.Settings.png
-
- Windows
-
-
- Windows
- TextEditDialog.cs
-
-
- Windows
- TextEditDialog.cs
+
+ LICENSE.txt
+ LICENSE.txt
-
- Windows
- MainView.cs
+
+
+
+ TreeView.Root.png
-
- Windows
- ResXFileCodeGenerator
- Resources.Designer.cs
- Designer
+
+ TreeView.File.png
-
- Windows
- AboutDialog.cs
+
+ TreeView.FileMissing.png
-
- Windows
- ReferenceDialog.cs
+
+ TreeView.Folder.png
-
- Windows
- True
- Resources.resx
- True
-
-
- Windows
- SettingsSingleFileGenerator
- Settings.Designer.cs
-
-
- Windows
- True
- Settings.settings
- True
-
-
- Windows
- NewContentDialog.cs
+
+ TreeView.FolderMissing.png
+
+
+ TreeView.Missing.png
-
-
-
- MacOS,Linux
-
-
- MacOS,Linux
-
-
- MacOS,Linux
-
-
- MacOS,Linux
-
-
- MacOS,Linux
-
-
- MacOS,Linux
-
-
- MacOS,Linux
-
-
- MacOS,Linux
-
-
- MacOS,Linux
-
-
- MacOS,Linux
-
-
- MacOS,Linux
-
-
- MacOS,Linux
-
-
- MacOS,Linux
-
-
- MacOS,Linux
-
-
- MacOS,Linux
-
-
- MacOS,Linux
-
-
- MacOS,Linux
-
-
- MacOS,Linux
-
-
- MacOS,Linux
-
-
- MacOS,Linux
-
-
- MacOS,Linux
-
-
- MacOS,Linux
-
-
- MacOS
-
-
- Linux
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ Build.EndFailed.png
+
+
+ Build.EndSucceed.png
+
+
+ Build.Fail.png
+
+
+ Build.Information.png
+
+
+ Build.Processing.png
+
+
+ Build.Skip.png
+
+
+ Build.Start.png
+
+
+ Build.Succeed.png
+
+
+ Build.SucceedWithWarnings.png
+
-
-
- Toolbar.New.png
+
+
+ Commands.Build.png
-
- Toolbar.Open.png
+
+ Commands.CancelBuild.png
-
- Toolbar.Save.png
+
+ Commands.Clean.png
-
- Toolbar.NewItem.png
+
+ Commands.Close.png
-
- Toolbar.ExistingItem.png
+
+ Commands.Delete.png
-
- Toolbar.NewFolder.png
+
+ Commands.ExistingFolder.png
-
- Toolbar.ExistingFolder.png
+
+ Commands.ExistingItem.png
-
- Toolbar.Build.png
+
+ Commands.Help.png
-
- Toolbar.Rebuild.png
+
+ Commands.New.png
-
- Toolbar.Clean.png
+
+ Commands.NewFolder.png
-
- Toolbar.FilterOutput.png
+
+ Commands.NewItem.png
-
-
-
- Linux
+
+ Commands.Open.png
+
+
+ Commands.OpenItem.png
+
+
+ Commands.Rebuild.png
+
+
+ Commands.Redo.png
+
+
+ Commands.Rename.png
+
+
+ Commands.Save.png
+
+
+ Commands.SaveAs.png
+
+
+ Commands.Undo.png
-
+
PreserveNewest
-
-
+
+
PreserveNewest
-
-
+
+
PreserveNewest
-
-
+
+
PreserveNewest
-
-
+
+
PreserveNewest
-
-
+
+
PreserveNewest
-
-
+
+
PreserveNewest
-
-
+
+
PreserveNewest
-
-
+
+
PreserveNewest
-
-
+
+
PreserveNewest
-
-
+
+
PreserveNewest
-
+
MacOS
diff --git a/Build/Projects/PipelineReferences.definition b/Build/Projects/PipelineReferences.definition
index ca930345589..eacdb9c35d2 100644
--- a/Build/Projects/PipelineReferences.definition
+++ b/Build/Projects/PipelineReferences.definition
@@ -2,6 +2,11 @@
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 00000000000..b42d7a0769c
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,407 @@
+# Change Log
+
+
+## 3.6 Release - 2/28/2017
+
+ - Fixed XML deserialization of Curve type. [#5494](https://github.com/MonoGame/MonoGame/pull/5494)
+ - Fix #5498 Pipeline Tool template loading on MacOS. [#5501](https://github.com/MonoGame/MonoGame/pull/5501)
+ - Fix typo in the exclude.addins which cause warnings when installing the Addin in XS. [#5500](https://github.com/MonoGame/MonoGame/pull/5500)
+ - Added support for arbitrary defines passed to the Effect compiler. [#5496](https://github.com/MonoGame/MonoGame/pull/5496)
+ - Fixed GraphicsDevice.Present() to check for current render target. [#5389](https://github.com/MonoGame/MonoGame/pull/5389)
+ - Custom texture compression for SpriteFonts. [#5299](https://github.com/MonoGame/MonoGame/pull/5299)
+ - Performance improvements to SpriteBatch.DrawString(). [#5226](https://github.com/MonoGame/MonoGame/pull/5226)
+ - Removed the OUYA platform [#5194](https://github.com/MonoGame/MonoGame/pull/5194)
+ - Dispose of all graphical resources in unit tests. [#5133](https://github.com/MonoGame/MonoGame/pull/5133)
+ - Throw NoSuitableGraphicsDeviceException if graphics device creation fails. [#5130](https://github.com/MonoGame/MonoGame/pull/5130)
+ - Optimized and added additional constructors to Color. [#5117](https://github.com/MonoGame/MonoGame/pull/5117)
+ - Added SamplerState.TextureFilterMode to correctly support comparison filtering. [#5112](https://github.com/MonoGame/MonoGame/pull/5112)
+ - Fixed Apply3D() on stereo SoundEffect. [#5099](https://github.com/MonoGame/MonoGame/pull/5099)
+ - Fixed Effect.OnApply to return void to match XNA. [#5090](https://github.com/MonoGame/MonoGame/pull/5090)
+ - Fix crash when DynamicSoundEffectInstance not disposed. [#5075](https://github.com/MonoGame/MonoGame/pull/5075)
+ - Texture2D.FromStream now correctly throws on null arguments. [#5050](https://github.com/MonoGame/MonoGame/pull/5050)
+ - Implemented GraphicsAdapter for DirectX platforms. [#5024](https://github.com/MonoGame/MonoGame/pull/5024)
+ - Fixed initialization of GameComponent when created within another GameComponent. [#5020](https://github.com/MonoGame/MonoGame/pull/5020)
+ - Improved SoundEffect internal platform extendability. [#5006](https://github.com/MonoGame/MonoGame/pull/5006)
+ - Refactored audio processing for platform extensibility. [#5001](https://github.com/MonoGame/MonoGame/pull/5001)
+ - Refactored texture processing for platform extensibility. [#4996](https://github.com/MonoGame/MonoGame/pull/4996)
+ - Refactor ShaderProfile to allow for pipeline extensibility. [#4992](https://github.com/MonoGame/MonoGame/pull/4992)
+ - Removed unnessasary dictionary lookup for user index buffers for DirectX platforms. [#4988](https://github.com/MonoGame/MonoGame/pull/4988)
+ - New SetRenderTargets() method which allows for variable target count. [#4987](https://github.com/MonoGame/MonoGame/pull/4987)
+ - Added support for XACT reverb and filter effects. [#4974](https://github.com/MonoGame/MonoGame/pull/4974)
+ - Remove array in GamePadDPad constructor. [#4970](https://github.com/MonoGame/MonoGame/pull/4970)
+ - Updated to the latest version of Protobuild. [#4964](https://github.com/MonoGame/MonoGame/pull/4964)
+ - Fixed static VBs and IBs on UWP on XB1. [#4955](https://github.com/MonoGame/MonoGame/pull/4955)
+ - Updated to the latest version of Protobuild. [#4950](https://github.com/MonoGame/MonoGame/pull/4950)
+ - Update Xamarin Studio addin for latest platform changes. [#4926](https://github.com/MonoGame/MonoGame/pull/4926)
+ - Replace OpenTK with custom OpenGL bindings [#4874](https://github.com/MonoGame/MonoGame/pull/4874)
+ - Fix Mouse updating when moving the Window. [#4924](https://github.com/MonoGame/MonoGame/pull/4924)
+ - Fix incorrect use of startIndex in Texture2D.GetData DX. [#4833](https://github.com/MonoGame/MonoGame/pull/4833)
+ - Cleanup of AssemblyInfo for framework assembly. [#4810](https://github.com/MonoGame/MonoGame/pull/4810)
+ - New SDL2 backend for desktop GL platforms. [#4428](https://github.com/MonoGame/MonoGame/pull/4428)
+ - Two MaterialProcessor properties fixed. [#4746](https://github.com/MonoGame/MonoGame/pull/4746)
+ - Fixed thumbstick virtual buttons to always use independent axes. [#4742](https://github.com/MonoGame/MonoGame/pull/4742)
+ - Fixed back buffer MSAA on DirectX platforms. [#4739](https://github.com/MonoGame/MonoGame/pull/4739)
+ - Added new CHANGELOG.md to project. [#4732](https://github.com/MonoGame/MonoGame/pull/4732)
+ - Added obsolete attribute and updated documentation. [#4731](https://github.com/MonoGame/MonoGame/pull/4731)
+ - Fixed layout of UWP windows in VS template to ignore window chrome. [#4727](https://github.com/MonoGame/MonoGame/pull/4727)
+ - Remove support for reading raw assets through ContentManager. [#4726](https://github.com/MonoGame/MonoGame/pull/4726)
+ - Implemented DynamicSoundEffectInstance for DirectX and OpenAL platforms. [#4715](https://github.com/MonoGame/MonoGame/pull/4715)
+ - Removed unused Yeti Mp3 compressor. [#4713](https://github.com/MonoGame/MonoGame/pull/4713)
+ - MonoGame Portable Assemblies. [#4712](https://github.com/MonoGame/MonoGame/pull/4712)
+ - Fixed RGBA64 packing and added unit tests. [#4683](https://github.com/MonoGame/MonoGame/pull/4683)
+ - Fix Gamepad crash when platform doesn't support the amount. [#4677](https://github.com/MonoGame/MonoGame/pull/4677)
+ - Fixed Song stopping before they are finished on Windows. [#4668](https://github.com/MonoGame/MonoGame/pull/4668)
+ - Removed the Linux .deb installer. [#4665](https://github.com/MonoGame/MonoGame/pull/4665)
+ - OpenAssetImporter is now automatically selected for all the formats it supports. [#4663](https://github.com/MonoGame/MonoGame/pull/4663)
+ - Fixed broken unit tests under Linux. [#4614](https://github.com/MonoGame/MonoGame/pull/4614)
+ - Split out Title Container into partial classes. [#4590](https://github.com/MonoGame/MonoGame/pull/4590)
+ - Added Rider Support to Linux installer. [#4589](https://github.com/MonoGame/MonoGame/pull/4589)
+ - Implement vertexStride in VertexBuffer.SetData for OpenGL. [#4568](https://github.com/MonoGame/MonoGame/pull/4568)
+ - Performance improvement to SpriteBatch vertex generation. [#4547](https://github.com/MonoGame/MonoGame/pull/4547)
+ - Optimization of indices initialization in SpriteBatcher. [#4546](https://github.com/MonoGame/MonoGame/pull/4546)
+ - Optimized ContentReader to decode LZ4 compressed streams directly. [#4522](https://github.com/MonoGame/MonoGame/pull/4522)
+ - TitleContainer partial class cleanup. [#4520](https://github.com/MonoGame/MonoGame/pull/4520)
+ - Remove raw asset support from ContentManager. [#4489](https://github.com/MonoGame/MonoGame/pull/4489)
+ - Initial implementation of RenderTargetCube for OpenGL. [#4488](https://github.com/MonoGame/MonoGame/pull/4488)
+ - Removed unnecessary platform differences in MGFX. [#4486](https://github.com/MonoGame/MonoGame/pull/4486)
+ - SoundEffect fixes and tests. [#4469](https://github.com/MonoGame/MonoGame/pull/4469)
+ - Cleanup FX syntax for shader compiler. [#4462](https://github.com/MonoGame/MonoGame/pull/4462)
+ - General Improvements to Pipeline Gtk implementation. [#4459](https://github.com/MonoGame/MonoGame/pull/4459)
+ - ShaderProfile Refactor. [#4438](https://github.com/MonoGame/MonoGame/pull/4438)
+ - GraphicsDeviceManager partial class refactor. [#4425](https://github.com/MonoGame/MonoGame/pull/4425)
+ - Remove legacy Storage classes. [#4320](https://github.com/MonoGame/MonoGame/pull/4320)
+ - Added mipmap generation for DirectX render targets. [#4189](https://github.com/MonoGame/MonoGame/pull/4189)
+
+
+## 3.5.1 Release - 3/30/2016
+
+ - Fixed negative values when pressing up on left thumbstick on Mac.
+ - Removed exception and just return empty state when requesting an invalid GamePad index.
+ - Fixed texture processing for 64bpp textures.
+ - Fixed Texture2D.SaveAsPng on Mac.
+
+
+## 3.5 Release - 3/17/2016
+
+ - Content Pipeline Integration for Xamarin Studio and MonoDevleop on Mac and Linux.
+ - Automatic inclusion of XNBs into your final project on Mac and Linux.
+ - Improved Mac and Linux installers.
+ - Assemblies are now installed locally on Mac and Linux just like they are on Windows.
+ - New cross-platform Desktop project where same binary and content will work on Windows, Linux and Mac desktops.
+ - Better Support for Xamarin.Mac and Xam.Mac.
+ - Apple TV support (requires to be built from source at the moment).
+ - Various sound system fixes.
+ - New GraphicsMetrics API.
+ - Optimizations to SpriteBatch performance and garbage generation.
+ - Many improvements to the Pipeline tool: added toolbar, new filtered output view, new templates, drag and drop, and more.
+ - New GamePad support for UWP.
+ - Mac and Linux now support Vorbis compressed music.
+ - Major refactor of texture support in content pipeline.
+ - Added 151 new unit tests.
+ - Big improvements to FBX and model content processing.
+ - Various fixes to XML serialization.
+ - MediaLibrary implementation for Windows platforms.
+ - Removed PlayStation Mobile platform.
+ - Added content pipeline extension template project.
+ - Support for binding multiple vertex buffers in a draw call.
+ - Fixed deadzone issues in GamePad support.
+ - OcclusionQuery support for DX platforms.
+ - Fixed incorrect z depth in SpriteBatch.
+ - Lots of OpenTK backend fixes.
+ - Much improved font processing.
+ - Added new VertexPosition vertex format.
+ - Better VS project template installation under Windows.
+
+
+## 3.4 Release - 4/29/2015
+
+ - Removed old XNA content pipeline extensions.
+ - Added all missing PackedVector types.
+ - Replacement of old SDL joystick path with OpenTK.
+ - Added SamplerState.ComparisonFunction feature to DX and OGL platforms.
+ - Fixed bug where content importers would not be autodetected on upper case file extensions.
+ - Fixed compatibility with XNA sound effect XNBs.
+ - Lots of reference doc improvements.
+ - Added SamplerState.BorderColor feature to DX and OGL platforms.
+ - Lots of improvements to the Mac, Linux and Windows versions of the Pipeline GUI tool.
+ - Fixes for bad key mapping on Linux.
+ - Support for texture arrays on DX platforms.
+ - Fixed broken ModelMesh.Tag
+ - VS templates will now only install if VS is detected on your system.
+ - Added Color.MonoGameOrange.
+ - Fixed Xact SoundBack loading bug on Android.
+ - Added support for a bunch of missing render states to MGFX.
+ - Added support for sRGB texture formats to DX and OGL platforms.
+ - Added RasterizerState.DepthClipEnable support for DX and OGL platforms.
+ - New support for the Windows 10 UAP plafform.
+ - Fixed bug which caused the GamePad left thumbstick to not work correctly.
+ - Preliminary base classed for future Joystick API.
+ - Performance improvement on iOS by avoiding unnessasary GL context changes.
+ - Fixed bug where MediaPlayer volume affected all sounds.
+ - New XamarinStudio/MonoDevelop Addin for Mac.
+ - New Mac installer packages.
+
+
+## 3.3 Release - 3/16/2015
+
+ - Support for vertex texture fetch on Windows.
+ - New modern classes for KeyboardInput and MessageBox.
+ - Added more validation to draw calls and render states.
+ - Cleaned up usage of statics to support multiple GraphicsDevice instances.
+ - Support Window.Position on WindowsGL platform.
+ - Reduction of redundant OpenGL calls.
+ - Fullscreen support for Windows DX platform.
+ - Implemented Texture2D SaveAsPng and SaveAsJpeg for Android.
+ - Improved GamePad deadzone calculations.
+ - We now use FFmpeg for audio content building.
+ - BoundingSphere fixes and optimizations.
+ - Many improvements to Linux platform.
+ - Various fixes to FontTextureProcessor.
+ - New Windows Universal App template for Windows Store and Windows Phone support.
+ - Many fixes to reduce garbage generation during runtime.
+ - Adding support for TextureFormatOptions to FontDescriptionProcessor.
+ - XNA compatibility improvements to FontDescriptionProcessor.
+ - Resuscitated the unit test framework with 100s of additional unit tests.
+ - BoundingFrustum fixes and optimizations.
+ - Added VS2013 project templates.
+ - Moved to new MonoGame logo.
+ - Added MSAA render target support for OpenGL platforms.
+ - Added optional content compression support to content pipeline and runtime.
+ - TextureCube content reader and GetData fixes.
+ - New OpenAL software implementation for Android.
+ - Xact compatibility improvements.
+ - Lots of Android fixes and improvements.
+ - Added MediaLibrary implementation for Android, iOS, Windows Phone, and Windows Store.
+ - Added ReflectiveWriter implementation to content pipeline.
+ - Fixes to Texture2D.GetData on DirectX platforms.
+ - SpriteFont rendering performance optimizations.
+ - Huge refactor of ModelProcessor to be more compatible with XNA.
+ - Moved NET and GamerServices into its own MonoGame.Framework.Net assembly.
+ - Runtime support for ETC1 textures for Androud.
+ - Improved compatibility for FBXImporter and XImporter.
+ - Multiple SpritBatch compatibility fixes.
+ - We now use FreeImage in TextureImporter to support many more input formats.
+ - MGFX parsing and render state improvements.
+ - New Pipeline GUI tool for managing content projects for Windows, Mac, and Linux desktops.
+ - New implementation of content pipeline IntermediateSerializer.
+ - All tools and content pipeline built for 64-bit.
+ - New documentation system.
+ - Implement web platform (JSIL) stubs.
+ - Lots of fixes to PSM.
+ - Added Protobuild support for project generation.
+ - Major refactor of internals to better separate platform specific code.
+ - Added MGCB command line tool to Windows installer.
+
+
+## 3.2 Release - 4/7/2014
+
+ - Implemented missing PackedVector types.
+ - VS2013 support for MonoGame templates.
+ - Big improvement to XInput performance on Windows/Windows8.
+ - Added GameWindow.TextInput event enhancement.
+ - Added Xamarin.Mac compatability.
+ - Support for WPF interop under DirectX.
+ - Enhancement to support multiple GameWindows on Windows under DirectX.
+ - Various SpriteFont compatibility improvements.
+ - OpenAL performance/memory/error handling improvements.
+ - Reduction of Effect runtime memory usage.
+ - Support for DXT/S3TC textures on Android.
+ - Touch support on Windows desktop games.
+ - Added new RenderTarget3D enhancement.
+ - OUYA gamepad improvements.
+ - Internal improvements to reduce garbage generation.
+ - Various windowing fixes for OpenTK on Linux, Mac, and Windows.
+ - Automatic support for content reloading on resume for Android.
+ - Support for TextureCube, Texture3D, and RenderTargetCube on DirectX.
+ - Added TitleContainer.SupportRetina enhancement for loading @2x content.
+ - Lots of Android/Kindle compatibility fixes.
+ - Added enhancement GameWindow.IsBorderless.
+ - OpenGL now supports multiple render targets.
+ - Game.IsRunningSlowly working accurately to XNA.
+ - Game tick resolution improvements.
+ - XACT compatibility improvements.
+ - Various fixes and improvements to math types.
+ - DrawUserIndexedPrimitives now works with 32bit indicies.
+ - GamerServices fixes under iOS.
+ - Various MonoGame FX improvements and fixes.
+ - Render target fixes for Windows Phone.
+ - MediaPlayer/MediaQueue/Song fixes on Windows Phone.
+ - XNA accuracy fixes to TitleContainer.
+ - Fixes to SpriteBatch performance and compatibility with XNA.
+ - Threading fixes around SoundEffectInstance.
+ - Support for Song.Duration.
+ - Fixed disposal of OpenGL shader program cache.
+ - Improved support of PoT textures in OpenGL.
+ - Implemented missing EffectParameter SetValue/GetValue calls.
+ - Touch fixes to Windows Phone.
+ - Fixes to orientation support in iOS.
+ - Lots of PSM fixes which make it usable for 2D games.
+ - New Windows desktop platform using DirectX/XAudio.
+ - Old Windows project renamed WindowsGL.
+ - Fixed offsetInBytes parameter in IndexBuffer/VertexBuffer SetData.
+ - Fixed subpixel offset when viewport is changed in OpenGL.
+ - Tons of content pipeline improvements making it close to complete.
+
+
+## 3.0.1 Release - 3/3/2013
+
+ - Fix template error.
+ - Fix offsetInBytes parameter in IndexBuffer/VertexBuffer SetData.
+ - Fixes the scale applied on the origin in SpriteBatch.
+ - Fixed render targets on WP8.
+ - Removed minVertexIndex Exception.
+ - Fixed some threading issues on iOS.
+ - Use generic link for opening store on iOS.
+ - Fix Matrix::Transpose.
+ - Fixed vertexOffset in DrawUserIndexedPrimitives in GL.
+ - Keys.RightControl/RightShift Support for WinRT.
+ - Dispose in ShaderProgramCache.
+ - IsRunningSlowly Fix.
+
+
+## 3.0 Release - 1/21/2013
+
+ - 3D (many thanks to Infinite Flight Studios for the code and Sickhead Games in taking the time to merge the code in).
+ - New platforms: Windows 8, Windows Phone 8, OUYA, PlayStation Mobile (including Vita).
+ - Custom Effects.
+ - PVRTC support for iOS.
+ - iOS supports compressed Songs.
+ - Skinned Meshs.
+ - VS2012 templates.
+ - New Windows Installer.
+ - New MonoDevelop Package/AddIn.
+ - A LOT of bug fixes.
+ - Closer XNA 4 compatibility.
+
+
+## 2.5.1 Release - 6/18/2012
+
+ - Updated android to use enumerations rather than hardocded ids as part of the Mono for Android 4.2 update.
+ - Changed the Android video player to make use of the ViewView.
+ - Corrected namespaces for SongReader and SoundEffectReader.
+ - Updated the Keyboard mapping for android.
+ - Added RectangleArrayReader.
+ - Removed links to the third party GamePadBridge.
+ - Added some missing mouseState operators.
+ - Replaced all calls to DateTime.Now with DateTime.UtcNow.
+ - Fixed SpriteFont rendering (again).
+ - Added code to correclty dispose of Textures on all platforms.
+ - Added some fixes for the sound on iOS.
+ - Adding missing MediaQueue class.
+ - Fixed Rectangle Intersect code.
+ - Changed the way UserPrimitives work on windows.
+ - Made sure the @2x file support on iOS works.
+ - Updated project templates.
+ - Added project templates for MacOS.
+ - Fixed MonoDevelop.MonoGame AddIn so it works on Linux.
+
+
+## 2.5 Release - 3/29/2012
+
+### Fixes and Features
+ - Minor fixes to the Networking stack to make it more reliable when looking for games.
+ - SpriteBatch Fixes including making sure the matrix parameter is applied in both gles 1.1 and gles 2.0.
+ - Updated IDrawable and IUpdatable interfaces to match XNA 4.0.
+ - Fixed the Tick method.
+ - Updated VideoPlayer constructor contract to match XNA 4.0.
+ - Added Code to Lookup the Host Application Guid for Networking, the guid id is now pulled from the AssemblyInfo.cs if one is present.
+ - Uses OpenAL on all platforms except Android.
+ - Added Dxt5 decompression support.
+ - Improves SpriteFont to conform more closely to XNA 4.0.
+ - Moved DynamicVertexBuffer and DynamicIndexBuffer into its own files.
+
+### iOS
+ - Fixed Console.WriteLine problem.
+ - Fixed loading of @2x Retina files.
+ - Fixed Landscape Rendering.
+ - Fixed Orientations changes correctly animate.
+ - Fixed Guide.BeginShowKeyboardInput.
+ - Fixed StorageDevice AOT compile problem.
+ - Fixed SpriteBatch to respect matrices when drawn.
+ - Fixed DoubleTap, improves touches in serial Game instances.
+ - Fixed App startup in non-Portrait orientations.
+ - Fixed UnauthorizedAccessException using TitleContainer.
+ - Fixed a runtime JIT error that was occuring with List().
+ - Guide.ShowKeyboard is not working.
+ - App Backgrounding has regressed. A patch is already being tested in the develop branch and the fix will be rolled out as part of the v2.5.1.
+
+### Android
+ - Project Templates for MonoDevelop.
+ - Fixed a few issues with Gestures.
+ - Fixed the name of the assembly to be MonoGame.Framework.Android.
+ - Fixed a Memory Leak in Texture Loading.
+ - Force linear filter and clamp wrap on npot textures in ES2.0 on Android.
+ - Added SetData and GetData support for Texture2D.
+ - Guide.SignIn picks up the first email account on the phone.
+ - CatapultWars does not render correctly under gles 1.1.
+
+### MacOS X
+ - SoundEffectInstance.Stop now works correctly.
+
+### Linux
+ - Project Templates for Visual Studio and MonoDevelop.
+ - Fixed a bug when loading of Wav files.
+
+### Windows
+ - Project Templates for Visual Studio and MonoDevelop.
+ - Fixed a bug when loading of Wav files.
+ - Added Game.IsMouseVisible implementation for Windows.
+ - Guide.SignIn picks up the logged in user.
+ - Added a new Installer to install the MonoDevelop and / or Visual Studio Templates and binaries.
+
+
+## 2.1 Release - 10/28/2011
+
+### Features
+ - Content Manager rewritten to use partial classes and implementation of cached assets that are loaded. Greatly improves memory footprint.
+ - Experimental support for GamePads and Joysticks. Enhancements will be coming to integrate better for developers.
+ - ContentReader improvements across the board.
+ - Improved support for XACT audio.
+ - StarterKits VectorRumble.
+
+### iOS
+ - Gesture support has been improved.
+ - Better support for portrait to landscape rotations.
+ - Fixed a rendering bug related to upsidedown portrait mode.
+ - Better WaveBank support.
+ - The Guide functionality is only available in iOS, for this release.
+
+### Android
+ - Updated to support Mono for Android 4.0.
+ - Improvements to the Orientation Support.
+ - Changed Sound system to use SoundPool.
+ - Added Tap and DoubleTap Gesture Support.
+
+### MacOS X
+ - A lot of enhancements and fixes for Full Screen and Windowed control.
+ - Cursor support fixed for IsMouseVisible.
+ - Implementation of IsActive property and the events Activated and Deactivated.
+ - First steps of DrawPrimitives, DrawUserPrimitives, DrawIndexedPrimitives.
+ - Better WaveBank support.
+ - Support for ApplyChanges() and setting the backbuffer and viewport sizes correctly.
+
+### Linux
+ - All new implementation which share quite a bit of code between MacOS X and Windows.
+ - Added shader support via the Effects class.
+
+### Windows
+ - All new implementation which shares quite a bit of code between MacOS and Linux.
+
+
+## 2.0 Release - 10/28/2011
+
+ - Project renamed MonoGame.
+ - Project moved to GitHub.
+ - Support for Linux, Mac, Linux, and OpenGL on Windows.
+
+
+## 0.7 Release - 12/2/2009
+
+ - First stable release.
+ - Originally named XnaTouch.
+ - iPhone support only.
+ - 2D rendering support.
+ - Audio support.
+ - Networking support.
+ - Partial multitouch support.
+ - Partial accelerometer support.
diff --git a/CODESTYLE.md b/CODESTYLE.md
new file mode 100644
index 00000000000..f7c5216ac02
--- /dev/null
+++ b/CODESTYLE.md
@@ -0,0 +1,228 @@
+> #### NOTE: This code style standard for MonoGame is a work in progress and much of the code does not currently conform to these rules. This is something that will be addressed by the core team.
+
+# Introduction
+As the MonoGame project gains more traction and becomes more widely used, we are aiming to provide a more professional and consistent look to the large amount of source now in the project. It was a broadly supported decision by the core development team to follow the Microsoft coding guidelines (the default provided in Visual Studio's C# editor). These coding guidelines listed below have been based on a [MSDN blog post](http://blogs.msdn.com/b/brada/archive/2005/01/26/361363.aspx) from 2005 by Brad Abrams describing the internal coding guidelines at Microsoft, with some changes to suit our project.
+# Coding Guidelines
+## Tabs & Indenting
+Tab characters (\0x09) should not be used in code. All indentation should be done with 4 space characters.
+## Bracing
+Open braces should always be at the beginning of the line after the statement that begins the block. Contents of the brace should be indented by 4 spaces. Single statements do not have braces. For example:
+```
+if (someExpression)
+{
+ DoSomething();
+ DoAnotherThing();
+}
+else
+ DoSomethingElse();
+```
+
+`case` statements should be indented from the switch statement like this:
+```
+switch (someExpression)
+{
+ case 0:
+ DoSomething();
+ break;
+
+ case 1:
+ DoSomethingElse();
+ break;
+
+ case 2:
+ {
+ int n = 1;
+ DoAnotherThing(n);
+ }
+ break;
+}
+```
+
+Braces are not used for single statement blocks immediately following a `for`, `foreach`, `if`, `do`, etc. The single statement block should always be on the following line and indented by four spaces. This increases code readability and maintainability.
+```
+for (int i = 0; i < 100; ++i)
+ DoSomething(i);
+```
+
+## Single line property statements
+Single line property statements can have braces that begin and end on the same line. This should only be used for simple property statements. Add a single space before and after the braces.
+```
+public class Foo
+{
+ int bar;
+
+ public int Bar
+ {
+ get { return bar; }
+ set { bar = value; }
+ }
+}
+```
+
+## Commenting
+Comments should be used to describe intention, algorithmic overview, and/or logical flow. It would be ideal, if from reading the comments alone, someone other than the author could understand a functions intended behavior and general operation. While there are no minimum comment requirements and certainly some very small routines need no commenting at all, it is hoped that most routines will have comments reflecting the programmers intent and approach.
+
+Comments must provide added value or explanation to the code. Simply describing the code is not helpful or useful.
+```
+ // Wrong
+ // Set count to 1
+ count = 1;
+
+ // Right
+ // Set the initial reference count so it isn't cleaned up next frame
+ count = 1;
+```
+
+### Copyright/License notice
+Each file should start with a copyright notice. To avoid errors in doc comment builds, you dont want to use triple-slash doc comments. This is a short statement declaring the project name, copyright notice and directing the reader to the license document elsewhere in the project.
+```
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+```
+
+### Documentation Comments
+All methods should use XML doc comments. For internal dev comments, the `` tag should be used.
+```
+public class Foo
+{
+ /// Public stuff about the method
+ /// What a neat parameter!
+ /// Cool internal stuff!
+ public void MyMethod(int bar)
+ {
+
+ }
+}
+```
+
+### Comment Style
+The // (two slashes) style of comment tags should be used in most situations. Where ever possible, place comments above the code instead of beside it. Here are some examples:
+```
+ // This is required for WebClient to work through the proxy
+ GlobalProxySelection.Select = new WebProxy("http://itgproxy");
+
+ // Create object to access Internet resources
+ WebClient myClient = new WebClient();
+```
+
+## Spacing
+Spaces improve readability by decreasing code density. Here are some guidelines for the use of space characters within code:
+
+Do use a single space after a comma between function arguments.
+```
+Console.In.Read(myChar, 0, 1); // Right
+Console.In.Read(myChar,0,1); // Wrong
+```
+Do not use a space after the parenthesis and function arguments
+```
+CreateFoo(myChar, 0, 1) // Right
+CreateFoo( myChar, 0, 1 ) // Wrong
+```
+Do not use spaces between a function name and parenthesis.
+```
+CreateFoo() // Right
+CreateFoo () // Wrong
+```
+Do not use spaces inside brackets.
+```
+x = dataArray[index]; // Right
+x = dataArray[ index ]; // Wrong
+```
+Do use a single space before flow control statements
+```
+while (x == y) // Right
+while(x==y) // Wrong
+```
+Do use a single space before and after binary operators
+```
+if (x == y) // Right
+if (x==y) // Wrong
+```
+Do not use a space between a unary operator and the operand
+```
+++i; // Right
+++ i; // Wrong
+```
+Do not use a space before a semi-colon. Do use a space after a semi-colon if there is more on the same line
+```
+for (int i = 0; i < 100; ++i) // Right
+for (int i=0 ; i<100 ; ++i) // Wrong
+```
+
+## Naming
+Follow all .NET Framework Design Guidelines for both internal and external members. Highlights of these include:
+* Do not use Hungarian notation
+* Do use an underscore prefix for member variables, e.g. `_foo`
+* Do use camelCasing for member variables (first word all lowercase, subsequent words initial uppercase)
+* Do use camelCasing for parameters
+* Do use camelCasing for local variables
+* Do use PascalCasing for function, property, event, and class names (all words initial uppercase)
+* Do prefix interfaces names with I
+* Do not prefix enums, classes, or delegates with any letter
+
+The reasons to extend the public rules (no Hungarian, underscore prefix for member variables, etc.) is to produce a consistent source code appearance. In addition a goal is to have clean readable source. Code legibility should be a primary goal.
+
+## File Organization
+* Source files should contain only one public type, although multiple internal types are permitted if required
+* Source files should be given the name of the public type in the file
+* Directory names should follow the namespace for the class after `Framework`. For example, I would expect to find the public class `Microsoft.Xna.Framework.Graphics.GraphicsDevice` in **MonoGame.Framework\Graphics\GraphicsDevice.cs**
+* Class members should be grouped logically, and encapsulated into regions (Fields, Constructors, Properties, Events, Methods, Private interface implementations, Nested types)
+* Using statements should be before the namespace declaration.
+```
+using System;
+
+namespace MyNamespace
+{
+ public class MyClass : IFoo
+ {
+ #region Fields
+ int foo;
+ #endregion
+
+ #region Properties
+ public int Foo { get { } set { } }
+ #endregion
+
+ #region Constructors
+ public MyClass()
+ {
+
+ }
+ #endregion
+
+ #region Events
+ public event EventHandler FooChanged { add { } remove { } }
+ #endregion
+
+ #region Methods
+ void DoSomething()
+ {
+
+ }
+
+ void FindSomething()
+ {
+
+ }
+ #endregion
+
+ #region Private interface implementations
+ void IFoo.DoSomething()
+ {
+ DoSomething();
+ }
+ #endregion
+
+ #region Nested types
+ class NestedType
+ {
+
+ }
+ #endregion
+ }
+}
+```
+
+# Useful Links
+[C# Coding Conventions (MSDN)](http://msdn.microsoft.com/en-us/library/ff926074.aspx)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index aeb326c3599..d33f92b98ba 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -31,7 +31,7 @@ Here are a few simple rules and suggestions to remember when contributing to Mon
* **PLEASE** be sure to write simple and descriptive commit messages.
* **DO NOT** surprise us with new APIs or big new features. Open an issue to discuss your ideas first.
* **DO NOT** reorder type members as it makes it difficult to compare code changes in a PR.
-* **DO** try to follow our [coding style](https://github.com/mono/MonoGame/wiki/Coding-Guidelines) for new code.
+* **DO** try to follow our [coding style](CODESTYLE.md) for new code.
* **DO** give priority to the existing style of the file you're changing.
* **DO** try to add to our [unit tests](Test) when adding new features or fixing bugs.
* **DO NOT** send PRs for code style changes or make code changes just for the sake of style.
diff --git a/Documentation/Styles/MonoGame/css/sharpdoc.css b/Documentation/Styles/MonoGame/css/sharpdoc.css
index 0df6f1e0b62..a8ca4c3e676 100644
--- a/Documentation/Styles/MonoGame/css/sharpdoc.css
+++ b/Documentation/Styles/MonoGame/css/sharpdoc.css
@@ -378,17 +378,6 @@
background-image:url("../images/android.png");
}
-.sharpdoc div.icon.ouya
-{
- background-image:url("../images/ouya.png");
-}
-
-.sharpdoc div.icon.psm
-{
- background-image:url("../images/psm.png");
-}
-
-
.sharpdoc .content .section .members table {
table-layout:fixed;
width:100%;
diff --git a/Documentation/config.xml b/Documentation/config.xml
index 4f73210c32c..319231d96a7 100644
--- a/Documentation/config.xml
+++ b/Documentation/config.xml
@@ -50,6 +50,8 @@
+
+
@@ -88,11 +90,6 @@
..\MonoGame.Framework\bin\Windows8\AnyCPU\Release\MonoGame.Framework.dll
-
-
- ..\MonoGame.Framework\bin\WindowsPhone\x86\Release\MonoGame.Framework.dll
-
-
..\MonoGame.Framework\bin\Web\AnyCPU\Release\MonoGame.Framework.dll
@@ -112,11 +109,4 @@
-->
-
-
-
diff --git a/Documentation/content_intro.md b/Documentation/content_intro.md
index e1a862a8b23..dce6e337783 100644
--- a/Documentation/content_intro.md
+++ b/Documentation/content_intro.md
@@ -6,7 +6,7 @@ This section will cover the following topics:
- What is Game Content
- [Using The Pipeline Tool](using_pipeline_tool.md)
- - [Using TrueType Fonts](adding_fonts.md)
+ - [Using TrueType Fonts](adding_ttf_fonts.md)
- [Custom Effects](custom_effects.md)
- Custom Content Types
diff --git a/Documentation/mgcb.md b/Documentation/mgcb.md
index 888755d965c..947d0d64002 100644
--- a/Documentation/mgcb.md
+++ b/Documentation/mgcb.md
@@ -48,15 +48,12 @@ An optional parameter which adds an assembly reference which contains importers,
Set the target platform for this build. It must be a member of the TargetPlatform enum:
* Windows
* Xbox360
-* WindowsPhone
* iOS
* Android
* Linux
* MacOSX
* WindowsStoreApp
* NativeClient
-* Ouya
-* PlayStationMobile
* PlayStation4
* WindowsPhone8
* RaspberryPi
diff --git a/Documentation/pipeline.md b/Documentation/pipeline.md
index 9c545da97d0..b8daab6855a 100644
--- a/Documentation/pipeline.md
+++ b/Documentation/pipeline.md
@@ -4,10 +4,10 @@ The MonoGame Pipeline Tool (Pipeline.exe) is the front-end GUI editor for MonoGa
-The Pipeline Tool is still relatively new to MonoGame, but it already has the following features:
+The Pipeline Tool has the following features:
* Create, open, and save MGCB projects.
- * Import existing .contentproj.
+ * Import existing XNA .contentproj.
* Tree view showing content of project.
* Property grid for editing content settings.
* Full undo/redo support.
@@ -17,7 +17,7 @@ The Pipeline Tool is still relatively new to MonoGame, but it already has the fo
* Support for custom importers/processors/writers.
* Template format for adding new custom content types.
-Currently the Pipeline Tool is only available on Windows and is part of the SDK installation. The Mac and Linux versions are in development and should be available soon.
+The Pipeline Tool is included in the SDK installation.
[Read detailed documentation](using_pipeline_tool.md)
diff --git a/Documentation/setting_up_monogame_linux.md b/Documentation/setting_up_monogame_linux.md
index bc859a09ec8..8f49b03acc7 100644
--- a/Documentation/setting_up_monogame_linux.md
+++ b/Documentation/setting_up_monogame_linux.md
@@ -13,27 +13,18 @@ sudo apt-get install libopenal-dev mono-runtime
### Developing MonoGame Applications
-##### From Ubuntu 15.04 or newer:
-
-* Go to [MonoGame Downloads page](http://www.monogame.net/downloads/)
-* Download MonoGame for Ubuntu
-* Open up "monogame-sdk.deb" and install it
-* That's it, MonoGame SDK is installed
-
-##### From other Linux distros:
-
* Go to [MonoGame Downloads page](http://www.monogame.net/downloads/)
-* Download MonoGame for other Linux distros
+* Download MonoGame for Linux
* Open up terminal and type in:
```
cd Downloads
chmod +x monogame-sdk.run
sudo ./monogame-sdk.run
```
-* During the installation process the installer will ask you if you wish to install any missing dependencies automatically. If you for some reason don't want to install them automatically or the dependency installer is not available for your linux distribution, here is the list of needed packages:
+* During the installation process the installer will give you the following list of dependencies, please make sure they are installed:
* monodevelop ([http://www.monodevelop.com/download/](http://www.monodevelop.com/download/))
* libopenal-dev
- * referenceassemblies-pcl
* gtk-sharp3
+ * referenceassemblies-pcl (needed to use PCL template)
* ttf-mscorefonts-installer (recommended, but not needed)
* That's it, MonoGame SDK is installed
diff --git a/Documentation/setting_up_monogame_source.md b/Documentation/setting_up_monogame_source.md
index c3e49a55d8a..ae1148aab22 100644
--- a/Documentation/setting_up_monogame_source.md
+++ b/Documentation/setting_up_monogame_source.md
@@ -4,22 +4,23 @@ This section will help you setup MonoGame by building it from source code.
Install the tools for the system you are building from:
* Windows:
- * [GitHub for Windows](https://windows.github.com/)
- * [Visual Studio](https://www.visualstudio.com/) or [Xamarin Studio](http://www.monodevelop.com/download/)
- * Optional download Xamarin.Android and Windows Phone 8 SDK
+ * [Git for Windows](https://git-scm.com/download/win)
+ * [Visual Studio](https://www.visualstudio.com/)
+ * [Xamarin.Android](https://www.xamarin.com/download) (Optional)
+ * [Windows Phone 8 SDK](https://www.microsoft.com/en-us/download/details.aspx?id=35471) (Optional)
* Mac:
- * [GitHub for Mac](https://mac.github.com/)
- * [Xamarin Studio](http://www.monodevelop.com/download/)
- * Optional download Xamarin.Android and Xamarin.iOS
+ * [Git](https://git-scm.com/download/mac)
+ * [Xamarin Studio](https://store.xamarin.com/)
+ * Xamarin.Android and Xamarin.iOS can be installed with the Xamarin Studio installer (Optional)
* Linux:
- * Install package called "git"
+ * [Git](https://git-scm.com/download/linux)
* [Monodevelop](http://www.monodevelop.com/download/linux/)
### Getting the source code
-If on Windows, start up Git Shell, if on Unix, start the terminal and type in the following:
+Start up a Terminal (Mac/Linux) or Git Bash (Windows) and clone the MonoGame repository:
```
-git clone https://github.com/mono/MonoGame.git
+git clone https://github.com/MonoGame/MonoGame.git
cd MonoGame
git submodule init
git submodule update
@@ -27,12 +28,13 @@ git submodule update
### Building from source
-If on Windows just start Protobuild.exe.
+MonoGame uses [Protobuild](https://protobuild.org/) to generate project and solution files. Protobuild.exe will be in your MonoGame folder. To run Protobuild:
-if on Unix open terminal and type in:
-```
-cd MonoGame
-mono Protobuild.exe
-```
+- On Windows run Protobuild.exe either by double-clicking or by executing it from the command line.
+- On Mac/Linux open a terminal and run `mono Protobuild.exe` in the MonoGame folder.
+
+Once the project and solution files are generated you can build them with the IDE you installed.
+
+### Referencing the projects
-Now the sln files are generated and you can build them with either Monodevelop or Visual Studio.
+First get the MonoGame SDK from the [downloads page](http://www.monogame.net/downloads/) and install it to get the IDE templates. Start up the IDE you have installed and create a new project from one of the templates. Click Add > Existing Project... on your solution and select the MonoGame.Framework project that matches the template (i.e. MonoGame.Framework.Windows.csproj for a MonoGame Windows project template). The project files are located in MonoGame/MonoGame.Framework. Delete the existing MonoGame.Framework reference and add a reference to the added project by clicking Add Reference... > Projects and selecting the project. You can run your game now. If you make changes to the MonoGame.Framework project it will automatically rebuild when running your game.
diff --git a/Documentation/tutorials.md b/Documentation/tutorials.md
index 423b8edc6f6..c36028ca27c 100644
--- a/Documentation/tutorials.md
+++ b/Documentation/tutorials.md
@@ -35,7 +35,6 @@ Tara Walker's "Building a Shooter Game" tutorial series.
- [BMFont rendering with MonoGame](http://www.craftworkgames.com/blog/tutorial-bmfont-rendering-with-monogame/)
- [RB Whitaker's MonoGame Tutorials](http://rbwhitaker.wikidot.com/monogame-tutorials)
- [RB Whitaker's XNA Tutorials including sharers, 2D, 3D, Game loops, advancted topics](http://rbwhitaker.wikidot.com/xna-tutorials)
- - [Another list of tutorials from the MonoGame github](https://github.com/mono/MonoGame/wiki/Tutorials)
- [Setting window position tutorial](http://projectdrake.net/blog/?p=176)
- [Using Spine with MonoGame - by Randolph Burt (Randeroo)](http://randolphburt.co.uk/2013/03/30/dragons-and-dancing-crabs/)
- [Mac porting series](http://benkane.wordpress.com/2012/01/20/the-great-porting-adventure-day-8/)
diff --git a/Documentation/using_pipeline_tool.md b/Documentation/using_pipeline_tool.md
index 3fca7416b60..20da1827815 100644
--- a/Documentation/using_pipeline_tool.md
+++ b/Documentation/using_pipeline_tool.md
@@ -1,4 +1,4 @@
-The [Pipeline Tool](pipeline.md) is used to organize and define content for use with MonoGame. It is installed as part of the MonoGame SDK Installer or can be build [directly from source](https://github.com/mono/MonoGame/tree/develop/Tools/Pipeline) if needed.
+The [Pipeline Tool](pipeline.md) is used to organize and build content for use with MonoGame. It is installed as part of the MonoGame SDK Installer or can be built [directly from source](https://github.com/mono/MonoGame/tree/develop/Tools/Pipeline) if needed.
## Create A Project
@@ -27,7 +27,7 @@ Note that currently the Pipeline tool is not setup to support multiple target pl
## Adding Content Items
-Once you have a project setup you can then add content to it for building. You can do this from the "Edit" menu:
+Once you have a project setup you can add content to it for building. You can do this from the "Edit" menu:
@@ -39,12 +39,12 @@ Selecting "New Item..." will bring up the New Item dialog which displays a list
-When you select "Add Item..." you get to select an existing item from disk to add to the content project.
+When you select "Existing Item..." you get to select an existing item from disk to add to the content project.
## Custom Content Processors
-Justl ike XNA, the MonoGame content pipeline supports custom content processors. To use them you need to rebuild them correctly to work against MonoGame.
+Just like XNA, the MonoGame content pipeline supports custom content processors. To use them you need to rebuild them correctly to work against MonoGame.
The first step is removing all `Microsoft.Xna.Framework.XXX` references and replacing them with references to `MonoGame.Framework` and `MonoGame.Framework.Content.Pipeline`. This is required as you will no longer be building against Microsoft XNA.
@@ -52,19 +52,26 @@ Once you references are working you then need to change your assembly target pla
After you have done these fixes you should be able to add these new processors to the content project "References".
+## Building Content
+
+The Pipeline Tool has 3 actions related to building content: Build, Rebuild and Clean. Build will build all content that needs to be built and put the xnb's in the output directory (bin by default). Content will be skipped if it hasn't changed since the last build. The time source content was last edited is saved in the intermediate directory (obj by default) to determine if content changed since the last build. Clean will empty the output and intermediate directories. Rebuild will first Clean and then Build.
## Linking Content To Your Game
-Once you have built your content you have a few different ways to add the XNBs to your game project.
+Once you have built your content you have a few different ways to add the xnb's to your game project. They all have the same goal, to get the built xnb's in your project output folder so a ContentManager can easily find and load them.
+
+### MonoGameContentReference
+
+The simplest method is to setup your game project from one of the templates that come with the SDK. When you create a new project it will include a Content.mgcb file with its Build Action set to MonoGameContentReference. This build action is defined in the .targets file [here](https://github.com/MonoGame/MonoGame/blob/develop/MonoGame.Framework.Content.Pipeline/MonoGame.Content.Builder.targets). MonoGameContentReference is set up so that when the project is built, the mgcb will build any new/modified content and copy the resulting xnb's to the project output directory so they can be used in the project. Note that this way you don't even have to manually build the content with the Pipeline Tool. Just add your content to the .mgcb with the Pipeline Tool and the rest will happen when you build your project. The content files do not need to be added to your project.
### Manual Copy
-The simplest and method is to simply copy the content into a Content folder under the output folder where you game executable is. This should work for most desktop builds.
+If you don't want to use the automated process, you can build the content project with the Pipeline Tool and copy the xnb's to the output folder of your project manually.
### Add As Content
-If you are using Visual Studio you can simply add the content files into your C# game project. Create a folder in the project called Content then right click on the folder and select Add -> Existing Item.
+If you are using Visual Studio you can simply add the xnb files to your C# game project. Create a folder in the project called Content then right click on the folder and select Add -> Existing Item.
@@ -85,7 +92,7 @@ Once the files are added you need to select them all and change their build acti
### Add With Wildcard
-The more automatic option is to hand edit your game .csproj and have it include you content using wildcards. To do this just open the .csproj with any text editor then add the following after any other ``:
+The more automatic option is to hand edit your game .csproj and have it include you content using wildcards. To do this just open the .csproj with any text editor then add the following after any other ``:
```
@@ -100,4 +107,4 @@ Then any files you put in a Content folder within your game project will automat
## Reporting Bugs
-The MonoGame content pipeline, MGCB, and the Pipeline tool are all very new in MonoGame. If you run into any problems with them please ask for help on the [community site](http://community.monogame.net/) or submit a [bug report on GitHub](https://github.com/mono/MonoGame/issues).
+If you run into any problems with MGCB or the Pipeline Tool, please ask for help on the [community site](http://community.monogame.net/) or submit a [bug report on GitHub](https://github.com/MonoGame/MonoGame/issues).
diff --git a/Documentation/welcome.md b/Documentation/welcome.md
index 79ba78ef564..b313fc08a29 100644
--- a/Documentation/welcome.md
+++ b/Documentation/welcome.md
@@ -3,8 +3,6 @@ Welcome to the MonoGame game library documentation hub.
### WORK IN PROGRESS
Note that the MonoGame documentation project is currently a work in progress and applies to the **latest development build** of MonoGame.
-If you are looking for documentation on previous releases of MonoGame you can reference our [GitHub wiki](http://github.com/mono/MonoGame/wiki) and the original [Codeplex documentation](http://monogame.codeplex.com/documentation) while we're getting this all sorted out.
-
### We Need Your Help!
Truly great open source projects require high quality documentation. This is call for volunteers to help us make the MonoGame documentation truly great. If you can help write tutorials, guides, code snippets, reference docs, video walkthroughs or just any improvement to our current documentation we could use your help!
diff --git a/IDE/MonoDevelop/MonoDevelop.MonoGame.Android/MonoDevelop.MonoGame.Android.csproj b/IDE/MonoDevelop/MonoDevelop.MonoGame.Android/MonoDevelop.MonoGame.Android.csproj
index b8cb33f833b..b6209ad111b 100644
--- a/IDE/MonoDevelop/MonoDevelop.MonoGame.Android/MonoDevelop.MonoGame.Android.csproj
+++ b/IDE/MonoDevelop/MonoDevelop.MonoGame.Android/MonoDevelop.MonoGame.Android.csproj
@@ -62,7 +62,6 @@
-
@@ -104,8 +103,8 @@
PreserveNewest
-
- PreserveNewest
-
+
+
+
diff --git a/IDE/MonoDevelop/MonoDevelop.MonoGame.Android/templates/Android/Activity1.cs b/IDE/MonoDevelop/MonoDevelop.MonoGame.Android/templates/Android/Activity1.cs
index db79b7ad21e..6aa5efde19e 100644
--- a/IDE/MonoDevelop/MonoDevelop.MonoGame.Android/templates/Android/Activity1.cs
+++ b/IDE/MonoDevelop/MonoDevelop.MonoGame.Android/templates/Android/Activity1.cs
@@ -8,10 +8,6 @@
using Android.Widget;
using Android.OS;
-#if OUYA
-using Ouya.Console.Api;
-#endif
-
using Microsoft.Xna.Framework;
namespace ${Namespace}
@@ -22,14 +18,11 @@ namespace ${Namespace}
Theme = "@style/Theme.Splash",
AlwaysRetainTaskState=true,
LaunchMode=LaunchMode.SingleInstance,
+ ScreenOrientation = ScreenOrientation.FullUser,
ConfigurationChanges = ConfigChanges.Orientation |
ConfigChanges.KeyboardHidden |
ConfigChanges.Keyboard |
ConfigChanges.ScreenSize)]
- #if OUYA
- [IntentFilter(new[] { Intent.ActionMain }
- , Categories = new[] { Intent.CategoryLauncher, OuyaIntent.CategoryGame })]
- #endif
public class Activity1 : AndroidGameActivity
{
protected override void OnCreate (Bundle bundle)
diff --git a/IDE/MonoDevelop/MonoDevelop.MonoGame.Android/templates/MonoGameOUYAProject.xpt.xml b/IDE/MonoDevelop/MonoDevelop.MonoGame.Android/templates/MonoGameOUYAProject.xpt.xml
deleted file mode 100644
index dac32e754c3..00000000000
--- a/IDE/MonoDevelop/MonoDevelop.MonoGame.Android/templates/MonoGameOUYAProject.xpt.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-
-
-
- <_Name>MonoGame for OUYA Application
- monogame/app/games
- monogame-project
- C#
- <_Description>Creates an MonoGame for OUYA Project
-
-
-
-
-
-
-
-
- ${ProjectName}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/IDE/MonoDevelop/MonoDevelop.MonoGame.Mac/MonoDevelop.MonoGame.Mac.csproj b/IDE/MonoDevelop/MonoDevelop.MonoGame.Mac/MonoDevelop.MonoGame.Mac.csproj
index c8153acaaec..888733b7cfa 100644
--- a/IDE/MonoDevelop/MonoDevelop.MonoGame.Mac/MonoDevelop.MonoGame.Mac.csproj
+++ b/IDE/MonoDevelop/MonoDevelop.MonoGame.Mac/MonoDevelop.MonoGame.Mac.csproj
@@ -39,18 +39,10 @@
/Applications/Xamarin Studio.app/Contents/Resources/lib/monodevelop/bin/MonoDevelop.Core.dll
False
-
- /Applications/Xamarin Studio.app/Contents/Resources/lib/monodevelop/AddIns/MonoDevelop.MonoMac/MonoDevelop.MonoMac.dll
- False
-
/Applications/Xamarin Studio.app/Contents/Resources/lib/monodevelop/AddIns/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.dll
False
-
- /Applications/Xamarin Studio.app/Contents/Resources/lib/monodevelop/AddIns/MonoDevelop.MacDev/MonoDevelop.MacDev.dll
- False
-
diff --git a/IDE/MonoDevelop/MonoDevelop.MonoGame.Mac/templates/MonoGameOSXProject.xpt.xml b/IDE/MonoDevelop/MonoDevelop.MonoGame.Mac/templates/MonoGameOSXProject.xpt.xml
index 39676ae2f16..0ab9c08f043 100644
--- a/IDE/MonoDevelop/MonoDevelop.MonoGame.Mac/templates/MonoGameOSXProject.xpt.xml
+++ b/IDE/MonoDevelop/MonoDevelop.MonoGame.Mac/templates/MonoGameOSXProject.xpt.xml
@@ -27,8 +27,6 @@
-
-
@@ -41,6 +39,11 @@
+
+
+
+
+
diff --git a/IDE/MonoDevelop/MonoDevelop.MonoGame.Mac/templates/MonoGameXamMacProject.xpt.xml b/IDE/MonoDevelop/MonoDevelop.MonoGame.Mac/templates/MonoGameXamMacProject.xpt.xml
index 712488048eb..d49598230c7 100644
--- a/IDE/MonoDevelop/MonoDevelop.MonoGame.Mac/templates/MonoGameXamMacProject.xpt.xml
+++ b/IDE/MonoDevelop/MonoDevelop.MonoGame.Mac/templates/MonoGameXamMacProject.xpt.xml
@@ -27,8 +27,6 @@
-
-
@@ -42,6 +40,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/IDE/MonoDevelop/MonoDevelop.MonoGame.iOS/templates/iOS/Info_tvOS.plist.xml b/IDE/MonoDevelop/MonoDevelop.MonoGame.iOS/templates/iOS/Info_tvOS.plist.xml
index 9316a2c3a1f..419e6eaddd0 100644
--- a/IDE/MonoDevelop/MonoDevelop.MonoGame.iOS/templates/iOS/Info_tvOS.plist.xml
+++ b/IDE/MonoDevelop/MonoDevelop.MonoGame.iOS/templates/iOS/Info_tvOS.plist.xml
@@ -24,8 +24,6 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
- UIMainStoryboardFile
- Main
XSAppIconAssets
Resources/Images.xcassets/AppIcons.appiconset
diff --git a/IDE/MonoDevelop/MonoDevelop.MonoGame/ContentItemTemplate.cs b/IDE/MonoDevelop/MonoDevelop.MonoGame/ContentItemTemplate.cs
index 1ab59e10dac..27564325e14 100644
--- a/IDE/MonoDevelop/MonoDevelop.MonoGame/ContentItemTemplate.cs
+++ b/IDE/MonoDevelop/MonoDevelop.MonoGame/ContentItemTemplate.cs
@@ -31,10 +31,11 @@ public override void Load (XmlElement filenode, FilePath baseDirectory)
}
}
- public override bool AddToProject (SolutionItem policyParent, Project project, string language, string directory, string name)
+ public override bool AddToProject(SolutionFolderItem policyParent, Project project, string language, string directory, string name)
{
- ProjectFile file = template.AddFileToProject (policyParent, project, language, directory, name);
- if (file != null) {
+ ProjectFile file = template.AddFileToProject(policyParent, project, language, directory, name);
+ if (file != null)
+ {
file.BuildAction = BuildAction.Content;
file.CopyToOutputDirectory = mode;
return true;
diff --git a/IDE/MonoDevelop/MonoDevelop.MonoGame/MonoDevelop.MonoGame.addin.xml b/IDE/MonoDevelop/MonoDevelop.MonoGame/MonoDevelop.MonoGame.addin.xml
index 8865dfee4eb..122f3c62e53 100644
--- a/IDE/MonoDevelop/MonoDevelop.MonoGame/MonoDevelop.MonoGame.addin.xml
+++ b/IDE/MonoDevelop/MonoDevelop.MonoGame/MonoDevelop.MonoGame.addin.xml
@@ -23,9 +23,10 @@
+
+
-
@@ -60,15 +61,24 @@
+
+
+
+
+
+
+
+
+
+
+
-
-
@@ -77,8 +87,8 @@
-
-
+
+
@@ -87,14 +97,15 @@
-
+
+
+ -->
@@ -109,9 +120,11 @@
+
@@ -151,9 +164,11 @@
+
@@ -163,7 +178,6 @@
-
@@ -202,4 +216,4 @@
-
\ No newline at end of file
+
diff --git a/IDE/MonoDevelop/MonoDevelop.MonoGame/MonoDevelop.MonoGame.csproj b/IDE/MonoDevelop/MonoDevelop.MonoGame/MonoDevelop.MonoGame.csproj
index cf6630daa7e..dc254792e2c 100644
--- a/IDE/MonoDevelop/MonoDevelop.MonoGame/MonoDevelop.MonoGame.csproj
+++ b/IDE/MonoDevelop/MonoDevelop.MonoGame/MonoDevelop.MonoGame.csproj
@@ -23,9 +23,9 @@
-
-
-
+
+
+
@@ -71,10 +71,8 @@
-
-
@@ -91,6 +89,9 @@
+
+
+
@@ -132,6 +133,9 @@
PreserveNewest
+
+ PreserveNewest
+
diff --git a/IDE/MonoDevelop/MonoDevelop.MonoGame/templates/Common/Game1.cs b/IDE/MonoDevelop/MonoDevelop.MonoGame/templates/Common/Game1.cs
index 5841d2e443b..67e26cbe970 100644
--- a/IDE/MonoDevelop/MonoDevelop.MonoGame/templates/Common/Game1.cs
+++ b/IDE/MonoDevelop/MonoDevelop.MonoGame/templates/Common/Game1.cs
@@ -2,7 +2,6 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
-using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Input;
namespace ${Namespace}
diff --git a/IDE/MonoDevelop/MonoDevelop.MonoGame/templates/Common/app.manifest b/IDE/MonoDevelop/MonoDevelop.MonoGame/templates/Common/app.manifest
new file mode 100644
index 00000000000..724f8c407db
--- /dev/null
+++ b/IDE/MonoDevelop/MonoDevelop.MonoGame/templates/Common/app.manifest
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true/pm
+
+
+
+
diff --git a/IDE/MonoDevelop/MonoDevelop.MonoGame/templates/MonoGamePipelineProject.xpt.xml b/IDE/MonoDevelop/MonoDevelop.MonoGame/templates/MonoGamePipelineProject.xpt.xml
index e58c278b12c..6e329adc742 100644
--- a/IDE/MonoDevelop/MonoDevelop.MonoGame/templates/MonoGamePipelineProject.xpt.xml
+++ b/IDE/MonoDevelop/MonoDevelop.MonoGame/templates/MonoGamePipelineProject.xpt.xml
@@ -13,7 +13,7 @@
-
+
diff --git a/IDE/MonoDevelop/MonoDevelop.MonoGame/templates/MonoGameProject.xpt.xml b/IDE/MonoDevelop/MonoDevelop.MonoGame/templates/MonoGameProject.xpt.xml
index 730231e1488..e30915ea618 100644
--- a/IDE/MonoDevelop/MonoDevelop.MonoGame/templates/MonoGameProject.xpt.xml
+++ b/IDE/MonoDevelop/MonoDevelop.MonoGame/templates/MonoGameProject.xpt.xml
@@ -24,12 +24,11 @@
-
-
+
@@ -38,8 +37,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
diff --git a/IDE/MonoDevelop/MonoDevelop.MonoGame/templates/MonoGameWindowsProject.xpt.xml b/IDE/MonoDevelop/MonoDevelop.MonoGame/templates/MonoGameWindowsProject.xpt.xml
index 8eecb2bf760..d046d4f7c3e 100644
--- a/IDE/MonoDevelop/MonoDevelop.MonoGame/templates/MonoGameWindowsProject.xpt.xml
+++ b/IDE/MonoDevelop/MonoDevelop.MonoGame/templates/MonoGameWindowsProject.xpt.xml
@@ -28,6 +28,7 @@
+
diff --git a/IDE/MonoDevelop/default.build b/IDE/MonoDevelop/default.build
index 1facd50a2f1..75af7fb1681 100644
--- a/IDE/MonoDevelop/default.build
+++ b/IDE/MonoDevelop/default.build
@@ -42,12 +42,10 @@
-
-
diff --git a/Installers/Linux/DEBIAN/control b/Installers/Linux/DEBIAN/control
deleted file mode 100644
index ad304f094d8..00000000000
--- a/Installers/Linux/DEBIAN/control
+++ /dev/null
@@ -1,6 +0,0 @@
-Package: monogame-sdk
-Section: Development
-Architecture: amd64
-Depends: libgtk-3-0 (>= 3.0.0), ttf-mscorefonts-installer (>= 1.0), monodevelop (>= 5.0), gtk-sharp3 (>= 2.99), referenceassemblies-pcl (>= 2014.04)
-Maintainer: Harry
-Description: MonoGame SDK
diff --git a/Installers/Linux/DEBIAN/postinst b/Installers/Linux/DEBIAN/postinst
deleted file mode 100755
index 1b89f49eb49..00000000000
--- a/Installers/Linux/DEBIAN/postinst
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/sh
-
-xdg-mime install /tmp/mgcb.xml --novendor
-xdg-mime default "Monogame Pipeline.desktop" text/mgcb
-sudo -H -u $SUDO_USER bash -c 'mdtool setup install -y /tmp/MonoDevelop.MonoGame.mpack'
-
-rm -rf /opt/MonoGameSDK
-ln -s /usr/lib/mono/xbuild/MonoGame/v3.0/Tools /opt/MonoGameSDK
-
-rm -f /usr/share/applications/Monogame\ Pipeline.desktop
-echo "[Desktop Entry]\nVersion=1.0\nEncoding=UTF-8\nName=MonoGame Pipeline\nGenericName=MonoGame Pipeline\nComment=Used to create platform specific .xnb files\nExec=monogame-pipeline %F\nTryExec=monogame-pipeline\nIcon=monogame\nStartupNotify=true\nTerminal=false\nType=Application\nMimeType=text/mgcb;\nCategories=Development;" >> /usr/share/applications/Monogame\ Pipeline.desktop
-
diff --git a/Installers/Linux/Main/mgcb b/Installers/Linux/Main/mgcb
deleted file mode 100755
index 7a9d85e4d42..00000000000
--- a/Installers/Linux/Main/mgcb
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/bash
-mono /usr/lib/mono/xbuild/MonoGame/v3.0/Tools/MGCB.exe "$@"
diff --git a/Installers/Linux/Main/monogame-pipeline b/Installers/Linux/Main/monogame-pipeline
deleted file mode 100755
index a9ccd43314d..00000000000
--- a/Installers/Linux/Main/monogame-pipeline
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/bash
-mono /usr/lib/mono/xbuild/MonoGame/v3.0/Tools/Pipeline.exe "$@"
diff --git a/Installers/Linux/RUN/postinstall.sh b/Installers/Linux/RUN/postinstall.sh
index f879c510275..939b37431d1 100755
--- a/Installers/Linux/RUN/postinstall.sh
+++ b/Installers/Linux/RUN/postinstall.sh
@@ -1,4 +1,24 @@
-#!/bin/sh
+#!/bin/bash
+
+# Functions
+echodep()
+{
+ line=" - $1"
+
+ while [ ${#line} -lt 50 ]
+ do
+ line="$line."
+ done
+
+ echo -ne "$line"
+
+ if eval "$2"
+ then
+ echo -e "\e[32m[Found]\e[0m"
+ else
+ echo -e "\e[31m[Not Found]\e[0m"
+ fi
+}
# Check installation priviledge
if [ "$(id -u)" != "0" ]; then
@@ -6,36 +26,62 @@ if [ "$(id -u)" != "0" ]; then
exit 1
fi
-# Check previous versions
-if type "mgcb" > /dev/null 2>&1
+DIR=$(pwd)
+IDIR="/usr/lib/mono/xbuild/MonoGame/v3.0"
+
+# Find MonoDevelop
+MDTOOL="?????"
+
+if type "monodevelop" > /dev/null 2>&1
then
- echo "Please uninstall any previous versions of MonoGame SDK" 1>&2
- exit 1
+ if eval "monodevelop --help | grep 'MonoDevelop 6' > /dev/null 2>&1"
+ then
+ MDTOOL="mdtool"
+ fi
fi
-DIR=$(pwd)
-IDIR="/usr/lib/mono/xbuild/MonoGame/v3.0"
+if type "monodevelop-stable" > /dev/null 2>&1
+then
+ if eval "monodevelop-stable --help | grep 'MonoDevelop 6' > /dev/null 2>&1"
+ then
+ MDTOOL="mdtool-stable"
+ fi
+fi
# Show dependency list
-echo "Please make sure the following packages are installed:"
-echo " - monodevelop"
-echo " - libopenal-dev"
-echo " - referenceassemblies-pcl / mono-pcl"
-echo " - ttf-mscorefonts-installer / mscore-fonts"
-echo " - gtk-sharp3"
+echo "Dependencies:"
+echodep "mono-runtime" "type 'mono' > /dev/null 2>&1"
+echodep "gtk-sharp3" "type 'gacutil' > /dev/null 2>&1 && gacutil /l gtk-sharp | grep -q 3.0.0.0"
+echo ""
+echo "Optional Dependencies:"
+echodep "MonoDevelop 6" "$MDTOOL > /dev/null 2>&1"
+echodep "Rider" "type 'rider' > /dev/null 2>&1"
+echodep "referenceassemblies-pcl / mono-pcl" "test -d /usr/lib/mono/xbuild/Microsoft/Portable"
+echodep "ttf-mscorefonts-installer / mscore-fonts" "fc-list | grep -q Arial"
+echo ""
read -p "Continue (Y, n): " choice2
case "$choice2" in
n|N ) exit ;;
*) ;;
esac
-# MonoDevelop addin
-read -p "Install monodevelop addin(Y, n): " choice2
-case "$choice2" in
- n|N ) ;;
- *)
- sudo -H -u $SUDO_USER bash -c "mdtool setup install -y $DIR/Main/MonoDevelop.MonoGame.mpack"
-esac
+# Check previous versions
+if type "mgcb" > /dev/null 2>&1
+then
+ echo "Previous version detected, trying to uninstall..."
+
+ # Try and uninstall previus versions
+ if [ -f /opt/monogame/uninstall.sh ]
+ then
+ sudo sh /opt/monogame/uninstall.sh
+ elif [ -f /opt/MonoGameSDK/uninstall.sh ]
+ then
+ sudo sh /opt/MonoGameSDK/uninstall.sh
+ else
+ echo "Could not uninstall, please uninstall any previous version of MonoGame SDK manually." 1>&2
+ exit 1
+ fi
+fi
# MonoGame SDK installation
echo "Installing MonoGame SDK..."
@@ -50,13 +96,44 @@ ln -s "$IDIR" "/opt/MonoGameSDK"
chmod +x "$IDIR/Tools/ffmpeg"
chmod +x "$IDIR/Tools/ffprobe"
+# Rider stuff
+if type "rider" > /dev/null 2>&1
+then
+ echo "Installing Rider files..."
+
+ FINDCOMMAND=$(type -a rider)
+ COMMAND=$(echo $FINDCOMMAND| cut -d' ' -f 3)
+
+ FINDRIDER=$(cat $COMMAND | grep "RUN_PATH")
+ RIDER=$(echo $FINDRIDER| cut -d"'" -f 2)
+
+ RIDERDIR=$(dirname $(dirname $RIDER))
+ RXBUILD="$RIDERDIR/lib/ReSharperHost/linux-x64/mono/lib/mono/xbuild/MonoGame"
+
+ mkdir -p "$RXBUILD"
+ ln -s "$IDIR" "$RXBUILD/v3.0"
+fi
+
+# MonoDevelop addin
+if [ "$MONODEVELOP" != "?????" ]
+then
+ echo "Installing MonoDevelop Addin..."
+ sudo -H -u $SUDO_USER bash -c "$MDTOOL setup install -y $DIR/Main/MonoDevelop.MonoGame.mpack > /dev/null"
+fi
+
# Monogame Pipeline terminal commands
echo "Creating launcher items..."
-cp $DIR/Main/monogame-pipeline /usr/bin/monogame-pipeline
-chmod +x /usr/bin/monogame-pipeline
+cat > /usr/bin/monogame-pipeline-tool <<'endmsg'
+#!/bin/bash
+mono /usr/lib/mono/xbuild/MonoGame/v3.0/Tools/Pipeline.exe "$@"
+endmsg
+chmod +x /usr/bin/monogame-pipeline-tool
-cp $DIR/Main/mgcb /usr/bin/mgcb
+cat > /usr/bin/mgcb <<'endmsg'
+#!/bin/bash
+mono /usr/lib/mono/xbuild/MonoGame/v3.0/Tools/MGCB.exe "$@"
+endmsg
chmod +x /usr/bin/mgcb
# MonoGame icon
@@ -65,15 +142,30 @@ cp $DIR/Main/monogame.svg /usr/share/icons/hicolor/scalable/mimetypes/monogame.s
gtk-update-icon-cache /usr/share/icons/hicolor/ -f
# Application launcher
-rm -f /usr/share/applications/Monogame\ Pipeline.desktop
-echo -e "[Desktop Entry]\nVersion=1.0\nEncoding=UTF-8\nName=MonoGame Pipeline\nGenericName=MonoGame Pipeline\nComment=Used to create platform specific .xnb files\nExec=monogame-pipeline %F\nTryExec=monogame-pipeline\nIcon=monogame\nStartupNotify=true\nTerminal=false\nType=Application\nMimeType=text/mgcb;\nCategories=Development;" | sudo tee --append /usr/share/applications/Monogame\ Pipeline.desktop > /dev/null
+cat > /usr/share/applications/MonogamePipeline.desktop <<'endmsg'
+[Desktop Entry]
+Version=1.0
+Encoding=UTF-8
+Name=MonoGame Pipeline Tool
+GenericName=MonoGame Pipeline Tool
+Comment=Creates platform specific content files.
+Exec=monogame-pipeline-tool %F
+TryExec=monogame-pipeline-tool
+Icon=monogame
+StartupNotify=true
+Terminal=false
+Type=Application
+MimeType=text/mgcb;
+Categories=Development;
+endmsg
# Mimetype
echo "Adding mimetype..."
-xdg-mime install $DIR/Main/mgcb.xml --novendor
-xdg-mime default "Monogame Pipeline.desktop" text/mgcb
+xdg-mime install $DIR/Main/mgcb.xml --novendor > /dev/null
+xdg-mime default "MonogamePipeline.desktop" text/mgcb
# Uninstall script
chmod +x $IDIR/uninstall.sh
-echo "To uninstall the pipeline please run $IDIR/uninstall.sh"
+ln -s $IDIR/uninstall.sh /usr/bin/monogame-uninstall
+echo "To uninstall MonoGame SDK you can run \"monogame-uninstall\" from terminal."
diff --git a/Installers/Linux/RUN/uninstall.sh b/Installers/Linux/RUN/uninstall.sh
index 9654ad15a3d..53fc13ee323 100755
--- a/Installers/Linux/RUN/uninstall.sh
+++ b/Installers/Linux/RUN/uninstall.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
#check removale priviledge
if [ "$(id -u)" != "0" ]; then
@@ -7,7 +7,8 @@ if [ "$(id -u)" != "0" ]; then
fi
#remove terminal commands for mgcb and pipeline tool
-rm -f /usr/bin/monogame-pipeline
+rm -f /usr/bin/monogame-pipeline-tool
+rm -f /usr/bin/monogame-uninstall
rm -f /usr/bin/mgcb
#remove application icon
diff --git a/Installers/Linux/compile.sh b/Installers/Linux/compile.sh
deleted file mode 100755
index 3bd3bd45d22..00000000000
--- a/Installers/Linux/compile.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-dpkg --build tmp_deb monogame-sdk.deb
-./../../ThirdParty/Dependencies/makeself/makeself.sh tmp_run/ monogame-sdk.run "Monogame Pipeline Installer" ./postinstall.sh
diff --git a/Installers/MacOS/Scripts/Pipeline/postinstall b/Installers/MacOS/Scripts/Pipeline/postinstall
index 27cfeeaa056..c89b179a820 100755
--- a/Installers/MacOS/Scripts/Pipeline/postinstall
+++ b/Installers/MacOS/Scripts/Pipeline/postinstall
@@ -37,6 +37,9 @@ if [ -d '/Applications/Xamarin Studio.app' ]
then
/Applications/Xamarin\ Studio.app/Contents/MacOS/mdtool setup uninstall MonoDevelop.MonoGame -y
fi
+if [ ! -d '/Library/Frameworks/Mono.framework/Versions/4.6.2' ]
+ ln -s /Library/Frameworks/Mono.framework/Versions/Current /Library/Frameworks/Mono.framework/Versions/4.6.2
+fi
sudo rm /usr/local/bin/mgcb
sudo rm /usr/local/bin/monogame-uninstall
" >> /usr/local/bin/monogame-uninstall
diff --git a/Installers/Windows/MonoGame.nsi b/Installers/Windows/MonoGame.nsi
index 649ba74cec7..aa9b4cbe378 100644
--- a/Installers/Windows/MonoGame.nsi
+++ b/Installers/Windows/MonoGame.nsi
@@ -75,7 +75,7 @@ RequestExecutionLevel admin
; The stuff to install
Section "MonoGame Core Components" CoreComponents ;No components page, name is not important
SectionIn RO
-
+
; Install the VS support files.
SetOutPath ${MSBuildInstallDir}
File '..\..\MonoGame.Framework.Content.Pipeline\MonoGame.Content.Builder.targets'
@@ -96,6 +96,7 @@ Section "MonoGame Core Components" CoreComponents ;No components page, name is n
!insertmacro VS_ASSOCIATE_EDITOR 'MonoGame Pipeline' '11.0' 'mgcb' '${MSBuildInstallDir}\Tools\Pipeline.exe'
!insertmacro VS_ASSOCIATE_EDITOR 'MonoGame Pipeline' '12.0' 'mgcb' '${MSBuildInstallDir}\Tools\Pipeline.exe'
!insertmacro VS_ASSOCIATE_EDITOR 'MonoGame Pipeline' '14.0' 'mgcb' '${MSBuildInstallDir}\Tools\Pipeline.exe'
+ !insertmacro VS_ASSOCIATE_EDITOR 'MonoGame Pipeline' '15.0' 'mgcb' '${MSBuildInstallDir}\Tools\Pipeline.exe'
!insertmacro APP_ASSOCIATE 'mgcb' 'MonoGame.ContentBuilderFile' 'A MonoGame content builder project.' '${MSBuildInstallDir}\Tools\Pipeline.exe,0' 'Open with Pipeline' '${MSBuildInstallDir}\Tools\Pipeline.exe "%1"'
; Install the assemblies for all the platforms we can
@@ -106,17 +107,27 @@ Section "MonoGame Core Components" CoreComponents ;No components page, name is n
File '..\..\MonoGame.Framework\bin\Android\AnyCPU\Release\*.dll'
File '..\..\MonoGame.Framework\bin\Android\AnyCPU\Release\*.xml'
- ; Install OUYA Assemblies
- SetOutPath '$INSTDIR\Assemblies\OUYA'
- File '..\..\MonoGame.Framework\bin\Ouya\AnyCPU\Release\*.dll'
- File '..\..\MonoGame.Framework\bin\Ouya\AnyCPU\Release\*.xml'
-
- ; Install Desktop OpenGL Assemblies
+ ; Install DesktopGL Assemblies
SetOutPath '$INSTDIR\Assemblies\DesktopGL'
File /nonfatal '..\..\MonoGame.Framework\bin\WindowsGL\AnyCPU\Release\*.dll'
File /nonfatal ' ..\..\MonoGame.Framework\bin\WindowsGL\AnyCPU\Release\*.xml'
- File '..\..\ThirdParty\Dependencies\OpenTK.dll'
- File '..\..\ThirdParty\Dependencies\OpenTK.dll.config'
+ File '..\..\ThirdParty\Dependencies\SDL\MacOS\Universal\libSDL2-2.0.0.dylib'
+ File '..\..\ThirdParty\Dependencies\openal-soft\MacOS\Universal\libopenal.1.dylib'
+ File '..\..\ThirdParty\Dependencies\MonoGame.Framework.dll.config'
+
+ ; Install x86 DesktopGL Dependencies
+ SetOutPath '$INSTDIR\Assemblies\DesktopGL\x86'
+ File '..\..\ThirdParty\Dependencies\SDL\Windows\x86\SDL2.dll'
+ File '..\..\ThirdParty\Dependencies\openal-soft\Windows\x86\soft_oal.dll'
+ File '..\..\ThirdParty\Dependencies\SDL\Linux\x86\libSDL2-2.0.so.0'
+ File '..\..\ThirdParty\Dependencies\openal-soft\Linux\x86\libopenal.so.1'
+
+ ; Install x64 DesktopGL Dependencies
+ SetOutPath '$INSTDIR\Assemblies\DesktopGL\x64'
+ File '..\..\ThirdParty\Dependencies\SDL\Windows\x64\SDL2.dll'
+ File '..\..\ThirdParty\Dependencies\openal-soft\Windows\x64\soft_oal.dll'
+ File '..\..\ThirdParty\Dependencies\SDL\Linux\x64\libSDL2-2.0.so.0'
+ File '..\..\ThirdParty\Dependencies\openal-soft\Linux\x64\libopenal.so.1'
; Install Windows Desktop DirectX Assemblies
SetOutPath '$INSTDIR\Assemblies\Windows'
@@ -133,18 +144,6 @@ Section "MonoGame Core Components" CoreComponents ;No components page, name is n
File '..\..\MonoGame.Framework\bin\WindowsPhone81\AnyCPU\Release\*.dll'
File '..\..\MonoGame.Framework\bin\WindowsPhone81\AnyCPU\Release\*.xml'
- ; Install Windows Phone ARM Assemblies
- SetOutPath '$INSTDIR\Assemblies\WindowsPhone\ARM'
- File '..\..\MonoGame.Framework\bin\WindowsPhone\ARM\Release\*.dll'
- File '..\..\MonoGame.Framework\bin\WindowsPhone\ARM\Release\*.xml'
- File '..\..\MonoGame.Framework\bin\WindowsPhone\ARM\Release\*.winmd'
-
- ; Install Windows Phone x86 Assemblies
- SetOutPath '$INSTDIR\Assemblies\WindowsPhone\x86'
- File '..\..\MonoGame.Framework\bin\WindowsPhone\x86\Release\*.dll'
- File '..\..\MonoGame.Framework\bin\WindowsPhone\x86\Release\*.xml'
- File '..\..\MonoGame.Framework\bin\WindowsPhone\x86\Release\*.winmd'
-
; Install Windows 10 UAP Assemblies
SetOutPath '$INSTDIR\Assemblies\WindowsUniversal'
File '..\..\MonoGame.Framework\bin\WindowsUniversal\AnyCPU\Release\*.dll'
@@ -156,17 +155,17 @@ Section "MonoGame Core Components" CoreComponents ;No components page, name is n
SetOutPath '$INSTDIR\Assemblies\iOS'
File '..\..\MonoGame.Framework\bin\iOS\iPhoneSimulator\Release\*.dll'
File '..\..\MonoGame.Framework\bin\iOS\iPhoneSimulator\Release\*.xml'
+ SetOutPath '$INSTDIR\Assemblies\tvOS'
+ File '..\..\MonoGame.Framework\bin\tvOS\iPhoneSimulator\Release\*.dll'
+ File '..\..\MonoGame.Framework\bin\tvOS\iPhoneSimulator\Release\*.xml'
SkipiOSAssemblies:
WriteRegStr HKLM 'SOFTWARE\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\${APPNAME} for Desktop OpenGL' '' '$INSTDIR\Assemblies\DesktopGL'
WriteRegStr HKLM 'SOFTWARE\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\${APPNAME} for Windows' '' '$INSTDIR\Assemblies\Windows'
WriteRegStr HKLM 'SOFTWARE\Microsoft\.NETFramework\v4.5.50709\AssemblyFoldersEx\${APPNAME} for Windows Store' '' '$INSTDIR\Assemblies\Windows8'
WriteRegStr HKLM 'SOFTWARE\Microsoft\.NETFramework\v4.5.50709\AssemblyFoldersEx\${APPNAME} for Windows Phone 8.1' '' '$INSTDIR\Assemblies\WindowsPhone81'
- WriteRegStr HKLM 'SOFTWARE\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\${APPNAME} for Windows Phone ARM' '' '$INSTDIR\Assemblies\WindowsPhone\ARM'
- WriteRegStr HKLM 'SOFTWARE\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\${APPNAME} for Windows Phone x86' '' '$INSTDIR\Assemblies\WindowsPhone\x86'
WriteRegStr HKLM 'SOFTWARE\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\${APPNAME} for Windows 10 Universal' '' '$INSTDIR\Assemblies\WindowsUniversal'
WriteRegStr HKLM 'SOFTWARE\Microsoft\MonoAndroid\v2.3\AssemblyFoldersEx\${APPNAME} for Android' '' '$INSTDIR\Assemblies\Android'
- WriteRegStr HKLM 'SOFTWARE\Microsoft\MonoAndroid\v2.3\AssemblyFoldersEx\${APPNAME} for OUYA' '' '$INSTDIR\Assemblies\OUYA'
WriteRegStr HKLM 'SOFTWARE\Microsoft\MonoTouch\v1.0\AssemblyFoldersEx\${APPNAME} for iOS' '' '$INSTDIR\Assemblies\iOS'
IfFileExists $WINDIR\SYSWOW64\*.* Is64bit Is32bit
@@ -178,10 +177,7 @@ Section "MonoGame Core Components" CoreComponents ;No components page, name is n
WriteRegStr HKLM 'SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.5.50709\AssemblyFoldersEx\${APPNAME} for Windows Store' '' '$INSTDIR\Assemblies\Windows8'
WriteRegStr HKLM 'SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.5.50709\AssemblyFoldersEx\${APPNAME} for Windows Phone 8.1' '' '$INSTDIR\Assemblies\WindowsPhone81'
WriteRegStr HKLM 'SOFTWARE\Wow6432Node\Microsoft\MonoAndroid\v2.3\AssemblyFoldersEx\${APPNAME} for Android' '' '$INSTDIR\Assemblies\Android'
- WriteRegStr HKLM 'SOFTWARE\Wow6432Node\Microsoft\MonoAndroid\v2.3\AssemblyFoldersEx\${APPNAME} for OUYA' '' '$INSTDIR\Assemblies\OUYA'
WriteRegStr HKLM 'SOFTWARE\Wow6432Node\Microsoft\MonoTouch\v1.0\AssemblyFoldersEx\${APPNAME} for iOS' '' '$INSTDIR\Assemblies\iOS'
- WriteRegStr HKLM 'SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\${APPNAME} for Windows Phone ARM' '' '$INSTDIR\Assemblies\WindowsPhone\ARM'
- WriteRegStr HKLM 'SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\${APPNAME} for Windows Phone x86' '' '$INSTDIR\Assemblies\WindowsPhone\x86'
WriteRegStr HKLM 'SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\${APPNAME} for Windows 10 Universal' '' '$INSTDIR\Assemblies\WindowsUniversal'
End32Bitvs64BitCheck:
@@ -203,19 +199,11 @@ Section "MonoGame Core Components" CoreComponents ;No components page, name is n
SectionEnd
-Section "OpenAL" OpenAL
- ; SetOutPath $INSTDIR
- File '..\..\ThirdParty\Dependencies\oalinst.exe'
- ExecWait '"$INSTDIR\oalinst.exe /S"'
-SectionEnd
-
Section "Visual Studio 2010 Templates" VS2010
- ReadRegStr $1 HKCU "SOFTWARE\Microsoft\VisualStudio\10.0" "UserProjectTemplatesLocation"
- ExpandEnvStrings $1 $1
- IfFileExists "$1\Visual C#\*.*" InstallTemplates CannotInstallTemplates
+ IfFileExists `$DOCUMENTS\Visual Studio 2010\Templates\ProjectTemplates\Visual C#\*.*` InstallTemplates CannotInstallTemplates
InstallTemplates:
- SetOutPath "$1\Visual C#\MonoGame"
+ SetOutPath "$DOCUMENTS\Visual Studio 2010\Templates\ProjectTemplates\Visual C#\MonoGame"
File /r '..\..\ProjectTemplates\VisualStudio2010\*.zip'
GOTO EndTemplates
CannotInstallTemplates:
@@ -226,11 +214,9 @@ SectionEnd
Section "Visual Studio 2012 Templates" VS2012
- ReadRegStr $1 HKCU "SOFTWARE\Microsoft\VisualStudio\11.0" "UserProjectTemplatesLocation"
- ExpandEnvStrings $1 $1
- IfFileExists "$1\Visual C#\*.*" InstallTemplates CannotInstallTemplates
+ IfFileExists `$DOCUMENTS\Visual Studio 2012\Templates\ProjectTemplates\Visual C#\*.*` InstallTemplates CannotInstallTemplates
InstallTemplates:
- SetOutPath "$1\Visual C#\MonoGame"
+ SetOutPath "$DOCUMENTS\Visual Studio 2012\Templates\ProjectTemplates\Visual C#\MonoGame"
File /r '..\..\ProjectTemplates\VisualStudio2012\*.zip'
File /r '..\..\ProjectTemplates\VisualStudio2010\*.zip'
GOTO EndTemplates
@@ -242,11 +228,9 @@ SectionEnd
Section "Visual Studio 2013 Templates" VS2013
- ReadRegStr $1 HKCU "SOFTWARE\Microsoft\VisualStudio\12.0" "UserProjectTemplatesLocation"
- ExpandEnvStrings $1 $1
- IfFileExists "$1\Visual C#\*.*" InstallTemplates CannotInstallTemplates
+ IfFileExists `$DOCUMENTS\Visual Studio 2013\Templates\ProjectTemplates\Visual C#\*.*` InstallTemplates CannotInstallTemplates
InstallTemplates:
- SetOutPath "$1\Visual C#\MonoGame"
+ SetOutPath "$DOCUMENTS\Visual Studio 2013\Templates\ProjectTemplates\Visual C#\MonoGame"
File /r '..\..\ProjectTemplates\VisualStudio2013\*.zip'
File /r '..\..\ProjectTemplates\VisualStudio2010\*.zip'
GOTO EndTemplates
@@ -258,12 +242,11 @@ SectionEnd
Section "Visual Studio 2015 Templates" VS2015
- ReadRegStr $1 HKCU "SOFTWARE\Microsoft\VisualStudio\14.0" "UserProjectTemplatesLocation"
- ExpandEnvStrings $1 $1
- IfFileExists "$1\Visual C#\*.*" InstallTemplates CannotInstallTemplates
+ IfFileExists `$DOCUMENTS\Visual Studio 2015\Templates\ProjectTemplates\Visual C#\*.*` InstallTemplates CannotInstallTemplates
InstallTemplates:
- SetOutPath "$1\Visual C#\MonoGame"
+ SetOutPath "$DOCUMENTS\Visual Studio 2015\Templates\ProjectTemplates\Visual C#\MonoGame"
File /r '..\..\ProjectTemplates\VisualStudio2010\*.zip'
+ File /r '..\..\ProjectTemplates\VisualStudio2013\WindowsPhone8.1.zip'
File /r '..\..\ProjectTemplates\VisualStudio2015\*.zip'
GOTO EndTemplates
CannotInstallTemplates:
@@ -272,6 +255,21 @@ Section "Visual Studio 2015 Templates" VS2015
SectionEnd
+Section "Visual Studio 2017 Templates" VS2017
+
+ IfFileExists `$DOCUMENTS\Visual Studio 2017\Templates\ProjectTemplates\Visual C#\*.*` InstallTemplates CannotInstallTemplates
+ InstallTemplates:
+ SetOutPath "$DOCUMENTS\Visual Studio 2017\Templates\ProjectTemplates\Visual C#\MonoGame"
+ File /r '..\..\ProjectTemplates\VisualStudio2010\*.zip'
+ File /r '..\..\ProjectTemplates\VisualStudio2013\WindowsPhone8.1.zip'
+ File /r '..\..\ProjectTemplates\VisualStudio2015\*.zip'
+ GOTO EndTemplates
+ CannotInstallTemplates:
+ DetailPrint "Visual Studio 2017 not found"
+ EndTemplates:
+
+SectionEnd
+
; Optional section (can be disabled by the user)
Section "Start Menu Shortcuts" Menu
CreateDirectory $SMPROGRAMS\${APPNAME}
@@ -295,67 +293,67 @@ Section "Start Menu Shortcuts" Menu
SectionEnd
LangString CoreComponentsDesc ${LANG_ENGLISH} "Install the Runtimes and the MSBuild extensions for MonoGame"
-LangString OpenALDesc ${LANG_ENGLISH} "Install the OpenAL drivers"
LangString MonoDevelopDesc ${LANG_ENGLISH} "Install the project templates for MonoDevelop"
LangString VS2010Desc ${LANG_ENGLISH} "Install the project templates for Visual Studio 2010"
LangString VS2012Desc ${LANG_ENGLISH} "Install the project templates for Visual Studio 2012"
LangString VS2013Desc ${LANG_ENGLISH} "Install the project templates for Visual Studio 2013"
LangString VS2015Desc ${LANG_ENGLISH} "Install the project templates for Visual Studio 2015"
+LangString VS2017Desc ${LANG_ENGLISH} "Install the project templates for Visual Studio 2017"
LangString MenuDesc ${LANG_ENGLISH} "Add a link to the MonoGame website to your start menu"
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${CoreComponents} $(CoreComponentsDesc)
- !insertmacro MUI_DESCRIPTION_TEXT ${OpenAL} $(OpenALDesc)
!insertmacro MUI_DESCRIPTION_TEXT ${MonoDevelop} $(MonoDevelopDesc)
!insertmacro MUI_DESCRIPTION_TEXT ${VS2010} $(VS2010Desc)
!insertmacro MUI_DESCRIPTION_TEXT ${VS2012} $(VS2012Desc)
!insertmacro MUI_DESCRIPTION_TEXT ${VS2013} $(VS2013Desc)
!insertmacro MUI_DESCRIPTION_TEXT ${VS2015} $(VS2015Desc)
+ !insertmacro MUI_DESCRIPTION_TEXT ${VS2017} $(VS2017Desc)
!insertmacro MUI_DESCRIPTION_TEXT ${Menu} $(MenuDesc)
!insertmacro MUI_FUNCTION_DESCRIPTION_END
Function checkVS2010
-ReadRegStr $1 HKCU "SOFTWARE\Microsoft\VisualStudio\10.0" "UserProjectTemplatesLocation"
-ExpandEnvStrings $1 $1
-IfFileExists "$1\Visual C#\*.*" end disable
+IfFileExists `$DOCUMENTS\Visual Studio 2010\Templates\ProjectTemplates\Visual C#\*.*` end disable
disable:
SectionSetFlags ${VS2010} $0
end:
FunctionEnd
Function checkVS2012
-ReadRegStr $1 HKCU "SOFTWARE\Microsoft\VisualStudio\11.0" "UserProjectTemplatesLocation"
-ExpandEnvStrings $1 $1
-IfFileExists "$1\Visual C#\*.*" end disable
+IfFileExists `$DOCUMENTS\Visual Studio 2012\Templates\ProjectTemplates\Visual C#\*.*` end disable
disable:
SectionSetFlags ${VS2012} $0
end:
FunctionEnd
Function checkVS2013
-ReadRegStr $1 HKCU "SOFTWARE\Microsoft\VisualStudio\12.0" "UserProjectTemplatesLocation"
-ExpandEnvStrings $1 $1
-IfFileExists "$1\Visual C#\*.*" end disable
+IfFileExists `$DOCUMENTS\Visual Studio 2013\Templates\ProjectTemplates\Visual C#\*.*` end disable
disable:
SectionSetFlags ${VS2013} $0
end:
FunctionEnd
Function checkVS2015
-ReadRegStr $1 HKCU "SOFTWARE\Microsoft\VisualStudio\14.0" "UserProjectTemplatesLocation"
-ExpandEnvStrings $1 $1
-IfFileExists "$1\Visual C#\*.*" end disable
+IfFileExists `$DOCUMENTS\Visual Studio 2015\Templates\ProjectTemplates\Visual C#\*.*` end disable
disable:
SectionSetFlags ${VS2015} $0
end:
FunctionEnd
+Function checkVS2017
+IfFileExists `$DOCUMENTS\Visual Studio 2017\Templates\ProjectTemplates\Visual C#\*.*` end disable
+ disable:
+ SectionSetFlags ${VS2017} $0
+ end:
+FunctionEnd
+
Function .onInit
IntOp $0 $0 | ${SF_RO}
Call checkVS2010
Call checkVS2012
Call checkVS2013
Call checkVS2015
+ Call checkVS2017
IntOp $0 ${SF_SELECTED} | ${SF_RO}
SectionSetFlags ${core_id} $0
FunctionEnd
@@ -375,7 +373,6 @@ Section "Uninstall"
DeleteRegKey HKLM 'SOFTWARE\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\${APPNAME} for Windows Phone x86'
DeleteRegKey HKLM 'SOFTWARE\Microsoft\.NETFramework\v4.5.50709\AssemblyFoldersEx\${APPNAME} for Windows 10 UAP'
DeleteRegKey HKLM 'SOFTWARE\Microsoft\MonoAndroid\v2.3\AssemblyFoldersEx\${APPNAME} for Android'
- DeleteRegKey HKLM 'SOFTWARE\Microsoft\MonoAndroid\v2.3\AssemblyFoldersEx\${APPNAME} for OUYA'
DeleteRegKey HKLM 'SOFTWARE\Microsoft\MonoTouch\v1.0\AssemblyFoldersEx\${APPNAME} for iOS'
DeleteRegKey HKCU 'Software\Microsoft\VisualStudio\10.0\Default Editors\mgcb'
@@ -397,7 +394,6 @@ Section "Uninstall"
DeleteRegKey HKLM 'SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\${APPNAME} for Windows Phone x86'
DeleteRegKey HKLM 'SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\${APPNAME} for Windows 10 UAP'
DeleteRegKey HKLM 'SOFTWARE\Wow6432Node\Microsoft\MonoAndroid\v2.3\AssemblyFoldersEx\${APPNAME} for Android'
- DeleteRegKey HKLM 'SOFTWARE\Wow6432Node\Microsoft\MonoAndroid\v2.3\AssemblyFoldersEx\${APPNAME} for OUYA'
DeleteRegKey HKLM 'SOFTWARE\Wow6432Node\Microsoft\MonoTouch\v1.0\AssemblyFoldersEx\${APPNAME} for iOS'
@@ -413,18 +409,11 @@ Section "Uninstall"
RMDir /r "$0\AddIns\MonoDevelop.MonoGame"
${EndIf}
- ReadRegStr $1 HKCU "SOFTWARE\Microsoft\VisualStudio\10.0" "UserProjectTemplatesLocation"
- ExpandEnvStrings $1 $1
- RMDir /r "$1\Visual C#\MonoGame"
- ReadRegStr $1 HKCU "SOFTWARE\Microsoft\VisualStudio\11.0" "UserProjectTemplatesLocation"
- ExpandEnvStrings $1 $1
- RMDir /r "$1\Visual C#\MonoGame"
- ReadRegStr $1 HKCU "SOFTWARE\Microsoft\VisualStudio\12.0" "UserProjectTemplatesLocation"
- ExpandEnvStrings $1 $1
- RMDir /r "$1\Visual C#\MonoGame"
- ReadRegStr $1 HKCU "SOFTWARE\Microsoft\VisualStudio\14.0" "UserProjectTemplatesLocation"
- ExpandEnvStrings $1 $1
- RMDir /r "$1\Visual C#\MonoGame"
+ RMDir /r "$DOCUMENTS\Visual Studio 2010\Templates\ProjectTemplates\Visual C#\MonoGame"
+ RMDir /r "$DOCUMENTS\Visual Studio 2012\Templates\ProjectTemplates\Visual C#\MonoGame"
+ RMDir /r "$DOCUMENTS\Visual Studio 2013\Templates\ProjectTemplates\Visual C#\MonoGame"
+ RMDir /r "$DOCUMENTS\Visual Studio 2015\Templates\ProjectTemplates\Visual C#\MonoGame"
+ RMDir /r "$DOCUMENTS\Visual Studio 2017\Templates\ProjectTemplates\Visual C#\MonoGame"
RMDir /r "${MSBuildInstallDir}"
RMDir /r "$SMPROGRAMS\${APPNAME}"
diff --git a/Installers/default.build b/Installers/default.build
index ad6ccf2f339..e33fd87de36 100644
--- a/Installers/default.build
+++ b/Installers/default.build
@@ -54,16 +54,29 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
@@ -84,13 +97,13 @@
-
-
+
+
-
-
+
+
@@ -144,31 +157,6 @@ fi
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- version: ${buildNumber}
-
-
-
-
-
-
-
@@ -189,9 +177,12 @@ fi
-
-
-
+
+
+
+
+
+
diff --git a/MonoGame.Framework.Content.Pipeline/Audio/AudioContent.cs b/MonoGame.Framework.Content.Pipeline/Audio/AudioContent.cs
index 2c78e4df1fa..3fb343ea400 100644
--- a/MonoGame.Framework.Content.Pipeline/Audio/AudioContent.cs
+++ b/MonoGame.Framework.Content.Pipeline/Audio/AudioContent.cs
@@ -3,385 +3,197 @@
// file 'LICENSE.txt', which is part of this source code package.
using System;
-using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
-using System.Linq;
namespace Microsoft.Xna.Framework.Content.Pipeline.Audio
{
///
- /// Encapsulates and provides operations, such as format conversions, on the source audio. This type is produced by the audio importers and used by audio processors to produce compiled audio assets.
+ /// Encapsulates and provides operations, such as format conversions, on the
+ /// source audio. This type is produced by the audio importers and used by audio
+ /// processors to produce compiled audio assets.
///
- public class AudioContent : ContentItem
+ /// Note that AudioContent can load and process audio files that are not supported by the importers.
+ public class AudioContent : ContentItem, IDisposable
{
- internal List data;
- TimeSpan duration;
- string fileName;
- AudioFileType fileType;
- AudioFormat format;
- int loopLength;
- int loopStart;
+ private bool _disposed;
+ private readonly string _fileName;
+ private readonly AudioFileType _fileType;
+ private ReadOnlyCollection _data;
+ private TimeSpan _duration;
+ private AudioFormat _format;
+ private int _loopStart;
+ private int _loopLength;
///
- /// Gets the raw audio data.
+ /// The name of the original source audio file.
///
- /// If unprocessed, the source data; otherwise, the processed data.
- public ReadOnlyCollection Data { get { return data.AsReadOnly(); } }
+ [ContentSerializer(AllowNull = false)]
+ public string FileName { get { return _fileName; } }
///
- /// Gets the duration (in milliseconds) of the audio data.
+ /// The type of the original source audio file.
///
- /// Duration of the audio data.
- public TimeSpan Duration { get { return duration; } }
+ public AudioFileType FileType { get { return _fileType; } }
///
- /// Gets the file name containing the audio data.
+ /// The current raw audio data without header information.
///
- /// The name of the file containing this data.
- [ContentSerializerAttribute]
- public string FileName { get { return fileName; } }
-
- ///
- /// Gets the AudioFileType of this audio source.
- ///
- /// The AudioFileType of this audio source.
- public AudioFileType FileType { get { return fileType; } }
-
- ///
- /// Gets the AudioFormat of this audio source.
- ///
- /// The AudioFormat of this audio source.
- public AudioFormat Format { get { return format; } }
-
- ///
- /// Gets the loop length, in samples.
- ///
- /// The number of samples in the loop.
- public int LoopLength { get { return loopLength; } }
+ ///
+ /// This changes from the source data to the output data after conversion.
+ /// For MP3 and WMA files this throws an exception to match XNA behavior.
+ ///
+ public ReadOnlyCollection Data
+ {
+ get
+ {
+ if (_disposed || _data == null)
+ throw new InvalidContentException("Could not read the audio data from file \"" + Path.GetFileName(_fileName) + "\".");
+ return _data;
+ }
+ }
///
- /// Gets the loop start, in samples.
+ /// The duration of the audio data.
///
- /// The number of samples to the start of the loop.
- public int LoopStart { get { return loopStart; } }
+ public TimeSpan Duration
+ {
+ get
+ {
+ return _duration;
+ }
+ }
///
- /// Initializes a new instance of AudioContent.
+ /// The current format of the audio data.
///
- /// Name of the audio source file to be processed.
- /// Type of the processed audio: WAV, MP3 or WMA.
- /// Constructs the object from the specified source file, in the format specified.
- public AudioContent(string audioFileName, AudioFileType audioFileType)
+ /// This changes from the source format to the output format after conversion.
+ public AudioFormat Format
{
- fileName = audioFileName;
- fileType = audioFileType;
- Read(audioFileName);
+ get
+ {
+ return _format;
+ }
}
///
- /// Returns the sample rate for the given quality setting.
+ /// The current loop length in samples.
///
- /// The quality setting.
- /// The sample rate for the quality.
- int QualityToSampleRate(ConversionQuality quality)
+ /// This changes from the source loop length to the output loop length after conversion.
+ public int LoopLength
{
- switch (quality)
+ get
{
- case ConversionQuality.Low:
- return Math.Max(8000, format.SampleRate / 2);
- }
-
- return Math.Max(8000, format.SampleRate);
+ return _loopLength;
+ }
}
///
- /// Returns the bitrate for the given quality setting.
+ /// The current loop start location in samples.
///
- /// The quality setting.
- /// The bitrate for the quality.
- int QualityToBitRate(ConversionQuality quality)
+ /// This changes from the source loop start to the output loop start after conversion.
+ public int LoopStart
{
- switch (quality)
+ get
{
- case ConversionQuality.Low:
- return 96000;
- case ConversionQuality.Medium:
- return 128000;
+ return _loopStart;
}
-
- return 192000;
}
///
- /// Transcodes the source audio to the target format and quality.
+ /// Initializes a new instance of AudioContent.
///
- /// Format to convert this audio to.
- /// Quality of the processed output audio. For streaming formats, it can be one of the following: Low (96 kbps), Medium (128 kbps), Best (192 kbps). For WAV formats, it can be one of the following: Low (11kHz ADPCM), Medium (22kHz ADPCM), Best (44kHz PCM)
- ///
- /// The name of the file that the converted audio should be saved into. This is used for SongContent, where
- /// the audio is stored external to the XNB file. If this is null, then the converted audio is stored in
- /// the Data property.
- ///
- public void ConvertFormat(ConversionFormat formatType, ConversionQuality quality, string saveToFile)
+ /// Name of the audio source file to be processed.
+ /// Type of the processed audio: WAV, MP3 or WMA.
+ /// Constructs the object from the specified source file, in the format specified.
+ public AudioContent(string audioFileName, AudioFileType audioFileType)
{
- var temporarySource = Path.GetTempFileName();
- var temporaryOutput = Path.GetTempFileName();
+ _fileName = audioFileName;
+
try
{
- using (var fs = new FileStream(temporarySource, FileMode.Create, FileAccess.Write))
- {
- var dataBytes = this.data.ToArray();
- fs.Write(dataBytes, 0, dataBytes.Length);
- }
+ // Get the full path to the file.
+ audioFileName = Path.GetFullPath(audioFileName);
- string ffmpegCodecName, ffmpegMuxerName;
- int format;
- switch (formatType)
- {
- case ConversionFormat.Adpcm:
- // ADPCM Microsoft
- ffmpegCodecName = "adpcm_ms";
- ffmpegMuxerName = "wav";
- format = 0x0002; /* WAVE_FORMAT_ADPCM */
- break;
- case ConversionFormat.Pcm:
- // PCM signed 16-bit little-endian
- ffmpegCodecName = "pcm_s16le";
- ffmpegMuxerName = "wav";
- format = 0x0001; /* WAVE_FORMAT_PCM */
- break;
- case ConversionFormat.WindowsMedia:
- // Windows Media Audio 2
- ffmpegCodecName = "wmav2";
- ffmpegMuxerName = "asf";
- format = 0x0161; /* WAVE_FORMAT_WMAUDIO2 */
- break;
- case ConversionFormat.Xma:
- throw new NotSupportedException(
- "XMA is not a supported encoding format. It is specific to the Xbox 360.");
- case ConversionFormat.ImaAdpcm:
- // ADPCM IMA WAV
- ffmpegCodecName = "adpcm_ima_wav";
- ffmpegMuxerName = "wav";
- format = 0x0011; /* WAVE_FORMAT_IMA_ADPCM */
- break;
- case ConversionFormat.Aac:
- // AAC (Advanced Audio Coding)
- // Requires -strict experimental
- ffmpegCodecName = "aac";
- ffmpegMuxerName = "ipod";
- format = 0x0000; /* WAVE_FORMAT_UNKNOWN */
- break;
- case ConversionFormat.Vorbis:
- // Vorbis
- ffmpegCodecName = "libvorbis";
- ffmpegMuxerName = "ogg";
- format = 0x0000; /* WAVE_FORMAT_UNKNOWN */
- break;
- default:
- // Unknown format
- throw new NotSupportedException();
- }
+ // Use probe to get the details of the file.
+ DefaultAudioProfile.ProbeFormat(audioFileName, out _fileType, out _format, out _duration, out _loopStart, out _loopLength);
- string ffmpegStdout, ffmpegStderr;
- var ffmpegExitCode = ExternalTool.Run(
- "ffmpeg",
- string.Format(
- "-y -i \"{0}\" -vn -c:a {1} -b:a {2} -f:a {3} -strict experimental \"{4}\"",
- temporarySource,
- ffmpegCodecName,
- QualityToBitRate(quality),
- ffmpegMuxerName,
- temporaryOutput),
- out ffmpegStdout,
- out ffmpegStderr);
- if (ffmpegExitCode != 0)
- {
- throw new InvalidOperationException("ffmpeg exited with non-zero exit code: \n" + ffmpegStdout + "\n" + ffmpegStderr);
- }
+ // Looks like XNA only cares about type mismatch when
+ // the type is WAV... else it is ok.
+ if ( (audioFileType == AudioFileType.Wav || _fileType == AudioFileType.Wav) &&
+ audioFileType != _fileType)
+ throw new ArgumentException("Incorrect file type!", "audioFileType");
- byte[] rawData;
- using (var fs = new FileStream(temporaryOutput, FileMode.Open, FileAccess.Read))
+ // Only provide the data for WAV files.
+ if (audioFileType == AudioFileType.Wav)
{
- rawData = new byte[fs.Length];
- fs.Read(rawData, 0, rawData.Length);
- }
+ byte[] rawData;
- if (saveToFile != null)
- {
- using (var fs = new FileStream(saveToFile, FileMode.Create, FileAccess.Write))
+ // Must be opened in read mode otherwise it fails to open
+ // read-only files (found in some source control systems)
+ using (var fs = new FileStream(_fileName, FileMode.Open, FileAccess.Read))
{
- fs.Write(rawData, 0, rawData.Length);
+ rawData = new byte[fs.Length];
+ fs.Read(rawData, 0, rawData.Length);
}
- this.data = null;
- }
- else
- {
- this.data = rawData.ToList();
- }
-
- // Get the audio metadata from the output file
- string ffprobeStdout, ffprobeStderr;
- var ffprobeExitCode = ExternalTool.Run(
- "ffprobe",
- string.Format("-i \"{0}\" -show_entries streams -v quiet -of flat", temporaryOutput),
- out ffprobeStdout,
- out ffprobeStderr);
- if (ffprobeExitCode != 0)
- {
- throw new InvalidOperationException("ffprobe exited with non-zero exit code.");
- }
-
- // Set default values if information is not available.
- int averageBytesPerSecond = 0;
- int bitsPerSample = 0;
- int blockAlign = 0;
- int channelCount = 0;
- int sampleRate = 0;
- double durationInSeconds = 0;
+ AudioFormat riffAudioFormat;
+ var stripped = DefaultAudioProfile.StripRiffWaveHeader(rawData, out riffAudioFormat);
- var numberFormat = System.Globalization.CultureInfo.InvariantCulture.NumberFormat;
- foreach (var line in ffprobeStdout.Split(new[] { '\r', '\n', '\0' }, StringSplitOptions.RemoveEmptyEntries))
- {
- var kv = line.Split(new[] { '=' }, 2);
-
- switch (kv[0])
+ if (riffAudioFormat != null)
{
- case "streams.stream.0.sample_rate":
- sampleRate = int.Parse(kv[1].Trim('"'), numberFormat);
- break;
- case "streams.stream.0.bits_per_sample":
- bitsPerSample = int.Parse(kv[1].Trim('"'), numberFormat);
- break;
- case "streams.stream.0.duration":
- durationInSeconds = double.Parse(kv[1].Trim('"'), numberFormat);
- break;
- case "streams.stream.0.channels":
- channelCount = int.Parse(kv[1].Trim('"'), numberFormat);
- break;
- case "streams.stream.0.bit_rate":
- averageBytesPerSecond = (int.Parse(kv[1].Trim('"'), numberFormat) / 8);
- break;
+ if (_format.BlockAlign != riffAudioFormat.BlockAlign)
+ throw new InvalidOperationException("Calcualted block align does not match RIFF " + _format.BlockAlign + " : " + riffAudioFormat.BlockAlign);
+ if (_format.ChannelCount != riffAudioFormat.ChannelCount || _format.Format != riffAudioFormat.Format
+ || _format.SampleRate != riffAudioFormat.SampleRate || _format.AverageBytesPerSecond != riffAudioFormat.AverageBytesPerSecond)
+ throw new InvalidOperationException("Probed audio format does not match RIFF");
}
- }
- // Calculate blockAlign.
- switch (formatType)
- {
- case ConversionFormat.Adpcm:
- case ConversionFormat.ImaAdpcm:
- case ConversionFormat.Pcm:
- // Block alignment value is the number of bytes in an atomic unit (that is, a block) of audio for a particular format. For Pulse Code Modulation (PCM) formats, the formula for calculating block alignment is as follows:
- // • Block Alignment = Bytes per Sample x Number of Channels
- // For example, the block alignment value for 16-bit PCM format mono audio is 2 (2 bytes per sample x 1 channel). For 16-bit PCM format stereo audio, the block alignment value is 4.
- // https://msdn.microsoft.com/en-us/library/system.speech.audioformat.speechaudioformatinfo.blockalign(v=vs.110).aspx
- // Get the raw PCM from the output WAV file
- using (var reader = new BinaryReader(new MemoryStream(rawData)))
- {
- data = GetRawWavData(reader, ref blockAlign).ToList();
- }
- break;
- default:
- // blockAlign is not available from ffprobe (and may or may not
- // be relevant for non-PCM formats anyway)
- break;
+ _data = Array.AsReadOnly(stripped);
}
-
- this.duration = TimeSpan.FromSeconds(durationInSeconds);
- this.format = new AudioFormat(
- averageBytesPerSecond,
- bitsPerSample,
- blockAlign,
- channelCount,
- format,
- sampleRate);
-
- // Loop start and length in number of samples. Defaults to entire sound
- loopStart = 0;
- if (data != null && bitsPerSample > 0 && channelCount > 0)
- loopLength = data.Count / ((bitsPerSample / 8) * channelCount);
- else
- loopLength = 0;
}
- finally
+ catch (Exception ex)
{
- File.Delete(temporarySource);
- File.Delete(temporaryOutput);
+ var message = string.Format("Failed to open file {0}. Ensure the file is a valid audio file and is not DRM protected.", Path.GetFileNameWithoutExtension(audioFileName));
+ throw new InvalidContentException(message, ex);
}
}
- private void Read(string filename)
+ ///
+ /// Transcodes the source audio to the target format and quality.
+ ///
+ /// Format to convert this audio to.
+ /// Quality of the processed output audio. For streaming formats, it can be one of the following: Low (96 kbps), Medium (128 kbps), Best (192 kbps). For WAV formats, it can be one of the following: Low (11kHz ADPCM), Medium (22kHz ADPCM), Best (44kHz PCM)
+ ///
+ /// The name of the file that the converted audio should be saved into. This is used for SongContent, where
+ /// the audio is stored external to the XNB file. If this is null, then the converted audio is stored in
+ /// the Data property.
+ ///
+ [Obsolete("You should prefer to use AudioProfile.")]
+ public void ConvertFormat(ConversionFormat formatType, ConversionQuality quality, string saveToFile)
{
- // Must be opened in read mode otherwise it fails to open read-only files (found in some source control systems)
- using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
- {
- var data = new byte[fs.Length];
- fs.Read(data, 0, data.Length);
- this.data = data.ToList();
- }
+ // Call the legacy conversion code.
+ DefaultAudioProfile.ConvertToFormat(this, formatType, quality, saveToFile);
}
- // Borrowed from AudioLoader to get the raw data from the WAV
- private static byte[] GetRawWavData(BinaryReader reader, ref int blockAlign)
+ public void SetData(byte[] data, AudioFormat format, TimeSpan duration, int loopStart, int loopLength)
{
- byte[] audioData;
-
- //header
- string signature = new string(reader.ReadChars(4));
- if (signature != "RIFF")
- {
- throw new NotSupportedException("Specified stream is not a wave file.");
- }
-
- reader.ReadInt32(); // riff_chunck_size
-
- string wformat = new string(reader.ReadChars(4));
- if (wformat != "WAVE")
- {
- throw new NotSupportedException("Specified stream is not a wave file.");
- }
-
- // WAVE header
- string format_signature = new string(reader.ReadChars(4));
- while (format_signature != "fmt ")
- {
- reader.ReadBytes(reader.ReadInt32());
- format_signature = new string(reader.ReadChars(4));
- }
-
- int format_chunk_size = reader.ReadInt32();
-
- // total bytes read: tbp
- int audio_format = reader.ReadInt16(); // 2
- int num_channels = reader.ReadInt16(); // 4
- int sample_rate = reader.ReadInt32(); // 8
- reader.ReadInt32(); // 12, byte_rate
- blockAlign = reader.ReadInt16(); // 14, block_align
- int bits_per_sample = reader.ReadInt16(); // 16
-
- // reads residual bytes
- if (format_chunk_size > 16)
- reader.ReadBytes(format_chunk_size - 16);
-
- string data_signature = new string(reader.ReadChars(4));
-
- while (data_signature.ToLowerInvariant() != "data")
- {
- reader.ReadBytes(reader.ReadInt32());
- data_signature = new string(reader.ReadChars(4));
- }
-
- if (data_signature != "data")
- {
- throw new NotSupportedException("Specified wave file is not supported.");
- }
-
- int data_chunk_size = reader.ReadInt32();
- audioData = reader.ReadBytes(data_chunk_size);
+ if (data == null)
+ throw new ArgumentNullException("data");
+ if (format == null)
+ throw new ArgumentNullException("format");
+
+ _data = Array.AsReadOnly(data);
+ _format = format;
+ _duration = duration;
+ _loopStart = loopStart;
+ _loopLength = loopLength;
+ }
- return audioData;
+ public void Dispose()
+ {
+ _disposed = true;
+ _data = null;
}
- }
+ }
}
diff --git a/MonoGame.Framework.Content.Pipeline/Audio/AudioFileType.cs b/MonoGame.Framework.Content.Pipeline/Audio/AudioFileType.cs
index 7894f95af49..bb3a8905b39 100644
--- a/MonoGame.Framework.Content.Pipeline/Audio/AudioFileType.cs
+++ b/MonoGame.Framework.Content.Pipeline/Audio/AudioFileType.cs
@@ -23,5 +23,10 @@ public enum AudioFileType
/// The WMA format
///
Wma,
+
+ ///
+ /// The Ogg format
+ ///
+ Ogg,
}
}
diff --git a/MonoGame.Framework.Content.Pipeline/Audio/AudioFormat.cs b/MonoGame.Framework.Content.Pipeline/Audio/AudioFormat.cs
index ea2a02a0283..8a79ed7dce1 100644
--- a/MonoGame.Framework.Content.Pipeline/Audio/AudioFormat.cs
+++ b/MonoGame.Framework.Content.Pipeline/Audio/AudioFormat.cs
@@ -89,7 +89,6 @@ private List ConstructNativeWaveFormat()
{
using (var writer = new BinaryWriter(memory))
{
- writer.Write((int)18); /* header size */
writer.Write((short)this.format);
writer.Write((short)this.channelCount);
writer.Write((int)this.sampleRate);
diff --git a/MonoGame.Framework.Content.Pipeline/Audio/AudioProfile.cs b/MonoGame.Framework.Content.Pipeline/Audio/AudioProfile.cs
new file mode 100644
index 00000000000..56e66312a46
--- /dev/null
+++ b/MonoGame.Framework.Content.Pipeline/Audio/AudioProfile.cs
@@ -0,0 +1,81 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+
+namespace Microsoft.Xna.Framework.Content.Pipeline.Audio
+{
+ public abstract class AudioProfile
+ {
+ private static readonly LoadedTypeCollection _profiles = new LoadedTypeCollection();
+
+ ///
+ /// Find the profile for this target platform.
+ ///
+ /// The platform target for audio.
+ ///
+ public static AudioProfile ForPlatform(TargetPlatform platform)
+ {
+ var profile = _profiles.FirstOrDefault(h => h.Supports(platform));
+ if (profile != null)
+ return profile;
+
+ throw new PipelineException("There is no supported audio profile for the '" + platform + "' platform!");
+ }
+
+ ///
+ /// Returns true if this profile supports audio processing for this platform.
+ ///
+ public abstract bool Supports(TargetPlatform platform);
+
+ ///
+ /// Converts the audio content to work on targeted platform.
+ ///
+ /// The platform to build the audio content for.
+ /// The suggested audio quality level.
+ /// The audio content to convert.
+ /// The quality used for conversion which could be different from the suggested quality.
+ public abstract ConversionQuality ConvertAudio(TargetPlatform platform, ConversionQuality quality, AudioContent content);
+
+ ///
+ /// Converts the audio content to a streaming format that works on targeted platform.
+ ///
+ /// The platform to build the audio content for.
+ /// The suggested audio quality level.
+ /// he audio content to convert.
+ ///
+ /// The quality used for conversion which could be different from the suggested quality.
+ public abstract ConversionQuality ConvertStreamingAudio(TargetPlatform platform, ConversionQuality quality, AudioContent content, ref string outputFileName);
+
+
+ protected static int QualityToSampleRate(ConversionQuality quality, int sourceSampleRate)
+ {
+ switch (quality)
+ {
+ case ConversionQuality.Low:
+ return Math.Max(8000, (int)Math.Floor(sourceSampleRate / 2.0));
+ case ConversionQuality.Medium:
+ return Math.Max(8000, (int)Math.Floor((sourceSampleRate / 4.0) * 3));
+ }
+
+ return Math.Max(8000, sourceSampleRate);
+ }
+
+ protected static int QualityToBitRate(ConversionQuality quality)
+ {
+ switch (quality)
+ {
+ case ConversionQuality.Low:
+ return 96000;
+ case ConversionQuality.Medium:
+ return 128000;
+ }
+
+ return 192000;
+ }
+ }
+}
diff --git a/MonoGame.Framework.Content.Pipeline/Audio/DefaultAudioProfile.cs b/MonoGame.Framework.Content.Pipeline/Audio/DefaultAudioProfile.cs
new file mode 100644
index 00000000000..6974c88b21c
--- /dev/null
+++ b/MonoGame.Framework.Content.Pipeline/Audio/DefaultAudioProfile.cs
@@ -0,0 +1,428 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+
+
+namespace Microsoft.Xna.Framework.Content.Pipeline.Audio
+{
+ internal class DefaultAudioProfile : AudioProfile
+ {
+ public override bool Supports(TargetPlatform platform)
+ {
+ return platform == TargetPlatform.Android ||
+ platform == TargetPlatform.DesktopGL ||
+ platform == TargetPlatform.MacOSX ||
+ platform == TargetPlatform.NativeClient ||
+ platform == TargetPlatform.RaspberryPi ||
+ platform == TargetPlatform.Windows ||
+ platform == TargetPlatform.WindowsPhone8 ||
+ platform == TargetPlatform.WindowsStoreApp ||
+ platform == TargetPlatform.iOS;
+ }
+
+ public override ConversionQuality ConvertAudio(TargetPlatform platform, ConversionQuality quality, AudioContent content)
+ {
+ // Default to PCM data.
+ var targetFormat = ConversionFormat.Pcm;
+ if (quality != ConversionQuality.Best)
+ {
+ if (platform == TargetPlatform.iOS || platform == TargetPlatform.MacOSX)
+ targetFormat = ConversionFormat.ImaAdpcm;
+ else
+ targetFormat = ConversionFormat.Adpcm;
+ }
+
+ return ConvertToFormat(content, targetFormat, quality, null);
+ }
+
+ public override ConversionQuality ConvertStreamingAudio(TargetPlatform platform, ConversionQuality quality, AudioContent content, ref string outputFileName)
+ {
+ // Most platforms will use AAC ("mp4") by default
+ var targetFormat = ConversionFormat.Aac;
+
+ if ( platform == TargetPlatform.Windows ||
+ platform == TargetPlatform.WindowsPhone8 ||
+ platform == TargetPlatform.WindowsStoreApp)
+ targetFormat = ConversionFormat.WindowsMedia;
+
+ else if (platform == TargetPlatform.DesktopGL)
+ targetFormat = ConversionFormat.Vorbis;
+
+ // Get the song output path with the target format extension.
+ outputFileName = Path.ChangeExtension(outputFileName, AudioHelper.GetExtension(targetFormat));
+
+ // Make sure the output folder for the file exists.
+ Directory.CreateDirectory(Path.GetDirectoryName(outputFileName));
+
+ return ConvertToFormat(content, targetFormat, quality, outputFileName);
+ }
+
+ public static void ProbeFormat(string sourceFile, out AudioFileType audioFileType, out AudioFormat audioFormat, out TimeSpan duration, out int loopStart, out int loopLength)
+ {
+ string ffprobeStdout, ffprobeStderr;
+ var ffprobeExitCode = ExternalTool.Run(
+ "ffprobe",
+ string.Format("-i \"{0}\" -show_format -show_entries streams -v quiet -of flat", sourceFile),
+ out ffprobeStdout,
+ out ffprobeStderr);
+ if (ffprobeExitCode != 0)
+ throw new InvalidOperationException("ffprobe exited with non-zero exit code.");
+
+ // Set default values if information is not available.
+ int averageBytesPerSecond = 0;
+ int bitsPerSample = 0;
+ int blockAlign = 0;
+ int channelCount = 0;
+ int sampleRate = 0;
+ int format = 0;
+ string sampleFormat = null;
+ double durationInSeconds = 0;
+ var formatName = string.Empty;
+
+ try
+ {
+ var numberFormat = CultureInfo.InvariantCulture.NumberFormat;
+ foreach (var line in ffprobeStdout.Split(new[] {'\r', '\n', '\0'}, StringSplitOptions.RemoveEmptyEntries))
+ {
+ var kv = line.Split(new[] {'='}, 2);
+
+ switch (kv[0])
+ {
+ case "streams.stream.0.sample_rate":
+ sampleRate = int.Parse(kv[1].Trim('"'), numberFormat);
+ break;
+ case "streams.stream.0.bits_per_sample":
+ bitsPerSample = int.Parse(kv[1].Trim('"'), numberFormat);
+ break;
+ case "streams.stream.0.start_time":
+ {
+ double seconds;
+ if (double.TryParse(kv[1].Trim('"'), NumberStyles.Any, numberFormat, out seconds))
+ durationInSeconds += seconds;
+ break;
+ }
+ case "streams.stream.0.duration":
+ durationInSeconds += double.Parse(kv[1].Trim('"'), numberFormat);
+ break;
+ case "streams.stream.0.channels":
+ channelCount = int.Parse(kv[1].Trim('"'), numberFormat);
+ break;
+ case "streams.stream.0.sample_fmt":
+ sampleFormat = kv[1].Trim('"').ToLowerInvariant();
+ break;
+ case "streams.stream.0.bit_rate":
+ averageBytesPerSecond = (int.Parse(kv[1].Trim('"'), numberFormat)/8);
+ break;
+ case "format.format_name":
+ formatName = kv[1].Trim('"').ToLowerInvariant();
+ break;
+ case "streams.stream.0.codec_tag":
+ {
+ var hex = kv[1].Substring(3, kv[1].Length - 4);
+ format = int.Parse(hex, NumberStyles.HexNumber);
+ break;
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidOperationException("Failed to parse ffprobe output.", ex);
+ }
+
+ // XNA seems to use the sample format for the bits per sample
+ // in the case of non-PCM formats like MP3 and WMA.
+ if (bitsPerSample == 0 && sampleFormat != null)
+ {
+ switch (sampleFormat)
+ {
+ case "u8":
+ case "u8p":
+ bitsPerSample = 8;
+ break;
+ case "s16":
+ case "s16p":
+ bitsPerSample = 16;
+ break;
+ case "s32":
+ case "s32p":
+ case "flt":
+ case "fltp":
+ bitsPerSample = 32;
+ break;
+ case "dbl":
+ case "dblp":
+ bitsPerSample = 64;
+ break;
+ }
+ }
+
+ // Figure out the file type.
+ var durationMs = (int)Math.Floor(durationInSeconds * 1000.0);
+ if (formatName == "wav")
+ {
+ audioFileType = AudioFileType.Wav;
+
+ // A quirk of XNA?
+ if (bitsPerSample == 32)
+ format = -2;
+ }
+ else if (formatName == "mp3")
+ {
+ audioFileType = AudioFileType.Mp3;
+ format = 1;
+ durationMs = (int)Math.Ceiling(durationInSeconds * 1000.0);
+ bitsPerSample = Math.Min(bitsPerSample, 16);
+ }
+ else if (formatName == "wma" || formatName == "asf")
+ {
+ audioFileType = AudioFileType.Wma;
+ format = 1;
+ durationMs = (int)Math.Ceiling(durationInSeconds * 1000.0);
+ bitsPerSample = Math.Min(bitsPerSample, 16);
+ }
+ else if (formatName == "ogg")
+ {
+ audioFileType = AudioFileType.Ogg;
+ format = 1;
+ durationMs = (int)Math.Ceiling(durationInSeconds * 1000.0);
+ bitsPerSample = Math.Min(bitsPerSample, 16);
+ }
+ else
+ audioFileType = (AudioFileType) (-1);
+
+ // XNA seems to calculate the block alignment directly from
+ // the bits per sample and channel count regardless of the
+ // format of the audio data.
+ if (bitsPerSample > 0)
+ blockAlign = (bitsPerSample * channelCount) / 8;
+
+ // XNA seems to only be accurate to the millisecond.
+ duration = TimeSpan.FromMilliseconds(durationMs);
+
+ // Looks like XNA calculates the average bps from
+ // the sample rate and block alignment.
+ if (blockAlign > 0)
+ averageBytesPerSecond = sampleRate * blockAlign;
+
+ audioFormat = new AudioFormat(
+ averageBytesPerSecond,
+ bitsPerSample,
+ blockAlign,
+ channelCount,
+ format,
+ sampleRate);
+
+ // Loop start and length in number of samples. For some
+ // reason XNA doesn't report loop length for non-WAV sources.
+ loopStart = 0;
+ if (audioFileType != AudioFileType.Wav)
+ loopLength = 0;
+ else
+ loopLength = (int)Math.Floor(sampleRate * durationInSeconds);
+ }
+
+ internal static byte[] StripRiffWaveHeader(byte[] data, out AudioFormat audioFormat)
+ {
+ audioFormat = null;
+
+ using (var reader = new BinaryReader(new MemoryStream(data)))
+ {
+ var signature = new string(reader.ReadChars(4));
+ if (signature != "RIFF")
+ return data;
+
+ reader.ReadInt32(); // riff_chunck_size
+
+ var wformat = new string(reader.ReadChars(4));
+ if (wformat != "WAVE")
+ return data;
+
+ // Look for the data chunk.
+ while (true)
+ {
+ var chunkSignature = new string(reader.ReadChars(4));
+ if (chunkSignature.ToLowerInvariant() == "data")
+ break;
+ if (chunkSignature.ToLowerInvariant() == "fmt ")
+ {
+ int fmtLength = reader.ReadInt32();
+ short formatTag = reader.ReadInt16();
+ short channels = reader.ReadInt16();
+ int sampleRate = reader.ReadInt32();
+ int avgBytesPerSec = reader.ReadInt32();
+ short blockAlign = reader.ReadInt16();
+ short bitsPerSample = reader.ReadInt16();
+ audioFormat = new AudioFormat(avgBytesPerSec, bitsPerSample, blockAlign, channels, formatTag, sampleRate);
+
+ fmtLength -= 2 + 2 + 4 + 4 + 2 + 2;
+ if (fmtLength < 0)
+ throw new InvalidOperationException("riff wave header has unexpected format");
+ reader.BaseStream.Seek(fmtLength, SeekOrigin.Current);
+ }
+ else
+ {
+ reader.BaseStream.Seek(reader.ReadInt32(), SeekOrigin.Current);
+ }
+ }
+
+ var dataSize = reader.ReadInt32();
+ data = reader.ReadBytes(dataSize);
+ }
+
+ return data;
+ }
+
+ public static void WritePcmFile(AudioContent content, string saveToFile)
+ {
+ string ffmpegStdout, ffmpegStderr;
+ var ffmpegExitCode = ExternalTool.Run(
+ "ffmpeg",
+ string.Format(
+ "-y -i \"{0}\" -vn -c:a pcm_s16le -b:a 192000 -f:a wav -strict experimental \"{1}\"",
+ content.FileName,
+ saveToFile),
+ out ffmpegStdout,
+ out ffmpegStderr);
+ if (ffmpegExitCode != 0)
+ throw new InvalidOperationException("ffmpeg exited with non-zero exit code: \n" + ffmpegStdout + "\n" + ffmpegStderr);
+ }
+
+ public static ConversionQuality ConvertToFormat(AudioContent content, ConversionFormat formatType, ConversionQuality quality, string saveToFile)
+ {
+ var temporaryOutput = Path.GetTempFileName();
+ try
+ {
+ string ffmpegCodecName, ffmpegMuxerName;
+ //int format;
+ switch (formatType)
+ {
+ case ConversionFormat.Adpcm:
+ // ADPCM Microsoft
+ ffmpegCodecName = "adpcm_ms";
+ ffmpegMuxerName = "wav";
+ //format = 0x0002; /* WAVE_FORMAT_ADPCM */
+ break;
+ case ConversionFormat.Pcm:
+ // XNA seems to preserve the bit size of the input
+ // format when converting to PCM.
+ if (content.Format.BitsPerSample == 8)
+ ffmpegCodecName = "pcm_u8";
+ else if (content.Format.BitsPerSample == 32)
+ ffmpegCodecName = "pcm_s32le";
+ else
+ ffmpegCodecName = "pcm_s16le";
+ ffmpegMuxerName = "wav";
+ //format = 0x0001; /* WAVE_FORMAT_PCM */
+ break;
+ case ConversionFormat.WindowsMedia:
+ // Windows Media Audio 2
+ ffmpegCodecName = "wmav2";
+ ffmpegMuxerName = "asf";
+ //format = 0x0161; /* WAVE_FORMAT_WMAUDIO2 */
+ break;
+ case ConversionFormat.Xma:
+ throw new NotSupportedException(
+ "XMA is not a supported encoding format. It is specific to the Xbox 360.");
+ case ConversionFormat.ImaAdpcm:
+ // ADPCM IMA WAV
+ ffmpegCodecName = "adpcm_ima_wav";
+ ffmpegMuxerName = "wav";
+ //format = 0x0011; /* WAVE_FORMAT_IMA_ADPCM */
+ break;
+ case ConversionFormat.Aac:
+ // AAC (Advanced Audio Coding)
+ // Requires -strict experimental
+ ffmpegCodecName = "aac";
+ ffmpegMuxerName = "ipod";
+ //format = 0x0000; /* WAVE_FORMAT_UNKNOWN */
+ break;
+ case ConversionFormat.Vorbis:
+ // Vorbis
+ ffmpegCodecName = "libvorbis";
+ ffmpegMuxerName = "ogg";
+ //format = 0x0000; /* WAVE_FORMAT_UNKNOWN */
+ break;
+ default:
+ // Unknown format
+ throw new NotSupportedException();
+ }
+
+ string ffmpegStdout, ffmpegStderr;
+ int ffmpegExitCode;
+ do
+ {
+ ffmpegExitCode = ExternalTool.Run(
+ "ffmpeg",
+ string.Format(
+ "-y -i \"{0}\" -vn -c:a {1} -b:a {2} -ar {3} -f:a {4} -strict experimental \"{5}\"",
+ content.FileName,
+ ffmpegCodecName,
+ QualityToBitRate(quality),
+ QualityToSampleRate(quality, content.Format.SampleRate),
+ ffmpegMuxerName,
+ temporaryOutput),
+ out ffmpegStdout,
+ out ffmpegStderr);
+ if (ffmpegExitCode != 0)
+ quality--;
+ } while (quality >= 0 && ffmpegExitCode != 0);
+
+ if (ffmpegExitCode != 0)
+ {
+ throw new InvalidOperationException("ffmpeg exited with non-zero exit code: \n" + ffmpegStdout + "\n" + ffmpegStderr);
+ }
+
+ byte[] rawData;
+ using (var fs = new FileStream(temporaryOutput, FileMode.Open, FileAccess.Read))
+ {
+ rawData = new byte[fs.Length];
+ fs.Read(rawData, 0, rawData.Length);
+ }
+
+ if (saveToFile != null)
+ {
+ using (var fs = new FileStream(saveToFile, FileMode.Create, FileAccess.Write))
+ fs.Write(rawData, 0, rawData.Length);
+ }
+
+ // Use probe to get the final format and information on the converted file.
+ AudioFileType audioFileType;
+ AudioFormat audioFormat;
+ TimeSpan duration;
+ int loopStart, loopLength;
+ ProbeFormat(temporaryOutput, out audioFileType, out audioFormat, out duration, out loopStart, out loopLength);
+
+ AudioFormat riffAudioFormat;
+ byte[] data = StripRiffWaveHeader(rawData, out riffAudioFormat);
+
+ // deal with adpcm
+ if (audioFormat.Format == 2)
+ {
+ // riff contains correct blockAlign
+ audioFormat = riffAudioFormat;
+
+ // fix loopLength -> has to be multiple of sample per block
+ // see https://msdn.microsoft.com/de-de/library/windows/desktop/ee415711(v=vs.85).aspx
+ ushort samplesPerBlock = (ushort)(audioFormat.BlockAlign * 2 / audioFormat.ChannelCount - 12); // from https://github.com/sharpdx/SharpDX/blob/master/Source/SharpDX/Multimedia/WaveFormatAdpcm.cs
+ loopLength = (int)(audioFormat.SampleRate * duration.TotalSeconds);
+ int remainder = loopLength % samplesPerBlock;
+ loopLength += samplesPerBlock - remainder;
+ }
+
+ content.SetData(data, audioFormat, duration, loopStart, loopLength);
+ }
+ finally
+ {
+ ExternalTool.DeleteFile(temporaryOutput);
+ }
+
+ return quality;
+ }
+ }
+}
diff --git a/MonoGame.Framework.Content.Pipeline/Builder/PathHelper.cs b/MonoGame.Framework.Content.Pipeline/Builder/PathHelper.cs
index 0342c42a2c5..8728a0c442c 100644
--- a/MonoGame.Framework.Content.Pipeline/Builder/PathHelper.cs
+++ b/MonoGame.Framework.Content.Pipeline/Builder/PathHelper.cs
@@ -3,7 +3,6 @@
// file 'LICENSE.txt', which is part of this source code package.
using System;
-using System.IO;
using Microsoft.Xna.Framework.Content.Pipeline;
namespace MonoGame.Framework.Content.Pipeline.Builder
@@ -40,41 +39,6 @@ public static string NormalizeWindows(string path)
return path.Replace('/', '\\');
}
- ///
- /// Returns a path string normalized to the current platform standard.
- ///
- public static string NormalizeOS(string path)
- {
-#if WINRT
- return NormalizeWindows(path);
-#else
- path = path.Replace('\\', Path.DirectorySeparatorChar);
- path = path.Replace('/', Path.DirectorySeparatorChar);
- return path;
-#endif
- }
-
- ///
- /// Returns a path string normalized to the current platform standard.
- ///
- /// Path to normalize
- /// The platform to normalize for
- /// The normalized path
- public static string NormalizeOS(string path, TargetPlatform targetPlatform)
- {
- switch (targetPlatform)
- {
- case TargetPlatform.Windows:
- case TargetPlatform.DesktopGL:
- case TargetPlatform.WindowsPhone8:
- case TargetPlatform.WindowsStoreApp:
- return NormalizeWindows(path);
-
- default:
- return Normalize(path);
- }
- }
-
///
/// Returns a path relative to the base path.
///
@@ -92,24 +56,5 @@ public static string GetRelativePath(string basePath, string path)
return Normalize(str);
}
-
- ///
- /// Returns a path relative to the base path.
- ///
- /// The path to make relative to. Must end with directory seperator.
- /// The path to be made relative to the basePath.
- /// The platform to normalize the path for.
- /// The relative path or the original string if it is not absolute or cannot be made relative.
- public static string GetRelativePath(string basePath, string path, TargetPlatform targetPlatform)
- {
- Uri uri;
- if (!Uri.TryCreate(path, UriKind.Absolute, out uri))
- return path;
-
- uri = new Uri(basePath).MakeRelativeUri(uri);
- var str = Uri.UnescapeDataString(uri.ToString());
-
- return NormalizeOS(str, targetPlatform);
- }
}
}
diff --git a/MonoGame.Framework.Content.Pipeline/Builder/PipelineManager.cs b/MonoGame.Framework.Content.Pipeline/Builder/PipelineManager.cs
index d781bad942b..d3a7da064de 100644
--- a/MonoGame.Framework.Content.Pipeline/Builder/PipelineManager.cs
+++ b/MonoGame.Framework.Content.Pipeline/Builder/PipelineManager.cs
@@ -59,7 +59,6 @@ private struct ProcessorInfo
public string IntermediateDirectory { get; private set; }
private ContentCompiler _compiler;
- private MethodInfo _compileMethod;
public ContentBuildLogger Logger { get; set; }
diff --git a/MonoGame.Framework.Content.Pipeline/DdsLoader.cs b/MonoGame.Framework.Content.Pipeline/DdsLoader.cs
index 459b57e947d..de971e50f0c 100644
--- a/MonoGame.Framework.Content.Pipeline/DdsLoader.cs
+++ b/MonoGame.Framework.Content.Pipeline/DdsLoader.cs
@@ -2,6 +2,7 @@
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.
+using System;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Graphics.PackedVector;
@@ -14,6 +15,7 @@ namespace Microsoft.Xna.Framework.Content.Pipeline
///
class DdsLoader
{
+ [Flags]
enum Ddsd : uint
{
Caps = 0x1, // Required in every DDS file
@@ -26,6 +28,7 @@ enum Ddsd : uint
Depth = 0x800000, // Required in a depth texture
}
+ [Flags]
enum DdsCaps : uint
{
Complex = 0x8, // Optional; must be used on any file that contains more than one surface (a mipmap, a cubic environment map, or mipmapped volume texture)
@@ -33,6 +36,7 @@ enum DdsCaps : uint
Texture = 0x1000, // Required
}
+ [Flags]
enum DdsCaps2 : uint
{
Cubemap = 0x200,
@@ -47,6 +51,7 @@ enum DdsCaps2 : uint
CubemapAllFaces = Cubemap | CubemapPositiveX | CubemapNegativeX | CubemapPositiveY | CubemapNegativeY | CubemapPositiveZ | CubemapNegativeZ,
}
+ [Flags]
enum Ddpf : uint
{
AlphaPixels = 0x1,
@@ -69,6 +74,7 @@ static uint MakeFourCC(string cc)
enum FourCC : uint
{
+ A32B32G32R32F = 116,
Dxt1 = 0x31545844,
Dxt2 = 0x32545844,
Dxt3 = 0x33545844,
@@ -108,9 +114,10 @@ static SurfaceFormat GetSurfaceFormat(ref DdsPixelFormat pixelFormat, out bool r
rbSwap = false;
if (pixelFormat.dwFlags.HasFlag(Ddpf.FourCC))
{
- // It is a compressed format
switch (pixelFormat.dwFourCC)
{
+ case FourCC.A32B32G32R32F:
+ return SurfaceFormat.Vector4;
case FourCC.Dxt1:
return SurfaceFormat.Dxt1;
case FourCC.Dxt2:
@@ -141,7 +148,7 @@ static SurfaceFormat GetSurfaceFormat(ref DdsPixelFormat pixelFormat, out bool r
}
else if (pixelFormat.dwRgbBitCount == 32)
{
- rbSwap = pixelFormat.dwBBitMask == 0xFF00;
+ rbSwap = pixelFormat.dwBBitMask == 0xFF;
return SurfaceFormat.Color;
}
throw new ContentLoadException("Unsupported RGBA pixel format");
@@ -154,9 +161,23 @@ static SurfaceFormat GetSurfaceFormat(ref DdsPixelFormat pixelFormat, out bool r
rbSwap = pixelFormat.dwBBitMask == 0x1F;
return SurfaceFormat.Bgr565;
}
+ else if (pixelFormat.dwRgbBitCount == 24)
+ {
+ rbSwap = pixelFormat.dwBBitMask == 0xFF;
+ return SurfaceFormat.Color;
+ }
+ else if (pixelFormat.dwRgbBitCount == 32)
+ {
+ rbSwap = pixelFormat.dwBBitMask == 0xFF;
+ return SurfaceFormat.Color;
+ }
throw new ContentLoadException("Unsupported RGB pixel format");
}
}
+ //else if (pixelFormat.dwFlags.HasFlag(Ddpf.Luminance))
+ //{
+ // return SurfaceFormat.Alpha8;
+ //}
throw new ContentLoadException("Unsupported pixel format");
}
@@ -184,6 +205,9 @@ static BitmapContent CreateBitmapContent(SurfaceFormat format, int width, int he
case SurfaceFormat.Dxt5:
return new Dxt5BitmapContent(width, height);
+
+ case SurfaceFormat.Vector4:
+ return new PixelBitmapContent(width, height);
}
throw new ContentLoadException("Unsupported SurfaceFormat " + format);
}
@@ -194,21 +218,29 @@ static int GetBitmapSize(SurfaceFormat format, int width, int height)
// https://msdn.microsoft.com/en-us/library/bb943991.aspx
int pitch = 0;
int rows = 0;
- if (format == SurfaceFormat.Dxt1)
- {
- pitch = MathHelper.Max(1, ((width + 3) / 4)) * 8;
- rows = (height + 3) / 4;
- }
- else if (format == SurfaceFormat.Dxt3 || format == SurfaceFormat.Dxt5)
- {
- pitch = MathHelper.Max(1, ((width + 3) / 4)) * 16;
- rows = (height + 3) / 4;
- }
- else if (format == SurfaceFormat.Color)
+
+ switch (format)
{
- pitch = (width * 32 + 7) / 8;
- rows = height;
+ case SurfaceFormat.Color:
+ case SurfaceFormat.Bgra4444:
+ case SurfaceFormat.Bgra5551:
+ case SurfaceFormat.Bgr565:
+ case SurfaceFormat.Vector4:
+ pitch = width * format.GetSize();
+ rows = height;
+ break;
+
+ case SurfaceFormat.Dxt1:
+ case SurfaceFormat.Dxt3:
+ case SurfaceFormat.Dxt5:
+ pitch = ((width + 3) / 4) * format.GetSize();
+ rows = (height + 3) / 4;
+ break;
+
+ default:
+ throw new ContentLoadException("Unsupported SurfaceFormat " + format);
}
+
return pitch * rows;
}
@@ -303,6 +335,35 @@ static internal TextureContent Import(string filename, ContentImporterContext co
var content = CreateBitmapContent(format, w, h);
var byteCount = GetBitmapSize(format, w, h);
var bytes = reader.ReadBytes(byteCount);
+ if (rbSwap)
+ {
+ switch (format)
+ {
+ case SurfaceFormat.Bgr565:
+ ByteSwapBGR565(bytes);
+ break;
+ case SurfaceFormat.Bgra4444:
+ ByteSwapBGRA4444(bytes);
+ break;
+ case SurfaceFormat.Bgra5551:
+ ByteSwapBGRA5551(bytes);
+ break;
+ case SurfaceFormat.Color:
+ if (header.ddspf.dwRgbBitCount == 32)
+ ByteSwapRGBX(bytes);
+ else if (header.ddspf.dwRgbBitCount == 24)
+ ByteSwapRGB(bytes);
+ break;
+ }
+ }
+ if ((format == SurfaceFormat.Color) && header.ddspf.dwFlags.HasFlag(Ddpf.Rgb) && !header.ddspf.dwFlags.HasFlag(Ddpf.AlphaPixels))
+ {
+ // Fill or add alpha with opaque
+ if (header.ddspf.dwRgbBitCount == 32)
+ ByteFillAlpha(bytes);
+ else if (header.ddspf.dwRgbBitCount == 24)
+ ByteExpandAlpha(ref bytes);
+ }
content.SetPixelData(bytes);
mipMaps.Add(content);
w = MathHelper.Max(1, w / 2);
@@ -314,5 +375,161 @@ static internal TextureContent Import(string filename, ContentImporterContext co
return output;
}
+
+ static void ByteFillAlpha(byte[] bytes)
+ {
+ for (int i = 0; i < bytes.Length; i += 4)
+ {
+ bytes[i + 3] = 255;
+ }
+ }
+
+ static void ByteExpandAlpha(ref byte[] bytes)
+ {
+ var rgba = new byte[bytes.Length + (bytes.Length / 3)];
+ int j = 0;
+ for (int i = 0; i < bytes.Length; i += 3)
+ {
+ rgba[j] = bytes[i];
+ rgba[j + 1] = bytes[i + 1];
+ rgba[j + 2] = bytes[i + 2];
+ rgba[j + 3] = 255;
+ j += 4;
+ }
+ bytes = rgba;
+ }
+
+ static void ByteSwapRGB(byte[] bytes)
+ {
+ for (int i = 0; i < bytes.Length; i += 3)
+ {
+ byte r = bytes[i];
+ bytes[i] = bytes[i + 2];
+ bytes[i + 2] = r;
+ }
+ }
+
+ static void ByteSwapRGBX(byte[] bytes)
+ {
+ for (int i = 0; i < bytes.Length; i += 4)
+ {
+ byte r = bytes[i];
+ bytes[i] = bytes[i + 2];
+ bytes[i + 2] = r;
+ }
+ }
+
+ static void ByteSwapBGRA4444(byte[] bytes)
+ {
+ for (int i = 0; i < bytes.Length; i += 2)
+ {
+ var r = bytes[i] & 0xF0;
+ var b = bytes[i + 1] & 0xF0;
+ bytes[i] = (byte)((bytes[i] & 0x0F) | b);
+ bytes[i + 1] = (byte)((bytes[i + 1] & 0x0F) | r);
+ }
+ }
+
+ static void ByteSwapBGRA5551(byte[] bytes)
+ {
+ for (int i = 0; i < bytes.Length; i += 2)
+ {
+ var r = (bytes[i] & 0xF8) >> 3;
+ var b = (bytes[i + 1] & 0x3E) >> 1;
+ bytes[i] = (byte)((bytes[i] & 0x07) | (b << 3));
+ bytes[i + 1] = (byte)((bytes[i + 1] & 0xC1) | (r << 1));
+ }
+ }
+
+ static void ByteSwapBGR565(byte[] bytes)
+ {
+ for (int i = 0; i < bytes.Length; i += 2)
+ {
+ var r = (bytes[i] & 0xF8) >> 3;
+ var b = bytes[i + 1] & 0x1F;
+ bytes[i] = (byte)((bytes[i] & 0x07) | (b << 3));
+ bytes[i + 1] = (byte)((bytes[i + 1] & 0xE0) | r);
+ }
+ }
+
+ internal static void WriteUncompressed(string filename, BitmapContent bitmapContent)
+ {
+ using (var writer = new BinaryWriter(new FileStream(filename, FileMode.Create, FileAccess.Write)))
+ {
+ // Write signature ("DDS ")
+ writer.Write((byte)0x44);
+ writer.Write((byte)0x44);
+ writer.Write((byte)0x53);
+ writer.Write((byte)0x20);
+
+ var header = new DdsHeader();
+ header.dwSize = 124;
+ header.dwFlags = Ddsd.Caps | Ddsd.Width | Ddsd.Height | Ddsd.Pitch | Ddsd.PixelFormat;
+ header.dwWidth = (uint)bitmapContent.Width;
+ header.dwHeight = (uint)bitmapContent.Height;
+ header.dwPitchOrLinearSize = (uint)(bitmapContent.Width * 4);
+ header.dwDepth = (uint)0;
+ header.dwMipMapCount = (uint)0;
+
+ writer.Write((uint)header.dwSize);
+ writer.Write((uint)header.dwFlags);
+ writer.Write((uint)header.dwHeight);
+ writer.Write((uint)header.dwWidth);
+ writer.Write((uint)header.dwPitchOrLinearSize);
+ writer.Write((uint)header.dwDepth);
+ writer.Write((uint)header.dwMipMapCount);
+
+ // 11 unsed and reserved DWORDS.
+ writer.Write((uint)0);
+ writer.Write((uint)0);
+ writer.Write((uint)0);
+ writer.Write((uint)0);
+ writer.Write((uint)0);
+ writer.Write((uint)0);
+ writer.Write((uint)0);
+ writer.Write((uint)0);
+ writer.Write((uint)0);
+ writer.Write((uint)0);
+ writer.Write((uint)0);
+
+ SurfaceFormat format;
+ if (!bitmapContent.TryGetFormat(out format) || format != SurfaceFormat.Color)
+ throw new NotSupportedException("Unsupported bitmap content!");
+
+ header.ddspf.dwSize = 32;
+ header.ddspf.dwFlags = Ddpf.AlphaPixels | Ddpf.Rgb;
+ header.ddspf.dwFourCC = 0;
+ header.ddspf.dwRgbBitCount = 32;
+ header.ddspf.dwRBitMask = 0x000000ff;
+ header.ddspf.dwGBitMask = 0x0000ff00;
+ header.ddspf.dwBBitMask = 0x00ff0000;
+ header.ddspf.dwABitMask = 0xff000000;
+
+ // Write the DDS_PIXELFORMAT
+ writer.Write((uint)header.ddspf.dwSize);
+ writer.Write((uint)header.ddspf.dwFlags);
+ writer.Write((uint)header.ddspf.dwFourCC);
+ writer.Write((uint)header.ddspf.dwRgbBitCount);
+ writer.Write((uint)header.ddspf.dwRBitMask);
+ writer.Write((uint)header.ddspf.dwGBitMask);
+ writer.Write((uint)header.ddspf.dwBBitMask);
+ writer.Write((uint)header.ddspf.dwABitMask);
+
+ header.dwCaps = DdsCaps.Texture;
+ header.dwCaps2 = 0;
+
+ // Continue reading DDS_HEADER
+ writer.Write((uint)header.dwCaps);
+ writer.Write((uint)header.dwCaps2);
+
+ // More reserved unused DWORDs.
+ writer.Write((uint)0);
+ writer.Write((uint)0);
+ writer.Write((uint)0);
+
+ // Write out the face data.
+ writer.Write(bitmapContent.GetPixelData());
+ }
+ }
}
}
diff --git a/MonoGame.Framework.Content.Pipeline/ExternalReference.cs b/MonoGame.Framework.Content.Pipeline/ExternalReference.cs
index 29764bf3aa0..d40d3ce1e09 100644
--- a/MonoGame.Framework.Content.Pipeline/ExternalReference.cs
+++ b/MonoGame.Framework.Content.Pipeline/ExternalReference.cs
@@ -60,7 +60,7 @@ public ExternalReference(string filename, ContentIdentity relativeToContent)
// that from here, so we'll work with the absolute path and let the
// higher level process sort out any relative paths they need.
var basePath = Path.GetDirectoryName(relativeToContent.SourceFilename);
- Filename = PathHelper.NormalizeOS(Path.GetFullPath(Path.Combine(basePath, filename)));
+ Filename = PathHelper.Normalize(Path.GetFullPath(Path.Combine(basePath, filename)));
}
}
}
diff --git a/MonoGame.Framework.Content.Pipeline/ExternalTool.cs b/MonoGame.Framework.Content.Pipeline/ExternalTool.cs
index d9ba2015e6e..0cc8062b331 100644
--- a/MonoGame.Framework.Content.Pipeline/ExternalTool.cs
+++ b/MonoGame.Framework.Content.Pipeline/ExternalTool.cs
@@ -114,6 +114,9 @@ public static int Run(string command, string arguments, out string stdout, out s
///
private static string FindCommand(string command)
{
+ // Expand any environment variables.
+ command = Environment.ExpandEnvironmentVariables(command);
+
// If we have a full path just pass it through.
if (File.Exists(command))
return command;
@@ -139,5 +142,20 @@ private static string FindCommand(string command)
return null;
}
+
+ ///
+ /// Safely deletes the file if it exists.
+ ///
+ /// The path to the file to delete.
+ public static void DeleteFile(string filePath)
+ {
+ try
+ {
+ File.Delete(filePath);
+ }
+ catch (Exception)
+ {
+ }
+ }
}
}
diff --git a/MonoGame.Framework.Content.Pipeline/Graphics/DefaultTextureProfile.cs b/MonoGame.Framework.Content.Pipeline/Graphics/DefaultTextureProfile.cs
new file mode 100644
index 00000000000..c7d9743a82f
--- /dev/null
+++ b/MonoGame.Framework.Content.Pipeline/Graphics/DefaultTextureProfile.cs
@@ -0,0 +1,142 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+using Microsoft.Xna.Framework.Content.Pipeline.Processors;
+using Microsoft.Xna.Framework.Graphics;
+
+
+namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
+{
+ internal class DefaultTextureProfile : TextureProfile
+ {
+ public override bool Supports(TargetPlatform platform)
+ {
+ return platform == TargetPlatform.Android ||
+ platform == TargetPlatform.DesktopGL ||
+ platform == TargetPlatform.MacOSX ||
+ platform == TargetPlatform.NativeClient ||
+ platform == TargetPlatform.RaspberryPi ||
+ platform == TargetPlatform.Windows ||
+ platform == TargetPlatform.WindowsPhone8 ||
+ platform == TargetPlatform.WindowsStoreApp ||
+ platform == TargetPlatform.iOS;
+ }
+
+ private static bool IsCompressedTextureFormat(TextureProcessorOutputFormat format)
+ {
+ switch (format)
+ {
+ case TextureProcessorOutputFormat.AtcCompressed:
+ case TextureProcessorOutputFormat.DxtCompressed:
+ case TextureProcessorOutputFormat.Etc1Compressed:
+ case TextureProcessorOutputFormat.PvrCompressed:
+ return true;
+ }
+ return false;
+ }
+
+ private static TextureProcessorOutputFormat GetTextureFormatForPlatform(TextureProcessorOutputFormat format, TargetPlatform platform)
+ {
+ // Select the default texture compression format for the target platform
+ if (format == TextureProcessorOutputFormat.Compressed)
+ {
+ if (platform == TargetPlatform.iOS)
+ format = TextureProcessorOutputFormat.PvrCompressed;
+ else if (platform == TargetPlatform.Android)
+ format = TextureProcessorOutputFormat.Etc1Compressed;
+ else
+ format = TextureProcessorOutputFormat.DxtCompressed;
+ }
+
+ if (IsCompressedTextureFormat(format))
+ {
+ // Make sure the target platform supports the selected texture compression format
+ if (platform == TargetPlatform.iOS)
+ {
+ if (format != TextureProcessorOutputFormat.PvrCompressed)
+ throw new PlatformNotSupportedException("iOS platform only supports PVR texture compression");
+ }
+ else if (platform == TargetPlatform.Windows ||
+ platform == TargetPlatform.WindowsPhone8 ||
+ platform == TargetPlatform.WindowsStoreApp ||
+ platform == TargetPlatform.DesktopGL ||
+ platform == TargetPlatform.MacOSX ||
+ platform == TargetPlatform.NativeClient)
+ {
+ if (format != TextureProcessorOutputFormat.DxtCompressed)
+ throw new PlatformNotSupportedException(format + " platform only supports DXT texture compression");
+ }
+ }
+
+ return format;
+ }
+
+ public override void Requirements(ContentProcessorContext context, TextureProcessorOutputFormat format, out bool requiresPowerOfTwo, out bool requiresSquare)
+ {
+ if (format == TextureProcessorOutputFormat.Compressed)
+ format = GetTextureFormatForPlatform(format, context.TargetPlatform);
+
+ // Does it require POT textures?
+ switch (format)
+ {
+ default:
+ requiresPowerOfTwo = false;
+ break;
+
+ case TextureProcessorOutputFormat.DxtCompressed:
+ requiresPowerOfTwo = context.TargetProfile == GraphicsProfile.Reach;
+ break;
+
+ case TextureProcessorOutputFormat.PvrCompressed:
+ case TextureProcessorOutputFormat.Etc1Compressed:
+ requiresPowerOfTwo = true;
+ break;
+ }
+
+ // Does it require square textures?
+ switch (format)
+ {
+ default:
+ requiresSquare = false;
+ break;
+
+ case TextureProcessorOutputFormat.PvrCompressed:
+ requiresSquare = true;
+ break;
+ }
+ }
+
+ protected override void PlatformCompressTexture(ContentProcessorContext context, TextureContent content, TextureProcessorOutputFormat format, bool generateMipmaps, bool isSpriteFont)
+ {
+ format = GetTextureFormatForPlatform(format, context.TargetPlatform);
+
+ // Make sure we're in a floating point format
+ content.ConvertBitmapType(typeof(PixelBitmapContent));
+
+ switch (format)
+ {
+ case TextureProcessorOutputFormat.AtcCompressed:
+ GraphicsUtil.CompressAti(content, generateMipmaps);
+ break;
+
+ case TextureProcessorOutputFormat.Color16Bit:
+ GraphicsUtil.CompressColor16Bit(content, generateMipmaps);
+ break;
+
+ case TextureProcessorOutputFormat.DxtCompressed:
+ GraphicsUtil.CompressDxt(context.TargetProfile, content, generateMipmaps, isSpriteFont);
+ break;
+
+ case TextureProcessorOutputFormat.Etc1Compressed:
+ GraphicsUtil.CompressEtc1(content, generateMipmaps);
+ break;
+
+ case TextureProcessorOutputFormat.PvrCompressed:
+ GraphicsUtil.CompressPvrtc(content, generateMipmaps, isSpriteFont);
+ break;
+ }
+ }
+ }
+}
diff --git a/MonoGame.Framework.Content.Pipeline/Graphics/DxtBitmapContent.cs b/MonoGame.Framework.Content.Pipeline/Graphics/DxtBitmapContent.cs
index 655395ae885..845c90c1c6a 100644
--- a/MonoGame.Framework.Content.Pipeline/Graphics/DxtBitmapContent.cs
+++ b/MonoGame.Framework.Content.Pipeline/Graphics/DxtBitmapContent.cs
@@ -11,21 +11,35 @@
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
{
- class DxtDataHandler
+ class DxtDataHandler: IDisposable
{
private BitmapContent _content;
byte[] _buffer;
int _offset;
+
+ GCHandle delegateHandleBeginImage;
+ GCHandle delegateHandleWriteData;
public OutputOptions.WriteDataDelegate WriteData { get; private set; }
public OutputOptions.ImageDelegate BeginImage { get; private set; }
- public DxtDataHandler(BitmapContent content)
+ public DxtDataHandler(BitmapContent content, OutputOptions outputOptions)
{
_content = content;
WriteData = new OutputOptions.WriteDataDelegate(WriteDataInternal);
BeginImage = new OutputOptions.ImageDelegate(BeginImageInternal);
+
+ // Keep the delegate from being re-located or collected by the garbage collector.
+ delegateHandleBeginImage = GCHandle.Alloc(BeginImage);
+ delegateHandleWriteData = GCHandle.Alloc(WriteData);
+
+ outputOptions.SetOutputHandler(BeginImage, WriteData);
+ }
+
+ ~DxtDataHandler()
+ {
+ Dispose(false);
}
void BeginImageInternal(int size, int width, int height, int depth, int face, int miplevel)
@@ -42,6 +56,36 @@ bool WriteDataInternal(IntPtr data, int length)
_content.SetPixelData(_buffer);
return true;
}
+
+ #region IDisposable Support
+ private bool disposed = false;
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ // Release managed objects
+ // ...
+ }
+
+ // Release native objects
+ delegateHandleBeginImage.Free();
+ delegateHandleWriteData.Free();
+
+ disposed = true;
+ }
+ }
+
+
+ // This code added to correctly implement the disposable pattern.
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
}
public abstract class DxtBitmapContent : BitmapContent
@@ -168,18 +212,18 @@ protected override bool TryCopyFrom(BitmapContent sourceBitmap, Rectangle source
inputOptions.SetMipmapData(dataPtr, colorBitmap.Width, colorBitmap.Height, 1, 0, 0);
inputOptions.SetMipmapGeneration(false);
inputOptions.SetGamma(1.0f, 1.0f);
-
- var outputOptions = new OutputOptions();
- outputOptions.SetOutputHeader(false);
-
- var handler = new DxtDataHandler(this);
- outputOptions.SetOutputHandler(handler.BeginImage, handler.WriteData);
-
+
var compressionOptions = new CompressionOptions();
compressionOptions.SetFormat(outputFormat);
compressionOptions.SetQuality(Quality.Normal);
- dxtCompressor.Compress(inputOptions, compressionOptions, outputOptions);
+ var outputOptions = new OutputOptions();
+ outputOptions.SetOutputHeader(false);
+
+ using (var handler = new DxtDataHandler(this, outputOptions))
+ {
+ dxtCompressor.Compress(inputOptions, compressionOptions, outputOptions);
+ }
}
finally
{
diff --git a/MonoGame.Framework.Content.Pipeline/Graphics/Font/GlyphPacker.cs b/MonoGame.Framework.Content.Pipeline/Graphics/Font/GlyphPacker.cs
index 309de263eb6..26617de58a3 100644
--- a/MonoGame.Framework.Content.Pipeline/Graphics/Font/GlyphPacker.cs
+++ b/MonoGame.Framework.Content.Pipeline/Graphics/Font/GlyphPacker.cs
@@ -68,9 +68,6 @@ static BitmapContent CopyGlyphsToOutput(List glyphs, int width, i
BitmapContent.Copy(sourceGlyph.Bitmap, sourceRegion, output, destinationRegion);
- // TODO: This causes artifacts around borders.
- //BitmapUtils.PadBorderPixels(output, destinationRegion);
-
sourceGlyph.Bitmap = output;
sourceGlyph.Subrect = destinationRegion;
}
diff --git a/MonoGame.Framework.Content.Pipeline/Graphics/FontHelper.cs b/MonoGame.Framework.Content.Pipeline/Graphics/FontHelper.cs
deleted file mode 100644
index f4c230a3ea3..00000000000
--- a/MonoGame.Framework.Content.Pipeline/Graphics/FontHelper.cs
+++ /dev/null
@@ -1,228 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Runtime.InteropServices;
-using System.Drawing;
-
-#if MACOS
-using MonoMac.CoreGraphics;
-using MonoMac.AppKit;
-using MonoMac.Foundation;
-using MonoMac.CoreText;
-using MonoMac.ImageIO;
-#endif
-
-namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
-{
-
- static class FontHelper
- {
- [StructLayout(LayoutKind.Sequential)]
- public struct ABC
- {
- public int abcA;
- public uint abcB;
- public int abcC;
- }
-
-#if WINDOWS
- [DllImport("gdi32.dll", ExactSpelling = true)]
- public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObj);
-
- [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
- public static extern int DeleteObject(IntPtr hObj);
-
- [DllImport("gdi32.dll", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
- public static extern bool GetCharABCWidthsW(IntPtr hdc, uint uFirstChar, uint uLastChar, [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStruct, SizeConst = 1)] ABC[] lpabc);
-
- public static ABC GetCharWidthABC(char ch, Font font, System.Drawing.Graphics gr)
- {
- ABC[] _temp = new ABC[1];
- IntPtr hDC = gr.GetHdc();
- Font ft = (Font)font.Clone();
- IntPtr hFt = ft.ToHfont();
- SelectObject(hDC, hFt);
- GetCharABCWidthsW(hDC, ch, ch, _temp);
- DeleteObject(hFt);
- gr.ReleaseHdc();
-
- return _temp[0];
- }
-
-#elif MACOS
-
- static CTFont nativeFont;
-
- public static ABC GetCharWidthABC(char ch, Font font, System.Drawing.Graphics gr)
- {
- ABC[] _temp = new ABC[1];
- var nativFont = CreateFont (font.Name, font.Size, font.Style, font.GdiCharSet, font.GdiVerticalFont);
- var atts = buildAttributedString(ch.ToString(), nativFont);
- // for now just a line not sure if this is going to work
- CTLine line = new CTLine(atts);
-
- float ascent;
- float descent;
- float leading;
- _temp[0].abcB = (uint)line.GetTypographicBounds(out ascent, out descent, out leading);
-
-
- return _temp[0];
- }
-
- const byte DefaultCharSet = 1;
- static bool underLine = false;
- static bool strikeThrough = false;
-
- static float dpiScale = 96f / 72f;
-
-
- static internal CTFont CreateFont (string familyName, float emSize)
- {
- return CreateFont (familyName, emSize, FontStyle.Regular, DefaultCharSet, false);
- }
-
- static internal CTFont CreateFont (string familyName, float emSize, FontStyle style)
- {
- return CreateFont (familyName, emSize, style, DefaultCharSet, false);
- }
-
- static internal CTFont CreateFont (string familyName, float emSize, FontStyle style, byte gdiCharSet)
- {
- return CreateFont (familyName, emSize, style, gdiCharSet, false);
- }
-
- static internal CTFont CreateFont (string familyName, float emSize, FontStyle style,
- byte gdiCharSet, bool gdiVerticalFont )
- {
- if (emSize <= 0)
- throw new ArgumentException("emSize is less than or equal to 0, evaluates to infinity, or is not a valid number.","emSize");
-
- CTFont nativeFont;
-
- // convert to 96 Dpi to be consistent with Windows
- var dpiSize = emSize * dpiScale;
-
- try {
- nativeFont = new CTFont(familyName,dpiSize);
- }
- catch
- {
- nativeFont = new CTFont("Helvetica",dpiSize);
- }
-
- CTFontSymbolicTraits tMask = CTFontSymbolicTraits.None;
-
- if ((style & FontStyle.Bold) == FontStyle.Bold)
- tMask |= CTFontSymbolicTraits.Bold;
- if ((style & FontStyle.Italic) == FontStyle.Italic)
- tMask |= CTFontSymbolicTraits.Italic;
- strikeThrough = (style & FontStyle.Strikeout) == FontStyle.Strikeout;
- underLine = (style & FontStyle.Underline) == FontStyle.Underline;
-
- var nativeFont2 = nativeFont.WithSymbolicTraits(dpiSize,tMask,tMask);
-
- if (nativeFont2 != null)
- nativeFont = nativeFont2;
-
- return nativeFont;
- }
-
- static NSString FontAttributedName = (NSString)"NSFont";
- static NSString ForegroundColorAttributedName = (NSString)"NSColor";
- static NSString UnderlineStyleAttributeName = (NSString)"NSUnderline";
- static NSString ParagraphStyleAttributeName = (NSString)"NSParagraphStyle";
- //NSAttributedString.ParagraphStyleAttributeName
- static NSString StrikethroughStyleAttributeName = (NSString)"NSStrikethrough";
-
- private static NSMutableAttributedString buildAttributedString(string text, CTFont font,
- Color? fontColor=null)
- {
-
-
- // Create a new attributed string from text
- NSMutableAttributedString atts =
- new NSMutableAttributedString(text);
-
- var attRange = new NSRange(0, atts.Length);
- var attsDic = new NSMutableDictionary();
-
- // Font attribute
- NSObject fontObject = new NSObject(font.Handle);
- attsDic.Add(FontAttributedName, fontObject);
- // -- end font
-
- if (fontColor.HasValue) {
-
- // Font color
- var fc = fontColor.Value;
- NSColor color = NSColor.FromDeviceRgba(fc.R / 255f,
- fc.G / 255f,
- fc.B / 255f,
- fc.A / 255f);
- NSObject colorObject = new NSObject(color.Handle);
- attsDic.Add(ForegroundColorAttributedName, colorObject);
- // -- end font Color
- }
-
- if (underLine) {
- // Underline
- int single = (int)MonoMac.AppKit.NSUnderlineStyle.Single;
- int solid = (int)MonoMac.AppKit.NSUnderlinePattern.Solid;
- var attss = single | solid;
- var underlineObject = NSNumber.FromInt32(attss);
- //var under = NSAttributedString.UnderlineStyleAttributeName.ToString();
- attsDic.Add(UnderlineStyleAttributeName, underlineObject);
- // --- end underline
- }
-
-
- if (strikeThrough) {
- // StrikeThrough
- // NSColor bcolor = NSColor.Blue;
- // NSObject bcolorObject = new NSObject(bcolor.Handle);
- // attsDic.Add(NSAttributedString.StrikethroughColorAttributeName, bcolorObject);
- int stsingle = (int)MonoMac.AppKit.NSUnderlineStyle.Single;
- int stsolid = (int)MonoMac.AppKit.NSUnderlinePattern.Solid;
- var stattss = stsingle | stsolid;
- var stunderlineObject = NSNumber.FromInt32(stattss);
-
- attsDic.Add(StrikethroughStyleAttributeName, stunderlineObject);
- // --- end underline
- }
-
-
- // Text alignment
- var alignment = CTTextAlignment.Left;
- var alignmentSettings = new CTParagraphStyleSettings();
- alignmentSettings.Alignment = alignment;
- var paragraphStyle = new CTParagraphStyle(alignmentSettings);
- NSObject psObject = new NSObject(paragraphStyle.Handle);
-
- // end text alignment
-
- attsDic.Add(ParagraphStyleAttributeName, psObject);
-
- atts.SetAttributes(attsDic, attRange);
-
- return atts;
-
- }
-
-#elif LINUX
- public static ABC GetCharWidthABC(char ch, Font font, System.Drawing.Graphics gr)
- {
- var sf = StringFormat.GenericTypographic;
- sf.Trimming = StringTrimming.None;
- sf.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;
- return new ABC
- {
- abcA = 0,
- abcB = (uint)gr.MeasureString(ch.ToString(), font, new PointF(0, 0), sf).Width,
- abcC = 0
- };
- }
-#endif
- }
-}
diff --git a/MonoGame.Framework.Content.Pipeline/Graphics/GraphicsUtil.cs b/MonoGame.Framework.Content.Pipeline/Graphics/GraphicsUtil.cs
index 0efe0bea41f..0d3a2c66f10 100644
--- a/MonoGame.Framework.Content.Pipeline/Graphics/GraphicsUtil.cs
+++ b/MonoGame.Framework.Content.Pipeline/Graphics/GraphicsUtil.cs
@@ -3,9 +3,7 @@
// file 'LICENSE.txt', which is part of this source code package.
using System;
-using System.Runtime.InteropServices;
-using System.Threading.Tasks;
-using Microsoft.Xna.Framework.Content.Pipeline.Processors;
+using System.IO;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Graphics.PackedVector;
using FreeImageAPI;
@@ -81,69 +79,6 @@ public static int GetNextPowerOfTwo(int value)
return nearestPower;
}
- ///
- /// Returns true if the format is a compressed format.
- ///
- /// The texture processor output format.
- /// True if the format is a compressed format.
- public static bool IsCompressedTextureFormat(TextureProcessorOutputFormat format)
- {
- switch (format)
- {
- case TextureProcessorOutputFormat.AtcCompressed:
- case TextureProcessorOutputFormat.DxtCompressed:
- case TextureProcessorOutputFormat.Etc1Compressed:
- case TextureProcessorOutputFormat.PvrCompressed:
- return true;
- }
- return false;
- }
-
- ///
- /// Determines if the texture format requires power-of-two dimensions on the target platform.
- ///
- /// The texture format.
- /// The target platform.
- /// The targeted graphics profile.
- /// True if the texture format requires power-of-two dimensions on the target platform.
- public static bool RequiresPowerOfTwo(TextureProcessorOutputFormat format, TargetPlatform platform, GraphicsProfile profile)
- {
- if (format == TextureProcessorOutputFormat.Compressed)
- format = GetTextureFormatForPlatform(format, platform);
-
- switch (format)
- {
- case TextureProcessorOutputFormat.DxtCompressed:
- return profile == GraphicsProfile.Reach;
-
- case TextureProcessorOutputFormat.PvrCompressed:
- case TextureProcessorOutputFormat.Etc1Compressed:
- return true;
- }
-
- return false;
- }
-
- ///
- /// Determines if the given texture format requires equal width and height on the target platform.
- ///
- /// The texture format.
- /// The target platform.
- /// True if the texture format requires equal width and height on the target platform.
- public static bool RequiresSquare(TextureProcessorOutputFormat format, TargetPlatform platform)
- {
- if (format == TextureProcessorOutputFormat.Compressed)
- format = GetTextureFormatForPlatform(format, platform);
-
- switch (format)
- {
- case TextureProcessorOutputFormat.PvrCompressed:
- return true;
- }
-
- return false;
- }
-
enum AlphaRange
{
///
@@ -188,95 +123,15 @@ static AlphaRange CalculateAlphaRange(BitmapContent bitmap)
return result;
}
- ///
- /// If format is TextureProcessorOutputFormat.Compressed, the appropriate compressed texture format for the target
- /// platform is returned. Otherwise the format is returned unchanged.
- ///
- /// The supplied texture format.
- /// The target platform.
- /// The texture format.
- public static TextureProcessorOutputFormat GetTextureFormatForPlatform(TextureProcessorOutputFormat format, TargetPlatform platform)
+ public static void CompressPvrtc(TextureContent content, bool generateMipMaps, bool isSpriteFont)
{
- // Select the default texture compression format for the target platform
- if (format == TextureProcessorOutputFormat.Compressed)
- {
- switch (platform)
- {
- case TargetPlatform.iOS:
- format = TextureProcessorOutputFormat.PvrCompressed;
- break;
-
- case TargetPlatform.Android:
- format = TextureProcessorOutputFormat.Etc1Compressed;
- break;
-
- default:
- format = TextureProcessorOutputFormat.DxtCompressed;
- break;
- }
- }
-
- if (IsCompressedTextureFormat(format))
+ // If sharp alpha is required (for a font texture page), use 16-bit color instead of PVR
+ if (isSpriteFont)
{
- // Make sure the target platform supports the selected texture compression format
- switch (platform)
- {
- case TargetPlatform.iOS:
- if (format != TextureProcessorOutputFormat.PvrCompressed)
- throw new PlatformNotSupportedException("iOS platform only supports PVR texture compression");
- break;
-
- case TargetPlatform.Windows:
- case TargetPlatform.WindowsPhone8:
- case TargetPlatform.WindowsStoreApp:
- case TargetPlatform.DesktopGL:
- case TargetPlatform.MacOSX:
- case TargetPlatform.NativeClient:
- if (format != TextureProcessorOutputFormat.DxtCompressed)
- throw new PlatformNotSupportedException(platform.ToString() + " platform only supports DXT texture compression");
- break;
- }
+ CompressColor16Bit(content, generateMipMaps);
+ return;
}
- return format;
- }
-
- ///
- /// Compresses TextureContent in a format appropriate to the platform
- ///
- public static void CompressTexture(GraphicsProfile profile, TextureContent content, TextureProcessorOutputFormat format, ContentProcessorContext context, bool generateMipmaps, bool sharpAlpha)
- {
- format = GetTextureFormatForPlatform(format, context.TargetPlatform);
-
- // Make sure we're in a floating point format
- content.ConvertBitmapType(typeof(PixelBitmapContent));
-
- switch (format)
- {
- case TextureProcessorOutputFormat.AtcCompressed:
- CompressAti(content, generateMipmaps);
- break;
-
- case TextureProcessorOutputFormat.Color16Bit:
- CompressColor16Bit(content, generateMipmaps);
- break;
-
- case TextureProcessorOutputFormat.DxtCompressed:
- CompressDxt(profile, content, generateMipmaps, sharpAlpha);
- break;
-
- case TextureProcessorOutputFormat.Etc1Compressed:
- CompressEtc1(content, generateMipmaps);
- break;
-
- case TextureProcessorOutputFormat.PvrCompressed:
- CompressPvrtc(content, generateMipmaps);
- break;
- }
- }
-
- private static void CompressPvrtc(TextureContent content, bool generateMipMaps)
- {
// Calculate number of mip levels
var width = content.Faces[0][0].Height;
var height = content.Faces[0][0].Width;
@@ -297,7 +152,7 @@ private static void CompressPvrtc(TextureContent content, bool generateMipMaps)
Compress(typeof(PvrtcRgba4BitmapContent), content, generateMipMaps);
}
- private static void CompressDxt(GraphicsProfile profile, TextureContent content, bool generateMipMaps, bool sharpAlpha)
+ public static void CompressDxt(GraphicsProfile profile, TextureContent content, bool generateMipMaps, bool isSpriteFont)
{
var face = content.Faces[0][0];
@@ -310,15 +165,17 @@ private static void CompressDxt(GraphicsProfile profile, TextureContent content,
// Test the alpha channel to figure out if we have alpha.
var alphaRange = CalculateAlphaRange(face);
- if (alphaRange == AlphaRange.Opaque)
+ if (isSpriteFont)
+ CompressFontDXT3(content, generateMipMaps);
+ else if (alphaRange == AlphaRange.Opaque)
Compress(typeof(Dxt1BitmapContent), content, generateMipMaps);
- else if (alphaRange == AlphaRange.Cutout || sharpAlpha)
+ else if (alphaRange == AlphaRange.Cutout)
Compress(typeof(Dxt3BitmapContent), content, generateMipMaps);
else
Compress(typeof(Dxt5BitmapContent), content, generateMipMaps);
}
-
- static void CompressAti(TextureContent content, bool generateMipMaps)
+
+ static public void CompressAti(TextureContent content, bool generateMipMaps)
{
var face = content.Faces[0][0];
var alphaRange = CalculateAlphaRange(face);
@@ -329,7 +186,7 @@ static void CompressAti(TextureContent content, bool generateMipMaps)
Compress(typeof(AtcInterpolatedBitmapContent), content, generateMipMaps);
}
- static void CompressEtc1(TextureContent content, bool generateMipMaps)
+ static public void CompressEtc1(TextureContent content, bool generateMipMaps)
{
var face = content.Faces[0][0];
var alphaRange = CalculateAlphaRange(face);
@@ -348,7 +205,7 @@ static void CompressEtc1(TextureContent content, bool generateMipMaps)
}
}
- static void CompressColor16Bit(TextureContent content, bool generateMipMaps)
+ static public void CompressColor16Bit(TextureContent content, bool generateMipMaps)
{
var face = content.Faces[0][0];
var alphaRange = CalculateAlphaRange(face);
@@ -416,5 +273,241 @@ static void Compress(Type targetType, TextureContent content, bool generateMipMa
content.ConvertBitmapType(targetType);
}
}
+
+ // Compress the greyscale font texture page using a specially-formulated DXT3 mode
+ static public unsafe void CompressFontDXT3(TextureContent content, bool generateMipmaps)
+ {
+ if (content.Faces.Count > 1)
+ throw new PipelineException("Font textures should only have one face");
+
+ var block = new Vector4[16];
+ for (int i = 0; i < content.Faces[0].Count; ++i)
+ {
+ var face = content.Faces[0][i];
+ var xBlocks = (face.Width + 3) / 4;
+ var yBlocks = (face.Height + 3) / 4;
+ var dxt3Size = xBlocks * yBlocks * 16;
+ var buffer = new byte[dxt3Size];
+
+ var bytes = face.GetPixelData();
+ fixed (byte* b = bytes)
+ {
+ Vector4* colors = (Vector4*)b;
+
+ int w = 0;
+ int h = 0;
+ int x = 0;
+ int y = 0;
+ while (h < (face.Height & ~3))
+ {
+ w = 0;
+ x = 0;
+
+ var h0 = h * face.Width;
+ var h1 = h0 + face.Width;
+ var h2 = h1 + face.Width;
+ var h3 = h2 + face.Width;
+
+ while (w < (face.Width & ~3))
+ {
+ block[0] = colors[w + h0];
+ block[1] = colors[w + h0 + 1];
+ block[2] = colors[w + h0 + 2];
+ block[3] = colors[w + h0 + 3];
+ block[4] = colors[w + h1];
+ block[5] = colors[w + h1 + 1];
+ block[6] = colors[w + h1 + 2];
+ block[7] = colors[w + h1 + 3];
+ block[8] = colors[w + h2];
+ block[9] = colors[w + h2 + 1];
+ block[10] = colors[w + h2 + 2];
+ block[11] = colors[w + h2 + 3];
+ block[12] = colors[w + h3];
+ block[13] = colors[w + h3 + 1];
+ block[14] = colors[w + h3 + 2];
+ block[15] = colors[w + h3 + 3];
+
+ int offset = (x + y * xBlocks) * 16;
+ CompressFontDXT3Block(block, buffer, offset);
+
+ w += 4;
+ ++x;
+ }
+
+ // Do partial block at end of row
+ if (w < face.Width)
+ {
+ var cols = face.Width - w;
+ Array.Clear(block, 0, 16);
+ for (int r = 0; r < 4; ++r)
+ {
+ h0 = (h + r) * face.Width;
+ for (int c = 0; c < cols; ++c)
+ block[(r * 4) + c] = colors[w + h0 + c];
+ }
+
+ int offset = (x + y * xBlocks) * 16;
+ CompressFontDXT3Block(block, buffer, offset);
+ }
+
+ h += 4;
+ ++y;
+ }
+
+ // Do last partial row
+ if (h < face.Height)
+ {
+ var rows = face.Height - h;
+ w = 0;
+ x = 0;
+ while (w < (face.Width & ~3))
+ {
+ Array.Clear(block, 0, 16);
+ for (int r = 0; r < rows; ++r)
+ {
+ var h0 = (h + r) * face.Width;
+ block[(r * 4) + 0] = colors[w + h0 + 0];
+ block[(r * 4) + 1] = colors[w + h0 + 1];
+ block[(r * 4) + 2] = colors[w + h0 + 2];
+ block[(r * 4) + 3] = colors[w + h0 + 3];
+ }
+
+ int offset = (x + y * xBlocks) * 16;
+ CompressFontDXT3Block(block, buffer, offset);
+
+ w += 4;
+ ++x;
+ }
+
+ // Do last partial block
+ if (w < face.Width)
+ {
+ var cols = face.Width - w;
+ Array.Clear(block, 0, 16);
+ for (int r = 0; r < rows; ++r)
+ {
+ var h0 = (h + r) * face.Width;
+ for (int c = 0; c < cols; ++c)
+ block[(r * 4) + c] = colors[w + h0 + c];
+ }
+
+ int offset = (x + y * xBlocks) * 16;
+ CompressFontDXT3Block(block, buffer, offset);
+ }
+ }
+ }
+
+ var dxt3 = new Dxt3BitmapContent(face.Width, face.Height);
+ dxt3.SetPixelData(buffer);
+ content.Faces[0][i] = dxt3;
+ }
+ }
+
+ // Maps a 2-bit greyscale to the index required for DXT3
+ // 00 = color0
+ // 01 = color1
+ // 10 = 2/3 * color0 + 1/3 * color1
+ // 11 = 1/3 * color0 + 2/3 * color1
+ static byte[] dxt3Map = new byte[] { 0, 2, 3, 1 };
+
+ // Compress a single 4x4 block from colors into buffer at the given offset
+ static void CompressFontDXT3Block(Vector4[] colors, byte[] buffer, int offset)
+ {
+ // Get the alpha into a 0-15 range
+ int a0 = (int)(colors[0].W * 15.0);
+ int a1 = (int)(colors[1].W * 15.0);
+ int a2 = (int)(colors[2].W * 15.0);
+ int a3 = (int)(colors[3].W * 15.0);
+ int a4 = (int)(colors[4].W * 15.0);
+ int a5 = (int)(colors[5].W * 15.0);
+ int a6 = (int)(colors[6].W * 15.0);
+ int a7 = (int)(colors[7].W * 15.0);
+ int a8 = (int)(colors[8].W * 15.0);
+ int a9 = (int)(colors[9].W * 15.0);
+ int a10 = (int)(colors[10].W * 15.0);
+ int a11 = (int)(colors[11].W * 15.0);
+ int a12 = (int)(colors[12].W * 15.0);
+ int a13 = (int)(colors[13].W * 15.0);
+ int a14 = (int)(colors[14].W * 15.0);
+ int a15 = (int)(colors[15].W * 15.0);
+
+ // Duplicate the top two bits into the bottom two bits so we get one of four values: b0000, b0101, b1010, b1111
+ a0 = (a0 & 0xC) | (a0 >> 2);
+ a1 = (a1 & 0xC) | (a1 >> 2);
+ a2 = (a2 & 0xC) | (a2 >> 2);
+ a3 = (a3 & 0xC) | (a3 >> 2);
+ a4 = (a4 & 0xC) | (a4 >> 2);
+ a5 = (a5 & 0xC) | (a5 >> 2);
+ a6 = (a6 & 0xC) | (a6 >> 2);
+ a7 = (a7 & 0xC) | (a7 >> 2);
+ a8 = (a8 & 0xC) | (a8 >> 2);
+ a9 = (a9 & 0xC) | (a9 >> 2);
+ a10 = (a10 & 0xC) | (a10 >> 2);
+ a11 = (a11 & 0xC) | (a11 >> 2);
+ a12 = (a12 & 0xC) | (a12 >> 2);
+ a13 = (a13 & 0xC) | (a13 >> 2);
+ a14 = (a14 & 0xC) | (a14 >> 2);
+ a15 = (a15 & 0xC) | (a15 >> 2);
+
+ // 4-bit alpha
+ buffer[offset + 0] = (byte)((a1 << 4) | a0);
+ buffer[offset + 1] = (byte)((a3 << 4) | a2);
+ buffer[offset + 2] = (byte)((a5 << 4) | a4);
+ buffer[offset + 3] = (byte)((a7 << 4) | a6);
+ buffer[offset + 4] = (byte)((a9 << 4) | a8);
+ buffer[offset + 5] = (byte)((a11 << 4) | a10);
+ buffer[offset + 6] = (byte)((a13 << 4) | a12);
+ buffer[offset + 7] = (byte)((a15 << 4) | a14);
+
+ // color0 (transparent)
+ buffer[offset + 8] = 0;
+ buffer[offset + 9] = 0;
+
+ // color1 (white)
+ buffer[offset + 10] = 255;
+ buffer[offset + 11] = 255;
+
+ // Get the red (to be used for green and blue channels as well) into a 0-15 range
+ a0 = (int)(colors[0].X * 15.0);
+ a1 = (int)(colors[1].X * 15.0);
+ a2 = (int)(colors[2].X * 15.0);
+ a3 = (int)(colors[3].X * 15.0);
+ a4 = (int)(colors[4].X * 15.0);
+ a5 = (int)(colors[5].X * 15.0);
+ a6 = (int)(colors[6].X * 15.0);
+ a7 = (int)(colors[7].X * 15.0);
+ a8 = (int)(colors[8].X * 15.0);
+ a9 = (int)(colors[9].X * 15.0);
+ a10 = (int)(colors[10].X * 15.0);
+ a11 = (int)(colors[11].X * 15.0);
+ a12 = (int)(colors[12].X * 15.0);
+ a13 = (int)(colors[13].X * 15.0);
+ a14 = (int)(colors[14].X * 15.0);
+ a15 = (int)(colors[15].X * 15.0);
+
+ // Duplicate the top two bits into the bottom two bits so we get one of four values: b0000, b0101, b1010, b1111
+ a0 = (a0 & 0xC) | (a0 >> 2);
+ a1 = (a1 & 0xC) | (a1 >> 2);
+ a2 = (a2 & 0xC) | (a2 >> 2);
+ a3 = (a3 & 0xC) | (a3 >> 2);
+ a4 = (a4 & 0xC) | (a4 >> 2);
+ a5 = (a5 & 0xC) | (a5 >> 2);
+ a6 = (a6 & 0xC) | (a6 >> 2);
+ a7 = (a7 & 0xC) | (a7 >> 2);
+ a8 = (a8 & 0xC) | (a8 >> 2);
+ a9 = (a9 & 0xC) | (a9 >> 2);
+ a10 = (a10 & 0xC) | (a10 >> 2);
+ a11 = (a11 & 0xC) | (a11 >> 2);
+ a12 = (a12 & 0xC) | (a12 >> 2);
+ a13 = (a13 & 0xC) | (a13 >> 2);
+ a14 = (a14 & 0xC) | (a14 >> 2);
+ a15 = (a15 & 0xC) | (a15 >> 2);
+
+ // Color indices (00 = color0, 01 = color1, 10 = 2/3 * color0 + 1/3 * color1, 11 = 1/3 * color0 + 2/3 * color1)
+ buffer[offset + 12] = (byte)((dxt3Map[a3 >> 2] << 6) | (dxt3Map[a2 >> 2] << 4) | (dxt3Map[a1 >> 2] << 2) | dxt3Map[a0 >> 2]);
+ buffer[offset + 13] = (byte)((dxt3Map[a7 >> 2] << 6) | (dxt3Map[a6 >> 2] << 4) | (dxt3Map[a5 >> 2] << 2) | dxt3Map[a4 >> 2]);
+ buffer[offset + 14] = (byte)((dxt3Map[a11 >> 2] << 6) | (dxt3Map[a10 >> 2] << 4) | (dxt3Map[a9 >> 2] << 2) | dxt3Map[a8 >> 2]);
+ buffer[offset + 15] = (byte)((dxt3Map[a15 >> 2] << 6) | (dxt3Map[a14 >> 2] << 4) | (dxt3Map[a13 >> 2] << 2) | dxt3Map[a12 >> 2]);
+ }
}
}
diff --git a/MonoGame.Framework.Content.Pipeline/Graphics/IndirectPositionCollection.cs b/MonoGame.Framework.Content.Pipeline/Graphics/IndirectPositionCollection.cs
index 9fda9c6f376..63b96f4cf5f 100644
--- a/MonoGame.Framework.Content.Pipeline/Graphics/IndirectPositionCollection.cs
+++ b/MonoGame.Framework.Content.Pipeline/Graphics/IndirectPositionCollection.cs
@@ -18,7 +18,7 @@ namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
///
public sealed class IndirectPositionCollection : IList
{
- private readonly VertexChannel _positionIndicies;
+ private readonly VertexChannel _positionIndices;
private readonly GeometryContent _geometry;
///
@@ -27,7 +27,7 @@ public sealed class IndirectPositionCollection : IList
/// Number of positions.
public int Count
{
- get { return _positionIndicies.Count; }
+ get { return _positionIndices.Count; }
}
///
@@ -38,10 +38,9 @@ public Vector3 this[int index]
{
get
{
- var remap = _positionIndicies[index];
+ var remap = _positionIndices[index];
return _geometry.Parent.Positions[remap];
}
-
set
{
throw Readonly();
@@ -60,10 +59,10 @@ bool ICollection.IsReadOnly
///
/// Initializes a new instance of IndirectPositionCollection.
///
- internal IndirectPositionCollection(GeometryContent geom, VertexChannel positionIndicies)
+ internal IndirectPositionCollection(GeometryContent geom, VertexChannel positionIndices)
{
_geometry = geom;
- _positionIndicies = positionIndicies;
+ _positionIndices = positionIndices;
}
///
diff --git a/MonoGame.Framework.Content.Pipeline/Graphics/MeshBuilder.cs b/MonoGame.Framework.Content.Pipeline/Graphics/MeshBuilder.cs
new file mode 100644
index 00000000000..6513360435d
--- /dev/null
+++ b/MonoGame.Framework.Content.Pipeline/Graphics/MeshBuilder.cs
@@ -0,0 +1,238 @@
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
+{
+ public sealed class MeshBuilder
+ {
+ private readonly MeshContent _meshContent;
+
+ private MaterialContent _currentMaterial;
+ private OpaqueDataDictionary _currentOpaqueData;
+ private bool _geometryDirty;
+ private GeometryContent _currentGeometryContent;
+
+ private readonly List _vertexChannels;
+ private readonly List _vertexChannelData;
+
+ private bool _finishedCreation;
+ private bool _finishedMesh;
+
+ ///
+ /// Gets or sets the current value for position merging of the mesh.
+ ///
+ public bool MergeDuplicatePositions { get; set; }
+
+ ///
+ /// Gets or sets the tolerance for .
+ ///
+ public float MergePositionTolerance { get; set; }
+
+ ///
+ /// Gets or sets the name of the current object being processed.
+ ///
+ public string Name
+ {
+ get
+ {
+ return _meshContent.Name;
+ }
+ set
+ {
+ _meshContent.Name = value;
+ }
+ }
+
+ ///
+ /// Reverses the triangle winding order of the specified mesh.
+ ///
+ public bool SwapWindingOrder { get; set; }
+
+
+ private MeshBuilder(string name)
+ {
+ _meshContent = new MeshContent();
+ _vertexChannels = new List();
+ _vertexChannelData = new List();
+ _currentGeometryContent = new GeometryContent();
+ _currentOpaqueData = new OpaqueDataDictionary();
+ _geometryDirty = true;
+ Name = name;
+ }
+
+ ///
+ /// Adds a vertex into the index collection.
+ ///
+ /// Index of the inserted vertex, in the collection.
+ /// This corresponds to the value returned by .
+ public void AddTriangleVertex(int indexIntoVertexCollection)
+ {
+ if (_finishedMesh)
+ throw new InvalidOperationException( "This MeshBuilder can no longer be used because FinishMesh has been called.");
+
+ _finishedCreation = true;
+
+ if (_geometryDirty)
+ {
+ _currentGeometryContent = new GeometryContent();
+ _currentGeometryContent.Material = _currentMaterial;
+ foreach (var kvp in _currentOpaqueData)
+ _currentGeometryContent.OpaqueData.Add(kvp.Key, kvp.Value);
+
+ // we have to copy our vertex channels to the new geometry
+ foreach (var channel in _vertexChannels)
+ {
+ _currentGeometryContent.Vertices.Channels.Add(channel.Name, channel.ElementType, null);
+ }
+ _meshContent.Geometry.Add(_currentGeometryContent);
+ _geometryDirty = false;
+
+ }
+ // Add the vertex to the mesh and then add the vertex position to the indices list
+ var pos = _currentGeometryContent.Vertices.Add(indexIntoVertexCollection);
+
+ // Then add the data for the other channels
+ for (var i = 0; i < _vertexChannels.Count; i++)
+ {
+ var channel = _currentGeometryContent.Vertices.Channels[i];
+ var data = _vertexChannelData[i];
+ if (data == null)
+ throw new InvalidOperationException(string.Format("Missing vertex channel data for channel {0}", channel.Name));
+
+ channel.Items.Add(data);
+ }
+
+ _currentGeometryContent.Indices.Add(pos);
+ }
+
+ public int CreateVertexChannel(string usage)
+ {
+ if (_finishedMesh)
+ throw new InvalidOperationException("This MeshBuilder can no longer be used because FinishMesh has been called.");
+ if (_finishedCreation)
+ throw new InvalidOperationException("Functions starting with 'Create' must be called before calling AddTriangleVertex");
+
+ var channel = new VertexChannel(usage);
+ _vertexChannels.Add(channel);
+ _vertexChannelData.Add(default(T));
+
+ _currentGeometryContent.Vertices.Channels.Add(usage, null);
+
+ return _vertexChannels.Count - 1;
+ }
+
+ ///
+ /// Inserts the specified vertex position into the vertex channel.
+ ///
+ /// Value of the x component of the vector.
+ /// Value of the y component of the vector.
+ /// Value of the z component of the vector.
+ /// Index of the inserted vertex.
+ public int CreatePosition(float x, float y, float z)
+ {
+ return CreatePosition(new Vector3(x, y, z));
+ }
+
+ ///
+ /// Inserts the specified vertex position into the vertex channel at the specified index.
+ ///
+ /// Value of the vertex being inserted.
+ /// Index of the vertex being inserted.
+ public int CreatePosition(Vector3 pos)
+ {
+ if (_finishedMesh)
+ throw new InvalidOperationException( "This MeshBuilder can no longer be used because FinishMesh has been called.");
+ if (_finishedCreation)
+ throw new InvalidOperationException("Functions starting with 'Create' must be called before calling AddTriangleVertex");
+
+ _meshContent.Positions.Add(pos);
+ return _meshContent.Positions.Count - 1;
+ }
+
+ ///
+ /// Ends the creation of a mesh.
+ ///
+ /// Resultant mesh.
+ public MeshContent FinishMesh()
+ {
+ if (_finishedMesh)
+ return _meshContent;
+
+ if (MergeDuplicatePositions)
+ MeshHelper.MergeDuplicatePositions(_meshContent, MergePositionTolerance);
+
+ MeshHelper.MergeDuplicateVertices(_meshContent);
+
+ MeshHelper.CalculateNormals(_meshContent, false);
+ if (SwapWindingOrder)
+ MeshHelper.SwapWindingOrder(_meshContent);
+
+ _finishedMesh = true;
+ return _meshContent;
+ }
+
+ ///
+ /// Sets the material for the next triangles.
+ ///
+ /// Material for the next triangles.
+ ///
+ /// Sets the material for the triangles being defined next. This material
+ /// and the opaque data dictionary, set with
+ /// define the object containing the next
+ /// triangles. When you set a new material or opaque data dictionary the
+ /// triangles you add afterwards will belong to a new
+ /// object.
+ ///
+ public void SetMaterial(MaterialContent material)
+ {
+ if (_currentMaterial == material)
+ return;
+
+ _currentMaterial = material;
+ _geometryDirty = true;
+ }
+
+ ///
+ /// Sets the opaque data for the next triangles.
+ ///
+ /// Opaque data dictionary for the next triangles.
+ ///
+ /// Sets the opaque data dictionary for the triangles being defined next. This dictionary
+ /// and the material, set with , define the
+ /// object containing the next triangles. When you set a new material or opaque data dictionary
+ /// the triangles you add afterwards will belong to a new object.
+ ///
+ public void SetOpaqueData(OpaqueDataDictionary opaqueData)
+ {
+ if (_currentOpaqueData == opaqueData)
+ return;
+
+ _currentOpaqueData = opaqueData;
+ _geometryDirty = true;
+ }
+
+ ///
+ /// Sets the specified vertex data with new data.
+ ///
+ /// Index of the vertex data channel being set. This should match the index returned by CreateVertexChannel.
+ /// New data values for the vertex data. The data type being set must match the data type for the vertex channel specified by vertexDataIndex.
+ public void SetVertexChannelData(int vertexDataIndex, object channelData)
+ {
+ if (_currentGeometryContent.Vertices.Channels[vertexDataIndex].ElementType != channelData.GetType())
+ throw new InvalidOperationException(string.Format("Channel {0} data has a different type from input. Expected: {1}. Actual: {2}",
+ vertexDataIndex, _currentGeometryContent.Vertices.Channels[vertexDataIndex].ElementType, channelData.GetType()));
+
+ _vertexChannelData[vertexDataIndex] = channelData;
+ }
+
+ ///
+ /// Initializes the creation of a mesh.
+ ///
+ /// Name of the mesh.
+ /// Object used when building the mesh.
+ public static MeshBuilder StartMesh(string name)
+ {
+ return new MeshBuilder(name);
+ }
+ }
+}
diff --git a/MonoGame.Framework.Content.Pipeline/Graphics/MeshHelper.cs b/MonoGame.Framework.Content.Pipeline/Graphics/MeshHelper.cs
index a3851a4fcc2..42a021f619b 100644
--- a/MonoGame.Framework.Content.Pipeline/Graphics/MeshHelper.cs
+++ b/MonoGame.Framework.Content.Pipeline/Graphics/MeshHelper.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Diagnostics;
+using System.Linq;
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
{
@@ -41,11 +43,12 @@ public static void CalculateNormals(MeshContent mesh, bool overwriteExistingNorm
///
public static void CalculateNormals(GeometryContent geom, bool overwriteExistingNormals)
{
+ VertexChannel channel;
// Look for an existing normals channel.
if (!geom.Vertices.Channels.Contains(VertexChannelNames.Normal()))
{
// We don't have existing normals, so add a new channel.
- geom.Vertices.Channels.Add(VertexChannelNames.Normal(), null);
+ channel = geom.Vertices.Channels.Add(VertexChannelNames.Normal(), null);
}
else
{
@@ -53,9 +56,10 @@ public static void CalculateNormals(GeometryContent geom, bool overwriteExisting
// normals then we're done here.
if (!overwriteExistingNormals)
return;
+
+ channel = geom.Vertices.Channels.Get(VertexChannelNames.Normal());
}
- var channel = geom.Vertices.Channels.Get(VertexChannelNames.Normal());
var positionIndices = geom.Vertices.PositionIndices;
Debug.Assert(positionIndices.Count == channel.Count, "The position and channel sizes were different!");
@@ -123,7 +127,7 @@ public static void CalculateNormals(GeometryContent geom, bool overwriteExisting
// Set the new normals on the vertex channel.
for (var i = 0; i < channel.Count; i++)
- channel[i] = normals[positionIndices[i]];
+ channel[i] = normals[geom.Indices[i]];
}
///
@@ -373,18 +377,122 @@ public static IList FlattenSkeleton(BoneContent skeleton)
return results;
}
+ ///
+ /// Merge any positions in the of the
+ /// specified mesh that are at a distance less than the specified tolerance
+ /// from each other.
+ ///
+ /// Mesh to be processed.
+ /// Tolerance value that determines how close
+ /// positions must be to each other to be merged.
+ ///
+ /// This method will also update the
+ /// in the of the specified mesh.
+ ///
public static void MergeDuplicatePositions(MeshContent mesh, float tolerance)
{
- throw new NotImplementedException();
+ if (mesh == null)
+ throw new ArgumentNullException("mesh");
+
+ // TODO Improve performance with spatial partitioning scheme
+ var indexLists = new List();
+ foreach (var geom in mesh.Geometry)
+ {
+ var list = new IndexUpdateList(geom.Vertices.PositionIndices);
+ indexLists.Add(list);
+ }
+
+ for (var i = mesh.Positions.Count - 1; i >= 1; i--)
+ {
+ var pi = mesh.Positions[i];
+ for (var j = i - 1; j >= 0; j--)
+ {
+ var pj = mesh.Positions[j];
+ if (Vector3.Distance(pi, pj) <= tolerance)
+ {
+ foreach (var list in indexLists)
+ list.Update(i, j);
+ mesh.Positions.RemoveAt(i);
+ }
+ }
+ }
}
+ ///
+ /// Merge vertices with the same and
+ /// data within the specified
+ /// .
+ ///
+ /// Geometry to be processed.
public static void MergeDuplicateVertices(GeometryContent geometry)
{
- throw new NotImplementedException();
+ if (geometry == null)
+ throw new ArgumentNullException("geometry");
+
+ var verts = geometry.Vertices;
+ var hashMap = new Dictionary>();
+
+ var indices = new IndexUpdateList(geometry.Indices);
+ var vIndex = 0;
+
+ for (var i = 0; i < geometry.Indices.Count; i++)
+ {
+ var iIndex = geometry.Indices[i];
+ var iData = new VertexData
+ {
+ Index = iIndex,
+ PositionIndex = verts.PositionIndices[vIndex],
+ ChannelData = new object[verts.Channels.Count]
+ };
+
+ for (var channel = 0; channel < verts.Channels.Count; channel++)
+ iData.ChannelData[channel] = verts.Channels[channel][vIndex];
+
+ var hash = iData.ComputeHash();
+
+ var merged = false;
+ List candidates;
+ if (hashMap.TryGetValue(hash, out candidates))
+ {
+ for (var candidateIndex = 0; candidateIndex < candidates.Count; candidateIndex++)
+ {
+ var c = candidates[candidateIndex];
+ if (!iData.ContentEquals(c))
+ continue;
+
+ // Match! Update the corresponding indices and remove the vertex
+ indices.Update(iIndex, c.Index);
+ verts.RemoveAt(vIndex);
+ merged = true;
+ }
+ if (!merged)
+ candidates.Add(iData);
+ }
+ else
+ {
+ // no vertices with the same hash yet, create a new list for the data
+ hashMap.Add(hash, new List { iData });
+ }
+
+ if (!merged)
+ vIndex++;
+ }
+
+ // update the indices because of the vertices we removed
+ indices.Pack();
}
+ ///
+ /// Merge vertices with the same and
+ /// data within the
+ /// of this mesh. If you want to merge positions too, call
+ /// on your mesh before this function.
+ ///
+ /// Mesh to be processed
public static void MergeDuplicateVertices(MeshContent mesh)
{
+ if (mesh == null)
+ throw new ArgumentNullException("mesh");
foreach (var geom in mesh.Geometry)
MergeDuplicateVertices(geom);
}
@@ -436,7 +544,7 @@ public static void TransformScene(NodeContent scene, Matrix transform)
if (transform == Matrix.Identity)
return;
- Matrix inverseTransform = Matrix.Invert(transform);
+ var inverseTransform = Matrix.Invert(transform);
var work = new Stack();
work.Push(scene);
@@ -483,5 +591,122 @@ internal static bool IsLeftHanded(ref Matrix xform)
float d = Vector3.Dot(xform.Right, Vector3.Cross(xform.Forward, xform.Up));
return d < 0.0f;
}
+
+ #region Private helpers
+
+ private static void UpdatePositionIndices(MeshContent mesh, int from, int to)
+ {
+ foreach (var geom in mesh.Geometry)
+ {
+ for (var i = 0; i < geom.Vertices.PositionIndices.Count; i++)
+ {
+ var index = geom.Vertices.PositionIndices[i];
+ if (index == from)
+ geom.Vertices.PositionIndices[i] = to;
+ }
+ }
+ }
+
+ private class VertexData
+ {
+ public int Index;
+ public int PositionIndex;
+ public object[] ChannelData;
+
+ // Compute a hash based on PositionIndex and ChannelData
+ public int ComputeHash()
+ {
+ var hash = PositionIndex;
+ foreach (var channel in ChannelData)
+ hash ^= channel.GetHashCode();
+
+ return hash;
+ }
+
+ // Check equality on PositionIndex and ChannelData
+ public bool ContentEquals(VertexData other)
+ {
+ if (PositionIndex != other.PositionIndex)
+ return false;
+
+ if (ChannelData.Length != other.ChannelData.Length)
+ return false;
+
+ for (var i = 0; i < ChannelData.Length; i++)
+ {
+ if (!Equals(ChannelData[i], other.ChannelData[i]))
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ // takes an IndexCollection and can efficiently update index values
+ private class IndexUpdateList
+ {
+ private readonly IList _collectionToUpdate;
+ private readonly Dictionary> _indexPositions;
+
+ // create the list, presort the values and compute the start positions of each value
+ public IndexUpdateList(IList collectionToUpdate)
+ {
+ _collectionToUpdate = collectionToUpdate;
+ _indexPositions = new Dictionary>();
+ Initialize();
+ }
+
+ private void Initialize()
+ {
+ for (var pos = 0; pos < _collectionToUpdate.Count; pos++)
+ {
+ var v = _collectionToUpdate[pos];
+ if (_indexPositions.ContainsKey(v))
+ _indexPositions[v].Add(pos);
+ else
+ _indexPositions.Add(v, new List {pos});
+ }
+ }
+
+ public void Update(int from, int to)
+ {
+ if (from == to || !_indexPositions.ContainsKey(from))
+ return;
+
+ foreach (var pos in _indexPositions[from])
+ _collectionToUpdate[pos] = to;
+
+ if (_indexPositions.ContainsKey(to))
+ _indexPositions[to].AddRange(_indexPositions[from]);
+ else
+ _indexPositions.Add(to, _indexPositions[from]);
+
+ _indexPositions.Remove(from);
+ }
+
+ // Pack all indices together starting from zero
+ // E.g. [5, 5, 3, 5, 21, 3] -> [1, 1, 0, 1, 2, 0]
+ // note that the order must be kept
+ public void Pack()
+ {
+ if (_collectionToUpdate.Count == 0)
+ return;
+
+ var sorted = new SortedSet(_collectionToUpdate);
+
+ var newIndex = 0;
+ foreach (var value in sorted)
+ {
+ foreach (var pos in _indexPositions[value])
+ _collectionToUpdate[pos] = newIndex;
+
+ newIndex++;
+ }
+
+ }
+
+ }
+
+ #endregion
}
}
diff --git a/MonoGame.Framework.Content.Pipeline/Graphics/NodeContent.cs b/MonoGame.Framework.Content.Pipeline/Graphics/NodeContent.cs
index 9efd1a4ddc1..92e16d63283 100644
--- a/MonoGame.Framework.Content.Pipeline/Graphics/NodeContent.cs
+++ b/MonoGame.Framework.Content.Pipeline/Graphics/NodeContent.cs
@@ -88,6 +88,7 @@ public NodeContent()
{
children = new NodeContentCollection(this);
animations = new AnimationContentDictionary();
+ Transform = Matrix.Identity;
}
}
}
diff --git a/MonoGame.Framework.Content.Pipeline/Graphics/TextureProfile.cs b/MonoGame.Framework.Content.Pipeline/Graphics/TextureProfile.cs
new file mode 100644
index 00000000000..1b67fb65f3c
--- /dev/null
+++ b/MonoGame.Framework.Content.Pipeline/Graphics/TextureProfile.cs
@@ -0,0 +1,99 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Xna.Framework.Content.Pipeline.Processors;
+
+namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
+{
+ public abstract class TextureProfile
+ {
+ private static readonly LoadedTypeCollection _profiles = new LoadedTypeCollection();
+
+ ///
+ /// Find the profile for this target platform.
+ ///
+ /// The platform target for textures.
+ ///
+ public static TextureProfile ForPlatform(TargetPlatform platform)
+ {
+ var profile = _profiles.FirstOrDefault(h => h.Supports(platform));
+ if (profile != null)
+ return profile;
+
+ throw new PipelineException("There is no supported texture profile for the '" + platform + "' platform!");
+ }
+
+ ///
+ /// Returns true if this profile supports texture processing for this platform.
+ ///
+ public abstract bool Supports(TargetPlatform platform);
+
+ ///
+ /// Determines if the texture format will require power-of-two dimensions and/or equal width and height.
+ ///
+ /// The processor context.
+ /// The desired texture format.
+ /// True if the texture format requires power-of-two dimensions.
+ /// True if the texture format requires equal width and height.
+ /// True if the texture format requires power-of-two dimensions.
+ public abstract void Requirements(ContentProcessorContext context, TextureProcessorOutputFormat format, out bool requiresPowerOfTwo, out bool requiresSquare);
+
+ ///
+ /// Performs conversion of the texture content to the correct format.
+ ///
+ /// The processor context.
+ /// The content to be compressed.
+ /// The user requested format for compression.
+ /// If mipmap generation is required.
+ /// If the texture has represents a sprite font, i.e. is greyscale and has sharp black/white contrast.
+ public void ConvertTexture(ContentProcessorContext context, TextureContent content, TextureProcessorOutputFormat format, bool generateMipmaps, bool isSpriteFont)
+ {
+ // We do nothing in this case.
+ if (format == TextureProcessorOutputFormat.NoChange)
+ return;
+
+ // If this is color just make sure the format is right and return it.
+ if (format == TextureProcessorOutputFormat.Color)
+ {
+ content.ConvertBitmapType(typeof(PixelBitmapContent));
+ if (generateMipmaps)
+ content.GenerateMipmaps(false);
+ return;
+ }
+
+ // Handle this common compression format.
+ if (format == TextureProcessorOutputFormat.Color16Bit)
+ {
+ GraphicsUtil.CompressColor16Bit(content, generateMipmaps);
+ return;
+ }
+
+ try
+ {
+ // All other formats require platform specific choices.
+ PlatformCompressTexture(context, content, format, generateMipmaps, isSpriteFont);
+ }
+ catch (EntryPointNotFoundException ex)
+ {
+ context.Logger.LogImportantMessage("Could not find the entry point to compress the texture. " + ex.ToString());
+ throw ex;
+ }
+ catch (DllNotFoundException ex)
+ {
+ context.Logger.LogImportantMessage("Could not compress texture. Required shared lib is missing. " + ex.ToString());
+ throw ex;
+ }
+ catch (Exception ex)
+ {
+ context.Logger.LogImportantMessage("Could not convert texture. " + ex.ToString());
+ throw ex;
+ }
+ }
+
+ protected abstract void PlatformCompressTexture(ContentProcessorContext context, TextureContent content, TextureProcessorOutputFormat format, bool generateMipmaps, bool isSpriteFont);
+ }
+}
diff --git a/MonoGame.Framework.Content.Pipeline/Graphics/VertexChannelCollection.cs b/MonoGame.Framework.Content.Pipeline/Graphics/VertexChannelCollection.cs
index 68f48c6db4d..4aaaba22bea 100644
--- a/MonoGame.Framework.Content.Pipeline/Graphics/VertexChannelCollection.cs
+++ b/MonoGame.Framework.Content.Pipeline/Graphics/VertexChannelCollection.cs
@@ -7,6 +7,7 @@
using System.Linq;
using System.Text;
using System.Collections;
+using System.Reflection;
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
{
@@ -51,14 +52,14 @@ public VertexChannel this[string name]
{
get
{
- int index = IndexOf(name);
+ var index = IndexOf(name);
if (index < 0)
throw new ArgumentException("name");
return channels[index];
}
set
{
- int index = IndexOf(name);
+ var index = IndexOf(name);
if (index < 0)
throw new ArgumentException("name");
channels[index] = value;
@@ -84,6 +85,7 @@ internal VertexChannelCollection(VertexContent vertexContent)
{
this.vertexContent = vertexContent;
channels = new List();
+ _insertOverload = GetType().GetMethods().First(m => m.Name == "Insert" && m.IsGenericMethodDefinition);
}
///
@@ -150,7 +152,7 @@ public VertexChannel ConvertChannelContent(int index)
throw new ArgumentOutOfRangeException("index");
// Get the channel at that index
- VertexChannel channel = this[index];
+ var channel = this[index];
// Remove it because we cannot add a new channel with the same name
RemoveAt(index);
VertexChannel result = null;
@@ -178,7 +180,7 @@ public VertexChannel ConvertChannelContent(int index)
/// New channel in the specified format.
public VertexChannel ConvertChannelContent(string name)
{
- int index = IndexOf(name);
+ var index = IndexOf(name);
if (index < 0)
throw new ArgumentException("name");
return ConvertChannelContent(index);
@@ -194,7 +196,7 @@ public VertexChannel Get(int index)
{
if (index < 0 || index >= channels.Count)
throw new ArgumentOutOfRangeException("index");
- VertexChannel channel = this[index];
+ var channel = this[index];
// Make sure the channel type is as expected
if (channel.ElementType != typeof(T))
throw new InvalidOperationException("Mismatched channel type");
@@ -209,7 +211,7 @@ public VertexChannel Get(int index)
/// The vertex channel.
public VertexChannel Get(string name)
{
- int index = IndexOf(name);
+ var index = IndexOf(name);
if (index < 0)
throw new ArgumentException("name");
return Get(index);
@@ -265,7 +267,7 @@ public VertexChannel Insert(int index, string name, IE
// Don't insert a channel with the same name
if (IndexOf(name) >= 0)
throw new ArgumentException("Vertex channel with name " + name + " already exists");
- VertexChannel channel = new VertexChannel(name);
+ var channel = new VertexChannel(name);
if (channelData != null)
{
// Insert the values from the enumerable into the channel
@@ -283,6 +285,9 @@ public VertexChannel Insert(int index, string name, IE
return channel;
}
+ // this reference the above Insert method and is initialized in the constructor
+ private readonly MethodInfo _insertOverload;
+
///
/// Inserts a new vertex channel at the specified position.
///
@@ -294,7 +299,7 @@ public VertexChannel Insert(int index, string name, IE
public VertexChannel Insert(int index, string name, Type elementType, IEnumerable channelData)
{
// Call the generic version of this method
- return (VertexChannel)GetType().GetMethod("Insert").MakeGenericMethod(elementType).Invoke(this, new object[] { index, name, channelData });
+ return (VertexChannel) _insertOverload.MakeGenericMethod(elementType).Invoke(this, new object[] { index, name, channelData });
}
///
@@ -304,7 +309,7 @@ public VertexChannel Insert(int index, string name, Type elementType, IEnumerabl
/// true if the channel was removed; false otherwise.
public bool Remove(string name)
{
- int index = IndexOf(name);
+ var index = IndexOf(name);
if (index >= 0)
{
channels.RemoveAt(index);
diff --git a/MonoGame.Framework.Content.Pipeline/Graphics/VertexChannelNames.cs b/MonoGame.Framework.Content.Pipeline/Graphics/VertexChannelNames.cs
index 79fe84fa75d..83f21a84048 100644
--- a/MonoGame.Framework.Content.Pipeline/Graphics/VertexChannelNames.cs
+++ b/MonoGame.Framework.Content.Pipeline/Graphics/VertexChannelNames.cs
@@ -76,8 +76,14 @@ public static int DecodeUsageIndex(string encodedName)
string baseName = DecodeBaseName(encodedName);
if (string.IsNullOrEmpty(baseName))
throw new InvalidOperationException("encodedName");
- // Subtract the base name from the string and convert the remainder to an integer
- return Int32.Parse(encodedName.Substring(baseName.Length), CultureInfo.InvariantCulture);
+
+ // Subtract the base name from the string and convert the remainder to an integer.
+ // TryParse solves the problem when name is just 'BlendIndicies' for example, in
+ // which case we default to index 0, assuming only 1 index.
+ int index = 0;
+ int.TryParse(encodedName.Substring(baseName.Length), NumberStyles.Integer, CultureInfo.InvariantCulture, out index);
+
+ return index;
}
///
diff --git a/MonoGame.Framework.Content.Pipeline/LoadedTypeCollection.cs b/MonoGame.Framework.Content.Pipeline/LoadedTypeCollection.cs
new file mode 100644
index 00000000000..c1ef063dcb0
--- /dev/null
+++ b/MonoGame.Framework.Content.Pipeline/LoadedTypeCollection.cs
@@ -0,0 +1,74 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+
+namespace Microsoft.Xna.Framework.Content.Pipeline
+{
+ ///
+ /// A helper for collecting instances of a particular type
+ /// by scanning the types in loaded assemblies.
+ ///
+ public class LoadedTypeCollection : IEnumerable
+ {
+ private static List _all;
+
+ public LoadedTypeCollection()
+ {
+ // Scan the already loaded assemblies.
+ if (_all == null)
+ {
+ var assemblies = AppDomain.CurrentDomain.GetAssemblies();
+ foreach (var ass in assemblies)
+ ScanAssembly(ass);
+ }
+
+ // Hook into assembly loading events to gather any new
+ // enumeration types that are found.
+ AppDomain.CurrentDomain.AssemblyLoad += (sender, args) => ScanAssembly(args.LoadedAssembly);
+ }
+
+ private static void ScanAssembly(Assembly ass)
+ {
+ // Initialize the list on first use.
+ if (_all == null)
+ _all = new List(24);
+
+ var thisAss = typeof(T).Assembly;
+
+ // If the assembly doesn't reference our assembly then it
+ // cannot contain this type... so skip scanning it.
+ var refAss = ass.GetReferencedAssemblies();
+ if (thisAss.FullName != ass.FullName && refAss.All(r => r.FullName != thisAss.FullName))
+ return;
+
+ var definedTypes = ass.DefinedTypes;
+
+ foreach (var type in definedTypes)
+ {
+ if (!type.IsSubclassOf(typeof(T)) || type.IsAbstract)
+ continue;
+
+ // Create an instance of the type and add it to our list.
+ var ttype = (T)Activator.CreateInstance(type);
+ _all.Add(ttype);
+ }
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return _all.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+}
diff --git a/MonoGame.Framework.Content.Pipeline/MonoGame.Content.Builder.targets b/MonoGame.Framework.Content.Pipeline/MonoGame.Content.Builder.targets
index 9252a6b770f..e342b783e8d 100644
--- a/MonoGame.Framework.Content.Pipeline/MonoGame.Content.Builder.targets
+++ b/MonoGame.Framework.Content.Pipeline/MonoGame.Content.Builder.targets
@@ -47,7 +47,7 @@
-
Resulting game asset.
public override AudioContent Import(string filename, ContentImporterContext context)
{
+ if (string.IsNullOrEmpty(filename))
+ throw new ArgumentNullException("filename");
+ if (context == null)
+ throw new ArgumentNullException("context");
+
+ if (!File.Exists(filename))
+ throw new FileNotFoundException(string.Format("Could not locate audio file {0}.", Path.GetFileName(filename)));
+
var content = new AudioContent(filename, AudioFileType.Mp3);
return content;
}
diff --git a/MonoGame.Framework.Content.Pipeline/OggImporter.cs b/MonoGame.Framework.Content.Pipeline/OggImporter.cs
index d48ab1973f6..506caad12e3 100644
--- a/MonoGame.Framework.Content.Pipeline/OggImporter.cs
+++ b/MonoGame.Framework.Content.Pipeline/OggImporter.cs
@@ -3,6 +3,7 @@
// file 'LICENSE.txt', which is part of this source code package.
using System;
+using System.IO;
using Microsoft.Xna.Framework.Content.Pipeline.Audio;
namespace Microsoft.Xna.Framework.Content.Pipeline
@@ -21,7 +22,15 @@ public class OggImporter : ContentImporter
/// Resulting game asset.
public override AudioContent Import(string filename, ContentImporterContext context)
{
- var content = new AudioContent(filename, AudioFileType.Wav);
+ if (string.IsNullOrEmpty(filename))
+ throw new ArgumentNullException("filename");
+ if (context == null)
+ throw new ArgumentNullException("context");
+
+ if (!File.Exists(filename))
+ throw new FileNotFoundException(string.Format("Could not locate audio file {0}.", Path.GetFileName(filename)));
+
+ var content = new AudioContent(filename, AudioFileType.Ogg);
return content;
}
}
diff --git a/MonoGame.Framework.Content.Pipeline/OpenAssetImporter.cs b/MonoGame.Framework.Content.Pipeline/OpenAssetImporter.cs
index 9ec47e1848d..6f51756a1f3 100644
--- a/MonoGame.Framework.Content.Pipeline/OpenAssetImporter.cs
+++ b/MonoGame.Framework.Content.Pipeline/OpenAssetImporter.cs
@@ -14,7 +14,45 @@
namespace Microsoft.Xna.Framework.Content.Pipeline
{
- [ContentImporter(".dae", ".fbx", ".x", DisplayName = "Open Asset Import Library - MonoGame", DefaultProcessor = "ModelProcessor")]
+ [ContentImporter(
+ ".dae", // Collada
+ ".gltf", "glb", // glTF
+ ".blend", // Blender 3D
+ ".3ds", // 3ds Max 3DS
+ ".ase", // 3ds Max ASE
+ ".obj", // Wavefront Object
+ ".ifc", // Industry Foundation Classes (IFC/Step)
+ ".xgl", ".zgl", // XGL
+ ".ply", // Stanford Polygon Library
+ ".dxf", // AutoCAD DXF
+ ".lwo", // LightWave
+ ".lws", // LightWave Scene
+ ".lxo", // Modo
+ ".stl", // Stereolithography
+ ".ac", // AC3D
+ ".ms3d", // Milkshape 3D
+ ".cob", ".scn", // TrueSpace
+ ".bvh", // Biovision BVH
+ ".csm", // CharacterStudio Motion
+ ".irrmesh", // Irrlicht Mesh
+ ".irr", // Irrlicht Scene
+ ".mdl", // Quake I, 3D GameStudio (3DGS)
+ ".md2", // Quake II
+ ".md3", // Quake III Mesh
+ ".pk3", // Quake III Map/BSP
+ ".mdc", // Return to Castle Wolfenstein
+ ".md5", // Doom 3
+ ".smd", ".vta", // Valve Model
+ ".ogex", // Open Game Engine Exchange
+ ".3d", // Unreal
+ ".b3d", // BlitzBasic 3D
+ ".q3d", ".q3s", // Quick3D
+ ".nff", // Neutral File Format, Sense8 WorldToolKit
+ ".off", // Object File Format
+ ".ter", // Terragen Terrain
+ ".hmp", // 3D GameStudio (3DGS) Terrain
+ ".ndo", // Izware Nendo
+ DisplayName = "Open Asset Import Library - MonoGame", DefaultProcessor = "ModelProcessor")]
public class OpenAssetImporter : ContentImporter
{
// Assimp has a few limitations (not all FBX files are supported):
@@ -442,6 +480,16 @@ private NodeContent ImportNodes(Node aiNode, Node aiParent, NodeContent parent)
// For the children, this is the new parent.
aiParent = aiNode;
parent = node;
+
+ if (_scene.HasAnimations)
+ {
+ foreach (var animation in _scene.Animations)
+ {
+ var animationContent = ImportAnimation(animation, node.Name);
+ if (animationContent.Channels.Count > 0)
+ node.Animations.Add(animationContent.Name, animationContent);
+ }
+ }
}
Debug.Assert(parent != null);
@@ -739,8 +787,9 @@ private NodeContent ImportBones(Node aiNode, Node aiParent, NodeContent parent)
/// Converts the specified animation to XNA.
///
/// The animation.
+ /// An optional filter.
/// The animation converted to XNA.
- private AnimationContent ImportAnimation(Animation aiAnimation)
+ private AnimationContent ImportAnimation(Animation aiAnimation, string nodeName = null)
{
var animation = new AnimationContent
{
@@ -754,8 +803,18 @@ private AnimationContent ImportAnimation(Animation aiAnimation)
// "nodeXyz_$AssimpFbx$_Rotation",
// "nodeXyz_$AssimpFbx$_Scaling"
// Group animation channels by name (strip the "_$AssimpFbx$" part).
- var channelGroups = aiAnimation.NodeAnimationChannels
+ IEnumerable < IGrouping < string,NodeAnimationChannel >> channelGroups;
+ if (nodeName != null)
+ {
+ channelGroups = aiAnimation.NodeAnimationChannels
+ .Where(channel => nodeName == GetNodeName(channel.NodeName))
.GroupBy(channel => GetNodeName(channel.NodeName));
+ }
+ else
+ {
+ channelGroups = aiAnimation.NodeAnimationChannels
+ .GroupBy(channel => GetNodeName(channel.NodeName));
+ }
foreach (var channelGroup in channelGroups)
{
diff --git a/MonoGame.Framework.Content.Pipeline/Processors/EffectProcessor.cs b/MonoGame.Framework.Content.Pipeline/Processors/EffectProcessor.cs
index 695c5594ead..f04d9a55732 100644
--- a/MonoGame.Framework.Content.Pipeline/Processors/EffectProcessor.cs
+++ b/MonoGame.Framework.Content.Pipeline/Processors/EffectProcessor.cs
@@ -54,23 +54,9 @@ public override CompiledEffectContent Process(EffectContent input, ContentProces
var options = new Options();
options.SourceFile = input.Identity.SourceFilename;
- switch (context.TargetPlatform)
- {
- case TargetPlatform.Windows:
- case TargetPlatform.WindowsPhone8:
- case TargetPlatform.WindowsStoreApp:
- options.Profile = ShaderProfile.DirectX_11;
- break;
- case TargetPlatform.iOS:
- case TargetPlatform.Android:
- case TargetPlatform.DesktopGL:
- case TargetPlatform.MacOSX:
- case TargetPlatform.RaspberryPi:
- options.Profile = ShaderProfile.OpenGL;
- break;
- default:
- throw new InvalidContentException(string.Format("{0} effects are not supported.", context.TargetPlatform), input.Identity);
- }
+ options.Profile = ShaderProfile.ForPlatform(context.TargetPlatform.ToString());
+ if (options.Profile == null)
+ throw new InvalidContentException(string.Format("{0} effects are not supported.", context.TargetPlatform), input.Identity);
options.Debug = DebugMode == EffectProcessorDebugMode.Debug;
options.Defines = Defines;
@@ -110,7 +96,7 @@ public override CompiledEffectContent Process(EffectContent input, ContentProces
foreach (var outfile in shaderInfo.AdditionalOutputFiles)
context.AddOutputFile(outfile);
}
- catch (ShaderCompilerException ex)
+ catch (ShaderCompilerException)
{
// This will log any warnings and errors and throw.
ProcessErrorsAndWarnings(true, shaderErrorsAndWarnings, input, context);
@@ -214,7 +200,7 @@ private static void ProcessErrorsAndWarnings(bool buildFailed, string shaderErro
if (identity == null)
{
identity = new ContentIdentity(fileName, input.Identity.SourceTool, lineAndColumn);
- allErrorsAndWarnings = message + Environment.NewLine;
+ allErrorsAndWarnings = errorsAndWarningArray[i] + Environment.NewLine;
}
else
allErrorsAndWarnings += errorsAndWarningArray[i] + Environment.NewLine;
diff --git a/MonoGame.Framework.Content.Pipeline/Processors/FontDescriptionProcessor.cs b/MonoGame.Framework.Content.Pipeline/Processors/FontDescriptionProcessor.cs
index 61612884c47..a3851589b8e 100644
--- a/MonoGame.Framework.Content.Pipeline/Processors/FontDescriptionProcessor.cs
+++ b/MonoGame.Framework.Content.Pipeline/Processors/FontDescriptionProcessor.cs
@@ -21,12 +21,16 @@ namespace Microsoft.Xna.Framework.Content.Pipeline.Processors
[ContentProcessor(DisplayName = "Sprite Font Description - MonoGame")]
public class FontDescriptionProcessor : ContentProcessor
{
+ [DefaultValue(true)]
+ public virtual bool PremultiplyAlpha { get; set; }
+
[DefaultValue(typeof(TextureProcessorOutputFormat), "Compressed")]
public virtual TextureProcessorOutputFormat TextureFormat { get; set; }
public FontDescriptionProcessor()
{
- this.TextureFormat = TextureProcessorOutputFormat.Compressed;
+ PremultiplyAlpha = true;
+ TextureFormat = TextureProcessorOutputFormat.Compressed;
}
public override SpriteFontContent Process(FontDescription input,
@@ -82,7 +86,11 @@ public override SpriteFontContent Process(FontDescription input,
#endif
context.Logger.LogMessage ("Building Font {0}", fontName);
- try {
+
+ // Get the platform specific texture profile.
+ var texProfile = TextureProfile.ForPlatform(context.TargetPlatform);
+
+ {
if (!File.Exists(fontName)) {
throw new Exception(string.Format("Could not load {0}", fontName));
}
@@ -96,11 +104,11 @@ public override SpriteFontContent Process(FontDescription input,
GlyphCropper.Crop(glyph);
}
- var format = GraphicsUtil.GetTextureFormatForPlatform(TextureFormat, context.TargetPlatform);
- var requiresPOT = GraphicsUtil.RequiresPowerOfTwo(format, context.TargetPlatform, context.TargetProfile);
- var requiresSquare = GraphicsUtil.RequiresSquare(format, context.TargetPlatform);
+ // We need to know how to pack the glyphs.
+ bool requiresPot, requiresSquare;
+ texProfile.Requirements(context, TextureFormat, out requiresPot, out requiresSquare);
- var face = GlyphPacker.ArrangeGlyphs(glyphs, requiresPOT, requiresSquare);
+ var face = GlyphPacker.ArrangeGlyphs(glyphs, requiresPot, requiresSquare);
// Adjust line and character spacing.
lineSpacing += input.Spacing;
@@ -123,16 +131,52 @@ public override SpriteFontContent Process(FontDescription input,
output.Kerning.Add(new Vector3(0, texRect.Width, 0));
}
- output.Texture.Faces[0].Add(face);
+ output.Texture.Faces[0].Add(face);
+ }
- if (GraphicsUtil.IsCompressedTextureFormat(format))
+ if (PremultiplyAlpha)
+ {
+ var bmp = output.Texture.Faces[0][0];
+ var data = bmp.GetPixelData();
+ var idx = 0;
+ for (; idx < data.Length; )
{
- GraphicsUtil.CompressTexture(context.TargetProfile, output.Texture, format, context, false, true);
+ var r = data[idx];
+
+ // Special case of simply copying the R component into the A, since R is the value of white alpha we want
+ data[idx + 0] = r;
+ data[idx + 1] = r;
+ data[idx + 2] = r;
+ data[idx + 3] = r;
+
+ idx += 4;
}
- }
- catch(Exception ex) {
- context.Logger.LogImportantMessage("{0}", ex.ToString());
- }
+
+ bmp.SetPixelData(data);
+ }
+ else
+ {
+ var bmp = output.Texture.Faces[0][0];
+ var data = bmp.GetPixelData();
+ var idx = 0;
+ for (; idx < data.Length; )
+ {
+ var r = data[idx];
+
+ // Special case of simply moving the R component into the A and setting RGB to solid white, since R is the value of white alpha we want
+ data[idx + 0] = 255;
+ data[idx + 1] = 255;
+ data[idx + 2] = 255;
+ data[idx + 3] = r;
+
+ idx += 4;
+ }
+
+ bmp.SetPixelData(data);
+ }
+
+ // Perform the final texture conversion.
+ texProfile.ConvertTexture(context, output.Texture, TextureFormat, false, true);
return output;
}
@@ -220,6 +264,9 @@ string FindFontFileFromFontName(string fontname, string style)
s = s.Trim();
var split = s.Split (':');
+ if (split.Length < 2)
+ return String.Empty;
+
//check font family, fontconfig might return a fallback
if (split [1].Contains (",")) { //this file defines multiple family names
var families = split [1].Split (',');
diff --git a/MonoGame.Framework.Content.Pipeline/Processors/FontTextureProcessor.cs b/MonoGame.Framework.Content.Pipeline/Processors/FontTextureProcessor.cs
index 2619ed8a187..a2776c9f136 100644
--- a/MonoGame.Framework.Content.Pipeline/Processors/FontTextureProcessor.cs
+++ b/MonoGame.Framework.Content.Pipeline/Processors/FontTextureProcessor.cs
@@ -111,10 +111,14 @@ public override SpriteFontContent Process(Texture2DContent input, ContentProcess
output.VerticalLineSpacing = Math.Max(output.VerticalLineSpacing, glyph.Subrect.Height);
}
- var format = GraphicsUtil.GetTextureFormatForPlatform(TextureFormat, context.TargetPlatform);
- var requiresPOT = GraphicsUtil.RequiresPowerOfTwo(format, context.TargetPlatform, context.TargetProfile);
- var requiresSquare = GraphicsUtil.RequiresSquare(format, context.TargetPlatform);
- face = GlyphPacker.ArrangeGlyphs(glyphs.ToArray(), requiresPOT, requiresSquare);
+ // Get the platform specific texture profile.
+ var texProfile = TextureProfile.ForPlatform(context.TargetPlatform);
+
+ // We need to know how to pack the glyphs.
+ bool requiresPot, requiresSquare;
+ texProfile.Requirements(context, TextureFormat, out requiresPot, out requiresSquare);
+
+ face = GlyphPacker.ArrangeGlyphs(glyphs.ToArray(), requiresPot, requiresSquare);
foreach (var glyph in glyphs)
{
@@ -151,17 +155,8 @@ public override SpriteFontContent Process(Texture2DContent input, ContentProcess
bmp.SetPixelData(data);
}
- if (GraphicsUtil.IsCompressedTextureFormat(format))
- {
- try
- {
- GraphicsUtil.CompressTexture(context.TargetProfile, output.Texture, format, context, false, true);
- }
- catch(Exception ex)
- {
- context.Logger.LogImportantMessage("{0}", ex.ToString());
- }
- }
+ // Perform the final texture conversion.
+ texProfile.ConvertTexture(context, output.Texture, TextureFormat, false, true);
return output;
}
diff --git a/MonoGame.Framework.Content.Pipeline/Processors/MaterialProcessor.cs b/MonoGame.Framework.Content.Pipeline/Processors/MaterialProcessor.cs
index 1507a067c09..a5a6349d887 100644
--- a/MonoGame.Framework.Content.Pipeline/Processors/MaterialProcessor.cs
+++ b/MonoGame.Framework.Content.Pipeline/Processors/MaterialProcessor.cs
@@ -77,7 +77,7 @@ public class MaterialProcessor : ContentProcessor
/// Specifies the texture format of output materials. Materials can either be left unchanged from the source asset, converted to a corresponding Color, or compressed using the appropriate DxtCompressed format.
@@ -86,7 +86,7 @@ public class MaterialProcessor : ContentProcessor
/// Initializes a new instance of the MaterialProcessor class.
@@ -173,8 +173,10 @@ public override MaterialContent Process(MaterialContent input, ContentProcessorC
// Build custom effects
var effectMaterial = input as EffectMaterialContent;
- if (effectMaterial != null)
+ if (effectMaterial != null && effectMaterial.CompiledEffect == null)
{
+ if (effectMaterial.Effect == null)
+ throw new PipelineException("EffectMaterialContent.Effect or EffectMaterialContent.CompiledEffect should be set for materials with a custom effect.");
effectMaterial.CompiledEffect = BuildEffect(effectMaterial.Effect, context);
// TODO: Docs say to validate OpaqueData for SetValue/SetValueTranspose
// Does that mean to match up with effect param names??
diff --git a/MonoGame.Framework.Content.Pipeline/Processors/ModelProcessor.cs b/MonoGame.Framework.Content.Pipeline/Processors/ModelProcessor.cs
index 883ae0416e1..2ed7cf71c05 100644
--- a/MonoGame.Framework.Content.Pipeline/Processors/ModelProcessor.cs
+++ b/MonoGame.Framework.Content.Pipeline/Processors/ModelProcessor.cs
@@ -24,7 +24,7 @@ public class ModelProcessor : ContentProcessor
private bool _premultiplyTextureAlpha = true;
private bool _premultiplyVertexColors = true;
private float _scale = 1.0f;
- private TextureProcessorOutputFormat _textureFormat = TextureProcessorOutputFormat.DxtCompressed;
+ private TextureProcessorOutputFormat _textureFormat = TextureProcessorOutputFormat.Compressed;
#endregion
@@ -83,7 +83,7 @@ public virtual float Scale
public virtual bool SwapWindingOrder { get; set; }
- [DefaultValue(typeof(TextureProcessorOutputFormat), "DxtCompressed")]
+ [DefaultValue(typeof(TextureProcessorOutputFormat), "Compressed")]
public virtual TextureProcessorOutputFormat TextureFormat
{
get { return _textureFormat; }
diff --git a/MonoGame.Framework.Content.Pipeline/Processors/SongProcessor.cs b/MonoGame.Framework.Content.Pipeline/Processors/SongProcessor.cs
index babbe5e9b8e..0e1ce4715eb 100644
--- a/MonoGame.Framework.Content.Pipeline/Processors/SongProcessor.cs
+++ b/MonoGame.Framework.Content.Pipeline/Processors/SongProcessor.cs
@@ -15,13 +15,17 @@ namespace Microsoft.Xna.Framework.Content.Pipeline.Processors
[ContentProcessor(DisplayName = "Song - MonoGame")]
public class SongProcessor : ContentProcessor
{
- ConversionQuality quality = ConversionQuality.Best;
+ ConversionQuality _quality = ConversionQuality.Best;
///
/// Gets or sets the target format quality of the audio content.
///
/// The ConversionQuality of this audio data.
- public ConversionQuality Quality { get { return quality; } set { quality = value; } }
+ public ConversionQuality Quality
+ {
+ get { return _quality; }
+ set { _quality = value; }
+ }
///
/// Initializes a new instance of SongProcessor.
@@ -38,33 +42,17 @@ public SongProcessor()
/// The built audio.
public override SongContent Process(AudioContent input, ContentProcessorContext context)
{
- // Most platforms will use AAC ("mp4") by default
- var targetFormat = ConversionFormat.Aac;
-
- switch (context.TargetPlatform)
- {
- case TargetPlatform.Windows:
- case TargetPlatform.WindowsPhone8:
- case TargetPlatform.WindowsStoreApp:
- targetFormat = ConversionFormat.WindowsMedia;
- break;
-
- case TargetPlatform.DesktopGL:
- targetFormat = ConversionFormat.Vorbis;
- break;
- }
-
- // Get the song output path with the target format extension.
- var songFileName = Path.ChangeExtension(context.OutputFilename, AudioHelper.GetExtension(targetFormat));
-
- // Make sure the output folder for the song exists.
- Directory.CreateDirectory(Path.GetDirectoryName(songFileName));
+ // The xnb name is the basis for the final song filename.
+ var songFileName = context.OutputFilename;
// Convert and write out the song media file.
- input.ConvertFormat(targetFormat, quality, songFileName);
+ var profile = AudioProfile.ForPlatform(context.TargetPlatform);
+ var finalQuality = profile.ConvertStreamingAudio(context.TargetPlatform, _quality, input, ref songFileName);
// Let the pipeline know about the song file so it can clean things up.
context.AddOutputFile(songFileName);
+ if (_quality != finalQuality)
+ context.Logger.LogMessage("Failed to convert using \"{0}\" quality, used \"{1}\" quality", _quality, finalQuality);
// Return the XNB song content.
return new SongContent(PathHelper.GetRelativePath(Path.GetDirectoryName(context.OutputFilename) + Path.DirectorySeparatorChar, songFileName), input.Duration);
diff --git a/MonoGame.Framework.Content.Pipeline/Processors/SoundEffectContent.cs b/MonoGame.Framework.Content.Pipeline/Processors/SoundEffectContent.cs
index 254cb0144e3..bd52d890b89 100644
--- a/MonoGame.Framework.Content.Pipeline/Processors/SoundEffectContent.cs
+++ b/MonoGame.Framework.Content.Pipeline/Processors/SoundEffectContent.cs
@@ -5,6 +5,7 @@
using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;
+using System.Linq;
namespace Microsoft.Xna.Framework.Content.Pipeline.Processors
{
@@ -13,8 +14,8 @@ namespace Microsoft.Xna.Framework.Content.Pipeline.Processors
///
public sealed class SoundEffectContent
{
- internal List format;
- internal List data;
+ internal byte[] format;
+ internal byte[] data;
internal int loopStart;
internal int loopLength;
internal int duration;
@@ -27,10 +28,10 @@ public sealed class SoundEffectContent
/// The start of the loop segment (must be block aligned).
/// The length of the loop segment (must be block aligned).
/// The duration of the wave file in milliseconds.
- internal SoundEffectContent(ReadOnlyCollection format, ReadOnlyCollection data, int loopStart, int loopLength, int duration)
+ internal SoundEffectContent(IEnumerable format, IEnumerable data, int loopStart, int loopLength, int duration)
{
- this.format = new List(format);
- this.data = new List(data);
+ this.format = format.ToArray();
+ this.data = data.ToArray();
this.loopStart = loopStart;
this.loopLength = loopLength;
this.duration = duration;
diff --git a/MonoGame.Framework.Content.Pipeline/Processors/SoundEffectProcessor.cs b/MonoGame.Framework.Content.Pipeline/Processors/SoundEffectProcessor.cs
index 335410b9d1a..a2180da6829 100644
--- a/MonoGame.Framework.Content.Pipeline/Processors/SoundEffectProcessor.cs
+++ b/MonoGame.Framework.Content.Pipeline/Processors/SoundEffectProcessor.cs
@@ -38,25 +38,15 @@ public SoundEffectProcessor()
/// The built audio.
public override SoundEffectContent Process(AudioContent input, ContentProcessorContext context)
{
- var targetFormat = ConversionFormat.Pcm;
-
- switch (quality)
- {
- case ConversionQuality.Medium:
- case ConversionQuality.Low:
- if ((context.TargetPlatform == TargetPlatform.iOS) || (context.TargetPlatform == TargetPlatform.MacOSX))
- targetFormat = ConversionFormat.ImaAdpcm;
- else
- {
- // TODO: For some reason this doesn't work on Windows
- // so we fallback to plain PCM and depend on the
- // bitrate reduction only.
- //targetFormat = ConversionFormat.Adpcm;
- }
- break;
- }
-
- input.ConvertFormat(targetFormat, quality, null);
+ if (input == null)
+ throw new ArgumentNullException("input");
+ if (context == null)
+ throw new ArgumentNullException("context");
+
+ var profile = AudioProfile.ForPlatform(context.TargetPlatform);
+ var finalQuality = profile.ConvertAudio(context.TargetPlatform, quality, input);
+ if (quality != finalQuality)
+ context.Logger.LogMessage("Failed to convert using \"{0}\" quality, used \"{1}\" quality", quality, finalQuality);
return new SoundEffectContent(input.Format.NativeWaveFormat, input.Data, input.LoopStart, input.LoopLength, (int)input.Duration.TotalMilliseconds);
}
diff --git a/MonoGame.Framework.Content.Pipeline/Processors/TextureProcessor.cs b/MonoGame.Framework.Content.Pipeline/Processors/TextureProcessor.cs
index 65a3775a1a6..f172978ba48 100644
--- a/MonoGame.Framework.Content.Pipeline/Processors/TextureProcessor.cs
+++ b/MonoGame.Framework.Content.Pipeline/Processors/TextureProcessor.cs
@@ -111,38 +111,9 @@ public override TextureContent Process(TextureContent input, ContentProcessorCon
input.ConvertBitmapType(originalType);
}
- if (TextureFormat == TextureProcessorOutputFormat.NoChange)
- return input;
-
- try
- {
- if (TextureFormat != TextureProcessorOutputFormat.Color)
- {
- input.ConvertBitmapType(typeof(PixelBitmapContent));
- GraphicsUtil.CompressTexture(context.TargetProfile, input, TextureFormat, context, GenerateMipmaps, false);
- }
- else
- {
- input.ConvertBitmapType(typeof(PixelBitmapContent));
- if (GenerateMipmaps)
- input.GenerateMipmaps(false);
- }
- }
- catch (EntryPointNotFoundException ex)
- {
- context.Logger.LogImportantMessage ("Could not find the entry point to compress the texture. " + ex.ToString());
- throw ex;
- }
- catch (DllNotFoundException ex)
- {
- context.Logger.LogImportantMessage ("Could not compress texture. Required shared lib is missing. " + ex.ToString());
- throw ex;
- }
- catch (Exception ex)
- {
- context.Logger.LogImportantMessage ("Could not convert texture. " + ex.ToString());
- throw ex;
- }
+ // Get the texture profile for the platform and let it convert the texture.
+ var texProfile = TextureProfile.ForPlatform(context.TargetPlatform);
+ texProfile.ConvertTexture(context, input, TextureFormat, GenerateMipmaps, false);
return input;
}
diff --git a/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/ContentWriter.cs b/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/ContentWriter.cs
index 678e12518a8..8d431fba41e 100644
--- a/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/ContentWriter.cs
+++ b/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/ContentWriter.cs
@@ -43,7 +43,6 @@ public sealed class ContentWriter : BinaryWriter
{
'w', // Windows (DirectX)
'x', // Xbox360
- 'm', // WindowsPhone
'i', // iOS
'a', // Android
'd', // DesktopGL
@@ -54,6 +53,8 @@ public sealed class ContentWriter : BinaryWriter
'M', // WindowsPhone8
'r', // RaspberryPi
'P', // PlayStation4
+ 'v', // PSVita
+ 'O', // XboxOne
};
///
diff --git a/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/EffectMaterialWriter.cs b/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/EffectMaterialWriter.cs
new file mode 100644
index 00000000000..79da149ef6c
--- /dev/null
+++ b/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/EffectMaterialWriter.cs
@@ -0,0 +1,29 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System.Collections.Generic;
+using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
+
+namespace Microsoft.Xna.Framework.Content.Pipeline.Serialization.Compiler
+{
+ [ContentTypeWriter]
+ class EffectMaterialWriter : BuiltInContentWriter
+ {
+ protected internal override void Write(ContentWriter output, EffectMaterialContent value)
+ {
+ output.WriteExternalReference(value.CompiledEffect);
+ var dict = new Dictionary();
+ foreach (var item in value.Textures)
+ {
+ dict.Add(item.Key, item.Value);
+ }
+ foreach (var item in value.OpaqueData)
+ {
+ if (item.Key != EffectMaterialContent.EffectKey && item.Key != EffectMaterialContent.CompiledEffectKey)
+ dict.Add(item.Key, item.Value);
+ }
+ output.WriteObject(dict);
+ }
+ }
+}
\ No newline at end of file
diff --git a/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/IndexBufferWriter.cs b/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/IndexBufferWriter.cs
index 47ff00d499b..cf09f372060 100644
--- a/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/IndexBufferWriter.cs
+++ b/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/IndexBufferWriter.cs
@@ -11,7 +11,17 @@ class IndexBufferWriter : BuiltInContentWriter
{
protected internal override void Write(ContentWriter output, IndexCollection value)
{
- var shortIndices = value.Count < ushort.MaxValue;
+ // Check if the buffer and can be saved as Int16.
+ var shortIndices = true;
+ foreach(var index in value)
+ {
+ if(index > ushort.MaxValue)
+ {
+ shortIndices = false;
+ break;
+ }
+ }
+
output.Write(shortIndices);
var byteCount = shortIndices
diff --git a/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/SoundEffectContentWriter.cs b/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/SoundEffectContentWriter.cs
index 8e29031d338..9348df3aae7 100644
--- a/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/SoundEffectContentWriter.cs
+++ b/MonoGame.Framework.Content.Pipeline/Serialization/Compiler/SoundEffectContentWriter.cs
@@ -17,11 +17,11 @@ class SoundEffectWriter : BuiltInContentWriter
/// The value to write to the output.
protected internal override void Write(ContentWriter output, SoundEffectContent value)
{
- // The WaveFormat provided by NAudio already contains the size
- output.Write(value.format.ToArray());
+ output.Write(value.format.Length);
+ output.Write(value.format);
- output.Write(value.data.Count);
- output.Write(value.data.ToArray());
+ output.Write(value.data.Length);
+ output.Write(value.data);
output.Write(value.loopStart);
output.Write(value.loopLength);
diff --git a/MonoGame.Framework.Content.Pipeline/Serialization/Intermediate/CurveKeyCollectionSerializer.cs b/MonoGame.Framework.Content.Pipeline/Serialization/Intermediate/CurveKeyCollectionSerializer.cs
new file mode 100644
index 00000000000..38fcd0f4407
--- /dev/null
+++ b/MonoGame.Framework.Content.Pipeline/Serialization/Intermediate/CurveKeyCollectionSerializer.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Collections.Generic;
+using System.Xml;
+
+namespace Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate
+{
+ [ContentTypeSerializer]
+ class CurveKeyCollectionSerializer : ContentTypeSerializer
+ {
+ public CurveKeyCollectionSerializer() :
+ base("Keys")
+ { }
+
+ public override bool CanDeserializeIntoExistingObject
+ { get { return true; } }
+
+ protected internal override CurveKeyCollection Deserialize(
+ IntermediateReader input,
+ ContentSerializerAttribute format,
+ CurveKeyCollection existingInstance)
+ {
+ var result = existingInstance ?? new CurveKeyCollection();
+
+ if (input.Xml.HasValue)
+ {
+ var elements = PackedElementsHelper.ReadElements(input);
+ if (elements.Length > 0)
+ {
+ // Each CurveKey consists of 5 elements
+ if (elements.Length % 5 != 0)
+ throw new InvalidContentException(
+ "Elements count in CurveKeyCollection is inncorect!");
+ try
+ {
+ // Parse all CurveKeys
+ for (int i = 0; i < elements.Length; i += 5)
+ {
+ // Order: Position, Value, TangentIn, TangentOut and Continuity
+ var curveKey = new CurveKey
+ (XmlConvert.ToSingle(elements[i]),
+ XmlConvert.ToSingle(elements[i + 1]),
+ XmlConvert.ToSingle(elements[i + 2]),
+ XmlConvert.ToSingle(elements[i + 3]),
+ (CurveContinuity)Enum.Parse(
+ typeof(CurveContinuity),
+ elements[i + 4],
+ true));
+ result.Add(curveKey);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new InvalidContentException
+ ("Error parsing CurveKey", e);
+ }
+ }
+ }
+ return result;
+ }
+
+
+ protected internal override void Serialize(
+ IntermediateWriter output,
+ CurveKeyCollection value,
+ ContentSerializerAttribute format)
+ {
+ var elements = new List();
+ foreach (var curveKey in value)
+ {
+ // Order: Position, Value, TangentIn, TangentOut and Continuity
+ elements.Add(XmlConvert.ToString(curveKey.Position));
+ elements.Add(XmlConvert.ToString(curveKey.Value));
+ elements.Add(XmlConvert.ToString(curveKey.TangentIn));
+ elements.Add(XmlConvert.ToString(curveKey.TangentOut));
+ elements.Add(curveKey.Continuity.ToString());
+ }
+ var str = PackedElementsHelper.JoinElements(elements);
+ output.Xml.WriteString(str);
+ }
+ }
+}
\ No newline at end of file
diff --git a/MonoGame.Framework.Content.Pipeline/Serialization/Intermediate/ElementSerializerT.cs b/MonoGame.Framework.Content.Pipeline/Serialization/Intermediate/ElementSerializerT.cs
index bd994f49d24..4b9a81a7be8 100644
--- a/MonoGame.Framework.Content.Pipeline/Serialization/Intermediate/ElementSerializerT.cs
+++ b/MonoGame.Framework.Content.Pipeline/Serialization/Intermediate/ElementSerializerT.cs
@@ -11,10 +11,6 @@ namespace Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate
{
abstract class ElementSerializer : ContentTypeSerializer
{
- private static readonly char [] _seperators = { ' ', '\t', '\n' };
-
- private const string _writeSeperator = " ";
-
private readonly int _elementCount;
protected ElementSerializer(string xmlTypeName, int elementCount) :
@@ -32,34 +28,11 @@ protected void ThrowElementCountException()
protected internal abstract void Serialize(T value, List results);
- private static string[] ReadElements(IntermediateReader input)
- {
- if (input.Xml.IsEmptyElement)
- return new string[0];
-
- string str = string.Empty;
- while (input.Xml.NodeType != XmlNodeType.EndElement)
- {
- if (input.Xml.NodeType == XmlNodeType.Comment)
- input.Xml.Read();
- else
- str += input.Xml.ReadString();
- }
-
- // Special case for char ' '
- if (str.Length > 0 && str.Trim() == string.Empty)
- return new string[] { str };
-
- var elements = str.Split(_seperators, StringSplitOptions.RemoveEmptyEntries);
- if (elements.Length == 1 && string.IsNullOrEmpty(elements[0]))
- return new string[0];
-
- return elements;
- }
+
protected internal void Deserialize(IntermediateReader input, List results)
{
- var elements = ReadElements(input);
+ var elements = PackedElementsHelper.ReadElements(input);
for (var index = 0; index < elements.Length;)
{
@@ -73,7 +46,7 @@ protected internal void Deserialize(IntermediateReader input, List results)
protected internal override T Deserialize(IntermediateReader input, ContentSerializerAttribute format, T existingInstance)
{
- var elements = ReadElements(input);
+ var elements = PackedElementsHelper.ReadElements(input);
if (elements.Length < _elementCount)
ThrowElementCountException();
@@ -87,7 +60,7 @@ protected internal void Serialize(IntermediateWriter output, List values)
var elements = new List();
for (var i = 0; i < values.Count; i++)
Serialize(values[i], elements);
- var str = string.Join(_writeSeperator, elements);
+ var str = PackedElementsHelper.JoinElements(elements);
output.Xml.WriteString(str);
}
@@ -95,7 +68,7 @@ protected internal override void Serialize(IntermediateWriter output, T value, C
{
var elements = new List();
Serialize(value, elements);
- var str = string.Join(_writeSeperator, elements);
+ var str = PackedElementsHelper.JoinElements(elements);
output.Xml.WriteString(str);
}
}
diff --git a/MonoGame.Framework.Content.Pipeline/Serialization/Intermediate/NamespaceAliasHelper.cs b/MonoGame.Framework.Content.Pipeline/Serialization/Intermediate/NamespaceAliasHelper.cs
index fc7dae29ce6..2d7b3e65c30 100644
--- a/MonoGame.Framework.Content.Pipeline/Serialization/Intermediate/NamespaceAliasHelper.cs
+++ b/MonoGame.Framework.Content.Pipeline/Serialization/Intermediate/NamespaceAliasHelper.cs
@@ -136,7 +136,7 @@ private static AliasedNamespace FindAlias(Dictionary a
}
///
- /// Returns just the portion relative to .
+ /// Returns just the portion relative to .
/// For example, given namespaceParent=Foo.Bar and @namespace=Foo.Bar.Baz, will return Baz.
///
private static string GetRelativeNamespace(string namespaceParent, string @namespace)
diff --git a/MonoGame.Framework.Content.Pipeline/Serialization/Intermediate/PackedElementsHelper.cs b/MonoGame.Framework.Content.Pipeline/Serialization/Intermediate/PackedElementsHelper.cs
new file mode 100644
index 00000000000..9e3117543d6
--- /dev/null
+++ b/MonoGame.Framework.Content.Pipeline/Serialization/Intermediate/PackedElementsHelper.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Xml;
+
+namespace Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate
+{
+ internal static class PackedElementsHelper
+ {
+ private static readonly char[] _seperators = { ' ', '\t', '\n' };
+
+ private const string _writeSeperator = " ";
+
+ internal static string[] ReadElements(IntermediateReader input)
+ {
+ if (input.Xml.IsEmptyElement)
+ return new string[0];
+
+ string str = string.Empty;
+ while (input.Xml.NodeType != XmlNodeType.EndElement)
+ {
+ if (input.Xml.NodeType == XmlNodeType.Comment)
+ input.Xml.Read();
+ else
+ str += input.Xml.ReadString();
+ }
+
+ // Special case for char ' '
+ if (str.Length > 0 && str.Trim() == string.Empty)
+ return new string[] { str };
+
+ var elements = str.Split(_seperators, StringSplitOptions.RemoveEmptyEntries);
+ if (elements.Length == 1 && string.IsNullOrEmpty(elements[0]))
+ return new string[0];
+
+ return elements;
+ }
+
+ public static string JoinElements(IEnumerable elements)
+ {
+ return string.Join(_writeSeperator, elements);
+ }
+ }
+}
\ No newline at end of file
diff --git a/MonoGame.Framework.Content.Pipeline/TargetPlatform.cs b/MonoGame.Framework.Content.Pipeline/TargetPlatform.cs
index 60bfd09df4b..df5f061c7e6 100644
--- a/MonoGame.Framework.Content.Pipeline/TargetPlatform.cs
+++ b/MonoGame.Framework.Content.Pipeline/TargetPlatform.cs
@@ -24,11 +24,6 @@ public enum TargetPlatform
///
Xbox360,
- ///
- /// Windows Phone
- ///
- WindowsPhone,
-
// MonoGame-specific platforms listed below
///
@@ -90,6 +85,16 @@ public enum TargetPlatform
/// Sony PlayStation4
///
PlayStation4,
+
+ ///
+ /// PlayStation Vita
+ ///
+ PSVita,
+
+ ///
+ /// Xbox One
+ ///
+ XboxOne,
}
diff --git a/MonoGame.Framework.Content.Pipeline/Utilities/Vector4Converter.cs b/MonoGame.Framework.Content.Pipeline/Utilities/Vector4Converter.cs
index a87c541c56f..40651188115 100644
--- a/MonoGame.Framework.Content.Pipeline/Utilities/Vector4Converter.cs
+++ b/MonoGame.Framework.Content.Pipeline/Utilities/Vector4Converter.cs
@@ -18,29 +18,30 @@ class Vector4Converter :
IVector4Converter,
IVector4Converter,
IVector4Converter,
- IVector4Converter
+ IVector4Converter,
+ IVector4Converter
{
Vector4 IVector4Converter.ToVector4(byte value)
{
var f = (float)value / (float)byte.MaxValue;
- return new Vector4(f, f, f, f);
+ return new Vector4(f, 0f, 0f, 1f);
}
Vector4 IVector4Converter.ToVector4(short value)
{
var f = (float)value / (float)short.MaxValue;
- return new Vector4(f, f, f, f);
+ return new Vector4(f, 0f, 0f, 1f);
}
Vector4 IVector4Converter.ToVector4(int value)
{
var f = (float)value / (float)int.MaxValue;
- return new Vector4(f, f, f, f);
+ return new Vector4(f, 0f, 0f, 1f);
}
Vector4 IVector4Converter.ToVector4(float value)
{
- return new Vector4(value, value, value, value);
+ return new Vector4(value, 0f, 0f, 1f);
}
Vector4 IVector4Converter.ToVector4(Color value)
@@ -48,6 +49,11 @@ Vector4 IVector4Converter.ToVector4(Color value)
return value.ToVector4();
}
+ Vector4 IVector4Converter.ToVector4(Vector4 value)
+ {
+ return value;
+ }
+
byte IVector4Converter.FromVector4(Vector4 value)
{
return (byte)(value.X * (float)byte.MaxValue);
@@ -72,5 +78,10 @@ Color IVector4Converter.FromVector4(Vector4 value)
{
return new Color(value);
}
+
+ Vector4 IVector4Converter.FromVector4(Vector4 value)
+ {
+ return value;
+ }
}
}
diff --git a/MonoGame.Framework.Content.Pipeline/WavImporter.cs b/MonoGame.Framework.Content.Pipeline/WavImporter.cs
index c369db20709..d6e730ac99a 100644
--- a/MonoGame.Framework.Content.Pipeline/WavImporter.cs
+++ b/MonoGame.Framework.Content.Pipeline/WavImporter.cs
@@ -3,6 +3,7 @@
// file 'LICENSE.txt', which is part of this source code package.
using System;
+using System.IO;
using Microsoft.Xna.Framework.Content.Pipeline.Audio;
namespace Microsoft.Xna.Framework.Content.Pipeline
@@ -19,9 +20,25 @@ public class WavImporter : ContentImporter
/// Name of a game asset file.
/// Contains information for importing a game asset, such as a logger interface.
/// Resulting game asset.
+ /// This importer only supports 8bit and 16bit depths with sample rates from 8KHz up to 48KHz.
public override AudioContent Import(string filename, ContentImporterContext context)
{
+ if (string.IsNullOrEmpty(filename))
+ throw new ArgumentNullException("filename");
+ if (context == null)
+ throw new ArgumentNullException("context");
+
+ if (!File.Exists(filename))
+ throw new FileNotFoundException(string.Format("Could not locate audio file {0}.", Path.GetFileName(filename)));
+
var content = new AudioContent(filename, AudioFileType.Wav);
+
+ // Validate the format of the input.
+ if (content.Format.SampleRate < 8000 || content.Format.SampleRate > 48000)
+ throw new InvalidContentException(string.Format("Audio file {0} contains audio data with unsupported sample rate of {1}KHz. Supported sample rates are from 8KHz up to 48KHz.", Path.GetFileName(filename), content.Format.SampleRate));
+ if (content.Format.BitsPerSample != 8 && content.Format.BitsPerSample != 16)
+ throw new InvalidContentException(string.Format("Audio file {0} contains audio data with unsupported bit depth of {1}. Supported bit depths are from 8bit and 16bit.", Path.GetFileName(filename), content.Format.BitsPerSample));
+
return content;
}
}
diff --git a/MonoGame.Framework.Content.Pipeline/WmaImporter.cs b/MonoGame.Framework.Content.Pipeline/WmaImporter.cs
index 5d945b49666..de0f5144848 100644
--- a/MonoGame.Framework.Content.Pipeline/WmaImporter.cs
+++ b/MonoGame.Framework.Content.Pipeline/WmaImporter.cs
@@ -3,6 +3,7 @@
// file 'LICENSE.txt', which is part of this source code package.
using System;
+using System.IO;
using Microsoft.Xna.Framework.Content.Pipeline.Audio;
namespace Microsoft.Xna.Framework.Content.Pipeline
@@ -28,6 +29,14 @@ public WmaImporter()
/// Resulting game asset.
public override AudioContent Import(string filename, ContentImporterContext context)
{
+ if (string.IsNullOrEmpty(filename))
+ throw new ArgumentNullException("filename");
+ if (context == null)
+ throw new ArgumentNullException("context");
+
+ if (!File.Exists(filename))
+ throw new FileNotFoundException(string.Format("Could not locate audio file {0}.", Path.GetFileName(filename)));
+
var content = new AudioContent(filename, AudioFileType.Wma);
return content;
}
diff --git a/MonoGame.Framework/Android/AndroidGameActivity.cs b/MonoGame.Framework/Android/AndroidGameActivity.cs
index 4892e73e270..23efe410542 100644
--- a/MonoGame.Framework/Android/AndroidGameActivity.cs
+++ b/MonoGame.Framework/Android/AndroidGameActivity.cs
@@ -11,11 +11,7 @@
namespace Microsoft.Xna.Framework
{
[CLSCompliant(false)]
-#if OUYA
- public class AndroidGameActivity : Ouya.Console.Api.OuyaActivity
-#else
public class AndroidGameActivity : Activity
-#endif
{
internal Game Game { private get; set; }
diff --git a/MonoGame.Framework/Android/AndroidGamePlatform.cs b/MonoGame.Framework/Android/AndroidGamePlatform.cs
index 6f8e94b4675..adafe49bc8b 100644
--- a/MonoGame.Framework/Android/AndroidGamePlatform.cs
+++ b/MonoGame.Framework/Android/AndroidGamePlatform.cs
@@ -11,8 +11,6 @@ namespace Microsoft.Xna.Framework
{
class AndroidGamePlatform : GamePlatform
{
- OpenALSoundController soundControllerInstance = null;
-
public AndroidGamePlatform(Game game)
: base(game)
{
@@ -27,7 +25,7 @@ public AndroidGamePlatform(Game game)
MediaLibrary.Context = Game.Activity;
try
{
- soundControllerInstance = OpenALSoundController.GetInstance;
+ OpenALSoundController soundControllerInstance = OpenALSoundController.GetInstance;
}
catch (DllNotFoundException ex)
{
diff --git a/MonoGame.Framework/Android/AndroidGameWindow.cs b/MonoGame.Framework/Android/AndroidGameWindow.cs
index 9a737bdccab..8800dbb2638 100644
--- a/MonoGame.Framework/Android/AndroidGameWindow.cs
+++ b/MonoGame.Framework/Android/AndroidGameWindow.cs
@@ -9,10 +9,6 @@
using Microsoft.Xna.Framework.Input.Touch;
using OpenTK;
-#if OUYA
-using Microsoft.Xna.Framework.Input;
-#endif
-
namespace Microsoft.Xna.Framework
{
[CLSCompliant(false)]
@@ -53,10 +49,6 @@ private void Initialize(Context context)
GameView.RequestFocus();
GameView.FocusableInTouchMode = true;
-
-#if OUYA
- GamePad.Initialize();
-#endif
}
#region AndroidGameView Methods
@@ -300,8 +292,6 @@ private void SetDisplayOrientation(DisplayOrientation value)
TouchPanelState.ReleaseAllTouches();
}
- Game.Activity.RequestedOrientation = requestedOrientation;
-
OnOrientationChanged();
}
}
diff --git a/MonoGame.Framework/Android/GamerServices/Guide.cs b/MonoGame.Framework/Android/GamerServices/Guide.cs
index 9e23c5e6077..2ecb9983ac3 100644
--- a/MonoGame.Framework/Android/GamerServices/Guide.cs
+++ b/MonoGame.Framework/Android/GamerServices/Guide.cs
@@ -50,7 +50,6 @@ purpose and non-infringement.
using Android.Views;
using Android.Widget;
using Microsoft.Xna.Framework.Net;
-using Microsoft.Xna.Framework.Storage;
#endregion Using clause
@@ -342,16 +341,6 @@ public static void ShowMatchMaker()
}
}
- public static IAsyncResult BeginShowStorageDeviceSelector( AsyncCallback callback, object state )
- {
- return null;
- }
-
- public static StorageDevice EndShowStorageDeviceSelector( IAsyncResult result )
- {
- return null;
- }
-
#region Properties
public static bool IsScreenSaverEnabled
{
@@ -410,4 +399,4 @@ public static AndroidGameWindow Window
#endregion
}
-}
\ No newline at end of file
+}
diff --git a/MonoGame.Framework/Android/MonoGameAndroidGameView.cs b/MonoGame.Framework/Android/MonoGameAndroidGameView.cs
index 22d03aeae4c..4b7b8093b70 100644
--- a/MonoGame.Framework/Android/MonoGameAndroidGameView.cs
+++ b/MonoGame.Framework/Android/MonoGameAndroidGameView.cs
@@ -27,9 +27,7 @@ internal class MonoGameAndroidGameView : AndroidGameView, View.IOnTouchListener,
public bool IsResuming { get; private set; }
private bool _lostContext;
-#if !OUYA
private bool backPressed;
-#endif
public MonoGameAndroidGameView(Context context, AndroidGameWindow androidGameWindow, Game game)
: base(context)
@@ -290,25 +288,22 @@ protected override void CreateFrameBuffer()
#endregion
- #region Key and Motion
+ #region Key and Motion and Gamepad
public override bool OnKeyDown(Keycode keyCode, KeyEvent e)
{
-#if OUYA
- if (GamePad.OnKeyDown(keyCode, e))
- return true;
-#endif
+ // Handle gamepad inputs in Android
+ if ((e.Source & InputSourceType.Gamepad) == InputSourceType.Gamepad)
+ return GamePad.OnKeyDown(keyCode, e);
Keyboard.KeyDown(keyCode);
// we need to handle the Back key here because it doesnt work any other way
-#if !OUYA
if (keyCode == Keycode.Back && !this.backPressed)
{
this.backPressed = true;
GamePad.Back = true;
return true;
}
-#endif
if (keyCode == Keycode.VolumeUp)
{
@@ -329,29 +324,25 @@ public override bool OnKeyDown(Keycode keyCode, KeyEvent e)
public override bool OnKeyUp(Keycode keyCode, KeyEvent e)
{
-#if OUYA
- if (GamePad.OnKeyUp(keyCode, e))
- return true;
-#endif
+ if ((e.Source & InputSourceType.Gamepad) == InputSourceType.Gamepad)
+ return GamePad.OnKeyUp(keyCode, e);
+
Keyboard.KeyUp(keyCode);
-#if !OUYA
+ // we need to handle the Back key here because it doesnt work any other way
if (keyCode == Keycode.Back)
this.backPressed = false;
-#endif
return true;
}
-#if OUYA
public override bool OnGenericMotionEvent(MotionEvent e)
{
- if (GamePad.OnGenericMotionEvent(e))
- return true;
+ if ((e.Source & InputSourceType.Gamepad) == InputSourceType.Gamepad || (e.Source & InputSourceType.Joystick) == InputSourceType.Joystick)
+ return GamePad.OnGenericMotionEvent(e);
return base.OnGenericMotionEvent(e);
}
-#endif
#endregion
}
diff --git a/MonoGame.Framework/Audio/AudioLoader.cs b/MonoGame.Framework/Audio/AudioLoader.cs
index dd708a792a6..d063f488a0d 100644
--- a/MonoGame.Framework/Audio/AudioLoader.cs
+++ b/MonoGame.Framework/Audio/AudioLoader.cs
@@ -1,6 +1,10 @@
using System;
using System.IO;
+#if GLES
using OpenTK.Audio.OpenAL;
+#else
+using OpenAL;
+#endif
namespace Microsoft.Xna.Framework.Audio
{
@@ -14,8 +18,8 @@ private static ALFormat GetSoundFormat(int channels, int bits)
{
switch (channels)
{
- case 1: return bits == 8 ? OpenTK.Audio.OpenAL.ALFormat.Mono8 : OpenTK.Audio.OpenAL.ALFormat.Mono16;
- case 2: return bits == 8 ? OpenTK.Audio.OpenAL.ALFormat.Stereo8 : OpenTK.Audio.OpenAL.ALFormat.Stereo16;
+ case 1: return bits == 8 ? ALFormat.Mono8 : ALFormat.Mono16;
+ case 2: return bits == 8 ? ALFormat.Stereo8 : ALFormat.Stereo16;
default: throw new NotSupportedException("The specified sound format is not supported.");
}
}
@@ -49,7 +53,7 @@ private static byte[] LoadWave(BinaryReader reader, out ALFormat format, out int
string signature = new string(reader.ReadChars(4));
if (signature != "RIFF")
{
- throw new NotSupportedException("Specified stream is not a wave file.");
+ throw new ArgumentException("Specified stream is not a wave file.");
}
reader.ReadInt32(); // riff_chunck_size
@@ -57,7 +61,7 @@ private static byte[] LoadWave(BinaryReader reader, out ALFormat format, out int
string wformat = new string(reader.ReadChars(4));
if (wformat != "WAVE")
{
- throw new NotSupportedException("Specified stream is not a wave file.");
+ throw new ArgumentException("Specified stream is not a wave file.");
}
// WAVE header
@@ -79,7 +83,7 @@ private static byte[] LoadWave(BinaryReader reader, out ALFormat format, out int
if (audio_format != 1)
{
- throw new NotSupportedException("Wave compression is not supported.");
+ throw new ArgumentException("Wave compression is not supported.");
}
// reads residual bytes
@@ -96,7 +100,7 @@ private static byte[] LoadWave(BinaryReader reader, out ALFormat format, out int
if (data_signature != "data")
{
- throw new NotSupportedException("Specified wave file is not supported.");
+ throw new ArgumentException("Specified wave file is not supported.");
}
int data_chunk_size = reader.ReadInt32();
diff --git a/MonoGame.Framework/Audio/DynamicSoundEffectInstance.OpenAL.cs b/MonoGame.Framework/Audio/DynamicSoundEffectInstance.OpenAL.cs
new file mode 100644
index 00000000000..620438e0b48
--- /dev/null
+++ b/MonoGame.Framework/Audio/DynamicSoundEffectInstance.OpenAL.cs
@@ -0,0 +1,166 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+using System.Collections.Generic;
+#if MONOMAC && PLATFORM_MACOS_LEGACY
+using MonoMac.OpenAL;
+#endif
+#if MONOMAC && !PLATFORM_MACOS_LEGACY
+using OpenTK.Audio.OpenAL;
+#endif
+#if GLES
+using OpenTK.Audio.OpenAL;
+#endif
+#if DESKTOPGL
+using OpenAL;
+#endif
+
+namespace Microsoft.Xna.Framework.Audio
+{
+ public sealed partial class DynamicSoundEffectInstance : SoundEffectInstance
+ {
+ private Queue _queuedBuffers;
+ private ALFormat _format;
+
+ private void PlatformCreate()
+ {
+ _format = _channels == AudioChannels.Mono ? ALFormat.Mono16 : ALFormat.Stereo16;
+ InitializeSound();
+
+ SourceId = controller.ReserveSource();
+ HasSourceId = true;
+
+ _queuedBuffers = new Queue();
+ }
+
+ private int PlatformGetPendingBufferCount()
+ {
+ return _queuedBuffers.Count;
+ }
+
+ private void PlatformPlay()
+ {
+ AL.GetError();
+
+ // Ensure that the source is not looped (due to source recycling)
+ AL.Source(SourceId, ALSourceb.Looping, false);
+ ALHelper.CheckError("Failed to set source loop state.");
+
+ AL.SourcePlay(SourceId);
+ ALHelper.CheckError("Failed to play the source.");
+ }
+
+ private void PlatformPause()
+ {
+ AL.GetError();
+ AL.SourcePause(SourceId);
+ ALHelper.CheckError("Failed to pause the source.");
+ }
+
+ private void PlatformResume()
+ {
+ AL.GetError();
+ AL.SourcePlay(SourceId);
+ ALHelper.CheckError("Failed to play the source.");
+ }
+
+ private void PlatformStop()
+ {
+ AL.GetError();
+ AL.SourceStop(SourceId);
+ ALHelper.CheckError("Failed to stop the source.");
+
+ // Remove all queued buffers
+ AL.Source(SourceId, ALSourcei.Buffer, 0);
+ while (_queuedBuffers.Count > 0)
+ {
+ var buffer = _queuedBuffers.Dequeue();
+ buffer.Dispose();
+ }
+ }
+
+ private void PlatformSubmitBuffer(byte[] buffer, int offset, int count)
+ {
+ // Get a buffer
+ OALSoundBuffer oalBuffer = new OALSoundBuffer();
+
+ // Bind the data
+ if (offset == 0)
+ {
+ oalBuffer.BindDataBuffer(buffer, _format, count, _sampleRate);
+ }
+ else
+ {
+ // BindDataBuffer does not support offset
+ var offsetBuffer = new byte[count];
+ Array.Copy(buffer, offset, offsetBuffer, 0, count);
+ oalBuffer.BindDataBuffer(offsetBuffer, _format, count, _sampleRate);
+ }
+
+ // Queue the buffer
+ AL.SourceQueueBuffer(SourceId, oalBuffer.OpenALDataBuffer);
+ ALHelper.CheckError();
+ _queuedBuffers.Enqueue(oalBuffer);
+
+
+ // If the source has run out of buffers, restart it
+ var sourceState = AL.GetSourceState(SourceId);
+ if (_state == SoundState.Playing && sourceState == ALSourceState.Stopped)
+ {
+ AL.SourcePlay(SourceId);
+ ALHelper.CheckError("Failed to resume source playback.");
+ }
+ }
+
+ private void PlatformDispose(bool disposing)
+ {
+ // Stop the source and bind null buffer so that it can be recycled
+ AL.GetError();
+ if (AL.IsSource(SourceId))
+ {
+ AL.SourceStop(SourceId);
+ AL.Source(SourceId, ALSourcei.Buffer, 0);
+ ALHelper.CheckError("Failed to stop the source.");
+ controller.RecycleSource(SourceId);
+ }
+
+ if (disposing)
+ {
+ while (_queuedBuffers.Count > 0)
+ {
+ var buffer = _queuedBuffers.Dequeue();
+ buffer.Dispose();
+ }
+
+ DynamicSoundEffectInstanceManager.RemoveInstance(this);
+ }
+ }
+
+ private void PlatformUpdateQueue()
+ {
+ // Get the completed buffers
+ AL.GetError();
+ int numBuffers;
+ AL.GetSource(SourceId, ALGetSourcei.BuffersProcessed, out numBuffers);
+ ALHelper.CheckError("Failed to get processed buffer count.");
+
+ // Unqueue them
+ if (numBuffers > 0)
+ {
+ AL.SourceUnqueueBuffers(SourceId, numBuffers);
+ ALHelper.CheckError("Failed to unqueue buffers.");
+ for (int i = 0; i < numBuffers; i++)
+ {
+ var buffer = _queuedBuffers.Dequeue();
+ buffer.Dispose();
+ }
+ }
+
+ // Raise the event for each removed buffer, if needed
+ for (int i = 0; i < numBuffers; i++)
+ CheckBufferCount();
+ }
+ }
+}
diff --git a/MonoGame.Framework/Audio/DynamicSoundEffectInstance.Web.cs b/MonoGame.Framework/Audio/DynamicSoundEffectInstance.Web.cs
new file mode 100644
index 00000000000..f72119f068b
--- /dev/null
+++ b/MonoGame.Framework/Audio/DynamicSoundEffectInstance.Web.cs
@@ -0,0 +1,48 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+
+namespace Microsoft.Xna.Framework.Audio
+{
+ public sealed partial class DynamicSoundEffectInstance : SoundEffectInstance
+ {
+ private void PlatformCreate()
+ {
+ }
+
+ private int PlatformGetPendingBufferCount()
+ {
+ return 0;
+ }
+
+ private void PlatformPlay()
+ {
+ }
+
+ private void PlatformPause()
+ {
+ }
+
+ private void PlatformResume()
+ {
+ }
+
+ private void PlatformStop()
+ {
+ }
+
+ private void PlatformSubmitBuffer(byte[] buffer, int offset, int count)
+ {
+ }
+
+ private void PlatformDispose(bool disposing)
+ {
+ }
+
+ private void PlatformUpdateQueue()
+ {
+ }
+ }
+}
diff --git a/MonoGame.Framework/Audio/DynamicSoundEffectInstance.XAudio.cs b/MonoGame.Framework/Audio/DynamicSoundEffectInstance.XAudio.cs
new file mode 100644
index 00000000000..d973274a92f
--- /dev/null
+++ b/MonoGame.Framework/Audio/DynamicSoundEffectInstance.XAudio.cs
@@ -0,0 +1,113 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework.Utilities;
+using SharpDX;
+using SharpDX.Multimedia;
+using SharpDX.XAudio2;
+
+namespace Microsoft.Xna.Framework.Audio
+{
+ public sealed partial class DynamicSoundEffectInstance : SoundEffectInstance
+ {
+ private Queue _queuedBuffers;
+ private Queue _pooledBuffers;
+ private static ByteBufferPool _bufferPool = new ByteBufferPool();
+
+ private void PlatformCreate()
+ {
+ _format = new WaveFormat(_sampleRate, (int)_channels);
+ _voice = new SourceVoice(SoundEffect.Device, _format, true);
+ _voice.BufferEnd += OnBufferEnd;
+ _queuedBuffers = new Queue();
+ _pooledBuffers = new Queue();
+ }
+
+ private int PlatformGetPendingBufferCount()
+ {
+ return _queuedBuffers.Count;
+ }
+
+ private void PlatformPlay()
+ {
+ _voice.Start();
+ }
+
+ private void PlatformPause()
+ {
+ _voice.Stop();
+ }
+
+ private void PlatformResume()
+ {
+ _voice.Start();
+ }
+
+ private void PlatformStop()
+ {
+ _voice.Stop();
+
+ // Dequeue all the submitted buffers
+ _voice.FlushSourceBuffers();
+
+ while (_queuedBuffers.Count > 0)
+ {
+ var buffer = _queuedBuffers.Dequeue();
+ buffer.Stream.Dispose();
+ _bufferPool.Return(_pooledBuffers.Dequeue());
+ }
+ }
+
+ private void PlatformSubmitBuffer(byte[] buffer, int offset, int count)
+ {
+ // we need to copy so datastream does not pin the buffer that the user might modify later
+ byte[] pooledBuffer;
+ pooledBuffer = _bufferPool.Get(count);
+ _pooledBuffers.Enqueue(pooledBuffer);
+ Buffer.BlockCopy(buffer, offset, pooledBuffer, 0, count);
+
+ var stream = DataStream.Create(pooledBuffer, true, false, offset, true);
+ var audioBuffer = new AudioBuffer(stream);
+ audioBuffer.AudioBytes = count;
+
+ _voice.SubmitSourceBuffer(audioBuffer, null);
+ _queuedBuffers.Enqueue(audioBuffer);
+ }
+
+ private void PlatformUpdateQueue()
+ {
+ // The XAudio implementation utilizes callbacks, so no work here.
+ }
+
+ private void PlatformDispose(bool disposing)
+ {
+ if (disposing)
+ {
+ while (_queuedBuffers.Count > 0)
+ {
+ var buffer = _queuedBuffers.Dequeue();
+ buffer.Stream.Dispose();
+ _bufferPool.Return(_pooledBuffers.Dequeue());
+ }
+ }
+ // _voice is disposed by SoundEffectInstance.PlatformDispose
+ }
+
+ private void OnBufferEnd(IntPtr obj)
+ {
+ // Release the buffer
+ if (_queuedBuffers.Count > 0)
+ {
+ var buffer = _queuedBuffers.Dequeue();
+ buffer.Stream.Dispose();
+ _bufferPool.Return(_pooledBuffers.Dequeue());
+ }
+
+ CheckBufferCount();
+ }
+
+ }
+}
diff --git a/MonoGame.Framework/Audio/DynamicSoundEffectInstance.cs b/MonoGame.Framework/Audio/DynamicSoundEffectInstance.cs
index e0e435beef3..6e6fc582c77 100644
--- a/MonoGame.Framework/Audio/DynamicSoundEffectInstance.cs
+++ b/MonoGame.Framework/Audio/DynamicSoundEffectInstance.cs
@@ -1,102 +1,306 @@
-using System;
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-#if MONOMAC
-using MonoMac.OpenAL;
-#else
-using OpenTK.Audio.OpenAL;
-#endif
namespace Microsoft.Xna.Framework.Audio
{
- public sealed class DynamicSoundEffectInstance : SoundEffectInstance
+ ///
+ /// A for which the audio buffer is provided by the game at run time.
+ ///
+ public sealed partial class DynamicSoundEffectInstance : SoundEffectInstance
{
- private AudioChannels channels;
- private int sampleRate;
- private ALFormat format;
- private bool looped;
- private int pendingBufferCount;
- // Events
- public event EventHandler BufferNeeded;
+ #region Public Properties
- internal void OnBufferNeeded(EventArgs args)
+ ///
+ /// This value has no effect on DynamicSoundEffectInstance.
+ /// It may not be set.
+ ///
+ public override bool IsLooped
{
- if (BufferNeeded != null)
+ get
{
- BufferNeeded(this, args);
+ return false;
+ }
+
+ set
+ {
+ AssertNotDisposed();
+ if (value == true)
+ throw new InvalidOperationException("IsLooped cannot be set true. Submit looped audio data to implement looping.");
}
}
- public DynamicSoundEffectInstance(int sampleRate, AudioChannels channels)
+ public override SoundState State
{
- this.sampleRate = sampleRate;
- this.channels = channels;
- switch (channels)
+ get
{
- case AudioChannels.Mono:
- this.format = ALFormat.Mono16;
- break;
- case AudioChannels.Stereo:
- this.format = ALFormat.Stereo16;
- break;
- default:
- break;
- }
- }
-
- /*
- public TimeSpan GetSampleDuration(int sizeInBytes)
- {
- throw new NotImplementedException();
+ AssertNotDisposed();
+ return _state;
+ }
}
- public int GetSampleSizeInBytes(TimeSpan duration)
+ ///
+ /// Returns the number of audio buffers queued for playback.
+ ///
+ public int PendingBufferCount
{
- throw new NotImplementedException();
+ get
+ {
+ AssertNotDisposed();
+ return PlatformGetPendingBufferCount();
+ }
}
- */
- public override void Play()
+ ///
+ /// The event that occurs when the number of queued audio buffers is less than or equal to 2.
+ ///
+ ///
+ /// This event may occur when is called or during playback when a buffer is completed.
+ ///
+ public event EventHandler BufferNeeded;
+
+ #endregion
+
+ private const int TargetPendingBufferCount = 3;
+ private int _buffersNeeded;
+ private int _sampleRate;
+ private AudioChannels _channels;
+ private SoundState _state;
+
+ #region Public Constructor
+
+ /// Sample rate, in Hertz (Hz).
+ /// Number of channels (mono or stereo).
+ public DynamicSoundEffectInstance(int sampleRate, AudioChannels channels)
{
- throw new NotImplementedException();
+ if ((sampleRate < 8000) || (sampleRate > 48000))
+ throw new ArgumentOutOfRangeException("sampleRate");
+ if ((channels != AudioChannels.Mono) && (channels != AudioChannels.Stereo))
+ throw new ArgumentOutOfRangeException("channels");
+
+ _sampleRate = sampleRate;
+ _channels = channels;
+ _state = SoundState.Stopped;
+ PlatformCreate();
+
+ // This instance is added to the pool so that its volume reflects master volume changes
+ // and it contributes to the playing instances limit, but the source/voice is not owned by the pool.
+ _isPooled = false;
+ _isDynamic = true;
}
- public void SubmitBuffer(byte[] buffer)
+ #endregion
+
+ #region Public Functions
+
+ ///
+ /// Returns the duration of an audio buffer of the specified size, based on the settings of this instance.
+ ///
+ /// Size of the buffer, in bytes.
+ /// The playback length of the buffer.
+ public TimeSpan GetSampleDuration(int sizeInBytes)
{
- this.SubmitBuffer(buffer, 0, buffer.Length);
+ AssertNotDisposed();
+ return SoundEffect.GetSampleDuration(sizeInBytes, _sampleRate, _channels);
}
- public void SubmitBuffer(byte[] buffer, int offset, int count)
+ ///
+ /// Returns the size, in bytes, of a buffer of the specified duration, based on the settings of this instance.
+ ///
+ /// The playback length of the buffer.
+ /// The data size of the buffer, in bytes.
+ public int GetSampleSizeInBytes(TimeSpan duration)
{
- BindDataBuffer(buffer, format, count, sampleRate);
+ AssertNotDisposed();
+ return SoundEffect.GetSampleSizeInBytes(duration, _sampleRate, _channels);
}
- public override bool IsLooped
+ ///
+ /// Plays or resumes the DynamicSoundEffectInstance.
+ ///
+ public override void Play()
{
- get
+ AssertNotDisposed();
+
+ if (_state != SoundState.Playing)
{
- return looped;
+ // Ensure that the volume reflects master volume, which is done by the setter.
+ Volume = Volume;
+
+ // Add the instance to the pool
+ if (!SoundEffectInstancePool.SoundsAvailable)
+ throw new InstancePlayLimitException();
+ SoundEffectInstancePool.Remove(this);
+
+ PlatformPlay();
+ _state = SoundState.Playing;
+
+ CheckBufferCount();
+ DynamicSoundEffectInstanceManager.AddInstance(this);
}
+ }
- set
+ ///
+ /// Pauses playback of the DynamicSoundEffectInstance.
+ ///
+ public override void Pause()
+ {
+ AssertNotDisposed();
+ PlatformPause();
+ _state = SoundState.Paused;
+ }
+
+ ///
+ /// Resumes playback of the DynamicSoundEffectInstance.
+ ///
+ public override void Resume()
+ {
+ AssertNotDisposed();
+
+ if (_state != SoundState.Playing)
{
- looped = value;
+ Volume = Volume;
+
+ // Add the instance to the pool
+ if (!SoundEffectInstancePool.SoundsAvailable)
+ throw new InstancePlayLimitException();
+ SoundEffectInstancePool.Remove(this);
}
+
+ PlatformResume();
+ _state = SoundState.Playing;
}
- public int PendingBufferCount
+ ///
+ /// Immediately stops playing the DynamicSoundEffectInstance.
+ ///
+ ///
+ /// Calling this also releases all queued buffers.
+ ///
+ public override void Stop()
{
- get
+ Stop(true);
+ }
+
+ ///
+ /// Stops playing the DynamicSoundEffectInstance.
+ /// If the parameter is false, this call has no effect.
+ ///
+ ///
+ /// Calling this also releases all queued buffers.
+ ///
+ /// When set to false, this call has no effect.
+ public override void Stop(bool immediate)
+ {
+ AssertNotDisposed();
+
+ if (immediate)
{
- return pendingBufferCount;
+ DynamicSoundEffectInstanceManager.RemoveInstance(this);
+
+ PlatformStop();
+ _state = SoundState.Stopped;
+
+ SoundEffectInstancePool.Add(this);
}
- private set
+ }
+
+ ///
+ /// Queues an audio buffer for playback.
+ ///
+ ///
+ /// The buffer length must conform to alignment requirements for the audio format.
+ ///
+ /// The buffer containing PCM audio data.
+ public void SubmitBuffer(byte[] buffer)
+ {
+ AssertNotDisposed();
+
+ if (buffer.Length == 0)
+ throw new ArgumentException("Buffer may not be empty.");
+
+ // Ensure that the buffer length matches alignment.
+ // The data must be 16-bit, so the length is a multiple of 2 (mono) or 4 (stereo).
+ var sampleSize = 2 * (int)_channels;
+ if (buffer.Length % sampleSize != 0)
+ throw new ArgumentException("Buffer length does not match format alignment.");
+
+ SubmitBuffer(buffer, 0, buffer.Length);
+ }
+
+ ///
+ /// Queues an audio buffer for playback.
+ ///
+ ///
+ /// The buffer length must conform to alignment requirements for the audio format.
+ ///
+ /// The buffer containing PCM audio data.
+ /// The starting position of audio data.
+ /// The amount of bytes to use.
+ public void SubmitBuffer(byte[] buffer, int offset, int count)
+ {
+ AssertNotDisposed();
+
+ if ((buffer == null) || (buffer.Length == 0))
+ throw new ArgumentException("Buffer may not be null or empty.");
+ if (count <= 0)
+ throw new ArgumentException("Number of bytes must be greater than zero.");
+ if ((offset + count) > buffer.Length)
+ throw new ArgumentException("Buffer is shorter than the specified number of bytes from the offset.");
+
+ // Ensure that the buffer length and start position match alignment.
+ var sampleSize = 2 * (int)_channels;
+ if (count % sampleSize != 0)
+ throw new ArgumentException("Number of bytes does not match format alignment.");
+ if (offset % sampleSize != 0)
+ throw new ArgumentException("Offset into the buffer does not match format alignment.");
+
+ PlatformSubmitBuffer(buffer, offset, count);
+ }
+
+ #endregion
+
+ #region Nonpublic Functions
+
+ private void AssertNotDisposed()
+ {
+ if (IsDisposed)
+ throw new ObjectDisposedException(null);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ PlatformDispose(disposing);
+ base.Dispose(disposing);
+ }
+
+ private void CheckBufferCount()
+ {
+ if ((PendingBufferCount < TargetPendingBufferCount) && (_state == SoundState.Playing))
+ _buffersNeeded++;
+ }
+
+ internal void UpdateQueue()
+ {
+ // Update the buffers
+ PlatformUpdateQueue();
+
+ // Raise the event
+ if (BufferNeeded != null)
{
- pendingBufferCount = value;
+ var eventCount = (_buffersNeeded < 3) ? _buffersNeeded : 3;
+ for (var i = 0; i < eventCount; i++)
+ {
+ BufferNeeded(this, EventArgs.Empty);
+ }
}
- }
- }
+ _buffersNeeded = 0;
+ }
+ #endregion
+ }
}
diff --git a/MonoGame.Framework/Audio/DynamicSoundEffectInstanceManager.cs b/MonoGame.Framework/Audio/DynamicSoundEffectInstanceManager.cs
new file mode 100644
index 00000000000..89d8f38371b
--- /dev/null
+++ b/MonoGame.Framework/Audio/DynamicSoundEffectInstanceManager.cs
@@ -0,0 +1,64 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.Xna.Framework.Audio
+{
+ ///
+ /// Handles the buffer events of all DynamicSoundEffectInstance instances.
+ ///
+ internal static class DynamicSoundEffectInstanceManager
+ {
+ private static readonly List _playingInstances;
+
+ static DynamicSoundEffectInstanceManager()
+ {
+ _playingInstances = new List();
+ }
+
+ public static void AddInstance(DynamicSoundEffectInstance instance)
+ {
+ var weakRef = new WeakReference(instance);
+ _playingInstances.Add(weakRef);
+ }
+
+ public static void RemoveInstance(DynamicSoundEffectInstance instance)
+ {
+ for (int i = _playingInstances.Count - 1; i >= 0; i--)
+ {
+ if (_playingInstances[i].Target == instance)
+ {
+ _playingInstances.RemoveAt(i);
+ return;
+ }
+ }
+ }
+
+ ///
+ /// Updates buffer queues of the currently playing instances.
+ ///
+ ///
+ /// XNA posts events always on the main thread.
+ ///
+ public static void UpdatePlayingInstances()
+ {
+ for (int i = _playingInstances.Count - 1; i >= 0; i--)
+ {
+ var target = _playingInstances[i].Target as DynamicSoundEffectInstance;
+ if (target != null)
+ {
+ if (!target.IsDisposed)
+ target.UpdateQueue();
+ }
+ else
+ {
+ // The instance has been disposed.
+ _playingInstances.RemoveAt(i);
+ }
+ }
+ }
+ }
+}
diff --git a/MonoGame.Framework/Audio/OALSoundBuffer.cs b/MonoGame.Framework/Audio/OALSoundBuffer.cs
index 45d5598c0b3..9a206cd42c2 100644
--- a/MonoGame.Framework/Audio/OALSoundBuffer.cs
+++ b/MonoGame.Framework/Audio/OALSoundBuffer.cs
@@ -6,10 +6,20 @@
#if MONOMAC && PLATFORM_MACOS_LEGACY
using MonoMac.OpenAL;
-#else
+#endif
+#if MONOMAC && !PLATFORM_MACOS_LEGACY
using OpenTK.Audio.OpenAL;
#endif
+#if GLES
+using OpenTK.Audio.OpenAL;
+#endif
+
+#if DESKTOPGL
+using OpenAL;
+#endif
+
+
namespace Microsoft.Xna.Framework.Audio
{
internal class OALSoundBuffer : IDisposable
@@ -49,12 +59,20 @@ public double Duration {
set;
}
- public void BindDataBuffer(byte[] dataBuffer, ALFormat format, int size, int sampleRate)
+ public void BindDataBuffer(byte[] dataBuffer, ALFormat format, int size, int sampleRate, int alignment = 0)
{
openALFormat = format;
dataSize = size;
this.sampleRate = sampleRate;
- AL.BufferData(openALDataBuffer, openALFormat, dataBuffer, dataSize, this.sampleRate);
+ int unpackedSize = 0;
+#if DESKTOPGL
+ if (alignment > 0) {
+ AL.Bufferi (openALDataBuffer, ALBufferi.UnpackBlockAlignmentSoft, alignment);
+ ALHelper.CheckError ("Failed to fill buffer.");
+ }
+#endif
+
+ AL.BufferData(openALDataBuffer, openALFormat, dataBuffer, size, this.sampleRate);
ALHelper.CheckError("Failed to fill buffer.");
int bits, channels;
@@ -73,12 +91,19 @@ public void BindDataBuffer(byte[] dataBuffer, ALFormat format, int size, int sam
alError = AL.GetError();
if (alError != ALError.NoError)
{
- Console.WriteLine("Failed to get buffer bits: {0}, format={1}, size={2}, sampleRate={3}", AL.GetErrorString(alError), format, size, sampleRate);
+ Console.WriteLine("Failed to get buffer channels: {0}, format={1}, size={2}, sampleRate={3}", AL.GetErrorString(alError), format, size, sampleRate);
Duration = -1;
}
else
{
- Duration = (float)(size / ((bits / 8) * channels)) / (float)sampleRate;
+ AL.GetBuffer (openALDataBuffer, ALGetBufferi.Size, out unpackedSize);
+ alError = AL.GetError ();
+ if (alError != ALError.NoError) {
+ Console.WriteLine ("Failed to get buffer size: {0}, format={1}, size={2}, sampleRate={3}", AL.GetErrorString (alError), format, size, sampleRate);
+ Duration = -1;
+ } else {
+ Duration = (float)(unpackedSize / ((bits / 8) * channels)) / (float)sampleRate;
+ }
}
}
//Console.WriteLine("Duration: " + Duration + " / size: " + size + " bits: " + bits + " channels: " + channels + " rate: " + sampleRate);
diff --git a/MonoGame.Framework/Audio/OggStream.cs b/MonoGame.Framework/Audio/OggStream.cs
index 85d95f0753c..b2f86b4793b 100644
--- a/MonoGame.Framework/Audio/OggStream.cs
+++ b/MonoGame.Framework/Audio/OggStream.cs
@@ -13,7 +13,7 @@
using System.Linq;
using System.Threading;
using NVorbis;
-using OpenTK.Audio.OpenAL;
+using OpenAL;
namespace Microsoft.Xna.Framework.Audio
{
@@ -320,7 +320,7 @@ internal void Close()
internal class OggStreamer : IDisposable
{
public readonly XRamExtension XRam = new XRamExtension();
- public readonly EffectsExtension Efx = new EffectsExtension();
+ public readonly EffectsExtension Efx = OpenALSoundController.Efx;
const float DefaultUpdateRate = 10;
const int DefaultBufferSize = 44100;
@@ -405,10 +405,8 @@ internal bool RemoveStream(OggStream stream)
public bool FillBuffer(OggStream stream, int bufferId)
{
int readSamples;
- long readerPosition = 0;
lock (readMutex)
{
- readerPosition = stream.Reader.DecodedPosition;
readSamples = stream.Reader.ReadSamples(readSampleBuffer, 0, BufferSize);
CastBuffer(readSampleBuffer, castBuffer, readSamples);
}
diff --git a/MonoGame.Framework/Audio/OpenAL.cs b/MonoGame.Framework/Audio/OpenAL.cs
new file mode 100644
index 00000000000..b72ec2440d6
--- /dev/null
+++ b/MonoGame.Framework/Audio/OpenAL.cs
@@ -0,0 +1,701 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+using System.Runtime.InteropServices;
+using Microsoft.Xna.Framework.Audio;
+
+namespace OpenAL
+{
+ public enum ALFormat
+ {
+ Mono8 = 0x1100,
+ Mono16 = 0x1101,
+ Stereo8 = 0x1102,
+ Stereo16 = 0x1103,
+ MonoMSADPCM =0x1302,
+ StereoMSADPCM =0x1303,
+ }
+
+ public enum ALError
+ {
+ NoError = 0,
+ InvalidName = 0xA001,
+ InvalidEnum = 0xA002,
+ InvalidValue = 0xA003,
+ InvalidOperation = 0xA004,
+ OutOfMemory = 0xA005,
+ }
+
+ public enum ALGetString
+ {
+ Extensions = 0xB004,
+ }
+
+ public enum ALBufferi
+ {
+ UnpackBlockAlignmentSoft = 0x200C,
+ LoopSoftPointsExt = 0x2015,
+ }
+
+ public enum ALGetBufferi
+ {
+ Bits = 0x2002,
+ Channels = 0x2003,
+ Size = 0x2004,
+ }
+
+ public enum ALSourceb
+ {
+ Looping = 0x1007,
+ }
+
+ public enum ALSourcei
+ {
+ Buffer = 0x1009,
+ EfxDirectFilter = 0x20005,
+ EfxAuxilarySendFilter = 0x20006,
+ }
+
+ public enum ALSourcef
+ {
+ Pitch = 0x1003,
+ Gain = 0x100A,
+ }
+
+ public enum ALGetSourcei
+ {
+ SampleOffset = 0x1025,
+ SourceState = 0x1010,
+ BuffersQueued = 0x1015,
+ BuffersProcessed = 0x1016,
+ }
+
+ public enum ALSourceState
+ {
+ Initial = 0x1011,
+ Playing = 0x1012,
+ Paused = 0x1013,
+ Stopped = 0x1014,
+ }
+
+ public enum ALListener3f
+ {
+ Position = 0x1004,
+ }
+
+ public enum ALSource3f
+ {
+ Position = 0x1004,
+ Velocity = 0x1006,
+ }
+
+ public enum ALDistanceModel
+ {
+ InverseDistanceClamped = 0xD002,
+ }
+
+ public enum AlcError
+ {
+ NoError = 0,
+ }
+
+ public enum AlcGetString
+ {
+ Extensions = 0x1006,
+ }
+
+ public enum EfxFilteri
+ {
+ FilterType = 0x8001,
+ }
+
+ public enum EfxFilterf
+ {
+ LowpassGain = 0x0001,
+ LowpassGainHF = 0x0002,
+ HighpassGain = 0x0001,
+ HighpassGainLF = 0x0002,
+ BandpassGain = 0x0001,
+ BandpassGainLF = 0x0002,
+ BandpassGainHF = 0x0003,
+ }
+
+ public enum EfxFilterType
+ {
+ None = 0x0000,
+ Lowpass = 0x0001,
+ Highpass = 0x0002,
+ Bandpass = 0x0003,
+ }
+
+ public enum EfxEffecti
+ {
+ EffectType = 0x8001,
+ SlotEffect = 0x0001,
+ }
+
+ public enum EfxEffectSlotf
+ {
+ EffectSlotGain = 0x0002,
+ }
+
+ public enum EfxEffectf
+ {
+ EaxReverbDensity = 0x0001,
+ EaxReverbDiffusion = 0x0002,
+ EaxReverbGain = 0x0003,
+ EaxReverbGainHF = 0x0004,
+ EaxReverbGainLF = 0x0005,
+ DecayTime = 0x0006,
+ DecayHighFrequencyRatio = 0x0007,
+ DecayLowFrequencyRation = 0x0008,
+ EaxReverbReflectionsGain = 0x0009,
+ EaxReverbReflectionsDelay = 0x000A,
+ ReflectionsPain = 0x000B,
+ LateReverbGain = 0x000C,
+ LateReverbDelay = 0x000D,
+ LateRevertPain = 0x000E,
+ EchoTime = 0x000F,
+ EchoDepth = 0x0010,
+ ModulationTime = 0x0011,
+ ModulationDepth = 0x0012,
+ AirAbsorbsionHighFrequency = 0x0013,
+ EaxReverbHFReference = 0x0014,
+ EaxReverbLFReference = 0x0015,
+ RoomRolloffFactor = 0x0016,
+ DecayHighFrequencyLimit = 0x0017,
+ }
+
+ public enum EfxEffectType
+ {
+ Reverb = 0x8000,
+ }
+
+ public class AL
+ {
+ public const string NativeLibName = "soft_oal.dll";
+
+ [CLSCompliant (false)]
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alEnable")]
+ public static extern void Enable (int cap);
+
+ [CLSCompliant(false)]
+ [DllImport(NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alBufferData")]
+ public static extern void BufferData(uint bid, int format, IntPtr data, int size, int freq);
+
+ public static void BufferData(int bid, ALFormat format, byte[] data, int size, int freq)
+ {
+ var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
+ BufferData((uint)bid, (int)format, handle.AddrOfPinnedObject(), size, freq);
+ handle.Free();
+ }
+
+ public static void BufferData(int bid, ALFormat format, short[] data, int size, int freq)
+ {
+ var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
+ BufferData((uint)bid, (int)format, handle.AddrOfPinnedObject(), size, freq);
+ handle.Free();
+ }
+
+ [CLSCompliant (false)]
+ [DllImport(NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alDeleteBuffers")]
+ public static unsafe extern void DeleteBuffers(int n, int* buffers);
+
+ public static void DeleteBuffers(int[] buffers)
+ {
+ DeleteBuffers (buffers.Length, ref buffers [0]);
+ }
+
+ public unsafe static void DeleteBuffers(int n, ref int buffers)
+ {
+ fixed (int* pbuffers = &buffers)
+ {
+ DeleteBuffers (n, pbuffers);
+ }
+ }
+
+ [CLSCompliant (false)]
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alBufferi")]
+ public static extern void Bufferi (int buffer, ALBufferi param, int value);
+
+ [DllImport(NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alGetBufferi")]
+ public static extern void GetBufferi(int bid, ALGetBufferi param, out int value);
+
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alBufferiv")]
+ public static extern void Bufferiv (int bid, ALBufferi param, int[] values);
+
+ public static void GetBuffer(int bid, ALGetBufferi param, out int value)
+ {
+ GetBufferi(bid, param, out value);
+ }
+
+ [CLSCompliant(false)]
+ [DllImport(NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alGenBuffers")]
+ public static unsafe extern void GenBuffers(int count, int* buffers);
+
+ internal unsafe static void GenBuffers (int count,out int[] buffers)
+ {
+ buffers = new int[count];
+ fixed (int* ptr = &buffers[0])
+ {
+ GenBuffers (count, ptr);
+ }
+ }
+
+ public static void GenBuffers(int count, out int buffer)
+ {
+ int[] ret;
+ GenBuffers(count, out ret);
+ buffer = ret[0];
+ }
+
+ public static int[] GenBuffers(int count)
+ {
+ int[] ret;
+ GenBuffers(count, out ret);
+ return ret;
+ }
+
+ [CLSCompliant(false)]
+ [DllImport(NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alGenSources")]
+ public static extern void GenSources(int n, uint[] sources);
+
+
+ public static void GenSources(int[] sources)
+ {
+ uint[] temp = new uint[sources.Length];
+ GenSources(temp.Length, temp);
+ for (int i = 0; i < temp.Length; i++)
+ {
+ sources[i] = (int)temp[i];
+ }
+ }
+
+ [DllImport(NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alGetError")]
+ public static extern ALError GetError();
+
+ [CLSCompliant(false)]
+ [DllImport(NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alIsBuffer")]
+ public static extern bool IsBuffer(uint buffer);
+
+ public static bool IsBuffer(int buffer)
+ {
+ return IsBuffer((uint)buffer);
+ }
+
+ [CLSCompliant(false)]
+ [DllImport(NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alSourcePause")]
+ public static extern void SourcePause(uint source);
+
+ public static void SourcePause(int source)
+ {
+ SourcePause((uint)source);
+ }
+
+ [CLSCompliant(false)]
+ [DllImport(NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alSourcePlay")]
+ public static extern void SourcePlay(uint source);
+
+ public static void SourcePlay(int source)
+ {
+ SourcePlay((uint)source);
+ }
+
+ public static string GetErrorString(ALError errorCode)
+ {
+ return errorCode.ToString ();
+ }
+
+ [CLSCompliant(false)]
+ [DllImport(NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alIsSource")]
+ public static extern bool IsSource(int source);
+
+ [CLSCompliant (false)]
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alDeleteSources")]
+ public static extern void DeleteSources(int n, ref int sources);
+
+ public static void DeleteSource(int source)
+ {
+ DeleteSources (1, ref source);
+ }
+
+ [CLSCompliant (false)]
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alSourceStop")]
+ public static extern void SourceStop (int sourceId);
+
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alSourcei")]
+ internal static extern void Source (int sourceId, int i, int a);
+
+ [CLSCompliant (false)]
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alSource3i")]
+ public static extern void Source (int sourceId, ALSourcei i, int a, int b, int c);
+
+ public static void Source (int sourceId, ALSourcei i, int a)
+ {
+ Source (sourceId, (int)i, a);
+ }
+
+ public static void Source (int sourceId, ALSourceb i, bool a) {
+ Source (sourceId, (int)i, a ? 1 : 0);
+ }
+
+ [CLSCompliant (false)]
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alSourcef")]
+ public static extern void Source (int sourceId, ALSourcef i, float a);
+
+ [CLSCompliant (false)]
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alSource3f")]
+ public static extern void Source (int sourceId, ALSource3f i, float x, float y, float z);
+
+ [CLSCompliant (false)]
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alGetSourcei")]
+ public static extern void GetSource (int sourceId, ALGetSourcei i, out int state);
+
+ public static ALSourceState GetSourceState(int sourceId) {
+ int state = (int)ALSourceState.Stopped;
+ GetSource (sourceId, ALGetSourcei.SourceState, out state);
+ return (ALSourceState)state;
+ }
+
+ [CLSCompliant (false)]
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alGetListener3f")]
+ public static extern void GetListener (ALListener3f param, out float value1, out float value2, out float value3);
+
+ public static void DistanceModel(ALDistanceModel model) { }
+
+ [CLSCompliant (false)]
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alSourceQueueBuffers")]
+ public unsafe static extern void SourceQueueBuffers (int sourceId, int numEntries, [In] int* buffers);
+
+ [CLSCompliant (false)]
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alSourceUnqueueBuffers")]
+ public unsafe static extern void SourceUnqueueBuffers (int sourceId, int numEntries, [In] int* salvaged);
+
+ [CLSCompliant (false)]
+ public unsafe static void SourceQueueBuffers (int sourceId, int numEntries, int [] buffers)
+ {
+ fixed (int* ptr = &buffers[0]) {
+ AL.SourceQueueBuffers (sourceId, numEntries, ptr);
+ }
+ }
+
+ public unsafe static void SourceQueueBuffer (int sourceId, int buffer)
+ {
+ AL.SourceQueueBuffers (sourceId, 1, &buffer);
+ }
+
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alSourceUnqueueBuffers")]
+ public static extern void SourceUnqueueBuffers (int sid, int numEntries, [Out] int [] bids);
+
+ public static unsafe int [] SourceUnqueueBuffers (int sourceId, int numEntries)
+ {
+ if (numEntries <= 0) {
+ throw new ArgumentOutOfRangeException ("numEntries", "Must be greater than zero.");
+ }
+ int [] array = new int [numEntries];
+ fixed (int* ptr = &array [0])
+ {
+ AL.SourceUnqueueBuffers (sourceId, numEntries, ptr);
+ }
+ return array;
+ }
+
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alGetEnumValue")]
+ public static extern int GetEnumValue (string enumName);
+
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alIsExtensionPresent")]
+ public static extern bool IsExtensionPresent (string extensionName);
+
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alGetProcAddress")]
+ public static extern IntPtr GetProcAddress (string functionName);
+
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alGetString")]
+ private static extern IntPtr alGetString (int p);
+
+ public static string GetString (int p)
+ {
+ return Marshal.PtrToStringAnsi (alGetString (p));
+ }
+
+ public static string Get (ALGetString p)
+ {
+ return GetString ((int)p);
+ }
+ }
+
+ public partial class Alc
+ {
+ public const string NativeLibName = "soft_oal.dll";
+
+ [CLSCompliant (false)]
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alcCreateContext")]
+ public static extern IntPtr CreateContext (IntPtr device, int [] attributes);
+
+ public static AlcError GetError()
+ {
+ return GetError (IntPtr.Zero);
+ }
+
+ [CLSCompliant (false)]
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alcGetError")]
+ public static extern AlcError GetError (IntPtr device);
+
+ [CLSCompliant (false)]
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alcGetCurrentContext")]
+ public static extern IntPtr GetCurrentContext ();
+
+ [CLSCompliant (false)]
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alcMakeContextCurrent")]
+ public static extern void MakeContextCurrent (IntPtr context);
+
+ [CLSCompliant (false)]
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alcDestroyContext")]
+ public static extern void DestroyContext (IntPtr context);
+
+ [CLSCompliant (false)]
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alcCloseDevice")]
+ public static extern void CloseDevice (IntPtr device);
+
+ [CLSCompliant (false)]
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alcOpenDevice")]
+ public static extern IntPtr OpenDevice ([MarshalAs (UnmanagedType.LPStr)] string device);
+
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alcIsExtensionPresent")]
+ public static extern bool IsExtensionPresent (IntPtr device, [MarshalAs (UnmanagedType.LPStr)] string extensionName);
+
+ [DllImport (NativeLibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "alcGetString")]
+ internal static extern IntPtr alGetString (IntPtr device, int p);
+
+ public static string GetString (IntPtr device, int p)
+ {
+ return Marshal.PtrToStringAnsi (alGetString (device, p));
+ }
+
+ public static string GetString (IntPtr device, AlcGetString p)
+ {
+ return GetString (device, (int)p);
+ }
+ }
+
+ public class XRamExtension
+ {
+ public enum XRamStorage
+ {
+ Automatic,
+ Hardware,
+ Accessible
+ }
+
+ private int RamSize;
+ private int RamFree;
+ private int StorageAuto;
+ private int StorageHardware;
+ private int StorageAccessible;
+
+ [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+ private delegate bool SetBufferModeDelegate (int n, ref int buffers, int value);
+
+ private SetBufferModeDelegate setBufferMode;
+
+ public XRamExtension ()
+ {
+ IsInitialized = false;
+ if (!AL.IsExtensionPresent ("EAX-RAM")) {
+ return;
+ }
+ RamSize = AL.GetEnumValue ("AL_EAX_RAM_SIZE");
+ RamFree = AL.GetEnumValue ("AL_EAX_RAM_FREE");
+ StorageAuto = AL.GetEnumValue ("AL_STORAGE_AUTOMATIC");
+ StorageHardware = AL.GetEnumValue ("AL_STORAGE_HARDWARE");
+ StorageAccessible = AL.GetEnumValue ("AL_STORAGE_ACCESSIBLE");
+ if (RamSize == 0 || RamFree == 0 || StorageAuto == 0 || StorageHardware == 0 || StorageAccessible == 0) {
+ return;
+ }
+ try {
+ setBufferMode = (XRamExtension.SetBufferModeDelegate)Marshal.GetDelegateForFunctionPointer (AL.GetProcAddress ("EAXSetBufferMode"), typeof (XRamExtension.SetBufferModeDelegate));
+ } catch (Exception) {
+ return;
+ }
+ IsInitialized = true;
+ }
+
+ public bool IsInitialized { get; private set; }
+
+ public bool SetBufferMode(int i, ref int id, XRamStorage storage) {
+ if (storage == XRamExtension.XRamStorage.Accessible) {
+ return setBufferMode (i, ref id, StorageAccessible);
+ }
+ if (storage != XRamExtension.XRamStorage.Hardware) {
+ return setBufferMode (i, ref id, StorageAuto);
+ }
+ return setBufferMode (i, ref id, StorageHardware);
+ }
+ }
+
+ [CLSCompliant (false)]
+ public class EffectsExtension
+ {
+ /* Effect API */
+
+ [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+ private delegate void alGenEffectsDelegate (int n, out uint effect);
+ [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+ private delegate void alDeleteEffectsDelegate (int n, ref int effect);
+ [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+ private delegate bool alIsEffectDelegate (uint effect);
+ [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+ private delegate void alEffectfDelegate (uint effect, EfxEffectf param, float value);
+ [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+ private delegate void alEffectiDelegate (uint effect, EfxEffecti param, int value);
+ [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+ private delegate void alGenAuxiliaryEffectSlotsDelegate (int n, out uint effectslots);
+ [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+ private delegate void alDeleteAuxiliaryEffectSlotsDelegate (int n, ref int effectslots);
+ [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+ private delegate void alAuxiliaryEffectSlotiDelegate (uint slot, EfxEffecti type, uint effect);
+ [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+ private delegate void alAuxiliaryEffectSlotfDelegate (uint slot, EfxEffectSlotf param, float value);
+
+ /* Filter API */
+ [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+ private unsafe delegate void alGenFiltersDelegate (int n, [Out] uint* filters);
+ [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+ private delegate void alFilteriDelegate (uint fid, EfxFilteri param, int value);
+ [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+ private delegate void alFilterfDelegate (uint fid, EfxFilterf param, float value);
+ [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+ private unsafe delegate void alDeleteFiltersDelegate (int n, [In] uint* filters);
+
+
+ private alGenEffectsDelegate alGenEffects;
+ private alDeleteEffectsDelegate alDeleteEffects;
+ private alIsEffectDelegate alIsEffect;
+ private alEffectfDelegate alEffectf;
+ private alEffectiDelegate alEffecti;
+ private alGenAuxiliaryEffectSlotsDelegate alGenAuxiliaryEffectSlots;
+ private alDeleteAuxiliaryEffectSlotsDelegate alDeleteAuxiliaryEffectSlots;
+ private alAuxiliaryEffectSlotiDelegate alAuxiliaryEffectSloti;
+ private alAuxiliaryEffectSlotfDelegate alAuxiliaryEffectSlotf;
+ private alGenFiltersDelegate alGenFilters;
+ private alFilteriDelegate alFilteri;
+ private alFilterfDelegate alFilterf;
+ private alDeleteFiltersDelegate alDeleteFilters;
+
+ internal static IntPtr device;
+ static EffectsExtension _instance;
+ public static EffectsExtension Instance {
+ get {
+ if (_instance == null)
+ _instance = new EffectsExtension ();
+ return _instance;
+ }
+ }
+
+ public EffectsExtension ()
+ {
+ IsInitialized = false;
+ if (!Alc.IsExtensionPresent (device, "ALC_EXT_EFX")) {
+ return;
+ }
+
+ alGenEffects = (alGenEffectsDelegate)Marshal.GetDelegateForFunctionPointer (AL.GetProcAddress ("alGenEffects"), typeof (alGenEffectsDelegate));
+ alDeleteEffects = (alDeleteEffectsDelegate)Marshal.GetDelegateForFunctionPointer (AL.GetProcAddress ("alDeleteEffects"), typeof (alDeleteEffectsDelegate));
+ alEffectf = (alEffectfDelegate)Marshal.GetDelegateForFunctionPointer (AL.GetProcAddress ("alEffectf"), typeof (alEffectfDelegate));
+ alEffecti = (alEffectiDelegate)Marshal.GetDelegateForFunctionPointer (AL.GetProcAddress ("alEffecti"), typeof (alEffectiDelegate));
+ alGenAuxiliaryEffectSlots = (alGenAuxiliaryEffectSlotsDelegate)Marshal.GetDelegateForFunctionPointer (AL.GetProcAddress ("alGenAuxiliaryEffectSlots"), typeof (alGenAuxiliaryEffectSlotsDelegate));
+ alDeleteAuxiliaryEffectSlots = (alDeleteAuxiliaryEffectSlotsDelegate)Marshal.GetDelegateForFunctionPointer (AL.GetProcAddress ("alDeleteAuxiliaryEffectSlots"), typeof (alDeleteAuxiliaryEffectSlotsDelegate));
+ alAuxiliaryEffectSloti = (alAuxiliaryEffectSlotiDelegate)Marshal.GetDelegateForFunctionPointer (AL.GetProcAddress ("alAuxiliaryEffectSloti"), typeof (alAuxiliaryEffectSlotiDelegate));
+ alAuxiliaryEffectSlotf = (alAuxiliaryEffectSlotfDelegate)Marshal.GetDelegateForFunctionPointer (AL.GetProcAddress ("alAuxiliaryEffectSlotf"), typeof (alAuxiliaryEffectSlotfDelegate));
+
+ alGenFilters = (alGenFiltersDelegate)Marshal.GetDelegateForFunctionPointer (AL.GetProcAddress ("alGenFilters"), typeof (alGenFiltersDelegate));
+ alFilteri = (alFilteriDelegate)Marshal.GetDelegateForFunctionPointer (AL.GetProcAddress ("alFilteri"), typeof (alFilteriDelegate));
+ alFilterf = (alFilterfDelegate)Marshal.GetDelegateForFunctionPointer (AL.GetProcAddress ("alFilterf"), typeof (alFilterfDelegate));
+ alDeleteFilters = (alDeleteFiltersDelegate)Marshal.GetDelegateForFunctionPointer (AL.GetProcAddress ("alDeleteFilters"), typeof (alDeleteFiltersDelegate));
+
+ IsInitialized = true;
+ }
+
+ public bool IsInitialized { get; private set; }
+
+ /*
+
+
+alEffecti (effect, EfxEffecti.FilterType, (int)EfxEffectType.Reverb);
+ ALHelper.CheckError ("Failed to set Filter Type.");
+
+ */
+
+ public void GenAuxiliaryEffectSlots (int count, out uint slot)
+ {
+ this.alGenAuxiliaryEffectSlots (count, out slot);
+ ALHelper.CheckError ("Failed to Genereate Aux slot");
+ }
+
+ public void GenEffect (out uint effect)
+ {
+ this.alGenEffects (1, out effect);
+ ALHelper.CheckError ("Failed to Generate Effect.");
+ }
+
+ public void DeleteAuxiliaryEffectSlot (int slot)
+ {
+ alDeleteAuxiliaryEffectSlots (1, ref slot);
+ }
+
+ public void DeleteEffect (int effect)
+ {
+ alDeleteEffects (1, ref effect);
+ }
+
+ public void BindEffectToAuxiliarySlot (uint slot, uint effect)
+ {
+ alAuxiliaryEffectSloti (slot,EfxEffecti.SlotEffect, effect);
+ ALHelper.CheckError ("Failed to bind Effect");
+ }
+
+ public void AuxiliaryEffectSlot (uint slot, EfxEffectSlotf param, float value)
+ {
+ alAuxiliaryEffectSlotf (slot, param, value);
+ ALHelper.CheckError ("Failes to set " + param + " " + value);
+ }
+
+ public void BindSourceToAuxiliarySlot (int SounceId, int slot, int slotnumber, int filter)
+ {
+ AL.Source (SounceId, ALSourcei.EfxAuxilarySendFilter, slot, slotnumber, filter);
+ }
+
+ public void Effect (uint effect, EfxEffectf param, float value)
+ {
+ alEffectf (effect, param, value);
+ ALHelper.CheckError ("Failed to set " + param + " " + value);
+ }
+
+ public void Effect (uint effect, EfxEffecti param, int value)
+ {
+ alEffecti (effect, param, value);
+ ALHelper.CheckError ("Failed to set " + param + " " + value);
+ }
+
+ public unsafe int GenFilter() {
+ uint filter = 0;
+ this.alGenFilters (1, &filter);
+ return (int)filter;
+ }
+ public void Filter(int sourceId, EfxFilteri filter, int EfxFilterType) {
+ this.alFilteri ((uint)sourceId, filter, EfxFilterType);
+ }
+ public void Filter(int sourceId, EfxFilterf filter, float EfxFilterType) {
+ this.alFilterf ((uint)sourceId, filter, EfxFilterType);
+ }
+ public void BindFilterToSource(int sourceId, int filterId) {
+ AL.Source (sourceId, ALSourcei.EfxDirectFilter, filterId);
+ }
+ public unsafe void DeleteFilter (int filterId)
+ {
+ alDeleteFilters (1, (uint*)&filterId);
+ }
+ }
+}
+
diff --git a/MonoGame.Framework/Audio/OpenALSoundController.cs b/MonoGame.Framework/Audio/OpenALSoundController.cs
index dfcccd9fd91..a980464af0a 100644
--- a/MonoGame.Framework/Audio/OpenALSoundController.cs
+++ b/MonoGame.Framework/Audio/OpenALSoundController.cs
@@ -7,12 +7,22 @@
#if MONOMAC && PLATFORM_MACOS_LEGACY
using MonoMac.OpenAL;
-#else
+#endif
+#if MONOMAC && !PLATFORM_MACOS_LEGACY
using OpenTK.Audio.OpenAL;
using OpenTK.Audio;
+#endif
+
+#if GLES
+using OpenTK.Audio.OpenAL;
using OpenTK;
#endif
+#if DESKTOPGL
+using OpenAL;
+#endif
+using OpenGL;
+
#if ANDROID
using System.Globalization;
using Android.Content.PM;
@@ -47,10 +57,19 @@ public static void CheckError(string message = "", params object[] args)
internal sealed class OpenALSoundController : IDisposable
{
private static OpenALSoundController _instance = null;
+#if SUPPORTS_EFX
+ private static EffectsExtension _efx = null;
+#endif
private IntPtr _device;
- private ContextHandle _context;
- //int outputSource;
- //int[] buffers;
+#if !DESKTOPGL
+ ContextHandle _context;
+ ContextHandle NullContext = ContextHandle.Zero;
+#else
+ private IntPtr _context;
+ IntPtr NullContext = IntPtr.Zero;
+#endif
+ //int outputSource;
+ //int[] buffers;
private AlcError _lastOpenALError;
private int[] allSourcesArray;
#if DESKTOPGL || ANGLE
@@ -81,24 +100,25 @@ internal sealed class OpenALSoundController : IDisposable
private const int DEFAULT_UPDATE_SIZE = 512;
private const int DEFAULT_UPDATE_BUFFER_COUNT = 2;
#elif DESKTOPGL
- #pragma warning disable 414
- private static AudioContext _acontext;
- #pragma warning restore 414
private static OggStreamer _oggstreamer;
#endif
private List availableSourcesCollection;
private List inUseSourcesCollection;
- private List playingSourcesCollection;
- private List purgeMe;
private bool _bSoundAvailable = false;
private Exception _SoundInitException; // Here to bubble back up to the developer
bool _isDisposed;
+ public bool SupportsADPCM = false;
///
/// Sets up the hardware resources used by the controller.
///
private OpenALSoundController()
{
+#if WINDOWS
+ // On Windows, set the DLL search path for correct native binaries
+ NativeHelper.InitDllDirectory();
+#endif
+
if (!OpenSoundController())
{
return;
@@ -109,11 +129,14 @@ private OpenALSoundController()
allSourcesArray = new int[MAX_NUMBER_OF_SOURCES];
AL.GenSources(allSourcesArray);
ALHelper.CheckError("Failed to generate sources.");
-
+ Filter = 0;
+#if SUPPORTS_EFX
+ if (Efx.IsInitialized) {
+ Filter = Efx.GenFilter ();
+ }
+#endif
availableSourcesCollection = new List(allSourcesArray);
inUseSourcesCollection = new List();
- playingSourcesCollection = new List();
- purgeMe = new List();
}
~OpenALSoundController()
@@ -137,6 +160,9 @@ private bool OpenSoundController()
try
{
_device = Alc.OpenDevice(string.Empty);
+#if DESKTOPGL
+ EffectsExtension.device = _device;
+#endif
}
catch (Exception ex)
{
@@ -240,16 +266,13 @@ Now use OpenSL ES to create an AudioPlayer with PCM buffer queue data locator.
AVAudioSession.Notifications.ObserveInterruption(handler);
int[] attribute = new int[0];
-#elif !DESKTOPGL
+#else
int[] attribute = new int[0];
#endif
+ _context = Alc.CreateContext(_device, attribute);
#if DESKTOPGL
- _acontext = new AudioContext();
- _context = Alc.GetCurrentContext();
_oggstreamer = new OggStreamer();
-#else
- _context = Alc.CreateContext(_device, attribute);
#endif
if (CheckALError("Could not create AL context"))
@@ -258,7 +281,7 @@ Now use OpenSL ES to create an AudioPlayer with PCM buffer queue data locator.
return(false);
}
- if (_context != ContextHandle.Zero)
+ if (_context != NullContext)
{
Alc.MakeContextCurrent(_context);
if (CheckALError("Could not make AL context current"))
@@ -266,6 +289,7 @@ Now use OpenSL ES to create an AudioPlayer with PCM buffer queue data locator.
CleanUpOpenAL();
return(false);
}
+ SupportsADPCM = AL.IsExtensionPresent ("AL_SOFT_MSADPCM");
return (true);
}
}
@@ -279,6 +303,18 @@ public static OpenALSoundController GetInstance {
return _instance;
}
}
+#if SUPPORTS_EFX
+ public static EffectsExtension Efx {
+ get {
+ if (_efx == null)
+ _efx = new EffectsExtension ();
+ return _efx;
+ }
+ }
+#endif
+ public int Filter {
+ get; private set;
+ }
public static void DestroyInstance()
{
@@ -317,25 +353,19 @@ public bool CheckALError (string operation)
///
private void CleanUpOpenAL()
{
- Alc.MakeContextCurrent(ContextHandle.Zero);
-#if DESKTOPGL
- if (_acontext != null)
- {
- _acontext.Dispose();
- _acontext = null;
- }
-#else
- if (_context != ContextHandle.Zero)
+ Alc.MakeContextCurrent(NullContext);
+
+ if (_context != NullContext)
{
Alc.DestroyContext (_context);
- _context = ContextHandle.Zero;
+ _context = NullContext;
}
if (_device != IntPtr.Zero)
{
Alc.CloseDevice (_device);
_device = IntPtr.Zero;
}
-#endif
+
_bSoundAvailable = false;
}
@@ -369,7 +399,10 @@ void Dispose(bool disposing)
AL.DeleteSource(allSourcesArray[i]);
ALHelper.CheckError("Failed to delete source.");
}
-
+#if SUPPORTS_EFX
+ if (Filter != 0 && Efx.IsInitialized)
+ Efx.DeleteFilter (Filter);
+#endif
CleanUpOpenAL();
}
}
@@ -378,27 +411,31 @@ void Dispose(bool disposing)
}
///
- /// Reserves the given sound buffer. If there are no available sources then false is
- /// returned, otherwise true will be returned and the sound buffer can be played. If
- /// the controller was not able to setup the hardware, then false will be returned.
+ /// Reserves a sound buffer and return its identifier. If there are no available sources
+ /// or the controller was not able to setup the hardware then an
+ /// is thrown.
///
- /// The sound buffer you want to play
- /// True if the buffer can be played, and false if not.
+ /// The source number of the reserved sound buffer.
public int ReserveSource()
{
if (!CheckInitState())
{
throw new InstancePlayLimitException();
}
+
int sourceNumber;
- if (availableSourcesCollection.Count == 0)
- {
- throw new InstancePlayLimitException();
- }
-
- sourceNumber = availableSourcesCollection.First ();
- inUseSourcesCollection.Add(sourceNumber);
- availableSourcesCollection.Remove (sourceNumber);
+
+ lock (availableSourcesCollection)
+ {
+ if (availableSourcesCollection.Count == 0)
+ {
+ throw new InstancePlayLimitException();
+ }
+
+ sourceNumber = availableSourcesCollection.Last();
+ inUseSourcesCollection.Add(sourceNumber);
+ availableSourcesCollection.Remove(sourceNumber);
+ }
return sourceNumber;
}
@@ -409,29 +446,16 @@ public void RecycleSource(int sourceId)
{
return;
}
- inUseSourcesCollection.Remove(sourceId);
- availableSourcesCollection.Add(sourceId);
- }
- public void PlaySound(SoundEffectInstance inst)
- {
- if (!CheckInitState())
- {
- return;
- }
- lock (playingSourcesCollection)
+ lock (availableSourcesCollection)
{
- playingSourcesCollection.Add(inst.SourceId);
+ inUseSourcesCollection.Remove(sourceId);
+ availableSourcesCollection.Add(sourceId);
}
- AL.SourcePlay(inst.SourceId);
- ALHelper.CheckError("Failed to play source.");
}
public void FreeSource(SoundEffectInstance inst)
{
- lock (playingSourcesCollection) {
- playingSourcesCollection.Remove(inst.SourceId);
- }
RecycleSource(inst.SourceId);
inst.SourceId = 0;
inst.HasSourceId = false;
@@ -471,47 +495,6 @@ public double SourceCurrentPosition (int sourceId)
return pos;
}
- ///
- /// Called repeatedly, this method cleans up the state of the management lists. This method
- /// will also lock on the playingSourcesCollection. Sources that are stopped will be recycled
- /// using the RecycleSource method.
- ///
- public void Update()
- {
- if (!_bSoundAvailable)
- {
- //OK to ignore this here because the game can run without sound.
- return;
- }
-
- ALSourceState state;
- lock (playingSourcesCollection)
- {
- for (int i = playingSourcesCollection.Count - 1; i >= 0; --i)
- {
- int sourceId = playingSourcesCollection[i];
- state = AL.GetSourceState(sourceId);
- ALHelper.CheckError("Failed to get source state.");
- if (state == ALSourceState.Stopped)
- {
- purgeMe.Add(sourceId);
- playingSourcesCollection.RemoveAt(i);
- }
- }
- }
- lock (purgeMe)
- {
- foreach (int sourceId in purgeMe)
- {
- AL.Source(sourceId, ALSourcei.Buffer, 0);
- ALHelper.CheckError("Failed to free source from buffer.");
- inUseSourcesCollection.Remove(sourceId);
- availableSourcesCollection.Add(sourceId);
- }
- purgeMe.Clear();
- }
- }
-
#if ANDROID
const string Lib = "openal32.dll";
const CallingConvention Style = CallingConvention.Cdecl;
diff --git a/MonoGame.Framework/Audio/SoundEffect.OpenAL.cs b/MonoGame.Framework/Audio/SoundEffect.OpenAL.cs
index 3a0d6aea688..7660542c121 100644
--- a/MonoGame.Framework/Audio/SoundEffect.OpenAL.cs
+++ b/MonoGame.Framework/Audio/SoundEffect.OpenAL.cs
@@ -8,16 +8,16 @@
#if MONOMAC && PLATFORM_MACOS_LEGACY
using MonoMac.AudioToolbox;
using MonoMac.AudioUnit;
-using MonoMac.AVFoundation;
-using MonoMac.Foundation;
using MonoMac.OpenAL;
#elif OPENAL
+#if GLES || MONOMAC
using OpenTK.Audio.OpenAL;
+#else
+using OpenAL;
+#endif
#if IOS || MONOMAC
using AudioToolbox;
using AudioUnit;
-using AVFoundation;
-using Foundation;
#endif
#endif
@@ -26,8 +26,8 @@ namespace Microsoft.Xna.Framework.Audio
public sealed partial class SoundEffect : IDisposable
{
internal const int MAX_PLAYING_INSTANCES = OpenALSoundController.MAX_NUMBER_OF_SOURCES;
-
- internal byte[] _data;
+ internal static uint ReverbSlot = 0;
+ internal static uint ReverbEffect = 0;
internal OALSoundBuffer SoundBuffer;
@@ -39,8 +39,10 @@ public sealed partial class SoundEffect : IDisposable
#region Public Constructors
- private void PlatformLoadAudioStream(Stream s)
+ private void PlatformLoadAudioStream(Stream s, out TimeSpan duration)
{
+ byte[] buffer;
+
#if OPENAL && !(MONOMAC || IOS)
ALFormat format;
@@ -48,33 +50,20 @@ private void PlatformLoadAudioStream(Stream s)
int freq;
var stream = s;
-#if ANDROID
- var needsDispose = false;
- try
- {
- // If seek is not supported (usually an indicator of a stream opened into the AssetManager), then copy
- // into a temporary MemoryStream.
- if (!s.CanSeek)
- {
- needsDispose = true;
- stream = new MemoryStream();
- s.CopyTo(stream);
- stream.Position = 0;
- }
-#endif
- _data = AudioLoader.Load(stream, out format, out size, out freq);
-#if ANDROID
- }
- finally
- {
- if (needsDispose)
- stream.Dispose();
- }
-#endif
+
+ buffer = AudioLoader.Load(stream, out format, out size, out freq);
+
Format = format;
Size = size;
Rate = freq;
+ var bytesPerSecond = freq;
+ if (format == ALFormat.Mono16 || format == ALFormat.Stereo8)
+ bytesPerSecond *= 2;
+ else if (format == ALFormat.Stereo16)
+ bytesPerSecond *= 4;
+
+ duration = TimeSpan.FromSeconds((float) size / bytesPerSecond);
#endif
#if MONOMAC || IOS
@@ -87,34 +76,16 @@ private void PlatformLoadAudioStream(Stream s)
afs.ParseBytes (audiodata, false);
Size = (int)afs.DataByteCount;
- _data = new byte[afs.DataByteCount];
- Array.Copy (audiodata, afs.DataOffset, _data, 0, afs.DataByteCount);
+ buffer = new byte[afs.DataByteCount];
+ Array.Copy (audiodata, afs.DataOffset, buffer, 0, afs.DataByteCount);
AudioStreamBasicDescription asbd = afs.DataFormat;
int channelsPerFrame = asbd.ChannelsPerFrame;
int bitsPerChannel = asbd.BitsPerChannel;
- // There is a random chance that properties asbd.ChannelsPerFrame and asbd.BitsPerChannel are invalid because of a bug in Xamarin.iOS
- // See: https://bugzilla.xamarin.com/show_bug.cgi?id=11074 (Failed to get buffer attributes error when playing sounds)
- if (channelsPerFrame <= 0 || bitsPerChannel <= 0)
- {
- NSError err;
- using (NSData nsData = NSData.FromArray(audiodata))
- using (AVAudioPlayer player = AVAudioPlayer.FromData(nsData, out err))
- {
- channelsPerFrame = (int)player.NumberOfChannels;
- bitsPerChannel = player.SoundSetting.LinearPcmBitDepth.GetValueOrDefault(16);
-
- Rate = (float)player.SoundSetting.SampleRate;
- _duration = TimeSpan.FromSeconds(player.Duration);
- }
- }
- else
- {
- Rate = (float)asbd.SampleRate;
- double duration = (Size / ((bitsPerChannel / 8) * channelsPerFrame)) / asbd.SampleRate;
- _duration = TimeSpan.FromSeconds(duration);
- }
+ Rate = (float)asbd.SampleRate;
+ double durationSec = (Size / ((bitsPerChannel / 8) * channelsPerFrame)) / asbd.SampleRate;
+ duration = TimeSpan.FromSeconds(durationSec);
if (channelsPerFrame == 1)
Format = (bitsPerChannel == 8) ? ALFormat.Mono8 : ALFormat.Mono16;
@@ -123,44 +94,76 @@ private void PlatformLoadAudioStream(Stream s)
}
#endif
+ // bind buffer
+ SoundBuffer = new OALSoundBuffer();
+ SoundBuffer.BindDataBuffer(buffer, Format, Size, (int)Rate);
}
- private void PlatformInitialize(byte[] buffer, int sampleRate, AudioChannels channels)
+ private void PlatformInitializePcm(byte[] buffer, int offset, int count, int sampleRate, AudioChannels channels, int loopStart, int loopLength)
{
- Rate = (float)sampleRate;
- Size = (int)buffer.Length;
-
-#if OPENAL && !(MONOMAC || IOS)
+ Rate = (float)sampleRate;
+ Size = (int)count;
+ Format = channels == AudioChannels.Stereo ? ALFormat.Stereo16 : ALFormat.Mono16;
- _data = buffer;
- Format = (channels == AudioChannels.Stereo) ? ALFormat.Stereo16 : ALFormat.Mono16;
-
-#endif
+ // bind buffer
+ SoundBuffer = new OALSoundBuffer();
+ SoundBuffer.BindDataBuffer(buffer, Format, Size, (int)Rate);
+ }
-#if MONOMAC || IOS
+ private void PlatformInitializeAdpcm (byte [] buffer, int offset, int count, int sampleRate, AudioChannels channels, int dataFormat, int loopStart, int loopLength)
+ {
+ Rate = (float)sampleRate;
+ Size = (int)count;
+ #if DESKTOPGL
+ Format = channels == AudioChannels.Stereo ? ALFormat.StereoMSADPCM : ALFormat.MonoMSADPCM;
+ #else
+ Format = channels == AudioChannels.Stereo ? (ALFormat)0x1303 : (ALFormat)0x1302;
+ #endif
- //buffer should contain 16-bit PCM wave data
- short bitsPerSample = 16;
+ // bind buffer
+ SoundBuffer = new OALSoundBuffer ();
+ SoundBuffer.BindDataBuffer (buffer, Format, Size, (int)Rate, dataFormat);
+ }
- if ((int)channels <= 1)
- Format = bitsPerSample == 8 ? ALFormat.Mono8 : ALFormat.Mono16;
- else
- Format = bitsPerSample == 8 ? ALFormat.Stereo8 : ALFormat.Stereo16;
+ private void PlatformInitializeFormat(byte[] header, byte[] buffer, int bufferSize, int loopStart, int loopLength)
+ {
+ var format = BitConverter.ToInt16(header, 0);
+ var channels = BitConverter.ToInt16(header, 2);
+ var sampleRate = BitConverter.ToInt32(header, 4);
+ var blockAlignment = BitConverter.ToInt16(header, 12);
+
+ // We need to decode MSADPCM.
+ var supportsADPCM = OpenALSoundController.GetInstance.SupportsADPCM;
+ if (format == 2 && !supportsADPCM)
+ {
+ using (var stream = new MemoryStream(buffer))
+ using (var reader = new BinaryReader(stream))
+ {
+ buffer = MSADPCMToPCM.MSADPCM_TO_PCM (reader, (short)channels, (short)blockAlignment);
+ format = 1;
+ }
+ }
- _name = "";
- _data = buffer;
+ if (!supportsADPCM && format != 1)
+ throw new NotSupportedException("Unsupported wave format!");
-#endif
- // bind buffer
- SoundBuffer = new OALSoundBuffer();
- SoundBuffer.BindDataBuffer(_data, Format, Size, (int)Rate);
+ if (supportsADPCM && format == 2) {
+ PlatformInitializeAdpcm(buffer, 0, buffer.Length, sampleRate, (AudioChannels)channels, blockAlignment, loopStart, loopLength);
+ } else {
+ PlatformInitializePcm (buffer, 0, buffer.Length, sampleRate, (AudioChannels)channels, loopStart, loopLength);
+ }
}
- private void PlatformInitialize(byte[] buffer, int offset, int count, int sampleRate, AudioChannels channels, int loopStart, int loopLength)
+ private void PlatformInitializeXact(MiniFormatTag codec, byte[] buffer, int channels, int sampleRate, int blockAlignment, int loopStart, int loopLength, out TimeSpan duration)
{
- _duration = GetSampleDuration(buffer.Length, sampleRate, channels);
+ if (codec == MiniFormatTag.Adpcm)
+ {
+ PlatformInitializeAdpcm(buffer, 0, buffer.Length, sampleRate, (AudioChannels)channels, (blockAlignment + 16) * 2, loopStart, loopLength);
+ duration = TimeSpan.FromSeconds(SoundBuffer.Duration);
+ return;
+ }
- throw new NotImplementedException();
+ throw new NotSupportedException("Unsupported sound format!");
}
#endregion
@@ -174,7 +177,53 @@ private void PlatformSetupInstance(SoundEffectInstance inst)
#endregion
- #region IDisposable Members
+ internal static void PlatformSetReverbSettings(ReverbSettings reverbSettings)
+ {
+#if SUPPORTS_EFX
+ if (!OpenALSoundController.Efx.IsInitialized)
+ return;
+
+ if (ReverbEffect != 0)
+ return;
+
+ var efx = OpenALSoundController.Efx;
+ efx.GenAuxiliaryEffectSlots (1, out ReverbSlot);
+ efx.GenEffect (out ReverbEffect);
+ efx.Effect (ReverbEffect, EfxEffecti.EffectType, (int)EfxEffectType.Reverb);
+ efx.Effect (ReverbEffect, EfxEffectf.EaxReverbReflectionsDelay, reverbSettings.ReflectionsDelayMs / 1000.0f);
+ efx.Effect (ReverbEffect, EfxEffectf.LateReverbDelay, reverbSettings.ReverbDelayMs / 1000.0f);
+ // map these from range 0-15 to 0-1
+ efx.Effect (ReverbEffect, EfxEffectf.EaxReverbDiffusion, reverbSettings.EarlyDiffusion / 15f);
+ efx.Effect (ReverbEffect, EfxEffectf.EaxReverbDiffusion, reverbSettings.LateDiffusion / 15f);
+ efx.Effect (ReverbEffect, EfxEffectf.EaxReverbGainLF, Math.Min (XactHelpers.ParseVolumeFromDecibels (reverbSettings.LowEqGain - 8f), 1.0f));
+ efx.Effect (ReverbEffect, EfxEffectf.EaxReverbLFReference, (reverbSettings.LowEqCutoff * 50f) + 50f);
+ efx.Effect (ReverbEffect, EfxEffectf.EaxReverbGainHF, XactHelpers.ParseVolumeFromDecibels (reverbSettings.HighEqGain - 8f));
+ efx.Effect (ReverbEffect, EfxEffectf.EaxReverbHFReference, (reverbSettings.HighEqCutoff * 500f) + 1000f);
+ // According to Xamarin docs EaxReverbReflectionsGain Unit: Linear gain Range [0.0f .. 3.16f] Default: 0.05f
+ efx.Effect (ReverbEffect, EfxEffectf.EaxReverbReflectionsGain, Math.Min (XactHelpers.ParseVolumeFromDecibels (reverbSettings.ReflectionsGainDb), 3.16f));
+ efx.Effect (ReverbEffect, EfxEffectf.EaxReverbGain, Math.Min (XactHelpers.ParseVolumeFromDecibels (reverbSettings.ReverbGainDb), 1.0f));
+ // map these from 0-100 down to 0-1
+ efx.Effect (ReverbEffect, EfxEffectf.EaxReverbDensity, reverbSettings.DensityPct / 100f);
+ efx.AuxiliaryEffectSlot (ReverbSlot, EfxEffectSlotf.EffectSlotGain, reverbSettings.WetDryMixPct / 200f);
+
+ // Dont know what to do with these EFX has no mapping for them. Just ignore for now
+ // we can enable them as we go.
+ //efx.SetEffectParam (ReverbEffect, EfxEffectf.PositionLeft, reverbSettings.PositionLeft);
+ //efx.SetEffectParam (ReverbEffect, EfxEffectf.PositionRight, reverbSettings.PositionRight);
+ //efx.SetEffectParam (ReverbEffect, EfxEffectf.PositionLeftMatrix, reverbSettings.PositionLeftMatrix);
+ //efx.SetEffectParam (ReverbEffect, EfxEffectf.PositionRightMatrix, reverbSettings.PositionRightMatrix);
+ //efx.SetEffectParam (ReverbEffect, EfxEffectf.LowFrequencyReference, reverbSettings.RearDelayMs);
+ //efx.SetEffectParam (ReverbEffect, EfxEffectf.LowFrequencyReference, reverbSettings.RoomFilterFrequencyHz);
+ //efx.SetEffectParam (ReverbEffect, EfxEffectf.LowFrequencyReference, reverbSettings.RoomFilterMainDb);
+ //efx.SetEffectParam (ReverbEffect, EfxEffectf.LowFrequencyReference, reverbSettings.RoomFilterHighFrequencyDb);
+ //efx.SetEffectParam (ReverbEffect, EfxEffectf.LowFrequencyReference, reverbSettings.DecayTimeSec);
+ //efx.SetEffectParam (ReverbEffect, EfxEffectf.LowFrequencyReference, reverbSettings.RoomSizeFeet);
+
+ efx.BindEffectToAuxiliarySlot (ReverbSlot, ReverbEffect);
+#endif
+ }
+
+#region IDisposable Members
private void PlatformDispose(bool disposing)
{
@@ -185,10 +234,29 @@ private void PlatformDispose(bool disposing)
}
}
- #endregion
+#endregion
+
+ internal static void InitializeSoundEffect()
+ {
+ try
+ {
+ // Getting the instance for the first time initializes OpenAL
+ var oal = OpenALSoundController.GetInstance;
+ }
+ catch (DllNotFoundException ex)
+ {
+ throw new NoAudioHardwareException("Failed to init OpenALSoundController", ex);
+ }
+ }
internal static void PlatformShutdown()
{
+#if SUPPORTS_EFX
+ if (ReverbEffect != 0) {
+ OpenALSoundController.Efx.DeleteAuxiliaryEffectSlot ((int)ReverbSlot);
+ OpenALSoundController.Efx.DeleteEffect ((int)ReverbEffect);
+ }
+#endif
OpenALSoundController.DestroyInstance();
}
}
diff --git a/MonoGame.Framework/Audio/SoundEffect.Web.cs b/MonoGame.Framework/Audio/SoundEffect.Web.cs
index 8b971b6c45f..aec6ae999e4 100644
--- a/MonoGame.Framework/Audio/SoundEffect.Web.cs
+++ b/MonoGame.Framework/Audio/SoundEffect.Web.cs
@@ -17,18 +17,24 @@ public sealed partial class SoundEffect : IDisposable
// This platform is only limited by memory.
internal const int MAX_PLAYING_INSTANCES = int.MaxValue;
- private void PlatformLoadAudioStream(Stream s)
+ private void PlatformLoadAudioStream(Stream s, out TimeSpan duration)
{
+ duration = TimeSpan.Zero;
}
- private void PlatformInitialize(byte[] buffer, int sampleRate, AudioChannels channels)
+ private void PlatformInitializePcm(byte[] buffer, int offset, int count, int sampleRate, AudioChannels channels, int loopStart, int loopLength)
{
}
-
- private void PlatformInitialize(byte[] buffer, int offset, int count, int sampleRate, AudioChannels channels, int loopStart, int loopLength)
+
+ private void PlatformInitializeFormat(byte[] header, byte[] buffer, int bufferSize, int loopStart, int loopLength)
+ {
+ }
+
+ private void PlatformInitializeXact(MiniFormatTag codec, byte[] buffer, int channels, int sampleRate, int blockAlignment, int loopStart, int loopLength, out TimeSpan duration)
{
+ throw new NotSupportedException("Unsupported sound format!");
}
-
+
private void PlatformSetupInstance(SoundEffectInstance instance)
{
}
@@ -37,6 +43,14 @@ private void PlatformDispose(bool disposing)
{
}
+ internal static void PlatformSetReverbSettings(ReverbSettings reverbSettings)
+ {
+ }
+
+ internal static void InitializeSoundEffect()
+ {
+ }
+
internal static void PlatformShutdown()
{
}
diff --git a/MonoGame.Framework/Audio/SoundEffect.XAudio.cs b/MonoGame.Framework/Audio/SoundEffect.XAudio.cs
index 26d16f2051d..c2a6389b754 100644
--- a/MonoGame.Framework/Audio/SoundEffect.XAudio.cs
+++ b/MonoGame.Framework/Audio/SoundEffect.XAudio.cs
@@ -12,7 +12,7 @@
namespace Microsoft.Xna.Framework.Audio
{
- public sealed partial class SoundEffect : IDisposable
+ partial class SoundEffect
{
#if WINDOWS || (WINRT && !WINDOWS_PHONE)
@@ -65,6 +65,29 @@ internal static X3DAudio Device3D
}
}
+
+ private static SubmixVoice _reverbVoice;
+
+ internal static SubmixVoice ReverbVoice
+ {
+ get
+ {
+ if (_reverbVoice == null)
+ {
+ var details = MasterVoice.VoiceDetails;
+ _reverbVoice = new SubmixVoice(Device, details.InputChannelCount, details.InputSampleRate);
+
+ var reverb = new SharpDX.XAudio2.Fx.Reverb();
+ var desc = new EffectDescriptor(reverb);
+ desc.InitialState = true;
+ desc.OutputChannelCount = details.InputChannelCount;
+ _reverbVoice.SetEffectChain(desc);
+ }
+
+ return _reverbVoice;
+ }
+ }
+
#endregion
internal DataStream _dataStream;
@@ -74,11 +97,9 @@ internal static X3DAudio Device3D
#region Initialization
- static SoundEffect()
- {
- InitializeSoundEffect();
- }
-
+ ///
+ /// Initializes XAudio.
+ ///
internal static void InitializeSoundEffect()
{
try
@@ -135,31 +156,74 @@ internal static void InitializeSoundEffect()
}
}
- private void PlatformInitialize(byte[] buffer, int sampleRate, AudioChannels channels)
+ private static DataStream ToDataStream(int offset, byte[] buffer, int length)
{
- CreateBuffers( new WaveFormat(sampleRate, (int)channels),
- DataStream.Create(buffer, true, false),
- 0,
- buffer.Length);
+ // NOTE: We make a copy here because old versions of
+ // DataStream.Create didn't work correctly for offsets.
+ var data = new byte[length - offset];
+ Buffer.BlockCopy(buffer, offset, data, 0, length - offset);
+
+ return DataStream.Create(data, true, false);
}
- private void PlatformInitialize(byte[] buffer, int offset, int count, int sampleRate, AudioChannels channels, int loopStart, int loopLength)
+ private void PlatformInitializePcm(byte[] buffer, int offset, int count, int sampleRate, AudioChannels channels, int loopStart, int loopLength)
{
CreateBuffers( new WaveFormat(sampleRate, (int)channels),
- DataStream.Create(buffer, true, false, offset),
+ ToDataStream(offset, buffer, count),
loopStart,
loopLength);
}
- private void PlatformLoadAudioStream(Stream s)
+ private void PlatformInitializeFormat(byte[] header, byte[] buffer, int bufferSize, int loopStart, int loopLength)
+ {
+ var format = BitConverter.ToInt16(header, 0);
+ var channels = BitConverter.ToInt16(header, 2);
+ var sampleRate = BitConverter.ToInt32(header, 4);
+ var blockAlignment = BitConverter.ToInt16(header, 12);
+
+ WaveFormat waveFormat;
+ if (format == 1)
+ waveFormat = new WaveFormat(sampleRate, channels);
+ else if (format == 2)
+ waveFormat = new WaveFormatAdpcm(sampleRate, channels, blockAlignment);
+ else
+ throw new NotSupportedException("Unsupported wave format!");
+
+ CreateBuffers( waveFormat,
+ ToDataStream(0, buffer, bufferSize),
+ loopStart,
+ loopLength);
+ }
+
+ private void PlatformInitializeXact(MiniFormatTag codec, byte[] buffer, int channels, int sampleRate, int blockAlignment, int loopStart, int loopLength, out TimeSpan duration)
+ {
+ if (codec == MiniFormatTag.Adpcm)
+ {
+ duration = TimeSpan.FromSeconds((float)loopLength / sampleRate);
+
+ CreateBuffers( new WaveFormatAdpcm(sampleRate, channels, blockAlignment),
+ ToDataStream(0, buffer, buffer.Length),
+ loopStart,
+ loopLength);
+
+ return;
+ }
+
+ throw new NotSupportedException("Unsupported sound format!");
+ }
+
+ private void PlatformLoadAudioStream(Stream s, out TimeSpan duration)
{
var soundStream = new SoundStream(s);
+ if (soundStream.Format.Encoding != WaveFormatEncoding.Pcm)
+ throw new ArgumentException("Ensure that the specified stream contains valid PCM mono or stereo wave data.");
+
var dataStream = soundStream.ToDataStream();
- var sampleLength = (int)(dataStream.Length / ((soundStream.Format.Channels * soundStream.Format.BitsPerSample) / 8));
- CreateBuffers( soundStream.Format,
- dataStream,
- 0,
- sampleLength);
+ var sampleCount = (int)(dataStream.Length / ((soundStream.Format.Channels * soundStream.Format.BitsPerSample) / 8));
+
+ CreateBuffers(soundStream.Format, dataStream, 0, sampleCount);
+
+ duration = TimeSpan.FromSeconds((float)sampleCount / soundStream.Format.SampleRate);
}
private void CreateBuffers(WaveFormat format, DataStream dataStream, int loopStart, int loopLength)
@@ -218,14 +282,52 @@ private void PlatformSetupInstance(SoundEffectInstance inst)
}
if (voice == null && Device != null)
- voice = new SourceVoice(Device, _format, VoiceFlags.None, XAudio2.MaximumFrequencyRatio);
+ {
+ voice = new SourceVoice(Device, _format, VoiceFlags.UseFilter, XAudio2.MaximumFrequencyRatio);
+ inst._voice = voice;
+ inst.UpdateOutputMatrix(); // Ensure the output matrix is set for this new voice
+ }
- inst._voice = voice;
inst._format = _format;
}
#endregion
+ internal static void PlatformSetReverbSettings(ReverbSettings reverbSettings)
+ {
+ // All parameters related to sampling rate or time are relative to a 48kHz
+ // voice and must be scaled for use with other sampling rates.
+ var timeScale = 48000.0f / ReverbVoice.VoiceDetails.InputSampleRate;
+
+ var settings = new SharpDX.XAudio2.Fx.ReverbParameters
+ {
+ ReflectionsGain = reverbSettings.ReflectionsGainDb,
+ ReverbGain = reverbSettings.ReverbGainDb,
+ DecayTime = reverbSettings.DecayTimeSec,
+ ReflectionsDelay = (byte)(reverbSettings.ReflectionsDelayMs * timeScale),
+ ReverbDelay = (byte)(reverbSettings.ReverbDelayMs * timeScale),
+ RearDelay = (byte)(reverbSettings.RearDelayMs * timeScale),
+ RoomSize = reverbSettings.RoomSizeFeet,
+ Density = reverbSettings.DensityPct,
+ LowEQGain = (byte)reverbSettings.LowEqGain,
+ LowEQCutoff = (byte)reverbSettings.LowEqCutoff,
+ HighEQGain = (byte)reverbSettings.HighEqGain,
+ HighEQCutoff = (byte)reverbSettings.HighEqCutoff,
+ PositionLeft = (byte)reverbSettings.PositionLeft,
+ PositionRight = (byte)reverbSettings.PositionRight,
+ PositionMatrixLeft = (byte)reverbSettings.PositionLeftMatrix,
+ PositionMatrixRight = (byte)reverbSettings.PositionRightMatrix,
+ EarlyDiffusion = (byte)reverbSettings.EarlyDiffusion,
+ LateDiffusion = (byte)reverbSettings.LateDiffusion,
+ RoomFilterMain = reverbSettings.RoomFilterMainDb,
+ RoomFilterFreq = reverbSettings.RoomFilterFrequencyHz * timeScale,
+ RoomFilterHF = reverbSettings.RoomFilterHighFrequencyDb,
+ WetDryMix = reverbSettings.WetDryMixPct
+ };
+
+ ReverbVoice.SetEffectParameters(0, settings);
+ }
+
private void PlatformDispose(bool disposing)
{
if (disposing)
@@ -238,11 +340,15 @@ private void PlatformDispose(bool disposing)
internal static void PlatformShutdown()
{
- SoundEffectInstancePool.Shutdown();
+ if (_reverbVoice != null)
+ {
+ _reverbVoice.DestroyVoice();
+ _reverbVoice.Dispose();
+ _reverbVoice = null;
+ }
if (MasterVoice != null)
{
- MasterVoice.DestroyVoice();
MasterVoice.Dispose();
MasterVoice = null;
}
diff --git a/MonoGame.Framework/Audio/SoundEffect.cs b/MonoGame.Framework/Audio/SoundEffect.cs
index cb89c484e19..531399ed730 100644
--- a/MonoGame.Framework/Audio/SoundEffect.cs
+++ b/MonoGame.Framework/Audio/SoundEffect.cs
@@ -9,7 +9,7 @@ namespace Microsoft.Xna.Framework.Audio
{
/// Represents a loaded sound resource.
///
- /// A SoundEffect represents the buffer used to hold audio data and metadata. SoundEffectInstances are used to play from SoundEffects. Multiple SoundEffectinstance objects can be created and played from the same SoundEffect object.
+ /// A SoundEffect represents the buffer used to hold audio data and metadata. SoundEffectInstances are used to play from SoundEffects. Multiple SoundEffectInstance objects can be created and played from the same SoundEffect object.
/// The only limit on the number of loaded SoundEffects is restricted by available memory. When a SoundEffect is disposed, all SoundEffectInstances created from it will become invalid.
/// SoundEffect.Play() can be used for 'fire and forget' sounds. If advanced playback controls like volume or pitch is required, use SoundEffect.CreateInstance().
///
@@ -17,44 +17,132 @@ public sealed partial class SoundEffect : IDisposable
{
#region Internal Audio Data
- private string _name;
+ private string _name = string.Empty;
private bool _isDisposed = false;
- private TimeSpan _duration = TimeSpan.Zero;
+ private readonly TimeSpan _duration;
#endregion
#region Internal Constructors
- internal SoundEffect() { }
+ // Only used from SoundEffect.FromStream.
+ private SoundEffect(Stream stream)
+ {
+ /*
+ The Stream object must point to the head of a valid PCM wave file. Also, this wave file must be in the RIFF bitstream format.
+ The audio format has the following restrictions:
+ Must be a PCM wave file
+ Can only be mono or stereo
+ Must be 8 or 16 bit
+ Sample rate must be between 8,000 Hz and 48,000 Hz
+ */
+
+ PlatformLoadAudioStream(stream, out _duration);
+ }
+
+ // Only used from SoundEffectReader.
+ internal SoundEffect(byte[] header, byte[] buffer, int bufferSize, int durationMs, int loopStart, int loopLength)
+ {
+ _duration = TimeSpan.FromMilliseconds(durationMs);
+
+ // Peek at the format... handle regular PCM data.
+ var format = BitConverter.ToInt16(header, 0);
+ if (format == 1)
+ {
+ var channels = BitConverter.ToInt16(header, 2);
+ var sampleRate = BitConverter.ToInt32(header, 4);
+ PlatformInitializePcm(buffer, 0, bufferSize, sampleRate, (AudioChannels)channels, loopStart, loopLength);
+ return;
+ }
+
+ // Everything else is platform specific.
+ PlatformInitializeFormat(header, buffer, bufferSize, loopStart, loopLength);
+ }
+
+ // Only used from XACT WaveBank.
+ internal SoundEffect(MiniFormatTag codec, byte[] buffer, int channels, int sampleRate, int blockAlignment, int loopStart, int loopLength)
+ {
+ // Handle the common case... the rest is platform specific.
+ if (codec == MiniFormatTag.Pcm)
+ {
+ _duration = TimeSpan.FromSeconds((float)buffer.Length / (sampleRate * blockAlignment));
+ PlatformInitializePcm(buffer, 0, buffer.Length, sampleRate, (AudioChannels)channels, loopStart, loopLength);
+ return;
+ }
+
+ PlatformInitializeXact(codec, buffer, channels, sampleRate, blockAlignment, loopStart, loopLength, out _duration);
+ }
#endregion
#region Public Constructors
- /// Buffer containing PCM wave data.
- /// Sample rate, in Hertz (Hz)
- /// Number of channels (mono or stereo).
+ ///
+ /// Create a sound effect.
+ ///
+ /// The buffer with the sound data.
+ /// The sound data sample rate in hertz.
+ /// The number of channels in the sound data.
+ /// This only supports uncompressed 16bit PCM wav data.
public SoundEffect(byte[] buffer, int sampleRate, AudioChannels channels)
+ : this(buffer, 0, buffer != null ? buffer.Length : 0, sampleRate, channels, 0, 0)
{
- _duration = GetSampleDuration(buffer.Length, sampleRate, channels);
-
- PlatformInitialize(buffer, sampleRate, channels);
}
- /// Buffer containing PCM wave data.
- /// Offset, in bytes, to the starting position of the audio data.
- /// Amount, in bytes, of audio data.
- /// Sample rate, in Hertz (Hz)
- /// Number of channels (mono or stereo).
- /// The position, in samples, where the audio should begin looping.
- /// The duration, in samples, that audio should loop over.
- /// Use SoundEffect.GetSampleDuration() to convert time to samples.
+ ///
+ /// Create a sound effect.
+ ///
+ /// The buffer with the sound data.
+ /// The offset to the start of the sound data in bytes.
+ /// The length of the sound data in bytes.
+ /// The sound data sample rate in hertz.
+ /// The number of channels in the sound data.
+ /// The position where the sound should begin looping in samples.
+ /// The duration of the sound data loop in samples.
+ /// This only supports uncompressed 16bit PCM wav data.
public SoundEffect(byte[] buffer, int offset, int count, int sampleRate, AudioChannels channels, int loopStart, int loopLength)
{
+ if (sampleRate < 8000 || sampleRate > 48000)
+ throw new ArgumentOutOfRangeException("sampleRate");
+ if ((int)channels != 1 && (int)channels != 2)
+ throw new ArgumentOutOfRangeException("channels");
+
+ if (buffer == null || buffer.Length == 0)
+ throw new ArgumentException("Ensure that the buffer length is non-zero.", "buffer");
+
+ var blockAlign = (int)channels * 2;
+ if ((buffer.Length % blockAlign) != 0)
+ throw new ArgumentException("Ensure that the buffer meets the block alignment requirements for the number of channels.", "buffer");
+
+ if (count <= 0)
+ throw new ArgumentException("Ensure that the count is greater than zero.", "count");
+ if ((count % blockAlign) != 0)
+ throw new ArgumentException("Ensure that the count meets the block alignment requirements for the number of channels.", "count");
+
+ if (offset < 0)
+ throw new ArgumentException("The offset cannot be negative.", "offset");
+ if (((ulong)count + (ulong)offset) > (ulong)buffer.Length)
+ throw new ArgumentException("Ensure that the offset+count region lines within the buffer.", "offset");
+
+ var totalSamples = buffer.Length / blockAlign;
+
+ if (loopStart < 0)
+ throw new ArgumentException("The loopStart cannot be negative.", "loopStart");
+ if (loopStart > totalSamples)
+ throw new ArgumentException("The loopStart cannot be greater than the total number of samples.", "loopStart");
+
+ if (loopLength == 0)
+ loopLength = totalSamples - loopStart;
+
+ if (loopLength < 0)
+ throw new ArgumentException("The loopLength cannot be negative.", "loopLength");
+ if (((ulong)loopStart + (ulong)loopLength) > (ulong)totalSamples)
+ throw new ArgumentException("Ensure that the loopStart+loopLength region lies within the sample range.", "loopLength");
+
_duration = GetSampleDuration(count, sampleRate, channels);
- PlatformInitialize(buffer, offset, count, sampleRate, channels, loopStart, loopLength);
+ PlatformInitializePcm(buffer, offset, count, sampleRate, channels, loopStart, loopLength);
}
#endregion
@@ -91,46 +179,42 @@ public SoundEffectInstance CreateInstance()
}
///
- /// Creates a SoundEffect object based on the specified data stream.
+ /// Creates a new SoundEffect object based on the specified data stream.
///
- /// Stream object containing PCM wave data.
+ /// A stream containing the PCM wave data.
/// A new SoundEffect object.
- /// The Stream object must point to the head of a valid PCM wave file. Also, this wave file must be in the RIFF bitstream format.
- public static SoundEffect FromStream(Stream s)
+ /// The stream must point to the head of a valid PCM wave file in the RIFF bitstream format.
+ public static SoundEffect FromStream(Stream stream)
{
- if (s == null)
- throw new ArgumentNullException();
-
- // Notes from the docs:
+ if (stream == null)
+ throw new ArgumentNullException("stream");
- /*The Stream object must point to the head of a valid PCM wave file. Also, this wave file must be in the RIFF bitstream format.
- The audio format has the following restrictions:
- Must be a PCM wave file
- Can only be mono or stereo
- Must be 8 or 16 bit
- Sample rate must be between 8,000 Hz and 48,000 Hz*/
-
- var sfx = new SoundEffect();
-
- sfx.PlatformLoadAudioStream(s);
-
- return sfx;
+ return new SoundEffect(stream);
}
///
- /// Gets the TimeSpan representation of a single sample.
+ /// Returns the duration for 16bit PCM audio.
///
- /// Size, in bytes, of audio data.
+ /// The length of the audio data in bytes.
/// Sample rate, in Hertz (Hz). Must be between 8000 Hz and 48000 Hz
/// Number of channels in the audio data.
- /// TimeSpan object that represents the calculated sample duration.
+ /// The duration of the audio data.
public static TimeSpan GetSampleDuration(int sizeInBytes, int sampleRate, AudioChannels channels)
{
+ if (sizeInBytes < 0)
+ throw new ArgumentException("Buffer size cannot be negative.", "sizeInBytes");
if (sampleRate < 8000 || sampleRate > 48000)
- throw new ArgumentOutOfRangeException();
+ throw new ArgumentOutOfRangeException("sampleRate");
- // Reference: http://social.msdn.microsoft.com/Forums/windows/en-US/5a92be69-3b4e-4d92-b1d2-141ef0a50c91/how-to-calculate-duration-of-wave-file-from-its-size?forum=winforms
var numChannels = (int)channels;
+ if (numChannels != 1 && numChannels != 2)
+ throw new ArgumentOutOfRangeException("channels");
+
+ if (sizeInBytes == 0)
+ return TimeSpan.Zero;
+
+ // Reference
+ // http://tinyurl.com/hq9slfy
var dur = sizeInBytes / (sampleRate * numChannels * 16f / 8f);
@@ -140,20 +224,25 @@ public static TimeSpan GetSampleDuration(int sizeInBytes, int sampleRate, AudioC
}
///
- /// Gets the size of a sample from a TimeSpan.
+ /// Returns the data size in bytes for 16bit PCM audio.
///
- /// TimeSpan object that contains the sample duration.
+ /// The total duration of the audio data.
/// Sample rate, in Hertz (Hz), of audio data. Must be between 8,000 and 48,000 Hz.
/// Number of channels in the audio data.
- /// Size of a single sample of audio data.
+ /// The size in bytes of a single sample of audio data.
public static int GetSampleSizeInBytes(TimeSpan duration, int sampleRate, AudioChannels channels)
{
+ if (duration < TimeSpan.Zero || duration > TimeSpan.FromMilliseconds(0x7FFFFFF))
+ throw new ArgumentOutOfRangeException("duration");
if (sampleRate < 8000 || sampleRate > 48000)
- throw new ArgumentOutOfRangeException();
-
- // Reference: http://social.msdn.microsoft.com/Forums/windows/en-US/5a92be69-3b4e-4d92-b1d2-141ef0a50c91/how-to-calculate-duration-of-wave-file-from-its-size?forum=winforms
+ throw new ArgumentOutOfRangeException("sampleRate");
var numChannels = (int)channels;
+ if (numChannels != 1 && numChannels != 2)
+ throw new ArgumentOutOfRangeException("channels");
+
+ // Reference
+ // http://tinyurl.com/hq9slfy
var sizeInBytes = duration.TotalSeconds * (sampleRate * numChannels * 16f / 8f);
diff --git a/MonoGame.Framework/Audio/SoundEffectInstance.OpenAL.cs b/MonoGame.Framework/Audio/SoundEffectInstance.OpenAL.cs
index de0d328cccd..d4d44f84a76 100644
--- a/MonoGame.Framework/Audio/SoundEffectInstance.OpenAL.cs
+++ b/MonoGame.Framework/Audio/SoundEffectInstance.OpenAL.cs
@@ -6,9 +6,16 @@
#if MONOMAC && PLATFORM_MACOS_LEGACY
using MonoMac.OpenAL;
-#elif OPENAL
+#endif
+#if MONOMAC && !PLATFORM_MACOS_LEGACY
+using OpenTK.Audio.OpenAL;
+#endif
+#if GLES
using OpenTK.Audio.OpenAL;
#endif
+#if DESKTOPGL
+using OpenAL;
+#endif
namespace Microsoft.Xna.Framework.Audio
{
@@ -19,13 +26,20 @@ public partial class SoundEffectInstance : IDisposable
private float _alVolume = 1;
internal int SourceId;
+ private float reverb = 0f;
+ bool applyFilter = false;
+#if SUPPORTS_EFX
+ EfxFilterType filterType;
+#endif
+ float filterQ;
+ float frequency;
int pauseCount;
- private OpenALSoundController controller;
+ internal OpenALSoundController controller;
internal bool HasSourceId = false;
- #region Initialization
+#region Initialization
///
/// Creates a standalone SoundEffectInstance from given wavedata.
@@ -44,7 +58,7 @@ internal void InitializeSound()
controller = OpenALSoundController.GetInstance;
}
- #endregion // Initialization
+#endregion // Initialization
///
/// Converts the XNA [-1, 1] pitch range to OpenAL pitch (0, INF) or Android SoundPool playback rate [0.5, 2].
@@ -52,19 +66,6 @@ internal void InitializeSound()
///
private static float XnaPitchToAlPitch(float xnaPitch)
{
- /*XNA sets pitch bounds to [-1.0f, 1.0f], each end being one octave.
- •OpenAL's AL_PITCH boundaries are (0.0f, INF). *
- •Consider the function f(x) = 2 ^ x
- •The domain is (-INF, INF) and the range is (0, INF). *
- •0.0f is the original pitch for XNA, 1.0f is the original pitch for OpenAL.
- •Note that f(0) = 1, f(1) = 2, f(-1) = 0.5, and so on.
- •XNA's pitch values are on the domain, OpenAL's are on the range.
- •Remember: the XNA limit is arbitrarily between two octaves on the domain. *
- •To convert, we just plug XNA pitch into f(x).*/
-
- if (xnaPitch < -1.0f || xnaPitch > 1.0f)
- throw new ArgumentOutOfRangeException("XNA PITCH MUST BE WITHIN [-1.0f, 1.0f]!");
-
return (float)Math.Pow(2, xnaPitch);
}
@@ -142,9 +143,14 @@ private void PlatformPlay()
// Pitch
AL.Source (SourceId, ALSourcef.Pitch, XnaPitchToAlPitch(_pitch));
ALHelper.CheckError("Failed to set source pitch.");
+#if SUPPORTS_EFX
+ ApplyReverb ();
+ ApplyFilter ();
+#endif
+ AL.SourcePlay(SourceId);
+ ALHelper.CheckError("Failed to play source.");
+
- controller.PlaySound (this);
- //Console.WriteLine ("playing: " + sourceId + " : " + soundEffect.Name);
SoundState = SoundState.Playing;
}
@@ -176,9 +182,6 @@ private void PlatformStop(bool immediate)
{
if (HasSourceId)
{
- //Console.WriteLine ("stop " + sourceId + " : " + soundEffect.Name);
-
-
if (!controller.CheckInitState())
{
return;
@@ -186,6 +189,14 @@ private void PlatformStop(bool immediate)
AL.SourceStop(SourceId);
ALHelper.CheckError("Failed to stop source.");
+#if SUPPORTS_EFX
+ // Reset the SendFilter to 0 if we are NOT using revert since
+ // sources are recyled
+ OpenALSoundController.Efx.BindSourceToAuxiliarySlot (SourceId, 0, 0, 0);
+ ALHelper.CheckError ("Failed to unset reverb.");
+ AL.Source (SourceId, ALSourcei.EfxDirectFilter, 0);
+ ALHelper.CheckError ("Failed to unset filter.");
+#endif
AL.Source(SourceId, ALSourcei.Buffer, 0);
ALHelper.CheckError("Failed to free source from buffer.");
@@ -266,6 +277,95 @@ private void PlatformSetVolume(float value)
}
}
+ internal void PlatformSetReverbMix(float mix)
+ {
+#if SUPPORTS_EFX
+ if (!OpenALSoundController.Efx.IsInitialized)
+ return;
+ reverb = mix;
+ if (State == SoundState.Playing) {
+ ApplyReverb ();
+ reverb = 0f;
+ }
+#endif
+ }
+
+#if SUPPORTS_EFX
+ void ApplyReverb ()
+ {
+ if (reverb > 0f && SoundEffect.ReverbSlot != 0) {
+ OpenALSoundController.Efx.BindSourceToAuxiliarySlot (SourceId, (int)SoundEffect.ReverbSlot, 0, 0);
+ ALHelper.CheckError ("Failed to set reverb.");
+ }
+ }
+
+ void ApplyFilter ()
+ {
+ if (applyFilter && controller.Filter > 0) {
+ var freq = frequency / 20000f;
+ var lf = 1.0f - freq;
+ var efx = OpenALSoundController.Efx;
+ efx.Filter (controller.Filter, EfxFilteri.FilterType, (int)filterType);
+ ALHelper.CheckError ("Failed to set filter.");
+ switch (filterType) {
+ case EfxFilterType.Lowpass:
+ efx.Filter (controller.Filter, EfxFilterf.LowpassGainHF, freq);
+ ALHelper.CheckError ("Failed to set LowpassGainHF.");
+ break;
+ case EfxFilterType.Highpass:
+ efx.Filter (controller.Filter, EfxFilterf.HighpassGainLF, freq);
+ ALHelper.CheckError ("Failed to set HighpassGainLF.");
+ break;
+ case EfxFilterType.Bandpass:
+ efx.Filter (controller.Filter, EfxFilterf.BandpassGainHF, freq);
+ ALHelper.CheckError ("Failed to set BandpassGainHF.");
+ efx.Filter (controller.Filter, EfxFilterf.BandpassGainLF, lf);
+ ALHelper.CheckError ("Failed to set BandpassGainLF.");
+ break;
+ }
+ AL.Source (SourceId, ALSourcei.EfxDirectFilter, controller.Filter);
+ ALHelper.CheckError ("Failed to set DirectFilter.");
+ }
+ }
+#endif
+
+ internal void PlatformSetFilter(FilterMode mode, float filterQ, float frequency)
+ {
+#if SUPPORTS_EFX
+ if (!OpenALSoundController.Efx.IsInitialized)
+ return;
+
+ applyFilter = true;
+ switch (mode) {
+ case FilterMode.BandPass:
+ filterType = EfxFilterType.Bandpass;
+ break;
+ case FilterMode.LowPass:
+ filterType = EfxFilterType.Lowpass;
+ break;
+ case FilterMode.HighPass:
+ filterType = EfxFilterType.Highpass;
+ break;
+ }
+ this.filterQ = filterQ;
+ this.frequency = frequency;
+ if (State == SoundState.Playing) {
+ ApplyFilter ();
+ applyFilter = false;
+ }
+#endif
+ }
+
+ internal void PlatformClearFilter()
+ {
+#if SUPPORTS_EFX
+ if (!OpenALSoundController.Efx.IsInitialized)
+ return;
+
+ applyFilter = false;
+#endif
+ }
+
private void PlatformDispose(bool disposing)
{
diff --git a/MonoGame.Framework/Audio/SoundEffectInstance.Web.cs b/MonoGame.Framework/Audio/SoundEffectInstance.Web.cs
index 9278678f874..83981ee4331 100644
--- a/MonoGame.Framework/Audio/SoundEffectInstance.Web.cs
+++ b/MonoGame.Framework/Audio/SoundEffectInstance.Web.cs
@@ -59,6 +59,18 @@ private void PlatformSetVolume(float value)
{
}
+ internal void PlatformSetReverbMix(float mix)
+ {
+ }
+
+ internal void PlatformSetFilter(FilterMode mode, float filterQ, float frequency)
+ {
+ }
+
+ internal void PlatformClearFilter()
+ {
+ }
+
private void PlatformDispose(bool disposing)
{
}
diff --git a/MonoGame.Framework/Audio/SoundEffectInstance.XAudio.cs b/MonoGame.Framework/Audio/SoundEffectInstance.XAudio.cs
index ddb24ff4ce9..67591764292 100644
--- a/MonoGame.Framework/Audio/SoundEffectInstance.XAudio.cs
+++ b/MonoGame.Framework/Audio/SoundEffectInstance.XAudio.cs
@@ -11,10 +11,16 @@ namespace Microsoft.Xna.Framework.Audio
{
public partial class SoundEffectInstance : IDisposable
{
+ private static float[] _defaultChannelAzimuths = new float[] { 0f, 0f };
+
internal SourceVoice _voice;
internal WaveFormat _format;
- private static float[] _panMatrix;
+ private SharpDX.XAudio2.Fx.Reverb _reverb;
+
+ private static readonly float[] _outputMatrix = new float[16];
+
+ private float _reverbMix;
private bool _paused;
private bool _loop;
@@ -31,13 +37,20 @@ private void PlatformApply3D(AudioListener listener, AudioEmitter emitter)
return;
// Convert from XNA Emitter to a SharpDX Emitter
- var e = emitter.ToEmitter();
+ var e = ToDXEmitter(emitter);
e.CurveDistanceScaler = SoundEffect.DistanceScale;
e.DopplerScaler = SoundEffect.DopplerScale;
e.ChannelCount = _effect._format.Channels;
+
+ //stereo channel
+ if (e.ChannelCount > 1)
+ {
+ e.ChannelRadius = 0;
+ e.ChannelAzimuths = _defaultChannelAzimuths;
+ }
// Convert from XNA Listener to a SharpDX Listener
- var l = listener.ToListener();
+ var l = ToDXListener(listener);
// Number of channels in the sound being played.
// Not actually sure if XNA supported 3D attenuation of sterio sounds, but X3DAudio does.
@@ -56,6 +69,99 @@ private void PlatformApply3D(AudioListener listener, AudioEmitter emitter)
_voice.SetFrequencyRatio(dpsSettings.DopplerFactor);
}
+ private SharpDX.X3DAudio.Emitter _dxEmitter;
+ private SharpDX.X3DAudio.Listener _dxListener;
+
+ private SharpDX.X3DAudio.Emitter ToDXEmitter(AudioEmitter emitter)
+ {
+ // Pulling out Vector properties for efficiency.
+ var pos = emitter.Position;
+ var vel = emitter.Velocity;
+ var forward = emitter.Forward;
+ var up = emitter.Up;
+
+ // From MSDN:
+ // X3DAudio uses a left-handed Cartesian coordinate system,
+ // with values on the x-axis increasing from left to right, on the y-axis from bottom to top,
+ // and on the z-axis from near to far.
+ // Azimuths are measured clockwise from a given reference direction.
+ //
+ // From MSDN:
+ // The XNA Framework uses a right-handed coordinate system,
+ // with the positive z-axis pointing toward the observer when the positive x-axis is pointing to the right,
+ // and the positive y-axis is pointing up.
+ //
+ // Programmer Notes:
+ // According to this description the z-axis (forward vector) is inverted between these two coordinate systems.
+ // Therefore, we need to negate the z component of any position/velocity values, and negate any forward vectors.
+
+ forward *= -1.0f;
+ pos.Z *= -1.0f;
+ vel.Z *= -1.0f;
+
+ if (_dxEmitter == null)
+ _dxEmitter = new Emitter();
+
+#if WINDOWS_UAP
+ _dxEmitter.Position = new SharpDX.Mathematics.Interop.RawVector3 { X = pos.X, Y = pos.Y, Z = pos.Z };
+ _dxEmitter.Velocity = new SharpDX.Mathematics.Interop.RawVector3 { X = vel.X, Y = vel.Y, Z = vel.Z };
+ _dxEmitter.OrientFront = new SharpDX.Mathematics.Interop.RawVector3 { X = forward.X, Y = forward.Y, Z = forward.Z };
+ _dxEmitter.OrientTop = new SharpDX.Mathematics.Interop.RawVector3 { X = up.X, Y = up.Y, Z = up.Z };
+
+#else
+ _dxEmitter.Position = new SharpDX.Vector3(pos.X, pos.Y, pos.Z);
+ _dxEmitter.Velocity = new SharpDX.Vector3(vel.X, vel.Y, vel.Z);
+ _dxEmitter.OrientFront = new SharpDX.Vector3(forward.X, forward.Y, forward.Z);
+ _dxEmitter.OrientTop = new SharpDX.Vector3(up.X, up.Y, up.Z);
+ _dxEmitter.DopplerScaler = emitter.DopplerScale;
+#endif
+ return _dxEmitter;
+ }
+
+ private SharpDX.X3DAudio.Listener ToDXListener(AudioListener listener)
+ {
+ // Pulling out Vector properties for efficiency.
+ var pos = listener.Position;
+ var vel = listener.Velocity;
+ var forward = listener.Forward;
+ var up = listener.Up;
+
+ // From MSDN:
+ // X3DAudio uses a left-handed Cartesian coordinate system,
+ // with values on the x-axis increasing from left to right, on the y-axis from bottom to top,
+ // and on the z-axis from near to far.
+ // Azimuths are measured clockwise from a given reference direction.
+ //
+ // From MSDN:
+ // The XNA Framework uses a right-handed coordinate system,
+ // with the positive z-axis pointing toward the observer when the positive x-axis is pointing to the right,
+ // and the positive y-axis is pointing up.
+ //
+ // Programmer Notes:
+ // According to this description the z-axis (forward vector) is inverted between these two coordinate systems.
+ // Therefore, we need to negate the z component of any position/velocity values, and negate any forward vectors.
+
+ forward *= -1.0f;
+ pos.Z *= -1.0f;
+ vel.Z *= -1.0f;
+
+ if (_dxListener == null)
+ _dxListener = new Listener();
+
+#if WINDOWS_UAP
+ _dxListener.Position = new SharpDX.Mathematics.Interop.RawVector3 { X = pos.X, Y = pos.Y, Z = pos.Z };
+ _dxListener.Velocity = new SharpDX.Mathematics.Interop.RawVector3 { X = vel.X, Y = vel.Y, Z = vel.Z };
+ _dxListener.OrientFront = new SharpDX.Mathematics.Interop.RawVector3 { X = forward.X, Y = forward.Y, Z = forward.Z };
+ _dxListener.OrientTop = new SharpDX.Mathematics.Interop.RawVector3 { X = up.X, Y = up.Y, Z = up.Z };
+#else
+ _dxListener.Position = new SharpDX.Vector3(pos.X, pos.Y, pos.Z);
+ _dxListener.Velocity = new SharpDX.Vector3(vel.X, vel.Y, vel.Z);
+ _dxListener.OrientFront = new SharpDX.Vector3(forward.X, forward.Y, forward.Z);
+ _dxListener.OrientTop = new SharpDX.Vector3(up.X, up.Y, up.Z);
+#endif
+ return _dxListener;
+ }
+
private void PlatformPause()
{
if (_voice != null)
@@ -135,87 +241,68 @@ private void PlatformSetPan(float value)
_pan = MathHelper.Clamp(value, -1.0f, 1.0f);
// If we have no voice then nothing more to do.
- if (_voice == null || _effect == null)
+ if (_voice == null)
return;
- var srcChannelCount = _effect._format.Channels;
- var dstChannelCount = SoundEffect.MasterVoice.VoiceDetails.InputChannelCount;
-
- if (_panMatrix == null || _panMatrix.Length < dstChannelCount)
- _panMatrix = new float[Math.Max(dstChannelCount, 8)];
-
- // Default to full volume for all channels/destinations
- for (var i = 0; i < _panMatrix.Length; i++)
- _panMatrix[i] = 1.0f;
-
- // From X3DAudio documentation:
- /*
- For submix and mastering voices, and for source voices without a channel mask or a channel mask of 0,
- XAudio2 assumes default speaker positions according to the following table.
-
- Channels
+ UpdateOutputMatrix();
+ }
- Implicit Channel Positions
+ internal void UpdateOutputMatrix()
+ {
+ var srcChannelCount = _voice.VoiceDetails.InputChannelCount;
+ var dstChannelCount = SoundEffect.MasterVoice.VoiceDetails.InputChannelCount;
- 1 Always maps to FrontLeft and FrontRight at full scale in both speakers (special case for mono sounds)
- 2 FrontLeft, FrontRight (basic stereo configuration)
- 3 FrontLeft, FrontRight, LowFrequency (2.1 configuration)
- 4 FrontLeft, FrontRight, BackLeft, BackRight (quadraphonic)
- 5 FrontLeft, FrontRight, FrontCenter, SideLeft, SideRight (5.0 configuration)
- 6 FrontLeft, FrontRight, FrontCenter, LowFrequency, SideLeft, SideRight (5.1 configuration) (see the following remarks)
- 7 FrontLeft, FrontRight, FrontCenter, LowFrequency, SideLeft, SideRight, BackCenter (6.1 configuration)
- 8 FrontLeft, FrontRight, FrontCenter, LowFrequency, BackLeft, BackRight, SideLeft, SideRight (7.1 configuration)
- 9 or more No implicit positions (one-to-one mapping)
- */
+ // Set the pan on the correct channels based on the reverb mix.
+ if (!(_reverbMix > 0.0f))
+ _voice.SetOutputMatrix(srcChannelCount, dstChannelCount, CalculateOutputMatrix(_pan, 1.0f, srcChannelCount));
+ else
+ {
+ _voice.SetOutputMatrix(SoundEffect.ReverbVoice, srcChannelCount, dstChannelCount, CalculateOutputMatrix(_pan, _reverbMix, srcChannelCount));
+ _voice.SetOutputMatrix(SoundEffect.MasterVoice, srcChannelCount, dstChannelCount, CalculateOutputMatrix(_pan, 1.0f - Math.Min(_reverbMix, 1.0f), srcChannelCount));
+ }
+ }
- // Notes:
- //
- // Since XNA does not appear to expose any 'master' voice channel mask / speaker configuration,
- // I assume the mappings listed above should be used.
- //
- // Assuming it is correct to pan all channels which have a left/right component.
+ internal static float[] CalculateOutputMatrix(float pan, float scale, int inputChannels)
+ {
+ // XNA only ever outputs to the front left/right speakers (channels 0 and 1)
+ // Assumes there are at least 2 speaker channels to output to
- var lVal = 1.0f - _pan;
- var rVal = 1.0f + _pan;
+ // Clear all the channels.
+ var outputMatrix = _outputMatrix;
+ Array.Clear(outputMatrix, 0, outputMatrix.Length);
- switch (SoundEffect.Speakers)
+ if (inputChannels == 1) // Mono source
{
- case Speakers.Stereo:
- case Speakers.TwoPointOne:
- case Speakers.Surround:
- _panMatrix[0] = lVal;
- _panMatrix[1] = rVal;
- break;
-
- case Speakers.Quad:
- _panMatrix[0] = _panMatrix[2] = lVal;
- _panMatrix[1] = _panMatrix[3] = rVal;
- break;
-
- case Speakers.FourPointOne:
- _panMatrix[0] = _panMatrix[3] = lVal;
- _panMatrix[1] = _panMatrix[4] = rVal;
- break;
-
- case Speakers.FivePointOne:
- case Speakers.SevenPointOne:
- case Speakers.FivePointOneSurround:
- _panMatrix[0] = _panMatrix[4] = lVal;
- _panMatrix[1] = _panMatrix[5] = rVal;
- break;
-
- case Speakers.SevenPointOneSurround:
- _panMatrix[0] = _panMatrix[4] = _panMatrix[6] = lVal;
- _panMatrix[1] = _panMatrix[5] = _panMatrix[7] = rVal;
- break;
-
- case Speakers.Mono:
- default:
- // don't do any panning here
- break;
+ // Left/Right output levels:
+ // Pan -1.0: L = 1.0, R = 0.0
+ // Pan 0.0: L = 1.0, R = 1.0
+ // Pan +1.0: L = 0.0, R = 1.0
+ outputMatrix[0] = (pan > 0f) ? ((1f - pan) * scale) : scale; // Front-left output
+ outputMatrix[1] = (pan < 0f) ? ((1f + pan) * scale) : scale; // Front-right output
+ }
+ else if (inputChannels == 2) // Stereo source
+ {
+ // Left/Right input (Li/Ri) mix for Left/Right outputs (Lo/Ro):
+ // Pan -1.0: Lo = 0.5Li + 0.5Ri, Ro = 0.0Li + 0.0Ri
+ // Pan 0.0: Lo = 1.0Li + 0.0Ri, Ro = 0.0Li + 1.0Ri
+ // Pan +1.0: Lo = 0.0Li + 0.0Ri, Ro = 0.5Li + 0.5Ri
+ if (pan <= 0f)
+ {
+ outputMatrix[0] = (1f + pan * 0.5f) * scale; // Front-left output, Left input
+ outputMatrix[1] = (-pan * 0.5f) * scale; // Front-left output, Right input
+ outputMatrix[2] = 0f; // Front-right output, Left input
+ outputMatrix[3] = (1f + pan) * scale; // Front-right output, Right input
+ }
+ else
+ {
+ outputMatrix[0] = (1f - pan) * scale; // Front-left output, Left input
+ outputMatrix[1] = 0f; // Front-left output, Right input
+ outputMatrix[2] = (pan * 0.5f) * scale; // Front-right output, Left input
+ outputMatrix[3] = (1f - pan * 0.5f) * scale; // Front-right output, Right input
+ }
}
- _voice.SetOutputMatrix(srcChannelCount, dstChannelCount, _panMatrix);
+ return outputMatrix;
}
private void PlatformSetPitch(float value)
@@ -251,10 +338,56 @@ private void PlatformSetVolume(float value)
_voice.SetVolume(value, XAudio2.CommitNow);
}
+ internal void PlatformSetReverbMix(float mix)
+ {
+ // At least for XACT we can't go over 2x the volume on the mix.
+ _reverbMix = MathHelper.Clamp(mix, 0, 2);
+
+ // If we have no voice then nothing more to do.
+ if (_voice == null)
+ return;
+
+ if (!(_reverbMix > 0.0f))
+ _voice.SetOutputVoices(new VoiceSendDescriptor(SoundEffect.MasterVoice));
+ else
+ {
+ _voice.SetOutputVoices( new VoiceSendDescriptor(SoundEffect.ReverbVoice),
+ new VoiceSendDescriptor(SoundEffect.MasterVoice));
+ }
+
+ UpdateOutputMatrix();
+ }
+
+ internal void PlatformSetFilter(FilterMode mode, float filterQ, float frequency)
+ {
+ if (_voice == null)
+ return;
+
+ var filter = new FilterParameters
+ {
+ Frequency = XAudio2.CutoffFrequencyToRadians(frequency, _voice.VoiceDetails.InputSampleRate),
+ OneOverQ = 1.0f / filterQ,
+ Type = (FilterType)mode
+ };
+ _voice.SetFilterParameters(filter);
+ }
+
+ internal void PlatformClearFilter()
+ {
+ if (_voice == null)
+ return;
+
+ var filter = new FilterParameters { Frequency = 1.0f, OneOverQ = 1.0f, Type = FilterType.LowPassFilter };
+ _voice.SetFilterParameters(filter);
+ }
+
private void PlatformDispose(bool disposing)
{
if (disposing)
{
+ if (_reverb != null)
+ _reverb.Dispose();
+
if (_voice != null)
{
_voice.DestroyVoice();
@@ -263,6 +396,7 @@ private void PlatformDispose(bool disposing)
}
_voice = null;
_effect = null;
+ _reverb = null;
}
}
}
diff --git a/MonoGame.Framework/Audio/SoundEffectInstance.cs b/MonoGame.Framework/Audio/SoundEffectInstance.cs
index f237d39e1cc..14e3092fd84 100644
--- a/MonoGame.Framework/Audio/SoundEffectInstance.cs
+++ b/MonoGame.Framework/Audio/SoundEffectInstance.cs
@@ -15,6 +15,7 @@ public partial class SoundEffectInstance : IDisposable
private bool _isDisposed = false;
internal bool _isPooled = true;
internal bool _isXAct;
+ internal bool _isDynamic;
internal SoundEffect _effect;
private float _pan;
private float _volume;
@@ -22,7 +23,7 @@ public partial class SoundEffectInstance : IDisposable
/// Enables or Disables whether the SoundEffectInstance should repeat after playback.
/// This value has no effect on an already playing sound.
- public bool IsLooped
+ public virtual bool IsLooped
{
get { return PlatformGetIsLooped(); }
set { PlatformSetIsLooped(value); }
@@ -50,7 +51,8 @@ public float Pitch
get { return _pitch; }
set
{
- if (value < -1.0f || value > 1.0f)
+ // XAct sounds effects don't have pitch limits
+ if (!_isXAct && (value < -1.0f || value > 1.0f))
throw new ArgumentOutOfRangeException();
_pitch = value;
@@ -83,7 +85,7 @@ public float Volume
}
/// Gets the SoundEffectInstance's current playback state.
- public SoundState State { get { return PlatformGetState(); } }
+ public virtual SoundState State { get { return PlatformGetState(); } }
/// Indicates whether the object is disposed.
public bool IsDisposed { get { return _isDisposed; } }
@@ -129,15 +131,18 @@ public void Apply3D(AudioListener[] listeners, AudioEmitter emitter)
/// Pauses playback of a SoundEffectInstance.
/// Paused instances can be resumed with SoundEffectInstance.Play() or SoundEffectInstance.Resume().
- public void Pause()
+ public virtual void Pause()
{
PlatformPause();
}
/// Plays or resumes a SoundEffectInstance.
/// Throws an exception if more sounds are playing than the platform allows.
- public void Play()
+ public virtual void Play()
{
+ if (_isDisposed)
+ throw new ObjectDisposedException("SoundEffectInstance");
+
if (State == SoundState.Playing)
return;
@@ -161,13 +166,13 @@ public void Play()
/// Resumes playback for a SoundEffectInstance.
/// Only has effect on a SoundEffectInstance in a paused state.
- public void Resume()
+ public virtual void Resume()
{
PlatformResume();
}
/// Immediately stops playing a SoundEffectInstance.
- public void Stop()
+ public virtual void Stop()
{
PlatformStop(true);
}
@@ -175,7 +180,7 @@ public void Stop()
/// Stops playing a SoundEffectInstance, either immediately or as authored.
/// Determined whether the sound stops immediately, or after playing its release phase and/or transitions.
/// Stopping a sound with the immediate argument set to false will allow it to play any release phases, such as fade, before coming to a stop.
- public void Stop(bool immediate)
+ public virtual void Stop(bool immediate)
{
PlatformStop(immediate);
}
diff --git a/MonoGame.Framework/Audio/SoundEffectInstancePool.cs b/MonoGame.Framework/Audio/SoundEffectInstancePool.cs
index e15940a264a..4cea5d6e304 100644
--- a/MonoGame.Framework/Audio/SoundEffectInstancePool.cs
+++ b/MonoGame.Framework/Audio/SoundEffectInstancePool.cs
@@ -79,7 +79,9 @@ internal static SoundEffectInstance GetInstance(bool forXAct)
inst.Volume = 1.0f;
inst.Pan = 0.0f;
inst.Pitch = 0.0f;
- inst.IsLooped = false;
+ inst.IsLooped = false;
+ inst.PlatformSetReverbMix(0);
+ inst.PlatformClearFilter();
}
else
{
@@ -97,18 +99,18 @@ internal static SoundEffectInstance GetInstance(bool forXAct)
///
internal static void Update()
{
-#if OPENAL
- OpenALSoundController.GetInstance.Update();
-#endif
-
SoundEffectInstance inst = null;
// Cleanup instances which have finished playing.
for (var x = 0; x < _playingInstances.Count;)
{
- inst = _playingInstances[x];
-
- if (inst.State == SoundState.Stopped || inst.IsDisposed || inst._effect == null)
- {
+ inst = _playingInstances[x];
+
+ if (inst.IsDisposed || inst.State == SoundState.Stopped || (inst._effect == null && !inst._isDynamic))
+ {
+#if OPENAL
+ if (!inst.IsDisposed)
+ inst.Stop(true); // force stopping it to free its AL source
+#endif
Add(inst);
continue;
}
@@ -154,23 +156,5 @@ internal static void UpdateMasterVolume()
}
}
- internal static void Shutdown()
- {
- // We need to dispose all SoundEffectInstances before shutdown,
- // so as to destroy all SourceVoice instances,
- // before we can destroy our XAudio MasterVoice instance.
- // Otherwise XAudio shutdown fails, causing intermittent crashes.
- foreach (var inst in _playingInstances)
- {
- inst.Dispose();
- }
- _playingInstances.Clear();
-
- foreach (var inst in _pooledInstances)
- {
- inst.Dispose();
- }
- _pooledInstances.Clear();
- }
}
}
diff --git a/MonoGame.Framework/Audio/Xact/AudioCategory.cs b/MonoGame.Framework/Audio/Xact/AudioCategory.cs
index 328a679f5b0..86198bebd54 100644
--- a/MonoGame.Framework/Audio/Xact/AudioCategory.cs
+++ b/MonoGame.Framework/Audio/Xact/AudioCategory.cs
@@ -12,85 +12,71 @@ namespace Microsoft.Xna.Framework.Audio
///
/// Provides functionality for manipulating multiple sounds at a time.
///
- public struct AudioCategory : IEquatable
- {
- string name;
- AudioEngine engine;
+ public struct AudioCategory : IEquatable
+ {
+ readonly string _name;
+ readonly AudioEngine _engine;
+ readonly List _sounds;
- // Thisis a bit gross, but we use an array here
+ // This is a bit gross, but we use an array here
// instead of a field since AudioCategory is a struct
// This allows us to save _volume when the user
// holds onto a reference of AudioCategory, or when a cue
// is created/loaded after the volume's already been set.
- internal float[] _volume;
- internal bool isBackgroundMusic;
- internal bool isPublic;
-
- internal bool instanceLimit;
- internal int maxInstances;
-
- List sounds;
-
- //insatnce limiting behaviour
- internal enum MaxInstanceBehaviour {
- FailToPlay,
- Queue,
- ReplaceOldest,
- ReplaceQuietest,
- ReplaceLowestPriority,
- }
- internal MaxInstanceBehaviour instanceBehaviour;
-
- internal enum CrossfadeType {
- Linear,
- Logarithmic,
- EqualPower,
- }
- internal CrossfadeType fadeType;
- internal float fadeIn;
- internal float fadeOut;
-
-
- internal AudioCategory (AudioEngine audioengine, string name, BinaryReader reader)
- {
- Debug.Assert(audioengine != null);
+ internal float[] _volume;
+
+ internal bool isBackgroundMusic;
+ internal bool isPublic;
+
+ internal bool instanceLimit;
+ internal int maxInstances;
+ internal MaxInstanceBehavior InstanceBehavior;
+
+ internal CrossfadeType fadeType;
+ internal float fadeIn;
+ internal float fadeOut;
+
+
+ internal AudioCategory (AudioEngine audioengine, string name, BinaryReader reader)
+ {
+ Debug.Assert(audioengine != null);
Debug.Assert(!string.IsNullOrEmpty(name));
- this.sounds = new List();
- this.name = name;
- engine = audioengine;
+ _sounds = new List();
+ _name = name;
+ _engine = audioengine;
- maxInstances = reader.ReadByte ();
- instanceLimit = maxInstances != 0xff;
+ maxInstances = reader.ReadByte ();
+ instanceLimit = maxInstances != 0xff;
- fadeIn = (reader.ReadUInt16 () / 1000f);
- fadeOut = (reader.ReadUInt16 () / 1000f);
+ fadeIn = (reader.ReadUInt16 () / 1000f);
+ fadeOut = (reader.ReadUInt16 () / 1000f);
- byte instanceFlags = reader.ReadByte ();
- fadeType = (CrossfadeType)(instanceFlags & 0x7);
- instanceBehaviour = (MaxInstanceBehaviour)(instanceFlags >> 3);
+ byte instanceFlags = reader.ReadByte ();
+ fadeType = (CrossfadeType)(instanceFlags & 0x7);
+ InstanceBehavior = (MaxInstanceBehavior)(instanceFlags >> 3);
- reader.ReadUInt16 (); //unkn
+ reader.ReadUInt16 (); //unkn
var volume = XactHelpers.ParseVolumeFromDecibels(reader.ReadByte());
_volume = new float[1] { volume };
- byte visibilityFlags = reader.ReadByte ();
- isBackgroundMusic = (visibilityFlags & 0x1) != 0;
- isPublic = (visibilityFlags & 0x2) != 0;
- }
+ byte visibilityFlags = reader.ReadByte ();
+ isBackgroundMusic = (visibilityFlags & 0x1) != 0;
+ isPublic = (visibilityFlags & 0x2) != 0;
+ }
- internal void AddSound(XactSound sound)
- {
- sounds.Add(sound);
- }
+ internal void AddSound(XactSound sound)
+ {
+ _sounds.Add(sound);
+ }
internal int GetPlayingInstanceCount()
{
var sum = 0;
- for (var i = 0; i < sounds.Count; i++)
+ for (var i = 0; i < _sounds.Count; i++)
{
- if (sounds[i].Playing)
+ if (_sounds[i].Playing)
sum++;
}
return sum;
@@ -98,10 +84,10 @@ internal int GetPlayingInstanceCount()
internal XactSound GetOldestInstance()
{
- for (var i = 0; i < sounds.Count; i++)
+ for (var i = 0; i < _sounds.Count; i++)
{
- if (sounds[i].Playing)
- return sounds[i];
+ if (_sounds[i].Playing)
+ return _sounds[i];
}
return null;
}
@@ -109,42 +95,45 @@ internal XactSound GetOldestInstance()
///
/// Gets the category's friendly name.
///
- public string Name { get { return name; } }
+ public string Name { get { return _name; } }
///
/// Pauses all associated sounds.
///
- public void Pause ()
- {
- foreach (var sound in sounds)
- sound.Pause();
- }
+ public void Pause ()
+ {
+ foreach (var sound in _sounds)
+ sound.Pause();
+ }
///
/// Resumes all associated paused sounds.
///
- public void Resume ()
- {
- foreach (var sound in sounds)
- sound.Resume();
- }
+ public void Resume ()
+ {
+ foreach (var sound in _sounds)
+ sound.Resume();
+ }
///
/// Stops all associated sounds.
///
public void Stop(AudioStopOptions options)
- {
- foreach (var sound in sounds)
+ {
+ foreach (var sound in _sounds)
sound.Stop(options);
- }
+ }
- public void SetVolume(float volume)
+ public void SetVolume(float volume)
{
+ if (volume < 0)
+ throw new ArgumentException("The volume must be positive.");
+
_volume[0] = volume;
- foreach (var sound in sounds)
- sound.UpdateCategoryVolume(volume);
- }
+ foreach (var sound in _sounds)
+ sound.UpdateCategoryVolume(volume);
+ }
///
/// Determines whether two AudioCategory instances are equal.
@@ -154,7 +143,7 @@ public void SetVolume(float volume)
/// true if the objects are equal or false if they aren't.
public static bool operator ==(AudioCategory first, AudioCategory second)
{
- return first.engine == second.engine && first.name.Equals(second.name, StringComparison.Ordinal);
+ return first._engine == second._engine && first._name.Equals(second._name, StringComparison.Ordinal);
}
///
@@ -164,19 +153,19 @@ public void SetVolume(float volume)
/// Second AudioCategory instance to compare.
/// true if the objects are not equal or false if they are.
public static bool operator !=(AudioCategory first, AudioCategory second)
- {
- return first.engine != second.engine || !first.name.Equals(second.name, StringComparison.Ordinal);
- }
+ {
+ return first._engine != second._engine || !first._name.Equals(second._name, StringComparison.Ordinal);
+ }
///
/// Determines whether two AudioCategory instances are equal.
///
/// AudioCategory to compare with this instance.
/// true if the objects are equal or false if they aren't
- public bool Equals(AudioCategory other)
- {
- return engine == other.engine && name.Equals(other.name, StringComparison.Ordinal);
- }
+ public bool Equals(AudioCategory other)
+ {
+ return _engine == other._engine && _name.Equals(other._name, StringComparison.Ordinal);
+ }
///
/// Determines whether two AudioCategory instances are equal.
@@ -188,7 +177,7 @@ public override bool Equals(object obj)
if (obj is AudioCategory)
{
var other = (AudioCategory)obj;
- return engine == other.engine && name.Equals(other.name, StringComparison.Ordinal);
+ return _engine == other._engine && _name.Equals(other._name, StringComparison.Ordinal);
}
return false;
@@ -200,7 +189,7 @@ public override bool Equals(object obj)
/// Hash code for this object.
public override int GetHashCode()
{
- return name.GetHashCode() ^ engine.GetHashCode();
+ return _name.GetHashCode() ^ _engine.GetHashCode();
}
///
@@ -209,8 +198,8 @@ public override int GetHashCode()
/// Friendly name of the AudioCategory
public override string ToString()
{
- return name;
+ return _name;
}
- }
+ }
}
diff --git a/MonoGame.Framework/Audio/Xact/AudioEngine.cs b/MonoGame.Framework/Audio/Xact/AudioEngine.cs
index 0b058785730..24d03d2d6f5 100644
--- a/MonoGame.Framework/Audio/Xact/AudioEngine.cs
+++ b/MonoGame.Framework/Audio/Xact/AudioEngine.cs
@@ -7,269 +7,385 @@
using System.IO;
using System.Collections.Generic;
-using Microsoft.Xna.Framework;
-
namespace Microsoft.Xna.Framework.Audio
{
///
/// Class used to create and manipulate code audio objects.
///
- public class AudioEngine : IDisposable
- {
- ///
- ///Specifies the current content version.
- ///
- public const int ContentVersion = 46;
-
- internal Dictionary Wavebanks = new Dictionary();
-
- AudioCategory[] categories;
- Dictionary categoryLookup = new Dictionary();
-
- internal List _activeCues = new List();
-
- internal AudioCategory[] Categories { get { return categories; } }
-
- struct Variable {
- public string name;
- public float value;
-
- public bool isGlobal;
- public bool isReadOnly;
- public bool isPublic;
- public bool isReserved;
-
- public float initValue;
- public float maxValue;
- public float minValue;
- }
- Variable[] variables;
- Dictionary variableLookup = new Dictionary();
-
- enum RpcPointType {
- Linear,
- Fast,
- Slow,
- SinCos
- }
- struct RpcPoint {
- public float x, y;
- public RpcPointType type;
- }
-
- enum RpcParameter {
- Volume,
- Pitch,
- ReverbSend,
- FilterFrequency,
- FilterQFactor
- }
- struct RpcCurve {
- public int variable;
- public RpcParameter parameter;
- public RpcPoint[] points;
- }
-
- RpcCurve[] rpcCurves;
- private Stopwatch _stopwatch;
+ public class AudioEngine : IDisposable
+ {
+ private readonly AudioCategory[] _categories;
+ private readonly Dictionary _categoryLookup = new Dictionary();
+
+ private readonly RpcVariable[] _variables;
+ private readonly Dictionary _variableLookup = new Dictionary();
+
+ private readonly RpcVariable[] _cueVariables;
+
+ private readonly Stopwatch _stopwatch;
private TimeSpan _lastUpdateTime;
+ private readonly ReverbSettings _reverbSettings;
+ private readonly RpcCurve[] _reverbCurves;
+
+ internal List ActiveCues = new List();
+ internal AudioCategory[] Categories { get { return _categories; } }
+
+ internal Dictionary Wavebanks = new Dictionary();
+
+ internal readonly RpcCurve[] RpcCurves;
+
+ internal RpcVariable[] CreateCueVariables()
+ {
+ var clone = new RpcVariable[_cueVariables.Length];
+ Array.Copy(_cueVariables, clone, _cueVariables.Length);
+ return clone;
+ }
+
+ ///
+ /// The current content version.
+ ///
+ public const int ContentVersion = 39;
/// Path to a XACT settings file.
- public AudioEngine (string settingsFile)
- : this(settingsFile, TimeSpan.Zero, "")
- {
- }
+ public AudioEngine(string settingsFile)
+ : this(settingsFile, TimeSpan.Zero, "")
+ {
+ }
+
+ internal static Stream OpenStream(string filePath)
+ {
+ var stream = TitleContainer.OpenStream(filePath);
+
+#if ANDROID
+ // Read the asset into memory in one go. This results in a ~50% reduction
+ // in load times on Android due to slow Android asset streams.
+ var memStream = new MemoryStream();
+ stream.CopyTo(memStream);
+ memStream.Seek(0, SeekOrigin.Begin);
+ stream.Close();
+ stream = memStream;
+#endif
+
+ return stream;
+ }
/// Path to a XACT settings file.
/// Determines how many milliseconds the engine will look ahead when determing when to transition to another sound.
/// A string that specifies the audio renderer to use.
/// For the best results, use a lookAheadTime of 250 milliseconds or greater.
- public AudioEngine (string settingsFile, TimeSpan lookAheadTime, string rendererId)
- {
- //Read the xact settings file
- //Credits to alisci01 for initial format documentation
-#if !ANDROID
- using (var stream = TitleContainer.OpenStream(settingsFile))
- {
-#else
- using (var fileStream = Game.Activity.Assets.Open(settingsFile))
- {
- MemoryStream stream = new MemoryStream();
- fileStream.CopyTo(stream);
- stream.Position = 0;
-#endif
- using (var reader = new BinaryReader(stream)) {
- uint magic = reader.ReadUInt32 ();
- if (magic != 0x46534758) { //'XGFS'
- throw new ArgumentException ("XGS format not recognized");
- }
-
- reader.ReadUInt16 (); // toolVersion
-#if DEBUG
- uint formatVersion = reader.ReadUInt16 ();
- if (formatVersion != 42) {
- System.Diagnostics.Debug.WriteLine ("Warning: XGS format not supported");
- }
-#else
- reader.ReadUInt16 (); // formatVersion
-#endif
- reader.ReadUInt16 (); // crc
-
- reader.ReadUInt32 (); // lastModifiedLow
- reader.ReadUInt32 (); // lastModifiedHigh
-
- reader.ReadByte (); //unkn, 0x03. Platform?
-
- uint numCats = reader.ReadUInt16 ();
- uint numVars = reader.ReadUInt16 ();
-
- reader.ReadUInt16 (); //unkn, 0x16
- reader.ReadUInt16 (); //unkn, 0x16
-
- uint numRpc = reader.ReadUInt16 ();
- reader.ReadUInt16 (); // numDspPresets
- reader.ReadUInt16 (); // numDspParams
-
- uint catsOffset = reader.ReadUInt32 ();
- uint varsOffset = reader.ReadUInt32 ();
-
- reader.ReadUInt32 (); //unknown, leads to a short with value of 1?
- reader.ReadUInt32 (); // catNameIndexOffset
- reader.ReadUInt32 (); //unknown, two shorts of values 2 and 3?
- reader.ReadUInt32 (); // varNameIndexOffset
-
- uint catNamesOffset = reader.ReadUInt32 ();
- uint varNamesOffset = reader.ReadUInt32 ();
- uint rpcOffset = reader.ReadUInt32 ();
- reader.ReadUInt32 (); // dspPresetsOffset
- reader.ReadUInt32 (); // dspParamsOffset
- reader.BaseStream.Seek (catNamesOffset, SeekOrigin.Begin);
- string[] categoryNames = readNullTerminatedStrings (numCats, reader);
-
- categories = new AudioCategory[numCats];
- reader.BaseStream.Seek (catsOffset, SeekOrigin.Begin);
- for (int i=0; i();
+ var cueVariables = new List();
+ var globalVariables = new List();
+ reader.BaseStream.Seek (varsOffset, SeekOrigin.Begin);
+ for (var i=0; i < numVars; i++)
+ {
+ var v = new RpcVariable();
+ v.Name = varNames[i];
+ v.Flags = reader.ReadByte();
+ v.InitValue = reader.ReadSingle();
+ v.MinValue = reader.ReadSingle();
+ v.MaxValue = reader.ReadSingle();
+ v.Value = v.InitValue;
+
+ variables.Add(v);
+ if (!v.IsGlobal)
+ cueVariables.Add(v);
+ else
+ {
+ globalVariables.Add(v);
+ _variableLookup.Add(v.Name, globalVariables.Count - 1);
+ }
+ }
+ _cueVariables = cueVariables.ToArray();
+ _variables = globalVariables.ToArray();
+
+ var reverbCurves = new List();
+ RpcCurves = new RpcCurve[numRpc];
+ if (numRpc > 0)
+ {
+ reader.BaseStream.Seek(rpcOffset, SeekOrigin.Begin);
+ for (var i=0; i < numRpc; i++)
+ {
+ var curve = new RpcCurve();
+ curve.FileOffset = (uint)reader.BaseStream.Position;
+
+ var variable = variables[ reader.ReadUInt16() ];
+ if (variable.IsGlobal)
+ {
+ curve.IsGlobal = true;
+ curve.Variable = globalVariables.FindIndex(e => e.Name == variable.Name);
+ }
+ else
+ {
+ curve.IsGlobal = false;
+ curve.Variable = cueVariables.FindIndex(e => e.Name == variable.Name);
+ }
+
+ var pointCount = (int)reader.ReadByte();
+ curve.Parameter = (RpcParameter)reader.ReadUInt16();
+
+ curve.Points = new RpcPoint[pointCount];
+ for (var j=0; j < pointCount; j++)
+ {
+ curve.Points[j].Position = reader.ReadSingle();
+ curve.Points[j].Value = reader.ReadSingle();
+ curve.Points[j].Type = (RpcPointType)reader.ReadByte();
+ }
+
+ // If the parameter is greater than the max then this is a DSP
+ // parameter which is for reverb.
+ var dspParameter = curve.Parameter - RpcParameter.NumParameters;
+ if (dspParameter >= 0 && variable.IsGlobal)
+ reverbCurves.Add(curve);
+
+ RpcCurves[i] = curve;
+ }
+ }
+ _reverbCurves = reverbCurves.ToArray();
+
+ if (numDspPresets > 0)
+ {
+ // Note: It seemed like MS designed this to support multiple
+ // DSP effects, but in practice XACT only has one... Microsoft Reverb.
+ //
+ // So because of this we know exactly how many presets and
+ // parameters we should have.
+ if (numDspPresets != 1)
+ throw new Exception("Unexpected number of DSP presets!");
+ if (numDspParams != 22)
+ throw new Exception("Unexpected number of DSP parameters!");
+
+ reader.BaseStream.Seek(dspParamsOffset, SeekOrigin.Begin);
+ _reverbSettings = new ReverbSettings(reader);
+ }
+ }
_stopwatch = new Stopwatch();
_stopwatch.Start();
- }
-
- //wtf C#
- private static string[] readNullTerminatedStrings (uint count, BinaryReader reader)
- {
- string[] ret = new string[count];
- for (int i=0; i s = new List ();
- while (reader.PeekChar() != 0) {
- s.Add (reader.ReadChar ());
- }
- reader.ReadChar ();
- ret [i] = new string (s.ToArray ());
- }
- return ret;
- }
-
- /// Performs periodic work required by the audio engine.
+ }
+
+ internal int GetRpcIndex(uint fileOffset)
+ {
+ for (var i = 0; i < RpcCurves.Length; i++)
+ {
+ if (RpcCurves[i].FileOffset == fileOffset)
+ return i;
+ }
+
+ return -1;
+ }
+
+ private static string[] ReadNullTerminatedStrings(uint count, BinaryReader reader)
+ {
+ var ret = new string[count];
+
+ for (var i=0; i < count; i++)
+ {
+ var s = new List();
+ while (reader.PeekChar() != 0)
+ s.Add(reader.ReadChar());
+
+ reader.ReadChar();
+ ret[i] = new string(s.ToArray());
+ }
+
+ return ret;
+ }
+
+ ///
+ /// Performs periodic work required by the audio engine.
+ ///
/// Must be called at least once per frame.
- public void Update ()
+ public void Update()
{
var cur = _stopwatch.Elapsed;
var elapsed = cur - _lastUpdateTime;
_lastUpdateTime = cur;
var dt = (float)elapsed.TotalSeconds;
- for (var x = 0; x < _activeCues.Count; )
+ for (var x = 0; x < ActiveCues.Count; )
{
- var cue = _activeCues[x];
+ var cue = ActiveCues[x];
cue.Update(dt);
if (cue.IsStopped)
{
- _activeCues.Remove(cue);
+ ActiveCues.Remove(cue);
continue;
}
x++;
}
- }
-
+
+ // The only global curves we can process seem to be
+ // specifically for the reverb DSP effect.
+ if (_reverbSettings != null)
+ {
+ for (var i = 0; i < _reverbCurves.Length; i++)
+ {
+ var curve = _reverbCurves[i];
+ var result = curve.Evaluate(_variables[curve.Variable].Value);
+ var parameter = curve.Parameter - RpcParameter.NumParameters;
+ _reverbSettings[parameter] = result;
+ }
+
+ SoundEffect.PlatformSetReverbSettings(_reverbSettings);
+ }
+ }
+
/// Returns an audio category by name.
/// Friendly name of the category to get.
/// The AudioCategory with a matching name. Throws an exception if not found.
- public AudioCategory GetCategory (string name)
- {
- return categories [categoryLookup [name]];
- }
+ public AudioCategory GetCategory(string name)
+ {
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentNullException("name");
+
+ int i;
+ if (!_categoryLookup.TryGetValue(name, out i))
+ throw new InvalidOperationException("This resource could not be created.");
+
+ return _categories[i];
+ }
/// Gets the value of a global variable.
/// Friendly name of the variable.
/// float value of the queried variable.
/// A global variable has global scope. It can be accessed by all code within a project.
- public float GetGlobalVariable(string name)
- {
- return variables[variableLookup[name]].value;
- }
+ public float GetGlobalVariable(string name)
+ {
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentNullException("name");
+
+ int i;
+ if (!_variableLookup.TryGetValue(name, out i) || !_variables[i].IsPublic)
+ throw new IndexOutOfRangeException("The specified variable index is invalid.");
+
+ return _variables[i].Value;
+ }
+
+ internal float GetGlobalVariable(int index)
+ {
+ return _variables[index].Value;
+ }
/// Sets the value of a global variable.
/// Friendly name of the variable.
/// Value of the global variable.
- public void SetGlobalVariable (string name, float value)
- {
- variables [variableLookup [name]].value = value;
- }
-
- #region IDisposable implementation
- public void Dispose ()
- {
- }
- #endregion
- }
+ public void SetGlobalVariable(string name, float value)
+ {
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentNullException("name");
+
+ int i;
+ if (!_variableLookup.TryGetValue(name, out i) || !_variables[i].IsPublic)
+ throw new IndexOutOfRangeException("The specified variable index is invalid.");
+
+ _variables[i].SetValue(value);
+ }
+
+ ///
+ /// This event is triggered when the AudioEngine is disposed.
+ ///
+ public event EventHandler Disposing;
+
+ ///
+ /// Is true if the AudioEngine has been disposed.
+ ///
+ public bool IsDisposed { get; private set; }
+
+ ///
+ /// Disposes the AudioEngine.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ~AudioEngine()
+ {
+ Dispose(false);
+ }
+
+ private void Dispose(bool disposing)
+ {
+ if (IsDisposed)
+ return;
+
+ IsDisposed = true;
+
+ // TODO: Should we be forcing any active
+ // audio cues to stop here?
+
+ if (disposing && Disposing != null)
+ Disposing(this, EventArgs.Empty);
+ }
+ }
}
diff --git a/MonoGame.Framework/Audio/Xact/ClipEvent.cs b/MonoGame.Framework/Audio/Xact/ClipEvent.cs
index 47c30dd76a8..c2aace826aa 100644
--- a/MonoGame.Framework/Audio/Xact/ClipEvent.cs
+++ b/MonoGame.Framework/Audio/Xact/ClipEvent.cs
@@ -28,9 +28,9 @@ protected ClipEvent(XactClip clip, float timeStamp, float randomOffset)
public abstract void Resume();
public abstract void SetFade(float fadeInDuration, float fadeOutDuration);
public abstract void SetTrackVolume(float volume);
+ public abstract void SetTrackPan(float pan);
+ public abstract void SetState(float volume, float pitch, float reverbMix, float? filterFrequency, float? filterQFactor);
public abstract bool Update(float dt);
-
- public abstract void Apply3D(AudioListener listener, AudioEmitter emitter);
}
}
diff --git a/MonoGame.Framework/Audio/Xact/CrossfadeType.cs b/MonoGame.Framework/Audio/Xact/CrossfadeType.cs
new file mode 100644
index 00000000000..c1594e39579
--- /dev/null
+++ b/MonoGame.Framework/Audio/Xact/CrossfadeType.cs
@@ -0,0 +1,13 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+namespace Microsoft.Xna.Framework.Audio
+{
+ enum CrossfadeType
+ {
+ Linear,
+ Logarithmic,
+ EqualPower,
+ }
+}
\ No newline at end of file
diff --git a/MonoGame.Framework/Audio/Xact/Cue.cs b/MonoGame.Framework/Audio/Xact/Cue.cs
index 52f31b4205f..138987a30b9 100644
--- a/MonoGame.Framework/Audio/Xact/Cue.cs
+++ b/MonoGame.Framework/Audio/Xact/Cue.cs
@@ -12,64 +12,59 @@ namespace Microsoft.Xna.Framework.Audio
/// Cues also define specific properties such as pitch or volume.
/// Cues are referenced through SoundBank objects.
///
- public class Cue : IDisposable
- {
+ public class Cue : IDisposable
+ {
private readonly AudioEngine _engine;
private readonly string _name;
private readonly XactSound[] _sounds;
- private readonly float[] _probs;
+ private readonly float[] _probs;
+
+ private readonly RpcVariable[] _variables;
private XactSound _curSound;
- private float _volume = 1.0f;
+
+ private bool _applied3D;
+ private bool _played;
/// Indicates whether or not the cue is currently paused.
/// IsPlaying and IsPaused both return true if a cue is paused while playing.
- public bool IsPaused
- {
- get
+ public bool IsPaused
+ {
+ get
{
- if (_curSound != null)
- return _curSound.IsPaused;
+ if (_curSound != null)
+ return _curSound.IsPaused;
- return false;
- }
- }
+ return false;
+ }
+ }
/// Indicates whether or not the cue is currently playing.
/// IsPlaying and IsPaused both return true if a cue is paused while playing.
- public bool IsPlaying
- {
- get
+ public bool IsPlaying
+ {
+ get
{
- if (_curSound != null)
- return _curSound.Playing;
+ if (_curSound != null)
+ return _curSound.Playing;
- return false;
- }
- }
+ return false;
+ }
+ }
/// Indicates whether or not the cue is currently stopped.
- public bool IsStopped
- {
- get
+ public bool IsStopped
+ {
+ get
{
- if (_curSound != null)
+ if (_curSound != null)
return _curSound.Stopped;
- return true;
- }
- }
-
- public bool IsStopping
- {
- get
- {
- // TODO: Implement me!
- return false;
+ return !IsDisposed && !IsPrepared;
}
}
- public bool IsPreparing
+ public bool IsStopping
{
get
{
@@ -78,98 +73,122 @@ public bool IsPreparing
}
}
- public bool IsPrepared
+ public bool IsPreparing
{
- get
- {
- // TODO: Implement me!
- return false;
- }
+ get { return false; }
}
+ public bool IsPrepared { get; internal set; }
+
+ public bool IsCreated { get; internal set; }
+
/// Gets the friendly name of the cue.
/// The friendly name is a value set from the designer.
- public string Name
- {
- get { return _name; }
- }
-
- internal Cue(AudioEngine engine, string cuename, XactSound sound)
- {
- _engine = engine;
- _name = cuename;
- _sounds = new XactSound[1];
- _sounds[0] = sound;
- _probs = new float[1];
- _probs[0] = 1.0f;
- }
-
- internal Cue(AudioEngine engine, string cuename, XactSound[] sounds, float[] probs)
- {
+ public string Name
+ {
+ get { return _name; }
+ }
+
+ internal Cue(AudioEngine engine, string cuename, XactSound sound)
+ {
_engine = engine;
- _name = cuename;
- _sounds = sounds;
- _probs = probs;
- }
+ _name = cuename;
+ _sounds = new XactSound[1];
+ _sounds[0] = sound;
+ _probs = new float[1];
+ _probs[0] = 1.0f;
+ _variables = engine.CreateCueVariables();
+ }
+
+ internal Cue(AudioEngine engine, string cuename, XactSound[] sounds, float[] probs)
+ {
+ _engine = engine;
+ _name = cuename;
+ _sounds = sounds;
+ _probs = probs;
+ _variables = engine.CreateCueVariables();
+ }
+
+ internal void Prepare()
+ {
+ IsDisposed = false;
+ IsCreated = false;
+ IsPrepared = true;
+ _curSound = null;
+ }
/// Pauses playback.
- public void Pause()
- {
- if (_curSound != null)
- _curSound.Pause();
- }
+ public void Pause()
+ {
+ if (_curSound != null)
+ _curSound.Pause();
+ }
/// Requests playback of a prepared or preparing Cue.
/// Calling Play when the Cue already is playing can result in an InvalidOperationException.
- public void Play()
- {
- if (!_engine._activeCues.Contains(this))
- _engine._activeCues.Add(this);
-
- //TODO: Probabilities
+ public void Play()
+ {
+ if (!_engine.ActiveCues.Contains(this))
+ _engine.ActiveCues.Add(this);
+
+ //TODO: Probabilities
var index = XactHelpers.Random.Next(_sounds.Length);
_curSound = _sounds[index];
-
- _curSound.SetCueVolume(_volume);
- _curSound.Play();
- }
+
+ _curSound.Play(1.0f, _engine);
+ _played = true;
+ IsPrepared = false;
+ }
/// Resumes playback of a paused Cue.
- public void Resume()
- {
- if (_curSound != null)
- _curSound.Resume();
- }
+ public void Resume()
+ {
+ if (_curSound != null)
+ _curSound.Resume();
+ }
/// Stops playback of a Cue.
/// Specifies if the sound should play any pending release phases or transitions before stopping.
- public void Stop(AudioStopOptions options)
- {
- _engine._activeCues.Remove(this);
-
- if (_curSound != null)
+ public void Stop(AudioStopOptions options)
+ {
+ _engine.ActiveCues.Remove(this);
+
+ if (_curSound != null)
_curSound.Stop(options);
- }
-
+
+ IsPrepared = false;
+ }
+
+ private int FindVariable(string name)
+ {
+ // Do a simple linear search... which is fast
+ // for as little variables as most cues have.
+ for (var i = 0; i < _variables.Length; i++)
+ {
+ if (_variables[i].Name == name)
+ return i;
+ }
+
+ return -1;
+ }
+
///
/// Sets the value of a cue-instance variable based on its friendly name.
///
/// Friendly name of the variable to set.
/// Value to assign to the variable.
/// The friendly name is a value set from the designer.
- public void SetVariable (string name, float value)
- {
- if (name == "Volume")
- {
- _volume = value;
- if (_curSound != null)
- _curSound.SetCueVolume(_volume);
- }
- else
- {
- _engine.SetGlobalVariable (name, value);
- }
- }
+ public void SetVariable(string name, float value)
+ {
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentNullException("name");
+
+ var i = FindVariable(name);
+ if (i == -1 || !_variables[i].IsPublic)
+ throw new IndexOutOfRangeException("The specified variable index is invalid.");
+
+ _variables[i].SetValue(value);
+ }
/// Gets a cue-instance variable value based on its friendly name.
/// Friendly name of the variable.
@@ -178,13 +197,17 @@ public void SetVariable (string name, float value)
/// Cue-instance variables are useful when multiple instantiations of a single cue (and its associated sounds) are required (for example, a "car" cue where there may be more than one car at any given time). While a global variable allows multiple audio elements to be controlled in unison, a cue instance variable grants discrete control of each instance of a cue, even for each copy of the same cue.
/// The friendly name is a value set from the designer.
///
- public float GetVariable (string name)
- {
- if (name == "Volume")
- return _volume;
+ public float GetVariable(string name)
+ {
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentNullException("name");
- return _engine.GetGlobalVariable (name);
- }
+ var i = FindVariable(name);
+ if (i == -1 || !_variables[i].IsPublic)
+ throw new IndexOutOfRangeException("The specified variable index is invalid.");
+
+ return _variables[i].Value;
+ }
/// Updates the simulated 3D Audio settings calculated between an AudioEmitter and AudioListener.
/// The listener to calculate.
@@ -193,28 +216,133 @@ public float GetVariable (string name)
/// This must be called before Play().
/// Calling this method automatically converts the sound to monoaural and sets the speaker mix for any sound played by this cue to a value calculated with the listener's and emitter's positions. Any stereo information in the sound will be discarded.
///
- public void Apply3D(AudioListener listener, AudioEmitter emitter)
+ public void Apply3D(AudioListener listener, AudioEmitter emitter)
{
+ if (listener == null)
+ throw new ArgumentNullException("listener");
+ if (emitter == null)
+ throw new ArgumentNullException("emitter");
+
+ if (_played && !_applied3D)
+ throw new InvalidOperationException("You must call Apply3D on a Cue before calling Play to be able to call Apply3D after calling Play.");
+
+ var direction = listener.Position - emitter.Position;
+
+ // Set the distance for falloff.
+ var distance = direction.Length();
+ var i = FindVariable("Distance");
+ _variables[i].SetValue(distance);
+
+ // Calculate the orientation.
+ if (distance > 0.0f)
+ direction /= distance;
+ var right = Vector3.Cross(listener.Up, listener.Forward);
+ var slope = Vector3.Dot(direction, listener.Forward);
+ var angle = MathHelper.ToDegrees((float)Math.Acos(slope));
+ var j = FindVariable("OrientationAngle");
+ _variables[j].SetValue(angle);
if (_curSound != null)
- _curSound.Apply3D(listener, emitter);
+ _curSound.SetCuePan(Vector3.Dot(direction, right));
+
+ // Calculate doppler effect.
+ var relativeVelocity = emitter.Velocity - listener.Velocity;
+ relativeVelocity *= emitter.DopplerScale;
+
+ _applied3D = true;
}
internal void Update(float dt)
{
if (_curSound != null)
_curSound.Update(dt);
+
+ // Evaluate the runtime parameter controls.
+ var rpcCurves = _curSound.RpcCurves;
+ if (rpcCurves.Length > 0)
+ {
+ var volume = 1.0f;
+ var pitch = 0.0f;
+ var reverbMix = 1.0f;
+ float? filterFrequency = null;
+ float? filterQFactor = null;
+
+ for (var i = 0; i < rpcCurves.Length; i++)
+ {
+ var rpcCurve = _engine.RpcCurves[rpcCurves[i]];
+
+ // Some curves are driven by global variables and others by cue instance variables.
+ float value;
+ if (rpcCurve.IsGlobal)
+ value = rpcCurve.Evaluate(_engine.GetGlobalVariable(rpcCurve.Variable));
+ else
+ value = rpcCurve.Evaluate(_variables[rpcCurve.Variable].Value);
+
+ // Process the final curve value based on the parameter type it is.
+ switch (rpcCurve.Parameter)
+ {
+ case RpcParameter.Volume:
+ volume *= XactHelpers.ParseVolumeFromDecibels(value / 100.0f);
+ break;
+
+ case RpcParameter.Pitch:
+ pitch += value / 1000.0f;
+ break;
+
+ case RpcParameter.ReverbSend:
+ reverbMix *= XactHelpers.ParseVolumeFromDecibels(value / 100.0f);
+ break;
+
+ case RpcParameter.FilterFrequency:
+ filterFrequency = value;
+ break;
+
+ case RpcParameter.FilterQFactor:
+ filterQFactor = value;
+ break;
+
+ default:
+ throw new ArgumentOutOfRangeException("rpcCurve.Parameter");
+ }
+ }
+
+ _curSound.UpdateState(_engine, volume, pitch, reverbMix, filterFrequency, filterQFactor);
+ }
+ }
+
+ ///
+ /// This event is triggered when the Cue is disposed.
+ ///
+ public event EventHandler Disposing;
+
+ ///
+ /// Is true if the Cue has been disposed.
+ ///
+ public bool IsDisposed { get; internal set; }
+
+ ///
+ /// Disposes the Cue.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ private void Dispose(bool disposing)
+ {
+ if (IsDisposed)
+ return;
+
+ IsDisposed = true;
+
+ if (disposing)
+ {
+ IsCreated = false;
+ IsPrepared = false;
+
+ if (Disposing != null)
+ Disposing(this, EventArgs.Empty);
+ }
}
-
-
- /// Indicateds whether or not the object has been disposed.
- public bool IsDisposed { get { return false; } }
-
- #region IDisposable implementation
- /// Immediately releases any unmanaged resources used by this object.
- public void Dispose ()
- {
- }
- #endregion
- }
+ }
}
diff --git a/MonoGame.Framework/Audio/Xact/DspParameter.cs b/MonoGame.Framework/Audio/Xact/DspParameter.cs
new file mode 100644
index 00000000000..4ecf9cefe0a
--- /dev/null
+++ b/MonoGame.Framework/Audio/Xact/DspParameter.cs
@@ -0,0 +1,47 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System.IO;
+
+namespace Microsoft.Xna.Framework.Audio
+{
+ struct DspParameter
+ {
+ public float Value;
+ public readonly float MinValue;
+ public readonly float MaxValue;
+
+ public DspParameter(BinaryReader reader)
+ {
+ // This is 1 if the type is byte sized and 0 for
+ // floats... not sure if we should use this info.
+ reader.ReadByte();
+
+ // The value and the min/max range for limiting the
+ // results from the RPC curve when animated.
+ Value = reader.ReadSingle();
+ MinValue = reader.ReadSingle();
+ MaxValue = reader.ReadSingle();
+
+ // Looks to always be zero... maybe some padding
+ // for future expansion that never occured?
+ reader.ReadUInt16();
+ }
+
+ public void SetValue(float value)
+ {
+ if (value < MinValue)
+ Value = MinValue;
+ else if (value > MaxValue)
+ Value = MaxValue;
+ else
+ Value = value;
+ }
+
+ public override string ToString()
+ {
+ return "Value:" + Value + " MinValue:" + MinValue + " MaxValue:" + MaxValue;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MonoGame.Framework/Audio/Xact/FilterMode.cs b/MonoGame.Framework/Audio/Xact/FilterMode.cs
new file mode 100644
index 00000000000..e2970c2c8b5
--- /dev/null
+++ b/MonoGame.Framework/Audio/Xact/FilterMode.cs
@@ -0,0 +1,13 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+namespace Microsoft.Xna.Framework.Audio
+{
+ enum FilterMode
+ {
+ LowPass = 0,
+ BandPass = 1,
+ HighPass = 2,
+ }
+}
\ No newline at end of file
diff --git a/MonoGame.Framework/Audio/Xact/MaxInstanceBehavior.cs b/MonoGame.Framework/Audio/Xact/MaxInstanceBehavior.cs
new file mode 100644
index 00000000000..6bc9e61a1c4
--- /dev/null
+++ b/MonoGame.Framework/Audio/Xact/MaxInstanceBehavior.cs
@@ -0,0 +1,15 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+namespace Microsoft.Xna.Framework.Audio
+{
+ enum MaxInstanceBehavior
+ {
+ FailToPlay,
+ Queue,
+ ReplaceOldest,
+ ReplaceQuietest,
+ ReplaceLowestPriority,
+ }
+}
\ No newline at end of file
diff --git a/MonoGame.Framework/Audio/Xact/MiniFormatTag.cs b/MonoGame.Framework/Audio/Xact/MiniFormatTag.cs
new file mode 100644
index 00000000000..ee94888329a
--- /dev/null
+++ b/MonoGame.Framework/Audio/Xact/MiniFormatTag.cs
@@ -0,0 +1,17 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+namespace Microsoft.Xna.Framework.Audio
+{
+ enum MiniFormatTag
+ {
+ Pcm = 0x0,
+ Xma = 0x1,
+ Adpcm = 0x2,
+ Wma = 0x3,
+
+ // We allow XMA to be reused for a platform specific format.
+ PlatformSpecific = Xma,
+ }
+}
\ No newline at end of file
diff --git a/MonoGame.Framework/Audio/Xact/PlayWaveEvent.cs b/MonoGame.Framework/Audio/Xact/PlayWaveEvent.cs
index ca6c136e1a4..a54ef677b15 100644
--- a/MonoGame.Framework/Audio/Xact/PlayWaveEvent.cs
+++ b/MonoGame.Framework/Audio/Xact/PlayWaveEvent.cs
@@ -16,7 +16,7 @@ enum VariationType
Shuffle
};
- class PlayWaveEvent : ClipEvent
+ class PlayWaveEvent : ClipEvent
{
private readonly SoundBank _soundBank;
@@ -33,7 +33,15 @@ class PlayWaveEvent : ClipEvent
private readonly int _totalWeights;
private float _trackVolume;
+ private float _trackPitch;
+ private float _trackFilterFrequency;
+ private float _trackFilterQFactor;
+ private float _clipVolume;
+ private float _clipPitch;
+ private float _clipReverbMix;
+
+ private readonly Vector4? _filterVar;
private readonly Vector2? _volumeVar;
private readonly Vector2? _pitchVar;
@@ -44,7 +52,7 @@ class PlayWaveEvent : ClipEvent
public PlayWaveEvent( XactClip clip, float timeStamp, float randomOffset, SoundBank soundBank,
int[] waveBanks, int[] tracks, byte[] weights, int totalWeights,
- VariationType variation, Vector2? volumeVar, Vector2? pitchVar,
+ VariationType variation, Vector2? volumeVar, Vector2? pitchVar, Vector4? filterVar,
int loopCount, bool newWaveOnLoop)
: base(clip, timeStamp, randomOffset)
{
@@ -55,15 +63,25 @@ public PlayWaveEvent( XactClip clip, float timeStamp, float randomOffset, Soun
_totalWeights = totalWeights;
_volumeVar = volumeVar;
_pitchVar = pitchVar;
+ _filterVar = filterVar;
_wavIndex = -1;
_loopIndex = 0;
+
_trackVolume = 1.0f;
+ _trackPitch = 0;
+ _trackFilterFrequency = 0;
+ _trackFilterQFactor = 0;
+
+ _clipVolume = 1.0f;
+ _clipPitch = 0;
+ _clipReverbMix = 0;
+
_variation = variation;
_loopCount = loopCount;
_newWaveOnLoop = newWaveOnLoop;
}
- public override void Play()
+ public override void Play()
{
if (_wav != null && _wav.State != SoundState.Stopped)
_wav.Stop();
@@ -145,21 +163,34 @@ private void Play(bool pickNewWav)
return;
}
- // Set the volume.
- SetTrackVolume(_trackVolume);
-
- // Set the pitch.
+ // Do all the randoms before we play.
+ if (_volumeVar.HasValue)
+ _trackVolume = _volumeVar.Value.X + ((float)XactHelpers.Random.NextDouble() * _volumeVar.Value.Y);
if (_pitchVar.HasValue)
- _wav.Pitch = _pitchVar.Value.X + ((float)XactHelpers.Random.NextDouble() * _pitchVar.Value.Y);
- else
- _wav.Pitch = 0;
-
+ _trackPitch = _pitchVar.Value.X + ((float)XactHelpers.Random.NextDouble() * _pitchVar.Value.Y);
+ if (_clip.FilterEnabled)
+ {
+ if (_filterVar.HasValue)
+ {
+ _trackFilterFrequency = _filterVar.Value.X + ((float)XactHelpers.Random.NextDouble() * _filterVar.Value.Y);
+ _trackFilterQFactor = _filterVar.Value.Z + ((float)XactHelpers.Random.NextDouble() * _filterVar.Value.W);
+ }
+ else
+ {
+ _trackFilterFrequency = _clip.FilterFrequency;
+ _trackFilterQFactor = _clip.FilterQ;
+ }
+ }
+
// This is a shortcut for infinite looping of a single track.
_wav.IsLooped = _loopCount == 255 && trackCount == 1;
+
+ // Update all the wave states then play.
+ UpdateState();
_wav.Play();
- }
+ }
- public override void Stop()
+ public override void Stop()
{
if (_wav != null)
{
@@ -167,31 +198,58 @@ public override void Stop()
_wav = null;
}
_loopIndex = 0;
- }
+ }
- public override void Pause()
+ public override void Pause()
{
if (_wav != null)
_wav.Pause();
- }
+ }
- public override void Resume()
- {
+ public override void Resume()
+ {
if (_wav != null && _wav.State == SoundState.Paused)
_wav.Resume();
- }
+ }
public override void SetTrackVolume(float volume)
{
- _trackVolume = volume;
+ _clipVolume = volume;
+ if (_wav != null)
+ _wav.Volume = _trackVolume * _clipVolume;
+ }
+ public override void SetTrackPan(float pan)
+ {
if (_wav != null)
- {
- if (_volumeVar.HasValue)
- _wav.Volume = _trackVolume * (_volumeVar.Value.X + ((float)XactHelpers.Random.NextDouble() * _volumeVar.Value.Y));
- else
- _wav.Volume = _trackVolume;
- }
+ _wav.Pan = pan;
+ }
+
+ public override void SetState(float volume, float pitch, float reverbMix, float? filterFrequency, float? filterQFactor)
+ {
+ _clipVolume = volume;
+ _clipPitch = pitch;
+ _clipReverbMix = reverbMix;
+
+ // The RPC filter overrides the randomized track filter.
+ if (filterFrequency.HasValue)
+ _trackFilterFrequency = filterFrequency.Value;
+ if (filterQFactor.HasValue)
+ _trackFilterQFactor = filterQFactor.Value;
+
+ if (_wav != null)
+ UpdateState();
+ }
+
+ private void UpdateState()
+ {
+ _wav.Volume = _trackVolume * _clipVolume;
+ _wav.Pitch = _trackPitch + _clipPitch;
+
+ if (_clip.UseReverb)
+ _wav.PlatformSetReverbMix(_clipReverbMix);
+ if (_clip.FilterEnabled)
+ _wav.PlatformSetFilter(_clip.FilterMode, _trackFilterQFactor, _trackFilterFrequency);
}
public override void SetFade(float fadeInDuration, float fadeOutDuration)
@@ -223,12 +281,6 @@ public override bool Update(float dt)
return _wav != null && _wav.State != SoundState.Stopped;
}
-
- public override void Apply3D(AudioListener listener, AudioEmitter emitter)
- {
- if (_wav != null)
- _wav.Apply3D(listener, emitter);
- }
}
}
diff --git a/MonoGame.Framework/Audio/Xact/ReverbSettings.cs b/MonoGame.Framework/Audio/Xact/ReverbSettings.cs
new file mode 100644
index 00000000000..6c414f5f596
--- /dev/null
+++ b/MonoGame.Framework/Audio/Xact/ReverbSettings.cs
@@ -0,0 +1,69 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+using System.IO;
+
+namespace Microsoft.Xna.Framework.Audio
+{
+ class ReverbSettings
+ {
+ private readonly DspParameter[] _parameters = new DspParameter[22];
+
+ public ReverbSettings(BinaryReader reader)
+ {
+ _parameters[0] = new DspParameter(reader); // ReflectionsDelayMs
+ _parameters[1] = new DspParameter(reader); // ReverbDelayMs
+ _parameters[2] = new DspParameter(reader); // PositionLeft
+ _parameters[3] = new DspParameter(reader); // PositionRight
+ _parameters[4] = new DspParameter(reader); // PositionLeftMatrix
+ _parameters[5] = new DspParameter(reader); // PositionRightMatrix
+ _parameters[6] = new DspParameter(reader); // EarlyDiffusion
+ _parameters[7] = new DspParameter(reader); // LateDiffusion
+ _parameters[8] = new DspParameter(reader); // LowEqGain
+ _parameters[9] = new DspParameter(reader); // LowEqCutoff
+ _parameters[10] = new DspParameter(reader); // HighEqGain
+ _parameters[11] = new DspParameter(reader); // HighEqCutoff
+ _parameters[12] = new DspParameter(reader); // RearDelayMs
+ _parameters[13] = new DspParameter(reader); // RoomFilterFrequencyHz
+ _parameters[14] = new DspParameter(reader); // RoomFilterMainDb
+ _parameters[15] = new DspParameter(reader); // RoomFilterHighFrequencyDb
+ _parameters[16] = new DspParameter(reader); // ReflectionsGainDb
+ _parameters[17] = new DspParameter(reader); // ReverbGainDb
+ _parameters[18] = new DspParameter(reader); // DecayTimeSec
+ _parameters[19] = new DspParameter(reader); // DensityPct
+ _parameters[20] = new DspParameter(reader); // RoomSizeFeet
+ _parameters[21] = new DspParameter(reader); // WetDryMixPct
+ }
+
+ public float this[int index]
+ {
+ get { return _parameters[index].Value; }
+ set { _parameters[index].SetValue(value); }
+ }
+
+ public float ReflectionsDelayMs { get { return _parameters[0].Value; } }
+ public float ReverbDelayMs { get { return _parameters[1].Value; } }
+ public float PositionLeft { get { return _parameters[2].Value; } }
+ public float PositionRight { get { return _parameters[3].Value; } }
+ public float PositionLeftMatrix { get { return _parameters[4].Value; } }
+ public float PositionRightMatrix { get { return _parameters[5].Value; } }
+ public float EarlyDiffusion { get { return _parameters[6].Value; } }
+ public float LateDiffusion { get { return _parameters[7].Value; } }
+ public float LowEqGain { get { return _parameters[8].Value; } }
+ public float LowEqCutoff { get { return _parameters[9].Value; } }
+ public float HighEqGain { get { return _parameters[10].Value; } }
+ public float HighEqCutoff { get { return _parameters[11].Value; } }
+ public float RearDelayMs { get { return _parameters[12].Value; } }
+ public float RoomFilterFrequencyHz { get { return _parameters[13].Value; } }
+ public float RoomFilterMainDb { get { return _parameters[14].Value; } }
+ public float RoomFilterHighFrequencyDb { get { return _parameters[15].Value; } }
+ public float ReflectionsGainDb { get { return _parameters[16].Value; } }
+ public float ReverbGainDb { get { return _parameters[17].Value; } }
+ public float DecayTimeSec { get { return _parameters[18].Value; } }
+ public float DensityPct { get { return _parameters[19].Value; } }
+ public float RoomSizeFeet { get { return _parameters[20].Value; } }
+ public float WetDryMixPct { get { return _parameters[21].Value; } }
+ }
+}
\ No newline at end of file
diff --git a/MonoGame.Framework/Audio/Xact/RpcCurve.cs b/MonoGame.Framework/Audio/Xact/RpcCurve.cs
new file mode 100644
index 00000000000..bba1861d8bf
--- /dev/null
+++ b/MonoGame.Framework/Audio/Xact/RpcCurve.cs
@@ -0,0 +1,47 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+namespace Microsoft.Xna.Framework.Audio
+{
+ struct RpcCurve
+ {
+ public uint FileOffset;
+ public int Variable;
+ public bool IsGlobal;
+ public RpcParameter Parameter;
+ public RpcPoint[] Points;
+
+ public float Evaluate(float position)
+ {
+ // TODO: We need to implement the different RpcPointTypes.
+
+ var first = Points[0];
+ if (position <= first.Position)
+ return first.Value;
+
+ var second = Points[Points.Length - 1];
+ if (position >= second.Position)
+ return second.Value;
+
+ for (var i = 1; i < Points.Length; ++i)
+ {
+ second = Points[i];
+ if (second.Position >= position)
+ break;
+
+ first = second;
+ }
+
+ switch (first.Type)
+ {
+ default:
+ case RpcPointType.Linear:
+ {
+ var t = (position - first.Position) / (second.Position - first.Position);
+ return first.Value + ((second.Value - first.Value) * t);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/MonoGame.Framework/Audio/Xact/RpcParameter.cs b/MonoGame.Framework/Audio/Xact/RpcParameter.cs
new file mode 100644
index 00000000000..20b3d34d2fd
--- /dev/null
+++ b/MonoGame.Framework/Audio/Xact/RpcParameter.cs
@@ -0,0 +1,16 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+namespace Microsoft.Xna.Framework.Audio
+{
+ enum RpcParameter
+ {
+ Volume,
+ Pitch,
+ ReverbSend,
+ FilterFrequency,
+ FilterQFactor,
+ NumParameters,
+ }
+}
\ No newline at end of file
diff --git a/MonoGame.Framework/Audio/Xact/RpcPoint.cs b/MonoGame.Framework/Audio/Xact/RpcPoint.cs
new file mode 100644
index 00000000000..68be3816de9
--- /dev/null
+++ b/MonoGame.Framework/Audio/Xact/RpcPoint.cs
@@ -0,0 +1,13 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+namespace Microsoft.Xna.Framework.Audio
+{
+ struct RpcPoint
+ {
+ public RpcPointType Type;
+ public float Position;
+ public float Value;
+ }
+}
\ No newline at end of file
diff --git a/MonoGame.Framework/Audio/Xact/RpcPointType.cs b/MonoGame.Framework/Audio/Xact/RpcPointType.cs
new file mode 100644
index 00000000000..f70e1e949a2
--- /dev/null
+++ b/MonoGame.Framework/Audio/Xact/RpcPointType.cs
@@ -0,0 +1,14 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+namespace Microsoft.Xna.Framework.Audio
+{
+ enum RpcPointType
+ {
+ Linear,
+ Fast,
+ Slow,
+ SinCos
+ }
+}
\ No newline at end of file
diff --git a/MonoGame.Framework/Audio/Xact/RpcVariable.cs b/MonoGame.Framework/Audio/Xact/RpcVariable.cs
new file mode 100644
index 00000000000..39b76400f58
--- /dev/null
+++ b/MonoGame.Framework/Audio/Xact/RpcVariable.cs
@@ -0,0 +1,46 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+namespace Microsoft.Xna.Framework.Audio
+{
+ struct RpcVariable
+ {
+ public string Name;
+ public float Value;
+ public byte Flags;
+ public float InitValue;
+ public float MaxValue;
+ public float MinValue;
+
+ public bool IsPublic
+ {
+ get { return (Flags & 0x1) != 0; }
+ }
+
+ public bool IsReadOnly
+ {
+ get { return (Flags & 0x2) != 0; }
+ }
+
+ public bool IsGlobal
+ {
+ get { return (Flags & 0x4) == 0; }
+ }
+
+ public bool IsReserved
+ {
+ get { return (Flags & 0x8) != 0; }
+ }
+
+ public void SetValue(float value)
+ {
+ if (value < MinValue)
+ Value = MinValue;
+ else if (value > MaxValue)
+ Value = MaxValue;
+ else
+ Value = value;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MonoGame.Framework/Audio/Xact/SoundBank.cs b/MonoGame.Framework/Audio/Xact/SoundBank.cs
index 9520c4b6243..fad0d319db4 100644
--- a/MonoGame.Framework/Audio/Xact/SoundBank.cs
+++ b/MonoGame.Framework/Audio/Xact/SoundBank.cs
@@ -16,209 +16,206 @@ public class SoundBank : IDisposable
readonly AudioEngine _audioengine;
readonly string[] _waveBankNames;
readonly WaveBank[] _waveBanks;
- readonly Dictionary _cues = new Dictionary();
-
- public bool IsDisposed { get; private set; }
- internal AudioEngine AudioEngine { get { return _audioengine; } }
-
+ readonly float [] defaultProbability = new float [1] { 1.0f };
+ readonly Dictionary _sounds = new Dictionary();
+ readonly Dictionary _probabilities = new Dictionary ();
+
+ ///
+ /// Is true if the SoundBank has any live Cues in use.
+ ///
+ public bool IsInUse { get; private set; }
+
/// AudioEngine that will be associated with this sound bank.
/// Path to a .xsb SoundBank file.
public SoundBank(AudioEngine audioEngine, string fileName)
{
+ if (audioEngine == null)
+ throw new ArgumentNullException("audioEngine");
+ if (string.IsNullOrEmpty(fileName))
+ throw new ArgumentNullException("fileName");
+
_audioengine = audioEngine;
- fileName = FileHelpers.NormalizeFilePathSeparators(fileName);
-#if !ANDROID
- using (var stream = TitleContainer.OpenStream(fileName))
- {
-#else
- using (var fileStream = Game.Activity.Assets.Open(fileName))
+ using (var stream = AudioEngine.OpenStream(fileName))
+ using (var reader = new BinaryReader(stream))
{
- MemoryStream stream = new MemoryStream();
- fileStream.CopyTo(stream);
- stream.Position = 0;
-#endif
- using (var reader = new BinaryReader(stream))
- {
- // Thanks to Liandril for "xactxtract" for some of the offsets.
-
- uint magic = reader.ReadUInt32();
- if (magic != 0x4B424453) //"SDBK"
- throw new Exception ("Bad soundbank format");
-
- reader.ReadUInt16(); // toolVersion
-
- uint formatVersion = reader.ReadUInt16();
- // TODO: This does not match XNA, XNA uses 43.
- if (formatVersion != 46)
- Debug.WriteLine("Warning: SoundBank format {0} not supported.", formatVersion);
-
- reader.ReadUInt16(); // crc, TODO: Verify crc (FCS16)
-
- reader.ReadUInt32(); // lastModifiedLow
- reader.ReadUInt32(); // lastModifiedHigh
- reader.ReadByte(); // platform ???
-
- uint numSimpleCues = reader.ReadUInt16();
- uint numComplexCues = reader.ReadUInt16();
- reader.ReadUInt16(); //unkn
- reader.ReadUInt16(); // numTotalCues
- uint numWaveBanks = reader.ReadByte();
- reader.ReadUInt16(); // numSounds
- uint cueNameTableLen = reader.ReadUInt16();
- reader.ReadUInt16(); //unkn
-
- uint simpleCuesOffset = reader.ReadUInt32();
- uint complexCuesOffset = reader.ReadUInt32(); //unkn
- uint cueNamesOffset = reader.ReadUInt32();
- reader.ReadUInt32(); //unkn
- reader.ReadUInt32(); // variationTablesOffset
- reader.ReadUInt32(); //unkn
- uint waveBankNameTableOffset = reader.ReadUInt32();
- reader.ReadUInt32(); // cueNameHashTableOffset
- reader.ReadUInt32(); // cueNameHashValsOffset
- reader.ReadUInt32(); // soundsOffset
-
- //name = System.Text.Encoding.UTF8.GetString(soundbankreader.ReadBytes(64),0,64).Replace("\0","");
-
- //parse wave bank name table
- stream.Seek(waveBankNameTableOffset, SeekOrigin.Begin);
- _waveBanks = new WaveBank[numWaveBanks];
- _waveBankNames = new string[numWaveBanks];
- for (int i=0; i 0)
+ // Thanks to Liandril for "xactxtract" for some of the offsets.
+
+ uint magic = reader.ReadUInt32();
+ if (magic != 0x4B424453) //"SDBK"
+ throw new Exception ("Bad soundbank format");
+
+ reader.ReadUInt16(); // toolVersion
+
+ uint formatVersion = reader.ReadUInt16();
+ if (formatVersion != 43)
+ Debug.WriteLine("Warning: SoundBank format {0} not supported.", formatVersion);
+
+ reader.ReadUInt16(); // crc, TODO: Verify crc (FCS16)
+
+ reader.ReadUInt32(); // lastModifiedLow
+ reader.ReadUInt32(); // lastModifiedHigh
+ reader.ReadByte(); // platform ???
+
+ uint numSimpleCues = reader.ReadUInt16();
+ uint numComplexCues = reader.ReadUInt16();
+ reader.ReadUInt16(); //unkn
+ reader.ReadUInt16(); // numTotalCues
+ uint numWaveBanks = reader.ReadByte();
+ reader.ReadUInt16(); // numSounds
+ uint cueNameTableLen = reader.ReadUInt16();
+ reader.ReadUInt16(); //unkn
+
+ uint simpleCuesOffset = reader.ReadUInt32();
+ uint complexCuesOffset = reader.ReadUInt32(); //unkn
+ uint cueNamesOffset = reader.ReadUInt32();
+ reader.ReadUInt32(); //unkn
+ reader.ReadUInt32(); // variationTablesOffset
+ reader.ReadUInt32(); //unkn
+ uint waveBankNameTableOffset = reader.ReadUInt32();
+ reader.ReadUInt32(); // cueNameHashTableOffset
+ reader.ReadUInt32(); // cueNameHashValsOffset
+ reader.ReadUInt32(); // soundsOffset
+
+ //name = System.Text.Encoding.UTF8.GetString(soundbankreader.ReadBytes(64),0,64).Replace("\0","");
+
+ //parse wave bank name table
+ stream.Seek(waveBankNameTableOffset, SeekOrigin.Begin);
+ _waveBanks = new WaveBank[numWaveBanks];
+ _waveBankNames = new string[numWaveBanks];
+ for (int i=0; i 0)
+ {
+ stream.Seek(simpleCuesOffset, SeekOrigin.Begin);
+ for (int i = 0; i < numSimpleCues; i++)
{
- stream.Seek(simpleCuesOffset, SeekOrigin.Begin);
- for (int i = 0; i < numSimpleCues; i++)
+ reader.ReadByte(); // flags
+ uint soundOffset = reader.ReadUInt32();
+
+ var oldPosition = stream.Position;
+ stream.Seek(soundOffset, SeekOrigin.Begin);
+ XactSound sound = new XactSound(audioEngine, this, reader);
+ stream.Seek(oldPosition, SeekOrigin.Begin);
+
+ _sounds.Add(cueNames [i], new XactSound [] { sound } );
+ _probabilities.Add (cueNames [i], defaultProbability);
+ }
+ }
+
+ // Complex cues
+ if (numComplexCues > 0)
+ {
+ stream.Seek(complexCuesOffset, SeekOrigin.Begin);
+ for (int i = 0; i < numComplexCues; i++)
+ {
+ //Cue cue;
+
+ byte flags = reader.ReadByte();
+ if (((flags >> 2) & 1) != 0)
{
- reader.ReadByte(); // flags
uint soundOffset = reader.ReadUInt32();
+ reader.ReadUInt32(); //unkn
var oldPosition = stream.Position;
stream.Seek(soundOffset, SeekOrigin.Begin);
- XactSound sound = new XactSound(this, reader);
+ XactSound sound = new XactSound(audioEngine, this, reader);
stream.Seek(oldPosition, SeekOrigin.Begin);
- Cue cue = new Cue(_audioengine, cueNames[i], sound);
- _cues.Add(cue.Name, cue);
+ _sounds.Add (cueNames [numSimpleCues + i], new XactSound [] { sound });
+ _probabilities.Add (cueNames [numSimpleCues + i], defaultProbability);
}
- }
-
- // Complex cues
- if (numComplexCues > 0)
- {
- stream.Seek(complexCuesOffset, SeekOrigin.Begin);
- for (int i = 0; i < numComplexCues; i++)
+ else
{
- Cue cue;
+ uint variationTableOffset = reader.ReadUInt32();
+ reader.ReadUInt32(); // transitionTableOffset
- byte flags = reader.ReadByte();
- if (((flags >> 2) & 1) != 0)
- {
- uint soundOffset = reader.ReadUInt32();
- reader.ReadUInt32(); //unkn
+ //parse variation table
+ long savepos = stream.Position;
+ stream.Seek(variationTableOffset, SeekOrigin.Begin);
- var oldPosition = stream.Position;
- stream.Seek(soundOffset, SeekOrigin.Begin);
- XactSound sound = new XactSound(this, reader);
- stream.Seek(oldPosition, SeekOrigin.Begin);
+ uint numEntries = reader.ReadUInt16();
+ uint variationflags = reader.ReadUInt16();
+ reader.ReadByte();
+ reader.ReadUInt16();
+ reader.ReadByte();
- cue = new Cue(_audioengine, cueNames[numSimpleCues + i], sound);
- }
- else
- {
- uint variationTableOffset = reader.ReadUInt32();
- reader.ReadUInt32(); // transitionTableOffset
-
- //parse variation table
- long savepos = stream.Position;
- stream.Seek(variationTableOffset, SeekOrigin.Begin);
-
- uint numEntries = reader.ReadUInt16();
- uint variationflags = reader.ReadUInt16();
- reader.ReadByte();
- reader.ReadUInt16();
- reader.ReadByte();
+ XactSound[] cueSounds = new XactSound[numEntries];
+ float[] probs = new float[numEntries];
- XactSound[] cueSounds = new XactSound[numEntries];
- float[] probs = new float[numEntries];
-
- uint tableType = (variationflags >> 3) & 0x7;
- for (int j = 0; j < numEntries; j++)
+ uint tableType = (variationflags >> 3) & 0x7;
+ for (int j = 0; j < numEntries; j++)
+ {
+ switch (tableType)
{
- switch (tableType)
- {
- case 0: //Wave
- {
- int trackIndex = reader.ReadUInt16();
- int waveBankIndex = reader.ReadByte();
- reader.ReadByte(); // weightMin
- reader.ReadByte(); // weightMax
-
- cueSounds[j] = new XactSound(this, waveBankIndex, trackIndex);
- break;
- }
- case 1:
- {
- uint soundOffset = reader.ReadUInt32();
- reader.ReadByte(); // weightMin
- reader.ReadByte(); // weightMax
-
- var oldPosition = stream.Position;
- stream.Seek(soundOffset, SeekOrigin.Begin);
- cueSounds[j] = new XactSound(this, reader);
- stream.Seek(oldPosition, SeekOrigin.Begin);
- break;
- }
- case 3:
- {
- uint soundOffset = reader.ReadUInt32();
- reader.ReadSingle(); // weightMin
- reader.ReadSingle(); // weightMax
- reader.ReadUInt32(); // flags
-
- var oldPosition = stream.Position;
- stream.Seek(soundOffset, SeekOrigin.Begin);
- cueSounds[j] = new XactSound(this, reader);
- stream.Seek(oldPosition, SeekOrigin.Begin);
- break;
- }
- case 4: //CompactWave
- {
- int trackIndex = reader.ReadUInt16();
- int waveBankIndex = reader.ReadByte();
- cueSounds[j] = new XactSound(this, waveBankIndex, trackIndex);
- break;
- }
- default:
- throw new NotSupportedException();
- }
+ case 0: //Wave
+ {
+ int trackIndex = reader.ReadUInt16();
+ int waveBankIndex = reader.ReadByte();
+ reader.ReadByte(); // weightMin
+ reader.ReadByte(); // weightMax
+
+ cueSounds[j] = new XactSound(this, waveBankIndex, trackIndex);
+ break;
+ }
+ case 1:
+ {
+ uint soundOffset = reader.ReadUInt32();
+ reader.ReadByte(); // weightMin
+ reader.ReadByte(); // weightMax
+
+ var oldPosition = stream.Position;
+ stream.Seek(soundOffset, SeekOrigin.Begin);
+ cueSounds[j] = new XactSound(audioEngine, this, reader);
+ stream.Seek(oldPosition, SeekOrigin.Begin);
+ break;
+ }
+ case 3:
+ {
+ uint soundOffset = reader.ReadUInt32();
+ reader.ReadSingle(); // weightMin
+ reader.ReadSingle(); // weightMax
+ reader.ReadUInt32(); // flags
+
+ var oldPosition = stream.Position;
+ stream.Seek(soundOffset, SeekOrigin.Begin);
+ cueSounds[j] = new XactSound(audioEngine, this, reader);
+ stream.Seek(oldPosition, SeekOrigin.Begin);
+ break;
+ }
+ case 4: //CompactWave
+ {
+ int trackIndex = reader.ReadUInt16();
+ int waveBankIndex = reader.ReadByte();
+ cueSounds[j] = new XactSound(this, waveBankIndex, trackIndex);
+ break;
+ }
+ default:
+ throw new NotSupportedException();
}
-
- stream.Seek(savepos, SeekOrigin.Begin);
-
- cue = new Cue(_audioengine, cueNames[numSimpleCues + i], cueSounds, probs);
}
- // Instance limiting
- reader.ReadByte(); //instanceLimit
- reader.ReadUInt16(); //fadeInSec, divide by 1000.0f
- reader.ReadUInt16(); //fadeOutSec, divide by 1000.0f
- reader.ReadByte(); //instanceFlags
+ stream.Seek(savepos, SeekOrigin.Begin);
- _cues.Add(cue.Name, cue);
+ _sounds.Add (cueNames [numSimpleCues + i], cueSounds);
+ _probabilities.Add (cueNames [numSimpleCues + i], probs);
}
+
+ // Instance limiting
+ reader.ReadByte(); //instanceLimit
+ reader.ReadUInt16(); //fadeInSec, divide by 1000.0f
+ reader.ReadUInt16(); //fadeOutSec, divide by 1000.0f
+ reader.ReadByte(); //instanceFlags
}
- }
- }
+ }
+ }
}
internal SoundEffectInstance GetSoundEffectInstance(int waveBankIndex, int trackIndex)
@@ -237,7 +234,7 @@ internal SoundEffectInstance GetSoundEffectInstance(int waveBankIndex, int track
var sound = waveBank.GetSoundEffect(trackIndex);
return sound.GetPooledInstance(true);
}
-
+
///
/// Returns a pooled Cue object.
///
@@ -248,20 +245,46 @@ internal SoundEffectInstance GetSoundEffectInstance(int waveBankIndex, int track
///
public Cue GetCue(string name)
{
- // Note: In XNA this returns a new Cue instance, but that
- // generates garbage which is one reason to not do it.
- return _cues[name];
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentNullException("name");
+
+ XactSound[] sounds;
+ if (!_sounds.TryGetValue(name, out sounds))
+ throw new ArgumentException();
+
+ float [] probs;
+ if (!_probabilities.TryGetValue (name, out probs))
+ throw new ArgumentException ();
+
+ IsInUse = true;
+
+ var cue = new Cue (_audioengine, name, sounds, probs);
+ cue.Prepare();
+ return cue;
}
-
+
///
/// Plays a cue.
///
/// Name of the cue to play.
- public void PlayCue(string name)
- {
- var cue = GetCue(name);
+ public void PlayCue(string name)
+ {
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentNullException("name");
+
+ XactSound[] sounds;
+ if (!_sounds.TryGetValue(name, out sounds))
+ throw new ArgumentException();
+
+ float [] probs;
+ if (!_probabilities.TryGetValue (name, out probs))
+ throw new ArgumentException ();
+
+ IsInUse = true;
+ var cue = new Cue (_audioengine, name, sounds, probs);
+ cue.Prepare();
cue.Play();
- }
+ }
///
/// Plays a cue with static 3D positional information.
@@ -274,26 +297,67 @@ public void PlayCue(string name)
/// The cue emitter state.
public void PlayCue(string name, AudioListener listener, AudioEmitter emitter)
{
- var cue = GetCue(name);
- cue.Play();
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentNullException("name");
+
+ XactSound[] sounds;
+ if (!_sounds.TryGetValue(name, out sounds))
+ throw new InvalidOperationException();
+
+ float [] probs;
+ if (!_probabilities.TryGetValue (name, out probs))
+ throw new ArgumentException ();
+
+ IsInUse = true;
+
+ var cue = new Cue (_audioengine, name, sounds, probs);
+ cue.Prepare();
cue.Apply3D(listener, emitter);
+ cue.Play();
}
- #region IDisposable implementation
///
- /// Immediately releases any unmanaged resources used by this object.
+ /// This event is triggered when the SoundBank is disposed.
+ ///
+ public event EventHandler Disposing;
+
+ ///
+ /// Is true if the SoundBank has been disposed.
+ ///
+ public bool IsDisposed { get; private set; }
+
+ ///
+ /// Disposes the SoundBank.
///
public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ~SoundBank()
+ {
+ Dispose(false);
+ }
+
+ private void Dispose(bool disposing)
{
if (IsDisposed)
return;
- foreach (var cue in _cues.Values)
- cue.Dispose();
-
IsDisposed = true;
+
+ if (disposing)
+ {
+ //foreach (var cue in _cues.Values)
+ // cue.Dispose();
+
+ IsInUse = false;
+
+ if (Disposing != null)
+ Disposing(this, EventArgs.Empty);
+ }
}
- #endregion
}
}
diff --git a/MonoGame.Framework/Audio/Xact/VolumeEvent.cs b/MonoGame.Framework/Audio/Xact/VolumeEvent.cs
index 6e998828336..49adbf9dc29 100644
--- a/MonoGame.Framework/Audio/Xact/VolumeEvent.cs
+++ b/MonoGame.Framework/Audio/Xact/VolumeEvent.cs
@@ -38,6 +38,14 @@ public override void SetTrackVolume(float volume)
{
}
+ public override void SetTrackPan(float pan)
+ {
+ }
+
+ public override void SetState(float volume, float pitch, float reverbMix, float? filterFrequency, float? filterQFactor)
+ {
+ }
+
public override bool Update(float dt)
{
return false;
@@ -47,9 +55,6 @@ public override void SetFade(float fadeInDuration, float fadeOutDuration)
{
}
- public override void Apply3D(AudioListener listener, AudioEmitter emitter)
- {
- }
}
}
diff --git a/MonoGame.Framework/Audio/Xact/WaveBank.cs b/MonoGame.Framework/Audio/Xact/WaveBank.cs
index 5eccada30b1..c87e07d63ca 100644
--- a/MonoGame.Framework/Audio/Xact/WaveBank.cs
+++ b/MonoGame.Framework/Audio/Xact/WaveBank.cs
@@ -5,19 +5,14 @@
using System;
using System.IO;
-using Microsoft.Xna.Framework;
-using Microsoft.Xna.Framework.Utilities;
-
namespace Microsoft.Xna.Framework.Audio
{
/// Represents a collection of wave files.
public class WaveBank : IDisposable
{
- private SoundEffect[] _sounds;
+ private readonly SoundEffect[] _sounds;
private string _bankName;
- public bool IsDisposed { get; private set; }
-
struct Segment
{
public int Offset;
@@ -56,20 +51,28 @@ struct WaveBankData
private const int Flag_SeekTables = 0x00080000; // Bank includes seek tables.
private const int Flag_Mask = 0x000F0000;
- private const int MiniFormatTag_PCM = 0x0;
- private const int MiniFormatTag_XMA = 0x1;
- private const int MiniFormatTag_ADPCM = 0x2;
- private const int MiniForamtTag_WMA = 0x3;
+ ///
+ ///
+ public bool IsInUse { get; private set; }
+
+ ///
+ ///
+ public bool IsPrepared { get; private set; }
/// Instance of the AudioEngine to associate this wave bank with.
/// Path to the .xwb file to load.
/// This constructor immediately loads all wave data into memory at once.
public WaveBank(AudioEngine audioEngine, string nonStreamingWaveBankFilename)
{
+ if (audioEngine == null)
+ throw new ArgumentNullException("audioEngine");
+ if (string.IsNullOrEmpty(nonStreamingWaveBankFilename))
+ throw new ArgumentNullException("nonStreamingWaveBankFilename");
+
//XWB PARSING
//Adapted from MonoXNA
//Originally adaped from Luigi Auriemma's unxwb
-
+
WaveBankHeader wavebankheader;
WaveBankData wavebankdata;
WaveBankEntry wavebankentry;
@@ -85,19 +88,9 @@ public WaveBank(AudioEngine audioEngine, string nonStreamingWaveBankFilename)
int wavebank_offset = 0;
- nonStreamingWaveBankFilename = FileHelpers.NormalizeFilePathSeparators(nonStreamingWaveBankFilename);
+ BinaryReader reader = new BinaryReader(AudioEngine.OpenStream(nonStreamingWaveBankFilename));
-#if !ANDROID
- BinaryReader reader = new BinaryReader(TitleContainer.OpenStream(nonStreamingWaveBankFilename));
-#else
- Stream stream = Game.Activity.Assets.Open(nonStreamingWaveBankFilename);
- MemoryStream ms = new MemoryStream();
- stream.CopyTo( ms );
- stream.Close();
- ms.Position = 0;
- BinaryReader reader = new BinaryReader(ms);
-#endif
- reader.ReadBytes(4);
+ reader.ReadBytes(4);
wavebankheader.Version = reader.ReadInt32();
@@ -177,8 +170,8 @@ public WaveBank(AudioEngine audioEngine, string nonStreamingWaveBankFilename)
//SHOWFILEOFF;
//memset(&wavebankentry, 0, sizeof(wavebankentry));
- wavebankentry.LoopRegion.Length = 0;
- wavebankentry.LoopRegion.Offset = 0;
+ wavebankentry.LoopRegion.Length = 0;
+ wavebankentry.LoopRegion.Offset = 0;
if ((wavebankdata.Flags & Flag_Compact) != 0)
{
@@ -239,8 +232,8 @@ public WaveBank(AudioEngine audioEngine, string nonStreamingWaveBankFilename)
wavebankentry.PlayRegion.Offset += playregion_offset;
// Parse WAVEBANKMINIWAVEFORMAT
-
- int codec;
+
+ MiniFormatTag codec;
int chans;
int rate;
int align;
@@ -258,7 +251,7 @@ public WaveBank(AudioEngine audioEngine, string nonStreamingWaveBankFilename)
// | wBlockAlign
// wBitsPerSample
- codec = (wavebankentry.Format) & ((1 << 1) - 1);
+ codec = (MiniFormatTag)((wavebankentry.Format) & ((1 << 1) - 1));
chans = (wavebankentry.Format >> (1)) & ((1 << 3) - 1);
rate = (wavebankentry.Format >> (1 + 3 + 1)) & ((1 << 18) - 1);
align = (wavebankentry.Format >> (1 + 3 + 1 + 18)) & ((1 << 8) - 1);
@@ -295,7 +288,7 @@ public WaveBank(AudioEngine audioEngine, string nonStreamingWaveBankFilename)
// | wBlockAlign
// wBitsPerSample
- codec = (wavebankentry.Format) & ((1 << 2) - 1);
+ codec = (MiniFormatTag)((wavebankentry.Format) & ((1 << 2) - 1));
chans = (wavebankentry.Format >> (2)) & ((1 << 3) - 1);
rate = (wavebankentry.Format >> (2 + 3)) & ((1 << 18) - 1);
align = (wavebankentry.Format >> (2 + 3 + 18)) & ((1 << 8) - 1);
@@ -304,113 +297,16 @@ public WaveBank(AudioEngine audioEngine, string nonStreamingWaveBankFilename)
reader.BaseStream.Seek(wavebankentry.PlayRegion.Offset, SeekOrigin.Begin);
byte[] audiodata = reader.ReadBytes(wavebankentry.PlayRegion.Length);
-
- if (codec == MiniFormatTag_PCM) {
-
- //write PCM data into a wav
-#if DIRECTX
-
- // TODO: Wouldn't storing a SoundEffectInstance like this
- // result in the "parent" SoundEffect being garbage collected?
-
- SharpDX.Multimedia.WaveFormat waveFormat = new SharpDX.Multimedia.WaveFormat(rate, chans);
- var sfx = new SoundEffect(audiodata, 0, audiodata.Length, rate, (AudioChannels)chans, wavebankentry.LoopRegion.Offset, wavebankentry.LoopRegion.Length)
- {
- _format = waveFormat
- };
-
- _sounds[current_entry] = sfx;
-#else
- _sounds[current_entry] = new SoundEffect(audiodata, rate, (AudioChannels)chans);
-#endif
- } else if (codec == MiniForamtTag_WMA) { //WMA or xWMA (or XMA2)
- byte[] wmaSig = {0x30, 0x26, 0xb2, 0x75, 0x8e, 0x66, 0xcf, 0x11, 0xa6, 0xd9, 0x0, 0xaa, 0x0, 0x62, 0xce, 0x6c};
-
- bool isWma = true;
- for (int i=0; iInstance of the AudioEngine to associate this wave bank with.
/// Path to the .xwb to stream from.
/// DVD sector-aligned offset within the wave bank data file.
@@ -420,31 +316,61 @@ public WaveBank(AudioEngine audioEngine, string nonStreamingWaveBankFilename)
/// Note that packetsize is in sectors, which is 2048 bytes.
/// AudioEngine.Update() must be called at least once before using data from a streaming wave bank.
///
- public WaveBank(AudioEngine audioEngine, string streamingWaveBankFilename, int offset, short packetsize)
- : this(audioEngine, streamingWaveBankFilename)
- {
- if (offset != 0) {
- throw new NotImplementedException();
- }
- }
+ public WaveBank(AudioEngine audioEngine, string streamingWaveBankFilename, int offset, short packetsize)
+ : this(audioEngine, streamingWaveBankFilename)
+ {
+ if (offset != 0)
+ throw new NotImplementedException();
+ }
internal SoundEffect GetSoundEffect(int trackIndex)
{
return _sounds[trackIndex];
}
- #region IDisposable implementation
- public void Dispose ()
- {
+ ///
+ /// This event is triggered when the WaveBank is disposed.
+ ///
+ public event EventHandler Disposing;
+
+ ///
+ /// Is true if the WaveBank has been disposed.
+ ///
+ public bool IsDisposed { get; private set; }
+
+ ///
+ /// Disposes the WaveBank.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ~WaveBank()
+ {
+ Dispose(false);
+ }
+
+ private void Dispose(bool disposing)
+ {
if (IsDisposed)
return;
- foreach (var s in _sounds)
- s.Dispose();
-
IsDisposed = true;
+
+ if (disposing)
+ {
+ foreach (var s in _sounds)
+ s.Dispose();
+
+ IsPrepared = false;
+ IsInUse = false;
+
+ if (Disposing != null)
+ Disposing(this, EventArgs.Empty);
+ }
}
- #endregion
}
}
diff --git a/MonoGame.Framework/Audio/Xact/XactClip.cs b/MonoGame.Framework/Audio/Xact/XactClip.cs
index f372f0f73bc..fb8ffe5fbcb 100644
--- a/MonoGame.Framework/Audio/Xact/XactClip.cs
+++ b/MonoGame.Framework/Audio/Xact/XactClip.cs
@@ -7,50 +7,63 @@
namespace Microsoft.Xna.Framework.Audio
{
- class XactClip
- {
+ class XactClip
+ {
private readonly float _defaultVolume;
private float _volumeScale;
private float _volume;
- private readonly ClipEvent[] _events;
+ private readonly ClipEvent[] _events;
private float _time;
private int _nextEvent;
- public XactClip (SoundBank soundBank, BinaryReader clipReader)
- {
+ internal readonly bool FilterEnabled;
+ internal readonly FilterMode FilterMode;
+ internal readonly float FilterQ;
+ internal readonly ushort FilterFrequency;
+
+ internal readonly bool UseReverb;
+
+ public XactClip (SoundBank soundBank, BinaryReader clipReader, bool useReverb)
+ {
#pragma warning disable 0219
State = SoundState.Stopped;
- var volumeDb = XactHelpers.ParseDecibels(clipReader.ReadByte());
+ UseReverb = useReverb;
+
+ var volumeDb = XactHelpers.ParseDecibels(clipReader.ReadByte());
_defaultVolume = XactHelpers.ParseVolumeFromDecibels(volumeDb);
var clipOffset = clipReader.ReadUInt32();
- // Unknown!
- clipReader.ReadUInt32();
-
- var oldPosition = clipReader.BaseStream.Position;
- clipReader.BaseStream.Seek(clipOffset, SeekOrigin.Begin);
-
- var numEvents = clipReader.ReadByte();
- _events = new ClipEvent[numEvents];
-
- for (var i=0; i> 1) & 3);
+ FilterQ = (filterQAndFlags >> 3) * 0.01f;
+ FilterFrequency = clipReader.ReadUInt16();
+
+ var oldPosition = clipReader.BaseStream.Position;
+ clipReader.BaseStream.Seek(clipOffset, SeekOrigin.Begin);
+
+ var numEvents = clipReader.ReadByte();
+ _events = new ClipEvent[numEvents];
+
+ for (var i=0; i> 5) & 0xFFFF) * 0.001f;
var unknown = eventInfo >> 21;
- switch (eventId) {
+ switch (eventId) {
case 0:
// Stop Event
throw new NotImplementedException("Stop event");
- case 1:
+ case 1:
{
// Unknown!
clipReader.ReadByte();
@@ -61,9 +74,9 @@ public XactClip (SoundBank soundBank, BinaryReader clipReader)
var panEnabled = (eventFlags & 0x02) == 0x02;
var useCenterSpeaker = (eventFlags & 0x04) == 0x04;
- int trackIndex = clipReader.ReadUInt16();
+ int trackIndex = clipReader.ReadUInt16();
int waveBankIndex = clipReader.ReadByte();
- var loopCount = clipReader.ReadByte();
+ var loopCount = clipReader.ReadByte();
var panAngle = clipReader.ReadUInt16() / 100.0f;
var panArc = clipReader.ReadUInt16() / 100.0f;
@@ -79,10 +92,11 @@ public XactClip (SoundBank soundBank, BinaryReader clipReader)
VariationType.Ordered,
null,
null,
+ null,
loopCount,
false);
- break;
+ break;
}
case 3:
@@ -141,6 +155,7 @@ public XactClip (SoundBank soundBank, BinaryReader clipReader)
variationType,
null,
null,
+ null,
loopCount,
newWaveOnLoop);
@@ -173,14 +188,31 @@ public XactClip (SoundBank soundBank, BinaryReader clipReader)
var maxVolume = XactHelpers.ParseVolumeFromDecibels(clipReader.ReadByte());
// Filter variation
- var minFrequency = clipReader.ReadSingle() / 1000.0f;
- var maxFrequency = clipReader.ReadSingle() / 1000.0f;
+ var minFrequency = clipReader.ReadSingle();
+ var maxFrequency = clipReader.ReadSingle();
var minQ = clipReader.ReadSingle();
var maxQ = clipReader.ReadSingle();
// Unknown!
clipReader.ReadByte();
+ var variationFlags = clipReader.ReadByte();
+
+ // Enable pitch variation
+ Vector2? pitchVar = null;
+ if ((variationFlags & 0x10) == 0x10)
+ pitchVar = new Vector2(minPitch, maxPitch - minPitch);
+
+ // Enable volume variation
+ Vector2? volumeVar = null;
+ if ((variationFlags & 0x20) == 0x20)
+ volumeVar = new Vector2(minVolume, maxVolume - minVolume);
+
+ // Enable filter variation
+ Vector4? filterVar = null;
+ if ((variationFlags & 0x40) == 0x40)
+ filterVar = new Vector4(minFrequency, maxFrequency - minFrequency, minQ, maxQ - minQ);
+
_events[i] = new PlayWaveEvent(
this,
timeStamp,
@@ -191,8 +223,9 @@ public XactClip (SoundBank soundBank, BinaryReader clipReader)
null,
0,
VariationType.Ordered,
- new Vector2(minVolume, maxVolume - minVolume),
- new Vector2(minPitch, maxPitch - minPitch),
+ volumeVar,
+ pitchVar,
+ filterVar,
loopCount,
false);
@@ -223,8 +256,8 @@ public XactClip (SoundBank soundBank, BinaryReader clipReader)
var maxVolume = XactHelpers.ParseVolumeFromDecibels(clipReader.ReadByte());
// Filter variation range
- var minFrequency = clipReader.ReadSingle() / 1000.0f;
- var maxFrequency = clipReader.ReadSingle() / 1000.0f;
+ var minFrequency = clipReader.ReadSingle();
+ var maxFrequency = clipReader.ReadSingle();
var minQ = clipReader.ReadSingle();
var maxQ = clipReader.ReadSingle();
@@ -290,6 +323,7 @@ public XactClip (SoundBank soundBank, BinaryReader clipReader)
variationType,
volumeVar,
pitchVar,
+ filterVar,
loopCount,
newWaveOnLoop);
@@ -331,14 +365,14 @@ public XactClip (SoundBank soundBank, BinaryReader clipReader)
// Marker Event
throw new NotImplementedException("Marker event");
- default:
+ default:
throw new NotSupportedException("Unknown event " + eventId);
- }
- }
-
+ }
+ }
+
clipReader.BaseStream.Seek (oldPosition, SeekOrigin.Begin);
#pragma warning restore 0219
- }
+ }
internal void Update(float dt)
{
@@ -379,36 +413,45 @@ internal void SetFade(float fadeInDuration, float fadeOutDuration)
evt.SetFade(fadeInDuration, fadeOutDuration);
}
}
-
- public void Play()
- {
- _time = 0.0f;
+
+ internal void UpdateState(float volume, float pitch, float reverbMix, float? filterFrequency, float? filterQFactor)
+ {
+ _volumeScale = volume;
+ var trackVolume = _volume * _volumeScale;
+
+ foreach (var evt in _events)
+ evt.SetState(trackVolume, pitch, reverbMix, filterFrequency, filterQFactor);
+ }
+
+ public void Play()
+ {
+ _time = 0.0f;
_nextEvent = 0;
- SetVolume(_defaultVolume);
+ SetVolume(_defaultVolume);
State = SoundState.Playing;
Update(0);
}
- public void Resume()
- {
+ public void Resume()
+ {
foreach (var evt in _events)
evt.Resume();
State = SoundState.Playing;
- }
-
- public void Stop()
+ }
+
+ public void Stop()
{
foreach (var evt in _events)
evt.Stop();
State = SoundState.Stopped;
}
-
- public void Pause()
+
+ public void Pause()
{
- foreach (var evt in _events)
- evt.Pause();
+ foreach (var evt in _events)
+ evt.Pause();
State = SoundState.Paused;
}
@@ -422,7 +465,7 @@ public void Pause()
public void SetVolumeScale(float volume)
{
_volumeScale = volume;
- UpdateVolumes();
+ UpdateVolumes();
}
///
@@ -435,17 +478,17 @@ public void SetVolume(float volume)
UpdateVolumes();
}
- private void UpdateVolumes()
- {
+ private void UpdateVolumes()
+ {
var volume = _volume * _volumeScale;
foreach (var evt in _events)
evt.SetTrackVolume(volume);
- }
+ }
- public void Apply3D(AudioListener listener, AudioEmitter emitter)
+ public void SetPan(float pan)
{
foreach (var evt in _events)
- evt.Apply3D(listener, emitter);
+ evt.SetTrackPan(pan);
}
}
}
diff --git a/MonoGame.Framework/Audio/Xact/XactHelpers.cs b/MonoGame.Framework/Audio/Xact/XactHelpers.cs
index d9361ce0b88..0ce35af0b21 100644
--- a/MonoGame.Framework/Audio/Xact/XactHelpers.cs
+++ b/MonoGame.Framework/Audio/Xact/XactHelpers.cs
@@ -21,10 +21,10 @@ public static float ParseDecibels(byte decibles)
//0x5a -12.0
//0x14 -38.0
//0x00 -96.0
- var a = -96.0;
- var b = 0.432254984608615;
- var c = 80.1748600297963;
- var d = 67.7385212334047;
+ const double a = -96.0;
+ const double b = 0.432254984608615;
+ const double c = 80.1748600297963;
+ const double d = 67.7385212334047;
var dB = (float)(((a - d) / (1 + (Math.Pow(decibles / c, b)))) + d);
return dB;
@@ -41,10 +41,10 @@ public static float ParseVolumeFromDecibels(byte decibles)
//0x5a -12.0
//0x14 -38.0
//0x00 -96.0
- var a = -96.0;
- var b = 0.432254984608615;
- var c = 80.1748600297963;
- var d = 67.7385212334047;
+ const double a = -96.0;
+ const double b = 0.432254984608615;
+ const double c = 80.1748600297963;
+ const double d = 67.7385212334047;
var dB = (float)(((a - d) / (1 + (Math.Pow(decibles / c, b)))) + d);
return ParseVolumeFromDecibels(dB);
diff --git a/MonoGame.Framework/Audio/Xact/XactSound.cs b/MonoGame.Framework/Audio/Xact/XactSound.cs
index 0524d9222f5..90c54c8731a 100644
--- a/MonoGame.Framework/Audio/Xact/XactSound.cs
+++ b/MonoGame.Framework/Audio/Xact/XactSound.cs
@@ -7,9 +7,9 @@
namespace Microsoft.Xna.Framework.Audio
{
- class XactSound
- {
- private readonly bool _complexSound;
+ class XactSound
+ {
+ private readonly bool _complexSound;
private readonly XactClip[] _soundClips;
private readonly int _waveBankIndex;
private readonly int _trackIndex;
@@ -17,11 +17,18 @@ class XactSound
private readonly float _pitch;
private readonly uint _categoryID;
private readonly SoundBank _soundBank;
+ private readonly bool _useReverb;
private SoundEffectInstance _wave;
- private float _cueVolume;
+ private float _cueVolume = 1;
+ private float _cuePitch = 0;
+ private float _cueReverbMix = 0;
+ private float? _cueFilterFrequency;
+ private float? _cueFilterQFactor;
+ internal readonly int[] RpcCurves;
+
public XactSound(SoundBank soundBank, int waveBankIndex, int trackIndex)
{
_complexSound = false;
@@ -29,56 +36,73 @@ public XactSound(SoundBank soundBank, int waveBankIndex, int trackIndex)
_soundBank = soundBank;
_waveBankIndex = waveBankIndex;
_trackIndex = trackIndex;
+ RpcCurves = new int[0];
}
- public XactSound(SoundBank soundBank, BinaryReader soundReader)
- {
+ public XactSound(AudioEngine engine, SoundBank soundBank, BinaryReader soundReader)
+ {
_soundBank = soundBank;
-
+
var flags = soundReader.ReadByte();
_complexSound = (flags & 0x1) != 0;
var hasRPCs = (flags & 0x0E) != 0;
- var hasEffects = (flags & 0x10) != 0;
+ var hasDSPs = (flags & 0x10) != 0;
_categoryID = soundReader.ReadUInt16();
_volume = XactHelpers.ParseVolumeFromDecibels(soundReader.ReadByte());
_pitch = soundReader.ReadInt16() / 1000.0f;
- soundReader.ReadByte(); //priority
+ soundReader.ReadByte(); //priority
soundReader.ReadUInt16(); // filter stuff?
-
- uint numClips = 0;
- if (_complexSound)
- numClips = soundReader.ReadByte();
- else
+
+ var numClips = 0;
+ if (_complexSound)
+ numClips = soundReader.ReadByte();
+ else
{
- _trackIndex = soundReader.ReadUInt16();
- _waveBankIndex = soundReader.ReadByte();
- }
-
- if (hasRPCs)
- {
- var current = soundReader.BaseStream.Position;
- var dataLength = soundReader.ReadUInt16();
- soundReader.BaseStream.Seek(current + dataLength, SeekOrigin.Begin);
- }
-
- if (hasEffects)
- {
- var current = soundReader.BaseStream.Position;
- var dataLength = soundReader.ReadUInt16();
- soundReader.BaseStream.Seek(current + dataLength, SeekOrigin.Begin);
- }
-
- if (_complexSound)
+ _trackIndex = soundReader.ReadUInt16();
+ _waveBankIndex = soundReader.ReadByte();
+ }
+
+ if (!hasRPCs)
+ RpcCurves = new int[0];
+ else
{
- _soundClips = new XactClip[numClips];
- for (int i=0; i= category.maxInstances)
{
@@ -113,11 +140,11 @@ public void Play()
}
}
- if (_complexSound)
+ if (_complexSound)
{
- foreach (XactClip clip in _soundClips)
- clip.Play();
- }
+ foreach (XactClip clip in _soundClips)
+ clip.Play();
+ }
else
{
if (_wave != null && _wave.State != SoundState.Stopped && _wave.IsLooped)
@@ -132,11 +159,12 @@ public void Play()
return;
}
- _wave.Pitch = _pitch;
- _wave.Volume = _volume * category._volume[0];
+ _wave.Pitch = _pitch + _cuePitch;
+ _wave.Volume = _volume * _cueVolume * category._volume[0];
+ _wave.PlatformSetReverbMix(_useReverb ? _cueReverbMix : 0.0f);
_wave.Play();
- }
- }
+ }
+ }
internal void Update(float dt)
{
@@ -168,8 +196,8 @@ internal void StopAll(AudioStopOptions options)
}
}
}
-
- public void Stop(AudioStopOptions options)
+
+ public void Stop(AudioStopOptions options)
{
if (_complexSound)
{
@@ -184,41 +212,41 @@ public void Stop(AudioStopOptions options)
_wave = null;
}
}
- }
-
- public void Pause()
+ }
+
+ public void Pause()
{
- if (_complexSound)
+ if (_complexSound)
{
foreach (var sound in _soundClips)
{
if (sound.State == SoundState.Playing)
sound.Pause();
}
- }
+ }
else
{
if (_wave != null && _wave.State == SoundState.Playing)
_wave.Pause();
- }
- }
+ }
+ }
- public void Resume()
+ public void Resume()
{
- if (_complexSound)
+ if (_complexSound)
{
foreach (var sound in _soundClips)
{
if (sound.State == SoundState.Paused)
sound.Resume();
}
- }
+ }
else
{
if (_wave != null && _wave.State == SoundState.Paused)
_wave.Resume();
- }
- }
+ }
+ }
internal void UpdateCategoryVolume(float categoryVolume)
{
@@ -237,29 +265,60 @@ internal void UpdateCategoryVolume(float categoryVolume)
}
}
- internal void SetCueVolume(float volume)
- {
+ internal void UpdateState(AudioEngine engine, float volume, float pitch, float reverbMix, float? filterFrequency, float? filterQFactor)
+ {
_cueVolume = volume;
- var category = _soundBank.AudioEngine.Categories[_categoryID];
- UpdateCategoryVolume(category._volume[0]);
+ var finalVolume = _volume * _cueVolume * engine.Categories[_categoryID]._volume[0];
+
+ _cueReverbMix = reverbMix;
+ _cueFilterFrequency = filterFrequency;
+ _cueFilterQFactor = filterQFactor;
+
+ _cuePitch = pitch;
+ var finalPitch = _pitch + _cuePitch;
+
+ if (_complexSound)
+ {
+ foreach (var clip in _soundClips)
+ clip.UpdateState(finalVolume, finalPitch, _useReverb ? _cueReverbMix : 0.0f, _cueFilterFrequency, _cueFilterQFactor);
+ }
+ else if (_wave != null)
+ {
+ _wave.PlatformSetReverbMix(_useReverb ? _cueReverbMix : 0.0f);
+ _wave.Pitch = finalPitch;
+ }
+ }
+
+ internal void SetCuePan(float pan)
+ {
+ if (_complexSound)
+ {
+ foreach (var clip in _soundClips)
+ clip.SetPan(pan);
+ }
+ else
+ {
+ if (_wave != null)
+ _wave.Pan = pan;
+ }
}
- public bool Playing
+ public bool Playing
{
- get
+ get
{
- if (_complexSound)
+ if (_complexSound)
{
foreach (var clip in _soundClips)
if (clip.State == SoundState.Playing)
return true;
return false;
- }
+ }
return _wave != null && _wave.State == SoundState.Playing;
- }
- }
+ }
+ }
public bool Stopped
{
@@ -278,34 +337,20 @@ public bool Stopped
}
}
- public bool IsPaused
- {
- get
- {
- if (_complexSound)
+ public bool IsPaused
+ {
+ get
+ {
+ if (_complexSound)
{
- foreach (var clip in _soundClips)
- if (clip.State == SoundState.Paused)
+ foreach (var clip in _soundClips)
+ if (clip.State == SoundState.Paused)
return true;
- return false;
+ return false;
}
return _wave != null && _wave.State == SoundState.Paused;
- }
- }
-
- public void Apply3D(AudioListener listener, AudioEmitter emitter)
- {
- if (_complexSound)
- {
- foreach (var clip in _soundClips)
- clip.Apply3D(listener, emitter);
- }
- else
- {
- if (_wave != null)
- _wave.Apply3D(listener, emitter);
}
}
}
diff --git a/MonoGame.Framework/BoundingBox.cs b/MonoGame.Framework/BoundingBox.cs
index 077a01da9fc..c51d7e40b36 100644
--- a/MonoGame.Framework/BoundingBox.cs
+++ b/MonoGame.Framework/BoundingBox.cs
@@ -214,16 +214,11 @@ public void Contains(ref Vector3 point, out ContainmentType result)
|| point.Z > this.Max.Z)
{
result = ContainmentType.Disjoint;
- }//or if point is on box because coordonate of point is lesser or equal
- else if (point.X == this.Min.X
- || point.X == this.Max.X
- || point.Y == this.Min.Y
- || point.Y == this.Max.Y
- || point.Z == this.Min.Z
- || point.Z == this.Max.Z)
- result = ContainmentType.Intersects;
+ }
else
+ {
result = ContainmentType.Contains;
+ }
}
private static readonly Vector3 MaxVector3 = new Vector3(float.MaxValue);
diff --git a/MonoGame.Framework/Color.cs b/MonoGame.Framework/Color.cs
index 534d52c17b5..bbfc61b9ec4 100644
--- a/MonoGame.Framework/Color.cs
+++ b/MonoGame.Framework/Color.cs
@@ -162,16 +162,21 @@ static Color()
Yellow = new Color(0xff00ffff);
YellowGreen = new Color(0xff32cd9a);
}
- // ARGB
+
+ // Stored as RGBA with R in the least significant octet:
+ // |-------|-------|-------|-------
+ // A B G R
private uint _packedValue;
- private Color(uint packedValue)
+ ///
+ /// Constructs an RGBA color from a packed value.
+ /// The value is a 32-bit unsigned integer, with R in the least significant octet.
+ ///
+ /// The packed value.
+ [CLSCompliant(false)]
+ public Color(uint packedValue)
{
_packedValue = packedValue;
- // ARGB
- //_packedValue = (packedValue << 8) | ((packedValue & 0xff000000) >> 24);
- // ABGR
- //_packedValue = (packedValue & 0xff00ff00) | ((packedValue & 0x000000ff) << 16) | ((packedValue & 0x00ff0000) >> 16);
}
///
@@ -179,13 +184,8 @@ private Color(uint packedValue)
///
/// A representing color.
public Color(Vector4 color)
+ : this((int)(color.X * 255), (int)(color.Y * 255), (int)(color.Z * 255), (int)(color.W * 255))
{
- _packedValue = 0;
-
- R = (byte)MathHelper.Clamp(color.X * 255, Byte.MinValue, Byte.MaxValue);
- G = (byte)MathHelper.Clamp(color.Y * 255, Byte.MinValue, Byte.MaxValue);
- B = (byte)MathHelper.Clamp(color.Z * 255, Byte.MinValue, Byte.MaxValue);
- A = (byte)MathHelper.Clamp(color.W * 255, Byte.MinValue, Byte.MaxValue);
}
///
@@ -193,13 +193,8 @@ public Color(Vector4 color)
///
/// A representing color.
public Color(Vector3 color)
+ : this((int)(color.X * 255), (int)(color.Y * 255), (int)(color.Z * 255))
{
- _packedValue = 0;
-
- R = (byte)MathHelper.Clamp(color.X * 255, Byte.MinValue, Byte.MaxValue);
- G = (byte)MathHelper.Clamp(color.Y * 255, Byte.MinValue, Byte.MaxValue);
- B = (byte)MathHelper.Clamp(color.Z * 255, Byte.MinValue, Byte.MaxValue);
- A = 255;
}
///
@@ -209,12 +204,16 @@ public Color(Vector3 color)
/// The alpha component value from 0 to 255.
public Color(Color color, int alpha)
{
- _packedValue = 0;
+ if ((alpha & 0xFFFFFF00) != 0)
+ {
+ var clampedA = (uint)MathHelper.Clamp(alpha, Byte.MinValue, Byte.MaxValue);
- R = color.R;
- G = color.G;
- B = color.B;
- A = (byte)MathHelper.Clamp(alpha, Byte.MinValue, Byte.MaxValue);
+ _packedValue = (color._packedValue & 0x00FFFFFF) | (clampedA << 24);
+ }
+ else
+ {
+ _packedValue = (color._packedValue & 0x00FFFFFF) | ((uint)alpha << 24);
+ }
}
///
@@ -222,49 +221,60 @@ public Color(Color color, int alpha)
///
/// A for RGB values of new instance.
/// Alpha component value from 0.0f to 1.0f.
- public Color(Color color, float alpha)
+ public Color(Color color, float alpha):
+ this(color, (int)(alpha * 255))
{
- _packedValue = 0;
-
- R = color.R;
- G = color.G;
- B = color.B;
- A = (byte)MathHelper.Clamp(alpha * 255, Byte.MinValue, Byte.MaxValue);
}
///
- /// Constructs an RGBA color from scalars which representing red, green and blue values. Alpha value will be opaque.
+ /// Constructs an RGBA color from scalars representing red, green and blue values. Alpha value will be opaque.
///
/// Red component value from 0.0f to 1.0f.
/// Green component value from 0.0f to 1.0f.
/// Blue component value from 0.0f to 1.0f.
public Color(float r, float g, float b)
+ : this((int)(r * 255), (int)(g * 255), (int)(b * 255))
{
- _packedValue = 0;
-
- R = (byte)MathHelper.Clamp(r * 255, Byte.MinValue, Byte.MaxValue);
- G = (byte)MathHelper.Clamp(g * 255, Byte.MinValue, Byte.MaxValue);
- B = (byte)MathHelper.Clamp(b * 255, Byte.MinValue, Byte.MaxValue);
- A = 255;
}
///
- /// Constructs an RGBA color from scalars which representing red, green and blue values. Alpha value will be opaque.
+ /// Constructs an RGBA color from scalars representing red, green, blue and alpha values.
+ ///
+ /// Red component value from 0.0f to 1.0f.
+ /// Green component value from 0.0f to 1.0f.
+ /// Blue component value from 0.0f to 1.0f.
+ /// Alpha component value from 0.0f to 1.0f.
+ public Color(float r, float g, float b, float alpha)
+ : this((int)(r * 255), (int)(g * 255), (int)(b * 255), (int)(alpha * 255))
+ {
+ }
+
+ ///
+ /// Constructs an RGBA color from scalars representing red, green and blue values. Alpha value will be opaque.
///
/// Red component value from 0 to 255.
/// Green component value from 0 to 255.
/// Blue component value from 0 to 255.
public Color(int r, int g, int b)
{
- _packedValue = 0;
- R = (byte)MathHelper.Clamp(r, Byte.MinValue, Byte.MaxValue);
- G = (byte)MathHelper.Clamp(g, Byte.MinValue, Byte.MaxValue);
- B = (byte)MathHelper.Clamp(b, Byte.MinValue, Byte.MaxValue);
- A = (byte)255;
+ _packedValue = 0xFF000000; // A = 255
+
+ if (((r | g | b) & 0xFFFFFF00) != 0)
+ {
+ var clampedR = (uint)MathHelper.Clamp(r, Byte.MinValue, Byte.MaxValue);
+ var clampedG = (uint)MathHelper.Clamp(g, Byte.MinValue, Byte.MaxValue);
+ var clampedB = (uint)MathHelper.Clamp(b, Byte.MinValue, Byte.MaxValue);
+
+ _packedValue |= (clampedB << 16) | (clampedG << 8) | (clampedR);
+ }
+ else
+ {
+ _packedValue |= ((uint)b << 16) | ((uint)g << 8) | ((uint)r);
+ }
}
///
- /// Constructs an RGBA color from scalars which representing red, green, blue and alpha values.
+ /// Constructs an RGBA color from scalars representing red, green, blue and alpha values.
///
/// Red component value from 0 to 255.
/// Green component value from 0 to 255.
@@ -272,28 +282,34 @@ public Color(int r, int g, int b)
/// Alpha component value from 0 to 255.
public Color(int r, int g, int b, int alpha)
{
- _packedValue = 0;
- R = (byte)MathHelper.Clamp(r, Byte.MinValue, Byte.MaxValue);
- G = (byte)MathHelper.Clamp(g, Byte.MinValue, Byte.MaxValue);
- B = (byte)MathHelper.Clamp(b, Byte.MinValue, Byte.MaxValue);
- A = (byte)MathHelper.Clamp(alpha, Byte.MinValue, Byte.MaxValue);
+ if (((r | g | b | alpha) & 0xFFFFFF00) != 0)
+ {
+ var clampedR = (uint)MathHelper.Clamp(r, Byte.MinValue, Byte.MaxValue);
+ var clampedG = (uint)MathHelper.Clamp(g, Byte.MinValue, Byte.MaxValue);
+ var clampedB = (uint)MathHelper.Clamp(b, Byte.MinValue, Byte.MaxValue);
+ var clampedA = (uint)MathHelper.Clamp(alpha, Byte.MinValue, Byte.MaxValue);
+
+ _packedValue = (clampedA << 24) | (clampedB << 16) | (clampedG << 8) | (clampedR);
+ }
+ else
+ {
+ _packedValue = ((uint)alpha << 24) | ((uint)b << 16) | ((uint)g << 8) | ((uint)r);
+ }
}
///
- /// Constructs an RGBA color from scalars which representing red, green, blue and alpha values.
+ /// Constructs an RGBA color from scalars representing red, green, blue and alpha values.
///
- /// Red component value from 0.0f to 1.0f.
- /// Green component value from 0.0f to 1.0f.
- /// Blue component value from 0.0f to 1.0f.
- /// Alpha component value from 0.0f to 1.0f.
- public Color(float r, float g, float b, float alpha)
+ ///
+ /// This overload sets the values directly without clamping, and may therefore be faster than the other overloads.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public Color(byte r, byte g, byte b, byte alpha)
{
- _packedValue = 0;
-
- R = (byte)MathHelper.Clamp(r * 255, Byte.MinValue, Byte.MaxValue);
- G = (byte)MathHelper.Clamp(g * 255, Byte.MinValue, Byte.MaxValue);
- B = (byte)MathHelper.Clamp(b * 255, Byte.MinValue, Byte.MaxValue);
- A = (byte)MathHelper.Clamp(alpha * 255, Byte.MinValue, Byte.MaxValue);
+ _packedValue = ((uint)alpha << 24) | ((uint)b << 16) | ((uint)g << 8) | (r);
}
///
@@ -380,10 +396,7 @@ public byte A
/// true if the instances are equal; false otherwise.
public static bool operator ==(Color a, Color b)
{
- return (a.A == b.A &&
- a.R == b.R &&
- a.G == b.G &&
- a.B == b.B);
+ return (a._packedValue == b._packedValue);
}
///
@@ -394,7 +407,7 @@ public byte A
/// true if the instances are not equal; false otherwise.
public static bool operator !=(Color a, Color b)
{
- return !(a == b);
+ return (a._packedValue != b._packedValue);
}
///
@@ -1721,14 +1734,10 @@ public static Color Lerp(Color value1, Color value2, Single amount)
}
///
- /// Performs linear interpolation of using on MathHelper.
- /// Less efficient but more precise compared to .
- /// See remarks section of on MathHelper for more info.
+ /// should be used instead of this function.
///
- /// Source .
- /// Destination .
- /// Interpolation factor.
/// Interpolated .
+ [Obsolete("Color.Lerp should be used instead of this function.")]
public static Color LerpPrecise(Color value1, Color value2, Single amount)
{
amount = MathHelper.Clamp(amount, 0, 1);
@@ -1779,7 +1788,7 @@ public Vector4 ToVector4()
return new Vector4(R / 255.0f, G / 255.0f, B / 255.0f, A / 255.0f);
}
- ///
+ ///
/// Gets or sets packed value of this .
///
[CLSCompliant(false)]
@@ -1844,7 +1853,7 @@ public static Color FromNonPremultiplied(Vector4 vector)
/// A which contains premultiplied alpha data.
public static Color FromNonPremultiplied(int r, int g, int b, int a)
{
- return new Color((byte)(r * a / 255),(byte)(g * a / 255), (byte)(b * a / 255), a);
+ return new Color(r * a / 255, g * a / 255, b * a / 255, a);
}
#region IEquatable Members
diff --git a/MonoGame.Framework/Content/ContentManager.cs b/MonoGame.Framework/Content/ContentManager.cs
index 1bbcbab0785..38bf5eea6a8 100644
--- a/MonoGame.Framework/Content/ContentManager.cs
+++ b/MonoGame.Framework/Content/ContentManager.cs
@@ -37,7 +37,6 @@ public partial class ContentManager : IDisposable
{
'w', // Windows (DirectX)
'x', // Xbox360
- 'm', // WindowsPhone
'i', // iOS
'a', // Android
'd', // DesktopGL
@@ -47,6 +46,8 @@ public partial class ContentManager : IDisposable
'M', // WindowsPhone8
'r', // RaspberryPi
'P', // PlayStation4
+ 'v', // PSVita
+ 'O', // XboxOne
// NOTE: There are additional idenfiers for consoles that
// are not defined in this repository. Be sure to ask the
@@ -59,7 +60,6 @@ public partial class ContentManager : IDisposable
'p', // PlayStationMobile
'g', // Windows (OpenGL)
'l', // Linux
- 'u', // Ouya
};
@@ -295,59 +295,17 @@ protected T ReadAsset(string assetName, Action recordDisposableO
}
}
- Stream stream = null;
- try
+ // Try to load as XNB file
+ var stream = OpenStream(assetName);
+ using (var xnbReader = new BinaryReader(stream))
{
- //try load it traditionally
- stream = OpenStream(assetName);
-
- // Try to load as XNB file
- try
+ using (var reader = GetContentReaderFromXnb(assetName, stream, xnbReader, recordDisposableObject))
{
- using (BinaryReader xnbReader = new BinaryReader(stream))
- {
- using (ContentReader reader = GetContentReaderFromXnb(assetName, ref stream, xnbReader, recordDisposableObject))
- {
- result = reader.ReadAsset();
- if (result is GraphicsResource)
- ((GraphicsResource)result).Name = originalAssetName;
- }
- }
- }
- finally
- {
- if (stream != null)
- {
- stream.Dispose();
- }
+ result = reader.ReadAsset();
+ if (result is GraphicsResource)
+ ((GraphicsResource)result).Name = originalAssetName;
}
}
- catch (ContentLoadException ex)
- {
- //MonoGame try to load as a non-content file
-
- assetName = TitleContainer.GetFilename(Path.Combine(RootDirectory, assetName));
-
- assetName = Normalize(assetName);
-
- if (string.IsNullOrEmpty(assetName))
- {
- throw new ContentLoadException("Could not load " + originalAssetName + " asset as a non-content file!", ex);
- }
-
- result = ReadRawAsset(assetName, originalAssetName);
-
- // Because Raw Assets skip the ContentReader step, they need to have their
- // disopsables recorded here. Doing it outside of this catch will
- // result in disposables being logged twice.
- if (result is IDisposable)
- {
- if (recordDisposableObject != null)
- recordDisposableObject(result as IDisposable);
- else
- disposableAssets.Add(result as IDisposable);
- }
- }
if (result == null)
throw new ContentLoadException("Could not load " + originalAssetName + " asset!");
@@ -355,74 +313,7 @@ protected T ReadAsset(string assetName, Action recordDisposableO
return (T)result;
}
- protected virtual string Normalize(string assetName)
- {
- if (typeof(T) == typeof(Texture2D) || typeof(T) == typeof(Texture))
- {
- return Texture2DReader.Normalize(assetName);
- }
- else if ((typeof(T) == typeof(SpriteFont)))
- {
- return SpriteFontReader.Normalize(assetName);
- }
-#if !WINRT
- else if ((typeof(T) == typeof(Song)))
- {
- return SongReader.Normalize(assetName);
- }
- else if ((typeof(T) == typeof(SoundEffect)))
- {
- return SoundEffectReader.Normalize(assetName);
- }
-#endif
- else if ((typeof(T) == typeof(Effect)))
- {
- return EffectReader.Normalize(assetName);
- }
- return null;
- }
-
- protected virtual object ReadRawAsset(string assetName, string originalAssetName)
- {
- if (typeof(T) == typeof(Texture2D) || typeof(T) == typeof(Texture))
- {
- using (Stream assetStream = TitleContainer.OpenStream(assetName))
- {
- Texture2D texture = Texture2D.FromStream(
- graphicsDeviceService.GraphicsDevice, assetStream);
- texture.Name = originalAssetName;
- return texture;
- }
- }
- else if ((typeof(T) == typeof(SpriteFont)))
- {
- //result = new SpriteFont(Texture2D.FromFile(graphicsDeviceService.GraphicsDevice,assetName), null, null, null, 0, 0.0f, null, null);
- throw new NotImplementedException();
- }
-#if !DIRECTX
- else if ((typeof(T) == typeof(Song)))
- {
- return new Song(assetName);
- }
- else if ((typeof(T) == typeof(SoundEffect)))
- {
- using (Stream s = TitleContainer.OpenStream(assetName))
- return SoundEffect.FromStream(s);
- }
-#endif
- else if ((typeof(T) == typeof(Effect)))
- {
- using (Stream assetStream = TitleContainer.OpenStream(assetName))
- {
- var data = new byte[assetStream.Length];
- assetStream.Read(data, 0, (int)assetStream.Length);
- return new Effect(this.graphicsDeviceService.GraphicsDevice, data);
- }
- }
- return null;
- }
-
- private ContentReader GetContentReaderFromXnb(string originalAssetName, ref Stream stream, BinaryReader xnbReader, Action recordDisposableObject)
+ private ContentReader GetContentReaderFromXnb(string originalAssetName, Stream stream, BinaryReader xnbReader, Action recordDisposableObject)
{
// The first 4 bytes should be the "XNB" header. i use that to detect an invalid file
byte x = xnbReader.ReadByte();
@@ -457,60 +348,8 @@ private ContentReader GetContentReaderFromXnb(string originalAssetName, ref Stre
if (compressedLzx)
{
- //thanks to ShinAli (https://bitbucket.org/alisci01/xnbdecompressor)
- // default window size for XNB encoded files is 64Kb (need 16 bits to represent it)
- LzxDecoder dec = new LzxDecoder(16);
- decompressedStream = new MemoryStream(decompressedSize);
int compressedSize = xnbLength - 14;
- long startPos = stream.Position;
- long pos = startPos;
-
- while (pos - startPos < compressedSize)
- {
- // the compressed stream is seperated into blocks that will decompress
- // into 32Kb or some other size if specified.
- // normal, 32Kb output blocks will have a short indicating the size
- // of the block before the block starts
- // blocks that have a defined output will be preceded by a byte of value
- // 0xFF (255), then a short indicating the output size and another
- // for the block size
- // all shorts for these cases are encoded in big endian order
- int hi = stream.ReadByte();
- int lo = stream.ReadByte();
- int block_size = (hi << 8) | lo;
- int frame_size = 0x8000; // frame size is 32Kb by default
- // does this block define a frame size?
- if (hi == 0xFF)
- {
- hi = lo;
- lo = (byte)stream.ReadByte();
- frame_size = (hi << 8) | lo;
- hi = (byte)stream.ReadByte();
- lo = (byte)stream.ReadByte();
- block_size = (hi << 8) | lo;
- pos += 5;
- }
- else
- pos += 2;
-
- // either says there is nothing to decode
- if (block_size == 0 || frame_size == 0)
- break;
-
- dec.Decompress(stream, block_size, decompressedStream, frame_size);
- pos += block_size;
-
- // reset the position of the input just incase the bit buffer
- // read in some unused bytes
- stream.Seek(pos, SeekOrigin.Begin);
- }
-
- if (decompressedStream.Position != decompressedSize)
- {
- throw new ContentLoadException("Decompression of " + originalAssetName + " failed. ");
- }
-
- decompressedStream.Seek(0, SeekOrigin.Begin);
+ decompressedStream = new LzxDecoderStream(stream, decompressedSize, compressedSize);
}
else if (compressedLz4)
{
@@ -585,58 +424,16 @@ protected virtual void ReloadAsset(string originalAssetName, T currentAsset)
throw new InvalidOperationException("No Graphics Device Service");
}
}
-
- Stream stream = null;
- try
- {
- //try load it traditionally
- stream = OpenStream(assetName);
-
- // Try to load as XNB file
- try
- {
- using (BinaryReader xnbReader = new BinaryReader(stream))
- {
- using (ContentReader reader = GetContentReaderFromXnb(assetName, ref stream, xnbReader, null))
- {
- reader.InitializeTypeReaders();
- reader.ReadObject(currentAsset);
- reader.ReadSharedResources();
- }
- }
- }
- finally
- {
- if (stream != null)
- {
- stream.Dispose();
- }
- }
- }
- catch (ContentLoadException)
- {
- // Try to reload as a non-xnb file.
- // Just textures supported for now.
-
- assetName = TitleContainer.GetFilename(Path.Combine(RootDirectory, assetName));
-
- assetName = Normalize(assetName);
-
- ReloadRawAsset(currentAsset, assetName, originalAssetName);
- }
- }
- protected virtual void ReloadRawAsset(T asset, string assetName, string originalAssetName)
- {
- if (asset is Texture2D)
+ var stream = OpenStream(assetName);
+ using (var xnbReader = new BinaryReader(stream))
{
- using (Stream assetStream = TitleContainer.OpenStream(assetName))
+ using (var reader = GetContentReaderFromXnb(assetName, stream, xnbReader, null))
{
- var textureAsset = asset as Texture2D;
- textureAsset.Reload(assetStream);
+ reader.ReadAsset(currentAsset);
}
}
- }
+ }
public virtual void Unload()
{
diff --git a/MonoGame.Framework/Content/ContentReader.cs b/MonoGame.Framework/Content/ContentReader.cs
index e752c7597cb..f869b6ce98c 100644
--- a/MonoGame.Framework/Content/ContentReader.cs
+++ b/MonoGame.Framework/Content/ContentReader.cs
@@ -77,6 +77,19 @@ internal object ReadAsset()
return result;
}
+ internal object ReadAsset(T existingInstance)
+ {
+ InitializeTypeReaders();
+
+ // Read primary object
+ object result = ReadObject(existingInstance);
+
+ // Read shared resources
+ ReadSharedResources();
+
+ return result;
+ }
+
internal void InitializeTypeReaders()
{
typeReaderManager = new ContentTypeReaderManager();
@@ -147,7 +160,7 @@ private void RecordDisposable(T result)
public T ReadObject()
{
- return ReadObject(default(T));
+ return InnerReadObject(default(T));
}
public T ReadObject(ContentTypeReader typeReader)
diff --git a/MonoGame.Framework/Content/ContentReaders/EffectMaterialReader.cs b/MonoGame.Framework/Content/ContentReaders/EffectMaterialReader.cs
index 315dc07ed27..538e559237e 100644
--- a/MonoGame.Framework/Content/ContentReaders/EffectMaterialReader.cs
+++ b/MonoGame.Framework/Content/ContentReaders/EffectMaterialReader.cs
@@ -1,42 +1,6 @@
-#region License
-/*
-Microsoft Public License (Ms-PL)
-MonoGame - Copyright © 2009 The MonoGame Team
-
-All rights reserved.
-
-This license governs use of the accompanying software. If you use the software, you accept this license. If you do not
-accept the license, do not use the software.
-
-1. Definitions
-The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under
-U.S. copyright law.
-
-A "contribution" is the original software, or any additions or changes to the software.
-A "contributor" is any person that distributes its contribution under this license.
-"Licensed patents" are a contributor's patent claims that read directly on its contribution.
-
-2. Grant of Rights
-(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
-each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
-(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
-each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
-
-3. Conditions and Limitations
-(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
-(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software,
-your patent license from such contributor to the software ends automatically.
-(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution
-notices that are present in the software.
-(D) If you distribute any portion of the software in source code form, you may do so only under this license by including
-a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object
-code form, you may only do so under a license that complies with this license.
-(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees
-or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent
-permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular
-purpose and non-infringement.
-*/
-#endregion License
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
using System;
using System.Collections.Generic;
diff --git a/MonoGame.Framework/Content/ContentReaders/EffectReader.cs b/MonoGame.Framework/Content/ContentReaders/EffectReader.cs
index 313f7c4a3d9..ef295c65c4f 100644
--- a/MonoGame.Framework/Content/ContentReaders/EffectReader.cs
+++ b/MonoGame.Framework/Content/ContentReaders/EffectReader.cs
@@ -3,7 +3,6 @@
// file 'LICENSE.txt', which is part of this source code package.
using Microsoft.Xna.Framework.Graphics;
-using System.Linq;
namespace Microsoft.Xna.Framework.Content
{
@@ -12,22 +11,6 @@ internal class EffectReader : ContentTypeReader
public EffectReader()
{
}
- static string [] supportedExtensions = new string[] {".fxg"};
-
- public static string Normalize(string FileName)
- {
- return ContentTypeReader.Normalize(FileName, supportedExtensions);
- }
-
- private static string TryFindAnyCased(string search, string[] arr, params string[] extensions)
- {
- return arr.FirstOrDefault(s => extensions.Any(ext => s.ToLowerInvariant() == (search.ToLowerInvariant() + ext)));
- }
-
- private static bool Contains(string search, string[] arr)
- {
- return arr.Any(s => s == search);
- }
protected internal override Effect Read(ContentReader input, Effect existingInstance)
{
@@ -35,10 +18,8 @@ protected internal override Effect Read(ContentReader input, Effect existingInst
byte[] data = input.ContentManager.GetScratchBuffer(dataSize);
input.Read(data, 0, dataSize);
var effect = new Effect(input.GraphicsDevice, data, 0, dataSize);
-
effect.Name = input.AssetName;
-
return effect;
}
}
-}
\ No newline at end of file
+}
diff --git a/MonoGame.Framework/Content/ContentReaders/SongReader.cs b/MonoGame.Framework/Content/ContentReaders/SongReader.cs
index 79ca26d2b86..59df217bf48 100644
--- a/MonoGame.Framework/Content/ContentReaders/SongReader.cs
+++ b/MonoGame.Framework/Content/ContentReaders/SongReader.cs
@@ -1,43 +1,6 @@
-// #region License
-// /*
-// Microsoft Public License (Ms-PL)
-// MonoGame - Copyright © 2009 The MonoGame Team
-//
-// All rights reserved.
-//
-// This license governs use of the accompanying software. If you use the software, you accept this license. If you do not
-// accept the license, do not use the software.
-//
-// 1. Definitions
-// The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under
-// U.S. copyright law.
-//
-// A "contribution" is the original software, or any additions or changes to the software.
-// A "contributor" is any person that distributes its contribution under this license.
-// "Licensed patents" are a contributor's patent claims that read directly on its contribution.
-//
-// 2. Grant of Rights
-// (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
-// each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
-// (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
-// each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
-//
-// 3. Conditions and Limitations
-// (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
-// (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software,
-// your patent license from such contributor to the software ends automatically.
-// (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution
-// notices that are present in the software.
-// (D) If you distribute any portion of the software in source code form, you may do so only under this license by including
-// a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object
-// code form, you may only do so under a license that complies with this license.
-// (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees
-// or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent
-// permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular
-// purpose and non-infringement.
-// */
-// #endregion License
-//
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
using System;
using System.IO;
@@ -49,17 +12,6 @@ namespace Microsoft.Xna.Framework.Content
{
internal class SongReader : ContentTypeReader
{
-#if ANDROID
- static string[] supportedExtensions = new string[] { ".mp3", ".ogg", ".mid" };
-#else
- static string[] supportedExtensions = new string[] { ".mp3" };
-#endif
-
- internal static string Normalize(string fileName)
- {
- return Normalize(fileName, supportedExtensions);
- }
-
protected internal override Song Read(ContentReader input, Song existingInstance)
{
var path = input.ReadString();
diff --git a/MonoGame.Framework/Content/ContentReaders/SoundEffectReader.cs b/MonoGame.Framework/Content/ContentReaders/SoundEffectReader.cs
index 459d9de1c38..19450e2cb3f 100644
--- a/MonoGame.Framework/Content/ContentReaders/SoundEffectReader.cs
+++ b/MonoGame.Framework/Content/ContentReaders/SoundEffectReader.cs
@@ -1,71 +1,18 @@
-// #region License
-// /*
-// Microsoft Public License (Ms-PL)
-// MonoGame - Copyright © 2009 The MonoGame Team
-//
-// All rights reserved.
-//
-// This license governs use of the accompanying software. If you use the software, you accept this license. If you do not
-// accept the license, do not use the software.
-//
-// 1. Definitions
-// The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under
-// U.S. copyright law.
-//
-// A "contribution" is the original software, or any additions or changes to the software.
-// A "contributor" is any person that distributes its contribution under this license.
-// "Licensed patents" are a contributor's patent claims that read directly on its contribution.
-//
-// 2. Grant of Rights
-// (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
-// each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
-// (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
-// each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
-//
-// 3. Conditions and Limitations
-// (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
-// (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software,
-// your patent license from such contributor to the software ends automatically.
-// (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution
-// notices that are present in the software.
-// (D) If you distribute any portion of the software in source code form, you may do so only under this license by including
-// a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object
-// code form, you may only do so under a license that complies with this license.
-// (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees
-// or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent
-// permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular
-// purpose and non-infringement.
-// */
-// #endregion License
-//
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
using System;
-using System.IO;
-
using Microsoft.Xna.Framework.Audio;
-#if WINRT
-using SharpDX.XAudio2;
-#endif
namespace Microsoft.Xna.Framework.Content
{
internal class SoundEffectReader : ContentTypeReader
{
-#if ANDROID
- static string[] supportedExtensions = new string[] { ".wav", ".mp3", ".ogg", ".mid" };
-#else
- static string[] supportedExtensions = new string[] { ".wav", ".aiff", ".ac3", ".mp3" };
-#endif
-
- internal static string Normalize(string fileName)
- {
- return Normalize(fileName, supportedExtensions);
- }
-
protected internal override SoundEffect Read(ContentReader input, SoundEffect existingInstance)
{
- // NXB format for SoundEffect...
+ // XNB format for SoundEffect...
//
// Byte [format size] Format WAVEFORMATEX structure
// UInt32 Data size
@@ -74,9 +21,9 @@ protected internal override SoundEffect Read(ContentReader input, SoundEffect ex
// Int32 Loop length In bytes (length must be format block aligned)
// Int32 Duration In milliseconds
- // WAVEFORMATEX structure...
+ // The header containss the WAVEFORMATEX header structure
+ // defined as the following...
//
- //typedef struct {
// WORD wFormatTag; // byte[0] +2
// WORD nChannels; // byte[2] +2
// DWORD nSamplesPerSec; // byte[4] +4
@@ -84,74 +31,29 @@ protected internal override SoundEffect Read(ContentReader input, SoundEffect ex
// WORD nBlockAlign; // byte[12] +2
// WORD wBitsPerSample; // byte[14] +2
// WORD cbSize; // byte[16] +2
- //} WAVEFORMATEX;
-
- byte[] header = input.ReadBytes(input.ReadInt32());
- byte[] data = input.ReadBytes(input.ReadInt32());
- int loopStart = input.ReadInt32();
- int loopLength = input.ReadInt32();
- input.ReadInt32();
+ //
+ // We let the sound effect deal with parsing this based
+ // on what format the audio data actually is.
+
+ var headerSize = input.ReadInt32();
+ var header = input.ReadBytes(headerSize);
-#if DIRECTX
- var count = data.Length;
- var format = (int)BitConverter.ToUInt16(header, 0);
- var sampleRate = (int)BitConverter.ToUInt16(header, 4);
- var channels = BitConverter.ToUInt16(header, 2);
- //var avgBPS = (int)BitConverter.ToUInt16(header, 8);
- var blockAlignment = (int)BitConverter.ToUInt16(header, 12);
- //var bps = (int)BitConverter.ToUInt16(header, 14);
+ // Read the audio data buffer.
+ var dataSize = input.ReadInt32();
+ var data = input.ContentManager.GetScratchBuffer(dataSize);
+ input.Read(data, 0, dataSize);
- SharpDX.Multimedia.WaveFormat waveFormat;
- if (format == 1)
- waveFormat = new SharpDX.Multimedia.WaveFormat(sampleRate, channels);
- else if (format == 2)
- waveFormat = new SharpDX.Multimedia.WaveFormatAdpcm(sampleRate, channels, blockAlignment);
- else
- throw new NotSupportedException("Unsupported wave format!");
+ var loopStart = input.ReadInt32();
+ var loopLength = input.ReadInt32();
+ var durationMs = input.ReadInt32();
- return new SoundEffect(data, 0, count, sampleRate, (AudioChannels)channels, loopStart, loopLength)
- {
- _format = waveFormat,
- Name = input.AssetName,
- };
-#else
- if(loopStart == loopLength)
- {
- // do nothing. just killing the warning for non-DirectX path
- }
- if (header[0] == 2 && header[1] == 0)
- {
- // We've found MSADPCM data! Let's decode it here.
- using (MemoryStream origDataStream = new MemoryStream(data))
- {
- using (BinaryReader reader = new BinaryReader(origDataStream))
- {
- byte[] newData = MSADPCMToPCM.MSADPCM_TO_PCM(
- reader,
- header[2],
- (short) ((header[12] / header[2]) - 22)
- );
- data = newData;
- }
- }
-
- // This is PCM data now!
- header[0] = 1;
- }
-
- int sampleRate = (
- (header[4]) +
- (header[5] << 8) +
- (header[6] << 16) +
- (header[7] << 24)
- );
+ // Create the effect.
+ var effect = new SoundEffect(header, data, dataSize, durationMs, loopStart, loopLength);
- var channels = (header[2] == 2) ? AudioChannels.Stereo : AudioChannels.Mono;
- return new SoundEffect(data, sampleRate, channels)
- {
- Name = input.AssetName
- };
-#endif
- }
+ // Store the original asset name for debugging later.
+ effect.Name = input.AssetName;
+
+ return effect;
+ }
}
}
diff --git a/MonoGame.Framework/Content/ContentReaders/SpriteFontReader.cs b/MonoGame.Framework/Content/ContentReaders/SpriteFontReader.cs
index 32e596e96b3..6323e1362fe 100644
--- a/MonoGame.Framework/Content/ContentReaders/SpriteFontReader.cs
+++ b/MonoGame.Framework/Content/ContentReaders/SpriteFontReader.cs
@@ -1,30 +1,6 @@
-#region License
-/*
-MIT License
-Copyright © 2006 The Mono.Xna Team
-
-All rights reserved.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-#endregion License
-
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
using System;
using System.Collections.Generic;
@@ -40,13 +16,6 @@ internal SpriteFontReader()
{
}
- static string[] supportedExtensions = new string[] { ".spritefont" };
-
- internal static string Normalize(string fileName)
- {
- return Normalize(fileName, supportedExtensions);
- }
-
protected internal override SpriteFont Read(ContentReader input, SpriteFont existingInstance)
{
if (existingInstance != null)
diff --git a/MonoGame.Framework/Content/ContentReaders/Texture2DReader.cs b/MonoGame.Framework/Content/ContentReaders/Texture2DReader.cs
index b9c957638f4..6a2c3c76fe9 100644
--- a/MonoGame.Framework/Content/ContentReaders/Texture2DReader.cs
+++ b/MonoGame.Framework/Content/ContentReaders/Texture2DReader.cs
@@ -17,17 +17,6 @@ internal Texture2DReader()
// Do nothing
}
-#if ANDROID
- static string[] supportedExtensions = new string[] { ".jpg", ".bmp", ".jpeg", ".png", ".gif" };
-#else
- static string[] supportedExtensions = new string[] { ".jpg", ".bmp", ".jpeg", ".png", ".gif", ".pict", ".tga" };
-#endif
-
- internal static string Normalize(string fileName)
- {
- return Normalize(fileName, supportedExtensions);
- }
-
protected internal override Texture2D Read(ContentReader reader, Texture2D existingInstance)
{
Texture2D texture = null;
@@ -76,99 +65,116 @@ protected internal override Texture2D Read(ContentReader reader, Texture2D exist
}
texture = existingInstance ?? new Texture2D(reader.GraphicsDevice, width, height, levelCountOutput > 1, convertedFormat);
-
- for (int level = 0; level < levelCount; level++)
- {
- var levelDataSizeInBytes = reader.ReadInt32();
- var levelData = reader.ContentManager.GetScratchBuffer(levelDataSizeInBytes);
- reader.Read(levelData, 0, levelDataSizeInBytes);
- int levelWidth = width >> level;
- int levelHeight = height >> level;
+#if OPENGL
+ Threading.BlockOnUIThread(() =>
+ {
+#endif
+ for (int level = 0; level < levelCount; level++)
+ {
+ var levelDataSizeInBytes = reader.ReadInt32();
+ var levelData = reader.ContentManager.GetScratchBuffer(levelDataSizeInBytes);
+ reader.Read(levelData, 0, levelDataSizeInBytes);
+ int levelWidth = width >> level;
+ int levelHeight = height >> level;
- if (level >= levelCountOutput)
- continue;
+ if (level >= levelCountOutput)
+ continue;
- //Convert the image data if required
- switch (surfaceFormat)
- {
- case SurfaceFormat.Dxt1:
- case SurfaceFormat.Dxt1SRgb:
- case SurfaceFormat.Dxt1a:
- if (!reader.GraphicsDevice.GraphicsCapabilities.SupportsDxt1 && convertedFormat == SurfaceFormat.Color)
- levelData = DxtUtil.DecompressDxt1(levelData, levelWidth, levelHeight);
- break;
- case SurfaceFormat.Dxt3:
- case SurfaceFormat.Dxt3SRgb:
- if (!reader.GraphicsDevice.GraphicsCapabilities.SupportsS3tc)
- if (!reader.GraphicsDevice.GraphicsCapabilities.SupportsS3tc && convertedFormat == SurfaceFormat.Color)
- levelData = DxtUtil.DecompressDxt3(levelData, levelWidth, levelHeight);
- break;
- case SurfaceFormat.Dxt5:
- case SurfaceFormat.Dxt5SRgb:
- if (!reader.GraphicsDevice.GraphicsCapabilities.SupportsS3tc)
- if (!reader.GraphicsDevice.GraphicsCapabilities.SupportsS3tc && convertedFormat == SurfaceFormat.Color)
- levelData = DxtUtil.DecompressDxt5(levelData, levelWidth, levelHeight);
- break;
- case SurfaceFormat.Bgra5551:
- {
-#if OPENGL
- // Shift the channels to suit OpenGL
- int offset = 0;
- for (int y = 0; y < levelHeight; y++)
+ //Convert the image data if required
+ switch (surfaceFormat)
+ {
+ case SurfaceFormat.Dxt1:
+ case SurfaceFormat.Dxt1SRgb:
+ case SurfaceFormat.Dxt1a:
+ if (!reader.GraphicsDevice.GraphicsCapabilities.SupportsDxt1 && convertedFormat == SurfaceFormat.Color)
+ {
+ levelData = DxtUtil.DecompressDxt1(levelData, levelWidth, levelHeight);
+ levelDataSizeInBytes = levelData.Length;
+ }
+ break;
+ case SurfaceFormat.Dxt3:
+ case SurfaceFormat.Dxt3SRgb:
+ if (!reader.GraphicsDevice.GraphicsCapabilities.SupportsS3tc)
+ if (!reader.GraphicsDevice.GraphicsCapabilities.SupportsS3tc &&
+ convertedFormat == SurfaceFormat.Color)
+ {
+ levelData = DxtUtil.DecompressDxt3(levelData, levelWidth, levelHeight);
+ levelDataSizeInBytes = levelData.Length;
+ }
+ break;
+ case SurfaceFormat.Dxt5:
+ case SurfaceFormat.Dxt5SRgb:
+ if (!reader.GraphicsDevice.GraphicsCapabilities.SupportsS3tc)
+ if (!reader.GraphicsDevice.GraphicsCapabilities.SupportsS3tc &&
+ convertedFormat == SurfaceFormat.Color)
+ {
+ levelData = DxtUtil.DecompressDxt5(levelData, levelWidth, levelHeight);
+ levelDataSizeInBytes = levelData.Length;
+ }
+ break;
+ case SurfaceFormat.Bgra5551:
{
- for (int x = 0; x < levelWidth; x++)
+#if OPENGL
+ // Shift the channels to suit OpenGL
+ int offset = 0;
+ for (int y = 0; y < levelHeight; y++)
{
- ushort pixel = BitConverter.ToUInt16(levelData, offset);
- pixel = (ushort)(((pixel & 0x7FFF) << 1) | ((pixel & 0x8000) >> 15));
- levelData[offset] = (byte)(pixel);
- levelData[offset + 1] = (byte)(pixel >> 8);
- offset += 2;
+ for (int x = 0; x < levelWidth; x++)
+ {
+ ushort pixel = BitConverter.ToUInt16(levelData, offset);
+ pixel = (ushort)(((pixel & 0x7FFF) << 1) | ((pixel & 0x8000) >> 15));
+ levelData[offset] = (byte)(pixel);
+ levelData[offset + 1] = (byte)(pixel >> 8);
+ offset += 2;
+ }
}
- }
#endif
- }
- break;
- case SurfaceFormat.Bgra4444:
- {
+ }
+ break;
+ case SurfaceFormat.Bgra4444:
+ {
#if OPENGL
- // Shift the channels to suit OpenGL
- int offset = 0;
- for (int y = 0; y < levelHeight; y++)
- {
- for (int x = 0; x < levelWidth; x++)
- {
- ushort pixel = BitConverter.ToUInt16(levelData, offset);
- pixel = (ushort)(((pixel & 0x0FFF) << 4) | ((pixel & 0xF000) >> 12));
- levelData[offset] = (byte)(pixel);
- levelData[offset + 1] = (byte)(pixel >> 8);
- offset += 2;
- }
- }
+ // Shift the channels to suit OpenGL
+ int offset = 0;
+ for (int y = 0; y < levelHeight; y++)
+ {
+ for (int x = 0; x < levelWidth; x++)
+ {
+ ushort pixel = BitConverter.ToUInt16(levelData, offset);
+ pixel = (ushort)(((pixel & 0x0FFF) << 4) | ((pixel & 0xF000) >> 12));
+ levelData[offset] = (byte)(pixel);
+ levelData[offset + 1] = (byte)(pixel >> 8);
+ offset += 2;
+ }
+ }
#endif
- }
- break;
- case SurfaceFormat.NormalizedByte4:
- {
- int bytesPerPixel = surfaceFormat.GetSize();
- int pitch = levelWidth * bytesPerPixel;
- for (int y = 0; y < levelHeight; y++)
- {
- for (int x = 0; x < levelWidth; x++)
- {
- int color = BitConverter.ToInt32(levelData, y * pitch + x * bytesPerPixel);
- levelData[y * pitch + x * 4] = (byte)(((color >> 16) & 0xff)); //R:=W
- levelData[y * pitch + x * 4 + 1] = (byte)(((color >> 8) & 0xff)); //G:=V
- levelData[y * pitch + x * 4 + 2] = (byte)(((color) & 0xff)); //B:=U
- levelData[y * pitch + x * 4 + 3] = (byte)(((color >> 24) & 0xff)); //A:=Q
- }
- }
- }
- break;
- }
+ }
+ break;
+ case SurfaceFormat.NormalizedByte4:
+ {
+ int bytesPerPixel = surfaceFormat.GetSize();
+ int pitch = levelWidth * bytesPerPixel;
+ for (int y = 0; y < levelHeight; y++)
+ {
+ for (int x = 0; x < levelWidth; x++)
+ {
+ int color = BitConverter.ToInt32(levelData, y * pitch + x * bytesPerPixel);
+ levelData[y * pitch + x * 4] = (byte)(((color >> 16) & 0xff)); //R:=W
+ levelData[y * pitch + x * 4 + 1] = (byte)(((color >> 8) & 0xff)); //G:=V
+ levelData[y * pitch + x * 4 + 2] = (byte)(((color) & 0xff)); //B:=U
+ levelData[y * pitch + x * 4 + 3] = (byte)(((color >> 24) & 0xff)); //A:=Q
+ }
+ }
+ }
+ break;
+ }
- texture.SetData(level, null, levelData, 0, levelDataSizeInBytes);
- }
-
+ texture.SetData(level, null, levelData, 0, levelDataSizeInBytes);
+ }
+#if OPENGL
+ });
+#endif
+
return texture;
}
}
diff --git a/MonoGame.Framework/Content/ContentReaders/Texture3DReader.cs b/MonoGame.Framework/Content/ContentReaders/Texture3DReader.cs
index 4aa43e94516..c5f11f5d8ad 100644
--- a/MonoGame.Framework/Content/ContentReaders/Texture3DReader.cs
+++ b/MonoGame.Framework/Content/ContentReaders/Texture3DReader.cs
@@ -23,19 +23,26 @@ protected internal override Texture3D Read(ContentReader reader, Texture3D exist
texture = new Texture3D(reader.GraphicsDevice, width, height, depth, levelCount > 1, format);
else
texture = existingInstance;
-
- for (int i = 0; i < levelCount; i++)
+
+#if OPENGL
+ Threading.BlockOnUIThread(() =>
{
- int dataSize = reader.ReadInt32();
- byte[] data = reader.ContentManager.GetScratchBuffer(dataSize);
- reader.Read(data, 0, dataSize);
- texture.SetData(i, 0, 0, width, height, 0, depth, data, 0, dataSize);
+#endif
+ for (int i = 0; i < levelCount; i++)
+ {
+ int dataSize = reader.ReadInt32();
+ byte[] data = reader.ContentManager.GetScratchBuffer(dataSize);
+ reader.Read(data, 0, dataSize);
+ texture.SetData(i, 0, 0, width, height, 0, depth, data, 0, dataSize);
- // Calculate dimensions of next mip level.
- width = Math.Max(width >> 1, 1);
- height = Math.Max(height >> 1, 1);
- depth = Math.Max(depth >> 1, 1);
- }
+ // Calculate dimensions of next mip level.
+ width = Math.Max(width >> 1, 1);
+ height = Math.Max(height >> 1, 1);
+ depth = Math.Max(depth >> 1, 1);
+ }
+#if OPENGL
+ });
+#endif
return texture;
}
diff --git a/MonoGame.Framework/Content/ContentReaders/TextureCubeReader.cs b/MonoGame.Framework/Content/ContentReaders/TextureCubeReader.cs
index d1432467f74..30b626602db 100644
--- a/MonoGame.Framework/Content/ContentReaders/TextureCubeReader.cs
+++ b/MonoGame.Framework/Content/ContentReaders/TextureCubeReader.cs
@@ -23,18 +23,25 @@ protected internal override TextureCube Read(ContentReader reader, TextureCube e
else
textureCube = existingInstance;
- for (int face = 0; face < 6; face++)
+#if OPENGL
+ Threading.BlockOnUIThread(() =>
{
- for (int i=0; i((CubeMapFace)face, i, null, faceData, 0, faceSize);
+ for (int i = 0; i < levels; i++)
+ {
+ int faceSize = reader.ReadInt32();
+ byte[] faceData = reader.ContentManager.GetScratchBuffer(faceSize);
+ reader.Read(faceData, 0, faceSize);
+ textureCube.SetData((CubeMapFace)face, i, null, faceData, 0, faceSize);
+ }
}
- }
-
- return textureCube;
+#if OPENGL
+ });
+#endif
+
+ return textureCube;
}
}
}
diff --git a/MonoGame.Framework/Content/ContentReaders/VideoReader.cs b/MonoGame.Framework/Content/ContentReaders/VideoReader.cs
index 9b6e539a1f1..0a0b1cdd3de 100644
--- a/MonoGame.Framework/Content/ContentReaders/VideoReader.cs
+++ b/MonoGame.Framework/Content/ContentReaders/VideoReader.cs
@@ -11,21 +11,6 @@ namespace Microsoft.Xna.Framework.Content
{
internal class VideoReader : ContentTypeReader
{
-#if ANDROID
- static string[] supportedExtensions = new string[] { ".3gp", ".mkv", ".mp4", ".ts", ".webm" };
-#elif IOS || MONOMAC
- static string[] supportedExtensions = new string[] { ".mp4", ".mov", ".avi", ".m4v", ".3gp" };
-#elif WINDOWS || WINRT
- static string[] supportedExtensions = new string[] { ".wma" };
-#else
- static string[] supportedExtensions = new string[] { ".mp4", ".mov", ".avi", ".m4v" };
-#endif
-
- internal static string Normalize(string fileName)
- {
- return Normalize(fileName, supportedExtensions);
- }
-
protected internal override Video Read(ContentReader input, Video existingInstance)
{
string path = input.ReadObject();
diff --git a/MonoGame.Framework/Content/ContentTypeReader.cs b/MonoGame.Framework/Content/ContentTypeReader.cs
index e8732a8f0d5..7b478746783 100644
--- a/MonoGame.Framework/Content/ContentTypeReader.cs
+++ b/MonoGame.Framework/Content/ContentTypeReader.cs
@@ -1,60 +1,15 @@
-#region License
-/*
-MIT License
-Copyright © 2006 The Mono.Xna Team
-
-All rights reserved.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-#endregion License
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
using System;
using System.IO;
-#if ANDROID
-using System.Linq;
-using System.Collections.Generic;
-#endif
-
-#if WINRT
-using Windows.Storage;
-#endif
-
namespace Microsoft.Xna.Framework.Content
{
public abstract class ContentTypeReader
{
- #region Private Member Variables
-
- private Type targetType;
-#if ANDROID
- // Keep this static so we only call Game.Activity.Assets.List() once
- // No need to call it for each file if the list will never change.
- // We do need one file list per folder though.
- static Dictionary filesInFolders = new Dictionary();
-#endif
-
- #endregion Private Member Variables
-
-
- #region Public Properties
+ private Type _targetType;
public virtual bool CanDeserializeIntoExistingObject
{
@@ -63,7 +18,7 @@ public virtual bool CanDeserializeIntoExistingObject
public Type TargetType
{
- get { return this.targetType; }
+ get { return _targetType; }
}
public virtual int TypeVersion
@@ -71,124 +26,39 @@ public virtual int TypeVersion
get { return 0; } // The default version (unless overridden) is zero
}
- #endregion Public Properties
-
-
- #region Protected Constructors
-
protected ContentTypeReader(Type targetType)
{
- this.targetType = targetType;
+ _targetType = targetType;
}
- #endregion Protected Constructors
-
-
- #region Protected Methods
-
protected internal virtual void Initialize(ContentTypeReaderManager manager)
{
// Do nothing. Are we supposed to add ourselves to the manager?
}
protected internal abstract object Read(ContentReader input, object existingInstance);
-
- #endregion Protected Methods
-
- #region Internal Static Helper Methods
-#if ANDROID
- internal static string Normalize(string fileName, string[] extensions)
- {
- int index = fileName.LastIndexOf(Path.DirectorySeparatorChar);
- string path = string.Empty;
- string file = fileName;
- if (index >= 0)
- {
- file = fileName.Substring(index + 1, fileName.Length - index - 1);
- path = fileName.Substring(0, index);
- }
-
- // Only read the assets file list once
- string[] files = null;
- if (!filesInFolders.TryGetValue(path, out files))
- {
- files = Android.App.Application.Context.Assets.List(path);
- filesInFolders[path] = files;
- }
-
- if (files.Any(s => s == file))
- return fileName;
-
- // FirstOrDefault returns null as the default if the file is not found. This crashed Path.Combine so check
- // for it first.
- string file2 = files.FirstOrDefault(s => extensions.Any(ext => s.ToLowerInvariant() == (file.ToLowerInvariant() + ext)));
- if (String.IsNullOrEmpty(file2))
- return null;
- return Path.Combine(path, file2);
- }
-#else
- public static string Normalize(string fileName, string[] extensions)
- {
-#if WINRT
- if (MetroHelper.AppDataFileExists(fileName))
- return fileName;
-#else
- if (File.Exists(fileName))
- return fileName;
-#endif
-
- foreach (string ext in extensions)
- {
- // Concat the file name with valid extensions
- string fileNamePlusExt = fileName + ext;
-
-#if WINRT
- if (MetroHelper.AppDataFileExists(fileNamePlusExt))
- return fileNamePlusExt;
-#else
- if (File.Exists(fileNamePlusExt))
- return fileNamePlusExt;
-#endif
- }
-
- return null;
- }
-#endif
- #endregion
}
public abstract class ContentTypeReader : ContentTypeReader
{
- #region Protected Constructors
-
protected ContentTypeReader()
: base(typeof(T))
{
// Nothing
}
- #endregion Protected Constructors
-
-
- #region Protected Methods
-
protected internal override object Read(ContentReader input, object existingInstance)
{
// as per the documentation http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.content.contenttypereader.read.aspx
// existingInstance
// The object receiving the data, or null if a new instance of the object should be created.
- if (existingInstance == null) {
- return this.Read (input, default(T));
+ if (existingInstance == null)
+ {
+ return Read(input, default(T));
}
- else {
- return this.Read (input, (T)existingInstance);
- }
-
- //return Read(input, (T)existingInstance);
+ return Read(input, (T)existingInstance);
}
protected internal abstract T Read(ContentReader input, T existingInstance);
-
- #endregion Protected Methods
}
}
\ No newline at end of file
diff --git a/MonoGame.Framework/Curve.cs b/MonoGame.Framework/Curve.cs
index 24611151836..bea6e0ccbfc 100644
--- a/MonoGame.Framework/Curve.cs
+++ b/MonoGame.Framework/Curve.cs
@@ -17,9 +17,9 @@ public class Curve
{
#region Private Fields
- private CurveKeyCollection _keys;
- private CurveLoopType _postLoop;
private CurveLoopType _preLoop;
+ private CurveLoopType _postLoop;
+ private CurveKeyCollection _keys;
#endregion
@@ -35,12 +35,13 @@ public bool IsConstant
}
///
- /// The collection of curve keys.
+ /// Defines how to handle weighting values that are less than the first control point in the curve.
///
[DataMember]
- public CurveKeyCollection Keys
+ public CurveLoopType PreLoop
{
- get { return this._keys; }
+ get { return this._preLoop; }
+ set { this._preLoop = value; }
}
///
@@ -54,13 +55,12 @@ public CurveLoopType PostLoop
}
///
- /// Defines how to handle weighting values that are less than the first control point in the curve.
+ /// The collection of curve keys.
///
[DataMember]
- public CurveLoopType PreLoop
+ public CurveKeyCollection Keys
{
- get { return this._preLoop; }
- set { this._preLoop = value; }
+ get { return this._keys; }
}
#endregion
diff --git a/MonoGame.Framework/Desktop/OpenTKGamePlatform.cs b/MonoGame.Framework/Desktop/OpenTKGamePlatform.cs
deleted file mode 100644
index 91e7cddd99d..00000000000
--- a/MonoGame.Framework/Desktop/OpenTKGamePlatform.cs
+++ /dev/null
@@ -1,327 +0,0 @@
-#region License
-/*
-Microsoft Public License (Ms-PL)
-MonoGame - Copyright © 2009-2011 The MonoGame Team
-
-All rights reserved.
-
-This license governs use of the accompanying software. If you use the software,
-you accept this license. If you do not accept the license, do not use the
-software.
-
-1. Definitions
-
-The terms "reproduce," "reproduction," "derivative works," and "distribution"
-have the same meaning here as under U.S. copyright law.
-
-A "contribution" is the original software, or any additions or changes to the
-software.
-
-A "contributor" is any person that distributes its contribution under this
-license.
-
-"Licensed patents" are a contributor's patent claims that read directly on its
-contribution.
-
-2. Grant of Rights
-
-(A) Copyright Grant- Subject to the terms of this license, including the
-license conditions and limitations in section 3, each contributor grants you a
-non-exclusive, worldwide, royalty-free copyright license to reproduce its
-contribution, prepare derivative works of its contribution, and distribute its
-contribution or any derivative works that you create.
-
-(B) Patent Grant- Subject to the terms of this license, including the license
-conditions and limitations in section 3, each contributor grants you a
-non-exclusive, worldwide, royalty-free license under its licensed patents to
-make, have made, use, sell, offer for sale, import, and/or otherwise dispose of
-its contribution in the software or derivative works of the contribution in the
-software.
-
-3. Conditions and Limitations
-
-(A) No Trademark License- This license does not grant you rights to use any
-contributors' name, logo, or trademarks.
-
-(B) If you bring a patent claim against any contributor over patents that you
-claim are infringed by the software, your patent license from such contributor
-to the software ends automatically.
-
-(C) If you distribute any portion of the software, you must retain all
-copyright, patent, trademark, and attribution notices that are present in the
-software.
-
-(D) If you distribute any portion of the software in source code form, you may
-do so only under this license by including a complete copy of this license with
-your distribution. If you distribute any portion of the software in compiled or
-object code form, you may only do so under a license that complies with this
-license.
-
-(E) The software is licensed "as-is." You bear the risk of using it. The
-contributors give no express warranties, guarantees or conditions. You may have
-additional consumer rights under your local laws which this license cannot
-change. To the extent permitted under your local laws, the contributors exclude
-the implied warranties of merchantability, fitness for a particular purpose and
-non-infringement.
-*/
-#endregion License
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading;
-
-using Microsoft.Xna.Framework.Audio;
-using Microsoft.Xna.Framework.Graphics;
-using Microsoft.Xna.Framework.Input.Touch;
-using Microsoft.Xna.Framework.Input;
-
-using OpenTK;
-using OpenTK.Graphics;
-
-using MonoGame.Utilities;
-
-namespace Microsoft.Xna.Framework
-{
- ///
- /// The backend options for OpenTK.
- ///
- public enum Backend
- {
- ///
- /// Use the default backend for the current OS. If SDL2 is found, it will be used.
- ///
- Default,
-
- ///
- /// Use the native backend. SDL2 is not considered.
- ///
- Native
- }
-
- ///
- /// Parameters that are used in configuring the platform.
- ///
- public static class PlatformParameters
- {
- ///
- /// The preferred backend for OpenTK to use.
- ///
- public static Backend PreferredBackend = MonoGame.Utilities.CurrentPlatform.OS == OS.Linux ? Backend.Native : Backend.Default;
- }
-
- class OpenTKGamePlatform : GamePlatform
- {
- private OpenTKGameWindow _view;
- private OpenALSoundController soundControllerInstance = null;
- // stored the current screen state, so we can check if it has changed.
- private bool isCurrentlyFullScreen = false;
- private int isExiting; // int, so we can use Interlocked.Increment
-
- int windowDelay = 2;
-
- public OpenTKGamePlatform(Game game)
- : base(game)
- {
- if (PlatformParameters.PreferredBackend != Backend.Default)
- Toolkit.Init(new ToolkitOptions { Backend = PlatformBackend.PreferNative });
-
- _view = new OpenTKGameWindow(game);
- this.Window = _view;
-
- // Setup our OpenALSoundController to handle our SoundBuffer pools
- try
- {
- soundControllerInstance = OpenALSoundController.GetInstance;
- }
- catch (DllNotFoundException ex)
- {
- throw (new NoAudioHardwareException("Failed to init OpenALSoundController", ex));
- }
- }
-
- public override GameRunBehavior DefaultRunBehavior
- {
- get { return GameRunBehavior.Synchronous; }
- }
-
- protected override void OnIsMouseVisibleChanged()
- {
- _view.SetMouseVisible(IsMouseVisible);
- }
-
- public override void RunLoop()
- {
- ResetWindowBounds();
- while (true)
- {
- _view.ProcessEvents();
-
- // Stop the main loop iff Game.Exit() has been called.
- // This can happen under the following circumstances:
- // 1. Game.Exit() is called programmatically.
- // 2. The GameWindow is closed through the 'X' (close) button
- // 3. The GameWindow is closed through Alt-F4 or Cmd-Q
- // Note: once Game.Exit() is called, we must stop raising
- // Update or Draw events as the GameWindow and/or OpenGL context
- // may no longer be available.
- // Note 2: Game.Exit() can be called asynchronously from
- // _view.ProcessEvents() (cases #2 and #3 above), so the
- // isExiting check must be placed *after* _view.ProcessEvents()
- // Note 3: We need to continue processing view events until
- // everything gets disposed of, otherwise it will close the window
- // and make the window handle invalid
- if (isExiting == 0)
- Game.Tick();
- else if (windowDelay == 2)
- {
- windowDelay--;
- Game.ExitEverything();
- }
- else if (windowDelay > 0)
- windowDelay--;
- else
- {
- _view.Dispose();
- break;
- }
- }
- }
-
- public override void StartRunLoop()
- {
- throw new NotSupportedException("The desktop platform does not support asynchronous run loops");
- }
-
- public override void Exit()
- {
- //(SJ) Why is this called here when it's not in any other project
- //Net.NetworkSession.Exit();
- Interlocked.Increment(ref isExiting);
-
- OpenTK.DisplayDevice.Default.RestoreResolution();
- }
-
- public override bool BeforeUpdate(GameTime gameTime)
- {
- IsActive = _view.Window.Focused;
-
- // Update our OpenAL sound buffer pools
- if (soundControllerInstance != null)
- soundControllerInstance.Update();
- return true;
- }
-
- public override bool BeforeDraw(GameTime gameTime)
- {
- return true;
- }
-
- public override void EnterFullScreen()
- {
- ResetWindowBounds();
- }
-
- public override void ExitFullScreen()
- {
- ResetWindowBounds();
- }
-
- internal void ResetWindowBounds()
- {
- Rectangle bounds;
-
- bounds = Window.ClientBounds;
-
- //Changing window style forces a redraw. Some games
- //have fail-logic and toggle fullscreen in their draw function,
- //so temporarily become inactive so it won't execute.
-
- bool wasActive = IsActive;
- IsActive = false;
-
- var graphicsDeviceManager = (GraphicsDeviceManager)
- Game.Services.GetService(typeof(IGraphicsDeviceManager));
-
- if (graphicsDeviceManager.IsFullScreen)
- {
- bounds = new Rectangle(0, 0,graphicsDeviceManager.PreferredBackBufferWidth,graphicsDeviceManager.PreferredBackBufferHeight);
-
- if (OpenTK.DisplayDevice.Default.Width != graphicsDeviceManager.PreferredBackBufferWidth ||
- OpenTK.DisplayDevice.Default.Height != graphicsDeviceManager.PreferredBackBufferHeight)
- {
- OpenTK.DisplayDevice.Default.ChangeResolution(graphicsDeviceManager.PreferredBackBufferWidth,
- graphicsDeviceManager.PreferredBackBufferHeight,
- OpenTK.DisplayDevice.Default.BitsPerPixel,
- OpenTK.DisplayDevice.Default.RefreshRate);
- }
- }
- else
- {
-
- // switch back to the normal screen resolution
- OpenTK.DisplayDevice.Default.RestoreResolution();
- // now update the bounds
- bounds.Width = graphicsDeviceManager.PreferredBackBufferWidth;
- bounds.Height = graphicsDeviceManager.PreferredBackBufferHeight;
- }
-
-
- // Now we set our Presentation Parameters
- var device = (GraphicsDevice)graphicsDeviceManager.GraphicsDevice;
- // FIXME: Eliminate the need for null checks by only calling
- // ResetWindowBounds after the device is ready. Or,
- // possibly break this method into smaller methods.
- if (device != null)
- {
- PresentationParameters parms = device.PresentationParameters;
- parms.BackBufferHeight = (int)bounds.Height;
- parms.BackBufferWidth = (int)bounds.Width;
-
- var viewport = new Viewport(0, 0,
- parms.BackBufferWidth,
- parms.BackBufferHeight);
-
- device.Viewport = viewport;
- }
-
- if (graphicsDeviceManager.IsFullScreen != isCurrentlyFullScreen)
- {
- _view.ToggleFullScreen();
- }
-
- // we only change window bounds if we are not fullscreen
- // or if fullscreen mode was just entered
- if (!graphicsDeviceManager.IsFullScreen || (graphicsDeviceManager.IsFullScreen != isCurrentlyFullScreen))
- _view.ChangeClientBounds(bounds);
-
- // store the current fullscreen state
- isCurrentlyFullScreen = graphicsDeviceManager.IsFullScreen;
-
- IsActive = wasActive;
- }
-
- public override void EndScreenDeviceChange(string screenDeviceName, int clientWidth, int clientHeight)
- {
-
- }
-
- public override void BeginScreenDeviceChange(bool willBeFullScreen)
- {
-
- }
-
- public override void Log(string Message)
- {
- Console.WriteLine(Message);
- }
-
- public override void Present()
- {
- var device = Game.GraphicsDevice;
- if (device != null)
- device.Present();
- }
- }
-}
diff --git a/MonoGame.Framework/Desktop/OpenTKGameWindow.cs b/MonoGame.Framework/Desktop/OpenTKGameWindow.cs
deleted file mode 100644
index 2e87da17652..00000000000
--- a/MonoGame.Framework/Desktop/OpenTKGameWindow.cs
+++ /dev/null
@@ -1,496 +0,0 @@
-#region License
-/*
-Microsoft Public License (Ms-PL)
-XnaTouch - Copyright © 2009 The XnaTouch Team
-
-All rights reserved.
-
-This license governs use of the accompanying software. If you use the software, you accept this license. If you do not
-accept the license, do not use the software.
-
-1. Definitions
-The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under
-U.S. copyright law.
-
-A "contribution" is the original software, or any additions or changes to the software.
-A "contributor" is any person that distributes its contribution under this license.
-"Licensed patents" are a contributor's patent claims that read directly on its contribution.
-
-2. Grant of Rights
-(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
-each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
-(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
-each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
-
-3. Conditions and Limitations
-(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
-(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software,
-your patent license from such contributor to the software ends automatically.
-(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution
-notices that are present in the software.
-(D) If you distribute any portion of the software in source code form, you may do so only under this license by including
-a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object
-code form, you may only do so under a license that complies with this license.
-(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees
-or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent
-permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular
-purpose and non-infringement.
-*/
-using MonoGame.Utilities;
-
-
-#endregion License
-
-#region Using Statements
-using System;
-using System.ComponentModel;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Reflection;
-using Microsoft.Xna.Framework.Graphics;
-using Microsoft.Xna.Framework.Input;
-using OpenTK;
-using OpenTK.Graphics;
-
-
-#endregion Using Statements
-
-namespace Microsoft.Xna.Framework
-{
- class OpenTKGameWindow : GameWindow, IDisposable
- {
- private bool _isResizable;
- private bool _isBorderless;
-
- //private DisplayOrientation _currentOrientation;
- private IntPtr _windowHandle;
- private INativeWindow window;
-
- protected Game game;
- private List keys;
- //private OpenTK.Graphics.GraphicsContext backgroundContext;
-
- // we need this variables to make changes beetween threads
- private WindowState windowState;
- private Rectangle clientBounds;
- private Rectangle targetBounds;
- private bool updateClientBounds;
- private int updateborder = 0;
- bool disposed;
-
- #region Internal Properties
-
- internal Game Game
- {
- get { return game; }
- set
- {
- if (game != value)
- {
- game = value;
- }
- }
- }
-
- internal INativeWindow Window { get { return window; } }
-
- #endregion
-
- #region Public Properties
-
- public override IntPtr Handle { get { return _windowHandle; } }
-
- public override string ScreenDeviceName { get { return window.Title; } }
-
- public override Rectangle ClientBounds
- {
- get
- {
- var pos = window.PointToScreen(new System.Drawing.Point(0));
- return new Rectangle(pos.X, pos.Y, clientBounds.Width, clientBounds.Height);
- }
- }
-
- // TODO: this is buggy on linux - report to opentk team
- public override bool AllowUserResizing
- {
- get { return _isResizable; }
- set
- {
- if (_isResizable != value)
- _isResizable = value;
- else
- return;
- if (_isBorderless)
- return;
- window.WindowBorder = _isResizable ? WindowBorder.Resizable : WindowBorder.Fixed;
- }
- }
-
- public override DisplayOrientation CurrentOrientation
- {
- get { return DisplayOrientation.LandscapeLeft; }
- }
-#if DESKTOPGL
- public override Microsoft.Xna.Framework.Point Position
- {
- get { return new Microsoft.Xna.Framework.Point(window.Location.X,window.Location.Y); }
- set { window.Location = new System.Drawing.Point(value.X,value.Y); }
- }
-
- public override System.Drawing.Icon Icon
- {
- get
- {
- return window.Icon;
- }
- set
- {
- window.Icon = value;
- }
- }
-#endif
- protected internal override void SetSupportedOrientations(DisplayOrientation orientations)
- {
- // Do nothing. Desktop platforms don't do orientation.
- }
-
- public override bool IsBorderless
- {
- get { return _isBorderless; }
- set
- {
- if (_isBorderless != value)
- _isBorderless = value;
- else
- return;
- if (_isBorderless)
- {
- window.WindowBorder = WindowBorder.Hidden;
- }
- else
- window.WindowBorder = _isResizable ? WindowBorder.Resizable : WindowBorder.Fixed;
- }
- }
-
- #endregion
-
- public OpenTKGameWindow(Game game)
- {
- Initialize(game);
- }
-
- ~OpenTKGameWindow()
- {
- Dispose(false);
- }
-
- #region Restricted Methods
-
- #region OpenTK GameWindow Methods
-
- #region Delegates
-
- private void OpenTkGameWindow_Closing(object sender, CancelEventArgs e)
- {
- //block the window from getting destroyed before we dispose of
- //the resources, than we will destroy it
- e.Cancel = true;
-
- Game.Exit();
- }
-
- private void Keyboard_KeyUp(object sender, OpenTK.Input.KeyboardKeyEventArgs e)
- {
- Keys xnaKey = KeyboardUtil.ToXna(e.Key);
- if (keys.Contains(xnaKey)) keys.Remove(xnaKey);
- }
-
- private void Keyboard_KeyDown(object sender, OpenTK.Input.KeyboardKeyEventArgs e)
- {
- if (_allowAltF4 && e.Key == OpenTK.Input.Key.F4 && keys.Contains(Keys.LeftAlt))
- {
- window.Close();
- return;
- }
- Keys xnaKey = KeyboardUtil.ToXna(e.Key);
- if (!keys.Contains(xnaKey)) keys.Add(xnaKey);
- }
-
- #endregion
-
- private void OnResize(object sender, EventArgs e)
- {
- // Ignore resize events until intialization is complete
- if (Game == null || Game.GraphicsDevice == null)
- return;
-
- lock (window)
- {
- var winWidth = window.ClientRectangle.Width;
- var winHeight = window.ClientRectangle.Height;
- var winRect = new Rectangle(0, 0, winWidth, winHeight);
-
- // If window size is zero, leave bounds unchanged
- // OpenTK appears to set the window client size to 1x1 when minimizing
- if (winWidth <= 1 || winHeight <= 1)
- return;
-
- //If we've already got a pending change, do nothing
- if (updateClientBounds)
- return;
-
- Game.GraphicsDevice.PresentationParameters.BackBufferWidth = winWidth;
- Game.GraphicsDevice.PresentationParameters.BackBufferHeight = winHeight;
-
- Game.GraphicsDevice.Viewport = new Viewport(0, 0, winWidth, winHeight);
-
- clientBounds = winRect;
-
- OnClientSizeChanged();
- }
- }
-
- internal void ProcessEvents()
- {
- lock (window)
- {
- if (CurrentPlatform.OS == OS.Linux)
- {
- if (updateborder == 1)
- UpdateBorder();
-
- if(updateborder > 0)
- updateborder--;
- }
-
- Window.ProcessEvents();
- UpdateWindowState();
- HandleInput();
- }
- }
-
- private void UpdateBorder()
- {
- WindowBorder desired;
- if (_isBorderless)
- desired = WindowBorder.Hidden;
- else
- desired = _isResizable ? WindowBorder.Resizable : WindowBorder.Fixed;
-
- if (desired != window.WindowBorder && window.WindowState != WindowState.Fullscreen)
- window.WindowBorder = desired;
- }
-
- private void UpdateWindowState()
- {
- // we should wait until window's not fullscreen to resize
- if (updateClientBounds)
- {
- var prevState = window.WindowState;
-
- if (CurrentPlatform.OS == OS.Linux)
- window.WindowBorder = WindowBorder.Resizable;
-
- updateClientBounds = false;
-
- if (CurrentPlatform.OS == OS.MacOSX)
- {
- // on Mac We need to do this calculation first.
- int centerOffsetX = -(targetBounds.Width - window.ClientRectangle.Width) / 2;
- int centerOffsetY = -(targetBounds.Height - window.ClientRectangle.Height) / 2;
- window.X = Math.Max(0, centerOffsetX + window.X);
- window.Y = Math.Max(0, centerOffsetY + window.Y);
-
- window.ClientRectangle = new System.Drawing.Rectangle(targetBounds.X,
- targetBounds.Y, targetBounds.Width, targetBounds.Height);
- }
-
- // if the window-state is set from the outside (maximized button pressed) we have to update it here.
- // if it was set from the inside (.IsFullScreen changed), we have to change the window.
- // this code might not cover all corner cases
- // window was maximized
- if ((windowState == WindowState.Normal && window.WindowState == WindowState.Maximized) ||
- (windowState == WindowState.Maximized && window.WindowState == WindowState.Normal))
- windowState = window.WindowState; // maximize->normal and normal->maximize are usually set from the outside
- else
- window.WindowState = windowState; // usually fullscreen-stuff is set from the code
-
- if (CurrentPlatform.OS != OS.MacOSX)
- {
- if (!Configuration.RunningOnSdl2 && prevState != WindowState.Fullscreen)
- {
- int centerOffsetX = -(targetBounds.Width - window.ClientRectangle.Width) / 2;
- int centerOffsetY = -(targetBounds.Height - window.ClientRectangle.Height) / 2;
- window.X = Math.Max(0, centerOffsetX + window.X);
- window.Y = Math.Max(0, centerOffsetY + window.Y);
- }
- window.ClientRectangle = new System.Drawing.Rectangle(targetBounds.X,
- targetBounds.Y, targetBounds.Width, targetBounds.Height);
- }
-
-
- // we need to create a small delay between resizing the window
- // and changing the border to avoid OpenTK Linux bug
- if (CurrentPlatform.OS == OS.Linux)
- updateborder = 2;
- else
- UpdateBorder();
-
- var context = GraphicsContext.CurrentContext;
- if (context != null)
- context.Update(window.WindowInfo);
-
- if (!Window.Visible)
- {
- Window.Visible = true;
-
- // Bug in OpenTK, it doesn't always set state if window is not visible
- window.WindowState = windowState;
- }
- }
- }
-
- private void HandleInput()
- {
- // mouse doesn't need to be treated here, Mouse class does it alone
-
- // keyboard
- Keyboard.SetKeys(keys);
- }
-
- private void OnKeyPress(object sender, KeyPressEventArgs e)
- {
- OnTextInput(sender, new TextInputEventArgs(e.KeyChar));
- }
-
- #endregion
-
- private void Initialize(Game game)
- {
- Game = game;
-
- GraphicsContext.ShareContexts = true;
-
- window = new NativeWindow();
- window.WindowBorder = WindowBorder.Resizable;
- window.Closing += new EventHandler(OpenTkGameWindow_Closing);
- window.Resize += OnResize;
- window.KeyDown += new EventHandler(Keyboard_KeyDown);
- window.KeyUp += new EventHandler(Keyboard_KeyUp);
-
- window.KeyPress += OnKeyPress;
-
- var assembly = Assembly.GetEntryAssembly();
- var t = Type.GetType ("Mono.Runtime");
-
- Title = assembly != null ? AssemblyHelper.GetDefaultWindowTitle() : "MonoGame Application";
-
- // In case when DesktopGL dll is compiled using .Net, and you
- // try to load it using Mono, it will cause a crash because of this.
- try
- {
- if (t == null && assembly != null)
- window.Icon = Icon.ExtractAssociatedIcon(assembly.Location);
- else {
- using (var stream = Assembly.GetEntryAssembly().GetManifestResourceStream(string.Format("{0}.Icon.ico", Assembly.GetEntryAssembly().EntryPoint.DeclaringType.Namespace)) ??
- Assembly.GetExecutingAssembly().GetManifestResourceStream("Microsoft.Xna.Framework.monogame.ico")) {
- if (stream != null)
- window.Icon = new Icon(stream);
- }
- }
- }
- catch { }
-
- updateClientBounds = false;
- clientBounds = new Rectangle(window.ClientRectangle.X, window.ClientRectangle.Y,
- window.ClientRectangle.Width, window.ClientRectangle.Height);
- windowState = window.WindowState;
-
- _windowHandle = window.WindowInfo.Handle;
-
- keys = new List();
-
- // mouse
- // TODO review this when opentk 1.1 is released
-#if DESKTOPGL || ANGLE
- Mouse.setWindows(this);
-#else
- Mouse.UpdateMouseInfo(window.Mouse);
-#endif
-
- // Default no resizing
- AllowUserResizing = false;
-
- // Default mouse cursor hidden
- SetMouseVisible(false);
- }
-
- protected override void SetTitle(string title)
- {
- window.Title = title;
- }
-
- internal void ToggleFullScreen()
- {
- if (windowState == WindowState.Fullscreen)
- windowState = WindowState.Normal;
- else
- windowState = WindowState.Fullscreen;
- updateClientBounds = true;
- }
-
- internal void ChangeClientBounds(Rectangle clientBounds)
- {
- if (this.clientBounds != clientBounds)
- {
- updateClientBounds = true;
- targetBounds = clientBounds;
- }
- }
-
- #endregion
-
- #region Public Methods
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (!disposed)
- {
- if (disposing)
- {
- // Disposing of window will cause a crash on Linux
- // tho it will get destroied anyway by not beeing updated
- window.Close();
- }
- // The window handle no longer exists
- _windowHandle = IntPtr.Zero;
-
- disposed = true;
- }
- }
-
- public override void BeginScreenDeviceChange(bool willBeFullScreen)
- {
- }
-
- public override void EndScreenDeviceChange(string screenDeviceName, int clientWidth, int clientHeight)
- {
-
- }
-
- public void SetMouseVisible(bool visible)
- {
- window.Cursor = visible ? MouseCursor.Default : MouseCursor.Empty;
- }
-
- #endregion
- }
-}
-
diff --git a/MonoGame.Framework/DesktopGL/GamerServices/Guide.cs b/MonoGame.Framework/DesktopGL/GamerServices/Guide.cs
index 2ba38f49dd0..f3344d348ed 100644
--- a/MonoGame.Framework/DesktopGL/GamerServices/Guide.cs
+++ b/MonoGame.Framework/DesktopGL/GamerServices/Guide.cs
@@ -47,7 +47,6 @@ purpose and non-infringement.
using System.Runtime.Remoting.Messaging;
using Microsoft.Xna.Framework.Net;
-using Microsoft.Xna.Framework.Storage;
#endregion Using clause
@@ -271,16 +270,6 @@ public static void ShowAchievements()
//}
}
- public static IAsyncResult BeginShowStorageDeviceSelector( AsyncCallback callback, object state )
- {
- return null;
- }
-
- public static StorageDevice EndShowStorageDeviceSelector( IAsyncResult result )
- {
- return null;
- }
-
#region Properties
public static bool IsScreenSaverEnabled
{
diff --git a/MonoGame.Framework/FrameworkDispatcher.cs b/MonoGame.Framework/FrameworkDispatcher.cs
index b9add27fae6..b05be31627e 100644
--- a/MonoGame.Framework/FrameworkDispatcher.cs
+++ b/MonoGame.Framework/FrameworkDispatcher.cs
@@ -2,15 +2,46 @@
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.
+using System;
+using Microsoft.Xna.Framework.Audio;
+
namespace Microsoft.Xna.Framework
{
- public static class FrameworkDispatcher
- {
- public static void Update()
- {
- // "Updates the status of various framework components
- // (such as power state and media), and raises related events"
- }
- }
+ ///
+ /// Helper class for processing internal framework events.
+ ///
+ ///
+ /// If you use class, is called automatically.
+ /// Otherwise you must call it as part of your game loop.
+ ///
+ public static class FrameworkDispatcher
+ {
+ private static bool _initialized = false;
+
+ ///
+ /// Processes framework events.
+ ///
+ public static void Update()
+ {
+ if (!_initialized)
+ Initialize();
+
+ DoUpdate();
+ }
+
+ private static void DoUpdate()
+ {
+ DynamicSoundEffectInstanceManager.UpdatePlayingInstances();
+ SoundEffectInstancePool.Update();
+ }
+
+ private static void Initialize()
+ {
+ // Initialize sound system
+ SoundEffect.InitializeSoundEffect();
+
+ _initialized = true;
+ }
+ }
}
diff --git a/MonoGame.Framework/Game.cs b/MonoGame.Framework/Game.cs
index 4642405ff71..965a7578395 100644
--- a/MonoGame.Framework/Game.cs
+++ b/MonoGame.Framework/Game.cs
@@ -53,7 +53,7 @@ public class Game : IDisposable
private TimeSpan _maxElapsedTime = TimeSpan.FromMilliseconds(500);
-
+ private bool _shouldExit;
private bool _suppressDraw;
public Game()
@@ -70,6 +70,9 @@ public Game()
Platform.Deactivated += OnDeactivated;
_services.AddService(typeof(GamePlatform), Platform);
+ // Calling Update() for first time initializes some systems
+ FrameworkDispatcher.Update();
+
#if WINDOWS_STOREAPP && !WINDOWS_PHONE81
Platform.ViewStateChanged += Platform_ApplicationViewChanged;
#endif
@@ -323,7 +326,7 @@ internal bool Initialized
#endif
public void Exit()
{
- Platform.Exit();
+ _shouldExit = true;
_suppressDraw = true;
}
@@ -350,7 +353,8 @@ public void RunOneFrame()
if (!Platform.BeforeRun())
return;
- if (!_initialized) {
+ if (!_initialized)
+ {
DoInitialize ();
_gameTimer = Stopwatch.StartNew();
_initialized = true;
@@ -394,11 +398,12 @@ public void Run(GameRunBehavior runBehavior)
Platform.StartRunLoop();
break;
case GameRunBehavior.Synchronous:
+ // XNA runs one Update even before showing the window
+ DoUpdate(new GameTime());
+
Platform.RunLoop();
-#if !DESKTOPGL
EndRun();
DoExiting();
-#endif
break;
default:
throw new ArgumentException(string.Format(
@@ -406,17 +411,6 @@ public void Run(GameRunBehavior runBehavior)
}
}
-#if DESKTOPGL
- // This code is used so that the Window could stay alive
- // while all the resources are getting destroyed
- internal void ExitEverything()
- {
- EndRun();
- DoExiting();
- this.Dispose();
- }
-#endif
-
private TimeSpan _accumulatedElapsedTime;
private readonly GameTime _gameTime = new GameTime();
private Stopwatch _gameTimer;
@@ -465,7 +459,7 @@ public void Tick()
var stepCount = 0;
// Perform as many full fixed length time steps as we can.
- while (_accumulatedElapsedTime >= TargetElapsedTime)
+ while (_accumulatedElapsedTime >= TargetElapsedTime && !_shouldExit)
{
_gameTime.TotalGameTime += TargetElapsedTime;
_accumulatedElapsedTime -= TargetElapsedTime;
@@ -514,6 +508,9 @@ public void Tick()
{
DoDraw(_gameTime);
}
+
+ if (_shouldExit)
+ Platform.Exit();
}
#endregion
@@ -534,7 +531,7 @@ protected virtual void UnloadContent() { }
protected virtual void Initialize()
{
- // TODO: We shouldn't need to do this here.
+ // TODO: This should be removed once all platforms use the new GraphicsDeviceManager
applyChanges(graphicsDeviceManager);
// According to the information given on MSDN (see link below), all
@@ -547,9 +544,6 @@ protected virtual void Initialize()
_graphicsDeviceService = (IGraphicsDeviceService)
Services.GetService(typeof(IGraphicsDeviceService));
- // FIXME: If this test fails, is LoadContent ever called? This
- // seems like a condition that warrants an exception more
- // than a silent failure.
if (_graphicsDeviceService != null &&
_graphicsDeviceService.GraphicsDevice != null)
{
@@ -660,11 +654,8 @@ internal void DoUpdate(GameTime gameTime)
AssertNotDisposed();
if (Platform.BeforeUpdate(gameTime))
{
- // Once per frame, we need to check currently
- // playing sounds to see if they've stopped,
- // and return them back to the pool if so.
- SoundEffectInstancePool.Update();
-
+ FrameworkDispatcher.Update();
+
Update(gameTime);
//The TouchPanel needs to know the time for when touches arrive
@@ -688,6 +679,9 @@ internal void DoDraw(GameTime gameTime)
internal void DoInitialize()
{
AssertNotDisposed();
+ if (GraphicsDevice == null && graphicsDeviceManager != null)
+ _graphicsDeviceManager.CreateDevice();
+
Platform.BeforeInitialize();
Initialize();
@@ -717,9 +711,6 @@ internal GraphicsDeviceManager graphicsDeviceManager
{
_graphicsDeviceManager = (IGraphicsDeviceManager)
Services.GetService(typeof(IGraphicsDeviceManager));
-
- if (_graphicsDeviceManager == null)
- throw new InvalidOperationException ("No Graphics Device Manager");
}
return (GraphicsDeviceManager)_graphicsDeviceManager;
}
@@ -732,12 +723,8 @@ internal GraphicsDeviceManager graphicsDeviceManager
// Components.ComponentAdded.
private void InitializeExistingComponents()
{
- // TODO: Would be nice to get rid of this copy, but since it only
- // happens once per game, it's fairly low priority.
- var copy = new IGameComponent[Components.Count];
- Components.CopyTo(copy, 0);
- foreach (var component in copy)
- component.Initialize();
+ for(int i = 0; i < Components.Count; ++i)
+ Components[i].Initialize();
}
private void CategorizeComponents()
diff --git a/MonoGame.Framework/GamePlatform.Desktop.cs b/MonoGame.Framework/GamePlatform.Desktop.cs
index f90a78f6019..50bd011794c 100644
--- a/MonoGame.Framework/GamePlatform.Desktop.cs
+++ b/MonoGame.Framework/GamePlatform.Desktop.cs
@@ -17,7 +17,7 @@ internal static GamePlatform PlatformCreate(Game game)
#if MONOMAC
return new MacGamePlatform(game);
#elif DESKTOPGL || ANGLE
- return new OpenTKGamePlatform(game);
+ return new SdlGamePlatform(game);
#elif WINDOWS && DIRECTX
return new MonoGame.Framework.WinFormsGamePlatform(game);
#elif WINDOWS_UAP
diff --git a/MonoGame.Framework/GamePlatform.Mobile.cs b/MonoGame.Framework/GamePlatform.Mobile.cs
index da033c5bb7e..64de849f8ea 100644
--- a/MonoGame.Framework/GamePlatform.Mobile.cs
+++ b/MonoGame.Framework/GamePlatform.Mobile.cs
@@ -16,8 +16,6 @@ internal static GamePlatform PlatformCreate(Game game)
return new AndroidGamePlatform(game);
#elif WINDOWS_PHONE81
return new MetroGamePlatform(game);
-#elif WINDOWS_PHONE
- return new MonoGame.Framework.WindowsPhone.WindowsPhoneGamePlatform(game);
#elif WEB
return new WebGamePlatform(game);
#endif
diff --git a/MonoGame.Framework/GamePlatform.cs b/MonoGame.Framework/GamePlatform.cs
index c22cb77b91e..da8b4102d06 100644
--- a/MonoGame.Framework/GamePlatform.cs
+++ b/MonoGame.Framework/GamePlatform.cs
@@ -16,8 +16,7 @@ abstract partial class GamePlatform : IDisposable
protected TimeSpan _inactiveSleepTime = TimeSpan.FromMilliseconds(20.0);
protected bool _needsToResetElapsedTime = false;
bool disposed;
- protected bool _alreadyInFullScreenMode = false;
- protected bool _alreadyInWindowedMode = false;
+ protected bool InFullScreenMode = false;
protected bool IsDisposed { get { return disposed; } }
#endregion
@@ -138,11 +137,6 @@ protected void RaiseAsyncRunLoopEnded()
public virtual void BeforeInitialize()
{
IsActive = true;
- if (this.Game.GraphicsDevice == null)
- {
- var graphicsDeviceManager = Game.Services.GetService(typeof(IGraphicsDeviceManager)) as IGraphicsDeviceManager;
- graphicsDeviceManager.CreateDevice();
- }
}
///
@@ -258,6 +252,14 @@ public virtual void Present() { }
protected virtual void OnIsMouseVisibleChanged() {}
+ ///
+ /// Used by the GraphicsDeviceManager to update the platform window
+ /// after the graphics device has changed the presentation.
+ ///
+ internal virtual void OnPresentationChanged()
+ {
+ }
+
#endregion Methods
#region IDisposable implementation
diff --git a/MonoGame.Framework/GameWindow.cs b/MonoGame.Framework/GameWindow.cs
index 520d5ac0098..1243580f117 100644
--- a/MonoGame.Framework/GameWindow.cs
+++ b/MonoGame.Framework/GameWindow.cs
@@ -29,10 +29,6 @@ public abstract class GameWindow {
/// which stretches across all screens.
///
public abstract Point Position { get; set; }
-#endif
-
-#if DESKTOPGL
- public abstract System.Drawing.Icon Icon { get; set; }
#endif
public abstract DisplayOrientation CurrentOrientation { get; }
@@ -124,7 +120,7 @@ protected void OnActivated ()
{
}
- protected void OnClientSizeChanged ()
+ internal void OnClientSizeChanged ()
{
if (ClientSizeChanged != null)
ClientSizeChanged (this, EventArgs.Empty);
diff --git a/MonoGame.Framework/Graphics/ClearOptions.cs b/MonoGame.Framework/Graphics/ClearOptions.cs
index 10a24ffe0d1..f580572446a 100644
--- a/MonoGame.Framework/Graphics/ClearOptions.cs
+++ b/MonoGame.Framework/Graphics/ClearOptions.cs
@@ -7,7 +7,7 @@
namespace Microsoft.Xna.Framework.Graphics
{
///
- /// Defines the buffers for clearing when calling operation.
+ /// Defines the buffers for clearing when calling operation.
///
[Flags]
public enum ClearOptions
diff --git a/MonoGame.Framework/Graphics/DisplayMode.cs b/MonoGame.Framework/Graphics/DisplayMode.cs
index fe1c2e45aa8..d44ca18f411 100644
--- a/MonoGame.Framework/Graphics/DisplayMode.cs
+++ b/MonoGame.Framework/Graphics/DisplayMode.cs
@@ -64,7 +64,7 @@ public int Width {
}
public Rectangle TitleSafeArea {
- get { return new Rectangle(0, 0, Width, Height); }
+ get { return GraphicsDevice.GetTitleSafeArea(0, 0, width, height); }
}
#endregion Properties
@@ -118,7 +118,7 @@ public override int GetHashCode()
public override string ToString()
{
- return "{{Width:" + this.width + " Height:" + this.height + " Format:" + this.Format + "}}";
+ return "{Width:" + this.width + " Height:" + this.height + " Format:" + this.Format + " AspectRatio:" + this.AspectRatio + "}";
}
#endregion Public Methods
diff --git a/MonoGame.Framework/Graphics/DisplayModeCollection.cs b/MonoGame.Framework/Graphics/DisplayModeCollection.cs
index b821567b9ec..06bb66f3dfb 100644
--- a/MonoGame.Framework/Graphics/DisplayModeCollection.cs
+++ b/MonoGame.Framework/Graphics/DisplayModeCollection.cs
@@ -33,38 +33,49 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
namespace Microsoft.Xna.Framework.Graphics
{
- public class DisplayModeCollection : IEnumerable, IEnumerable
+ public class DisplayModeCollection : IEnumerable
{
- private readonly List modes;
+ private readonly List _modes;
public IEnumerable this[SurfaceFormat format]
{
- get {
- List list = new List();
- foreach (DisplayMode mode in this.modes)
+ get
+ {
+ var list = new List();
+ foreach (var mode in _modes)
{
if (mode.Format == format)
- {
list.Add(mode);
- }
}
return list;
-
}
}
public IEnumerator GetEnumerator()
{
- return modes.GetEnumerator();
+ return _modes.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
- return modes.GetEnumerator();
+ return _modes.GetEnumerator();
}
- public DisplayModeCollection(List setmodes) {
- modes = setmodes;
+ internal DisplayModeCollection(List modes)
+ {
+ // Sort the modes in a consistent way that happens
+ // to match XNA behavior on some graphics devices.
+
+ modes.Sort(delegate(DisplayMode a, DisplayMode b)
+ {
+ if (a == b)
+ return 0;
+ if (a.Format <= b.Format && a.Width <= b.Width && a.Height <= b.Height)
+ return -1;
+ return 1;
+ });
+
+ _modes = modes;
}
}
}
\ No newline at end of file
diff --git a/MonoGame.Framework/Graphics/Effect/AlphaTestEffect.cs b/MonoGame.Framework/Graphics/Effect/AlphaTestEffect.cs
index 725ce9263a6..cc8f560df83 100644
--- a/MonoGame.Framework/Graphics/Effect/AlphaTestEffect.cs
+++ b/MonoGame.Framework/Graphics/Effect/AlphaTestEffect.cs
@@ -29,8 +29,6 @@ public class AlphaTestEffect : Effect, IEffectMatrices, IEffectFog
EffectParameter fogVectorParam;
EffectParameter worldViewProjParam;
- int _shaderIndex;
-
#endregion
#region Fields
@@ -317,7 +315,7 @@ void CacheEffectParameters()
///
/// Lazily computes derived parameter values immediately before applying the effect.
///
- protected internal override bool OnApply()
+ protected internal override void OnApply()
{
// Recompute the world+view+projection matrix or fog vector?
dirtyFlags = EffectHelpers.SetWorldViewProjAndFog(dirtyFlags, ref world, ref view, ref projection, ref worldView, fogEnabled, fogStart, fogEnd, worldViewProjParam, fogVectorParam);
@@ -433,15 +431,8 @@ protected internal override bool OnApply()
dirtyFlags &= ~EffectDirtyFlags.ShaderIndex;
- if (_shaderIndex != shaderIndex)
- {
- _shaderIndex = shaderIndex;
- CurrentTechnique = Techniques[_shaderIndex];
- return true;
- }
+ CurrentTechnique = Techniques[shaderIndex];
}
-
- return false;
}
diff --git a/MonoGame.Framework/Graphics/Effect/BasicEffect.cs b/MonoGame.Framework/Graphics/Effect/BasicEffect.cs
index f09857303f5..6da7ff1d7e0 100644
--- a/MonoGame.Framework/Graphics/Effect/BasicEffect.cs
+++ b/MonoGame.Framework/Graphics/Effect/BasicEffect.cs
@@ -34,8 +34,6 @@ public class BasicEffect : Effect, IEffectMatrices, IEffectLights, IEffectFog
EffectParameter worldInverseTransposeParam;
EffectParameter worldViewProjParam;
- int _shaderIndex = -1;
-
#endregion
#region Fields
@@ -438,7 +436,7 @@ void CacheEffectParameters(BasicEffect cloneSource)
///
/// Lazily computes derived parameter values immediately before applying the effect.
///
- protected internal override bool OnApply()
+ protected internal override void OnApply()
{
// Recompute the world+view+projection matrix or fog vector?
dirtyFlags = EffectHelpers.SetWorldViewProjAndFog(dirtyFlags, ref world, ref view, ref projection, ref worldView, fogEnabled, fogStart, fogEnd, worldViewProjParam, fogVectorParam);
@@ -493,15 +491,8 @@ protected internal override bool OnApply()
dirtyFlags &= ~EffectDirtyFlags.ShaderIndex;
- if (_shaderIndex != shaderIndex)
- {
- _shaderIndex = shaderIndex;
- CurrentTechnique = Techniques[_shaderIndex];
- return true;
- }
+ CurrentTechnique = Techniques[shaderIndex];
}
-
- return false;
}
diff --git a/MonoGame.Framework/Graphics/Effect/DualTextureEffect.cs b/MonoGame.Framework/Graphics/Effect/DualTextureEffect.cs
index 7b349271ef5..b64a3280bda 100644
--- a/MonoGame.Framework/Graphics/Effect/DualTextureEffect.cs
+++ b/MonoGame.Framework/Graphics/Effect/DualTextureEffect.cs
@@ -28,8 +28,6 @@ public class DualTextureEffect : Effect, IEffectMatrices, IEffectFog
EffectParameter fogVectorParam;
EffectParameter worldViewProjParam;
- int _shaderIndex = -1;
-
#endregion
#region Fields
@@ -293,7 +291,7 @@ void CacheEffectParameters()
///
/// Lazily computes derived parameter values immediately before applying the effect.
///
- protected internal override bool OnApply()
+ protected internal override void OnApply()
{
// Recompute the world+view+projection matrix or fog vector?
dirtyFlags = EffectHelpers.SetWorldViewProjAndFog(dirtyFlags, ref world, ref view, ref projection, ref worldView, fogEnabled, fogStart, fogEnd, worldViewProjParam, fogVectorParam);
@@ -319,15 +317,8 @@ protected internal override bool OnApply()
dirtyFlags &= ~EffectDirtyFlags.ShaderIndex;
- if (_shaderIndex != shaderIndex)
- {
- _shaderIndex = shaderIndex;
- CurrentTechnique = Techniques[_shaderIndex];
- return true;
- }
+ CurrentTechnique = Techniques[shaderIndex];
}
-
- return false;
}
diff --git a/MonoGame.Framework/Graphics/Effect/Effect.cs b/MonoGame.Framework/Graphics/Effect/Effect.cs
index 66b1b1f8742..a87a4075c5c 100644
--- a/MonoGame.Framework/Graphics/Effect/Effect.cs
+++ b/MonoGame.Framework/Graphics/Effect/Effect.cs
@@ -25,7 +25,7 @@ struct MGFXHeader
/// We should avoid supporting old versions for very long if at all
/// as users should be rebuilding content when packaging their game.
///
- public const int MGFXVersion = 7;
+ public const int MGFXVersion = 8;
public int Signature;
public int Version;
@@ -132,13 +132,8 @@ private MGFXHeader ReadHeader(byte[] effectCode, int index)
if (header.Version > MGFXHeader.MGFXVersion)
throw new Exception("This MGFX effect seems to be for a newer release of MonoGame.");
-#if DIRECTX
- if (header.Profile != 1)
-#else
- if (header.Profile != 0)
-#endif
- throw new Exception("This MGFX effect was built for a different platform!");
-
+ if (header.Profile != Shader.Profile)
+ throw new Exception("This MGFX effect was built for a different platform!");
return header;
}
@@ -192,9 +187,8 @@ public virtual Effect Clone()
return new Effect(this);
}
- protected internal virtual bool OnApply()
+ protected internal virtual void OnApply()
{
- return false;
}
protected override void Dispose(bool disposing)
@@ -242,13 +236,8 @@ private void ReadEffect (BinaryReader reader)
var buffers = (int)reader.ReadByte ();
ConstantBuffers = new ConstantBuffer[buffers];
for (var c = 0; c < buffers; c++)
- {
-
-#if OPENGL
- string name = reader.ReadString ();
-#else
- string name = null;
-#endif
+ {
+ var name = reader.ReadString ();
// Create the backing system memory buffer.
var sizeInBytes = (int)reader.ReadInt16 ();
@@ -421,9 +410,9 @@ private static EffectParameterCollection ReadParameters(BinaryReader reader)
{
case EffectParameterType.Bool:
case EffectParameterType.Int32:
-#if DIRECTX
- // Under DirectX we properly store integers and booleans
- // in an integer type.
+#if !OPENGL
+ // Under most platforms we properly store integers and
+ // booleans in an integer type.
//
// MojoShader on the otherhand stores everything in float
// types which is why this code is disabled under OpenGL.
diff --git a/MonoGame.Framework/Graphics/Effect/EffectParameter.cs b/MonoGame.Framework/Graphics/Effect/EffectParameter.cs
index 3ab0c53b876..38e8b336209 100644
--- a/MonoGame.Framework/Graphics/Effect/EffectParameter.cs
+++ b/MonoGame.Framework/Graphics/Effect/EffectParameter.cs
@@ -173,11 +173,11 @@ public bool GetValueBoolean ()
if (ParameterClass != EffectParameterClass.Scalar || ParameterType != EffectParameterType.Bool)
throw new InvalidCastException();
-#if DIRECTX
- return ((int[])Data)[0] != 0;
-#else
+#if OPENGL
// MojoShader encodes even booleans into a float.
return ((float[])Data)[0] != 0.0f;
+#else
+ return ((int[])Data)[0] != 0;
#endif
}
@@ -193,11 +193,11 @@ public int GetValueInt32 ()
if (ParameterClass != EffectParameterClass.Scalar || ParameterType != EffectParameterType.Int32)
throw new InvalidCastException();
-#if DIRECTX
- return ((int[])Data)[0];
-#else
+#if OPENGL
// MojoShader encodes integers into a float.
return (int)((float[])Data)[0];
+#else
+ return ((int[])Data)[0];
#endif
}
@@ -411,14 +411,13 @@ public void SetValue (bool value)
if (ParameterClass != EffectParameterClass.Scalar || ParameterType != EffectParameterType.Bool)
throw new InvalidCastException();
-#if DIRECTX
- // We store the bool as an integer as that
- // is what the constant buffers expect.
- ((int[])Data)[0] = value ? 1 : 0;
-#else
+#if OPENGL
// MojoShader encodes even booleans into a float.
((float[])Data)[0] = value ? 1 : 0;
+#else
+ ((int[])Data)[0] = value ? 1 : 0;
#endif
+
StateKey = unchecked(NextStateKey++);
}
@@ -434,11 +433,11 @@ public void SetValue (int value)
if (ParameterClass != EffectParameterClass.Scalar || ParameterType != EffectParameterType.Int32)
throw new InvalidCastException();
-#if DIRECTX
- ((int[])Data)[0] = value;
-#else
+#if OPENGL
// MojoShader encodes integers into a float.
((float[])Data)[0] = value;
+#else
+ ((int[])Data)[0] = value;
#endif
StateKey = unchecked(NextStateKey++);
}
diff --git a/MonoGame.Framework/Graphics/Effect/EffectPass.cs b/MonoGame.Framework/Graphics/Effect/EffectPass.cs
index 90814f37388..a2798ff15d5 100644
--- a/MonoGame.Framework/Graphics/Effect/EffectPass.cs
+++ b/MonoGame.Framework/Graphics/Effect/EffectPass.cs
@@ -63,12 +63,10 @@ internal EffectPass(Effect effect, EffectPass cloneSource)
public void Apply()
{
// Set/get the correct shader handle/cleanups.
- //
- // TODO: This "reapply" if the shader index changes
- // trick is sort of ugly. We should probably rework
- // this to use some sort of "technique/pass redirect".
- //
- if (_effect.OnApply())
+
+ var current = _effect.CurrentTechnique;
+ _effect.OnApply();
+ if (_effect.CurrentTechnique != current)
{
_effect.CurrentTechnique.Passes[0].Apply();
return;
@@ -76,8 +74,6 @@ public void Apply()
var device = _effect.GraphicsDevice;
-#if OPENGL || DIRECTX
-
if (_vertexShader != null)
{
device.VertexShader = _vertexShader;
@@ -110,8 +106,6 @@ public void Apply()
}
}
-#endif
-
// Set the render states if we have some.
if (_rasterizerState != null)
device.RasterizerState = _rasterizerState;
diff --git a/MonoGame.Framework/Graphics/Effect/EnvironmentMapEffect.cs b/MonoGame.Framework/Graphics/Effect/EnvironmentMapEffect.cs
index 7e7c5a31dea..00067b03e9e 100644
--- a/MonoGame.Framework/Graphics/Effect/EnvironmentMapEffect.cs
+++ b/MonoGame.Framework/Graphics/Effect/EnvironmentMapEffect.cs
@@ -36,8 +36,6 @@ public class EnvironmentMapEffect : Effect, IEffectMatrices, IEffectLights, IEff
EffectParameter worldInverseTransposeParam;
EffectParameter worldViewProjParam;
- int _shaderIndex = -1;
-
#endregion
#region Fields
@@ -458,7 +456,7 @@ void CacheEffectParameters(EnvironmentMapEffect cloneSource)
///
/// Lazily computes derived parameter values immediately before applying the effect.
///
- protected internal override bool OnApply()
+ protected internal override void OnApply()
{
// Recompute the world+view+projection matrix or fog vector?
dirtyFlags = EffectHelpers.SetWorldViewProjAndFog(dirtyFlags, ref world, ref view, ref projection, ref worldView, fogEnabled, fogStart, fogEnd, worldViewProjParam, fogVectorParam);
@@ -502,15 +500,8 @@ protected internal override bool OnApply()
dirtyFlags &= ~EffectDirtyFlags.ShaderIndex;
- if (_shaderIndex != shaderIndex)
- {
- _shaderIndex = shaderIndex;
- CurrentTechnique = Techniques[_shaderIndex];
- return true;
- }
+ CurrentTechnique = Techniques[shaderIndex];
}
-
- return false;
}
diff --git a/MonoGame.Framework/Graphics/Effect/Resources/AlphaTestEffect.dx11.mgfxo b/MonoGame.Framework/Graphics/Effect/Resources/AlphaTestEffect.dx11.mgfxo
index 2454af787a7..dce37ff8108 100644
Binary files a/MonoGame.Framework/Graphics/Effect/Resources/AlphaTestEffect.dx11.mgfxo and b/MonoGame.Framework/Graphics/Effect/Resources/AlphaTestEffect.dx11.mgfxo differ
diff --git a/MonoGame.Framework/Graphics/Effect/Resources/AlphaTestEffect.ogl.mgfxo b/MonoGame.Framework/Graphics/Effect/Resources/AlphaTestEffect.ogl.mgfxo
index e630765a206..3c69f43f230 100644
Binary files a/MonoGame.Framework/Graphics/Effect/Resources/AlphaTestEffect.ogl.mgfxo and b/MonoGame.Framework/Graphics/Effect/Resources/AlphaTestEffect.ogl.mgfxo differ
diff --git a/MonoGame.Framework/Graphics/Effect/Resources/BasicEffect.dx11.mgfxo b/MonoGame.Framework/Graphics/Effect/Resources/BasicEffect.dx11.mgfxo
index 70b27c298db..fa9c11234bf 100644
Binary files a/MonoGame.Framework/Graphics/Effect/Resources/BasicEffect.dx11.mgfxo and b/MonoGame.Framework/Graphics/Effect/Resources/BasicEffect.dx11.mgfxo differ
diff --git a/MonoGame.Framework/Graphics/Effect/Resources/BasicEffect.ogl.mgfxo b/MonoGame.Framework/Graphics/Effect/Resources/BasicEffect.ogl.mgfxo
index e3bdb463662..233d027ddb1 100644
Binary files a/MonoGame.Framework/Graphics/Effect/Resources/BasicEffect.ogl.mgfxo and b/MonoGame.Framework/Graphics/Effect/Resources/BasicEffect.ogl.mgfxo differ
diff --git a/MonoGame.Framework/Graphics/Effect/Resources/DualTextureEffect.dx11.mgfxo b/MonoGame.Framework/Graphics/Effect/Resources/DualTextureEffect.dx11.mgfxo
index 50947c32fcb..b9e6ab5ac79 100644
Binary files a/MonoGame.Framework/Graphics/Effect/Resources/DualTextureEffect.dx11.mgfxo and b/MonoGame.Framework/Graphics/Effect/Resources/DualTextureEffect.dx11.mgfxo differ
diff --git a/MonoGame.Framework/Graphics/Effect/Resources/DualTextureEffect.ogl.mgfxo b/MonoGame.Framework/Graphics/Effect/Resources/DualTextureEffect.ogl.mgfxo
index 8f5593cdfc6..48618169d86 100644
Binary files a/MonoGame.Framework/Graphics/Effect/Resources/DualTextureEffect.ogl.mgfxo and b/MonoGame.Framework/Graphics/Effect/Resources/DualTextureEffect.ogl.mgfxo differ
diff --git a/MonoGame.Framework/Graphics/Effect/Resources/EnvironmentMapEffect.dx11.mgfxo b/MonoGame.Framework/Graphics/Effect/Resources/EnvironmentMapEffect.dx11.mgfxo
index 5b25f4c11c4..bc07e5885fd 100644
Binary files a/MonoGame.Framework/Graphics/Effect/Resources/EnvironmentMapEffect.dx11.mgfxo and b/MonoGame.Framework/Graphics/Effect/Resources/EnvironmentMapEffect.dx11.mgfxo differ
diff --git a/MonoGame.Framework/Graphics/Effect/Resources/EnvironmentMapEffect.ogl.mgfxo b/MonoGame.Framework/Graphics/Effect/Resources/EnvironmentMapEffect.ogl.mgfxo
index eda7eaffedc..4b4ea60814a 100644
Binary files a/MonoGame.Framework/Graphics/Effect/Resources/EnvironmentMapEffect.ogl.mgfxo and b/MonoGame.Framework/Graphics/Effect/Resources/EnvironmentMapEffect.ogl.mgfxo differ
diff --git a/MonoGame.Framework/Graphics/Effect/Resources/Lighting.fxh b/MonoGame.Framework/Graphics/Effect/Resources/Lighting.fxh
index fce4b3a29f9..6b82db49dd1 100644
--- a/MonoGame.Framework/Graphics/Effect/Resources/Lighting.fxh
+++ b/MonoGame.Framework/Graphics/Effect/Resources/Lighting.fxh
@@ -33,7 +33,7 @@ ColorPair ComputeLights(float3 eyeVector, float3 worldNormal, uniform int numLig
float3 dotL = mul(-lightDirections, worldNormal);
float3 dotH = mul(halfVectors, worldNormal);
- float3 zeroL = step(0, dotL);
+ float3 zeroL = step(float3(0,0,0), dotL);
float3 diffuse = zeroL * dotL;
float3 specular = pow(max(dotH, 0) * zeroL, SpecularPower);
diff --git a/MonoGame.Framework/Graphics/Effect/Resources/SkinnedEffect.dx11.mgfxo b/MonoGame.Framework/Graphics/Effect/Resources/SkinnedEffect.dx11.mgfxo
index 85a2d757072..bc08a87ded7 100644
Binary files a/MonoGame.Framework/Graphics/Effect/Resources/SkinnedEffect.dx11.mgfxo and b/MonoGame.Framework/Graphics/Effect/Resources/SkinnedEffect.dx11.mgfxo differ
diff --git a/MonoGame.Framework/Graphics/Effect/Resources/SkinnedEffect.ogl.mgfxo b/MonoGame.Framework/Graphics/Effect/Resources/SkinnedEffect.ogl.mgfxo
index fac209dd5cb..1ed0bf4dbb6 100644
Binary files a/MonoGame.Framework/Graphics/Effect/Resources/SkinnedEffect.ogl.mgfxo and b/MonoGame.Framework/Graphics/Effect/Resources/SkinnedEffect.ogl.mgfxo differ
diff --git a/MonoGame.Framework/Graphics/Effect/Resources/SpriteEffect.dx11.mgfxo b/MonoGame.Framework/Graphics/Effect/Resources/SpriteEffect.dx11.mgfxo
index 3e7c2ba78be..abfe2f08ebf 100644
Binary files a/MonoGame.Framework/Graphics/Effect/Resources/SpriteEffect.dx11.mgfxo and b/MonoGame.Framework/Graphics/Effect/Resources/SpriteEffect.dx11.mgfxo differ
diff --git a/MonoGame.Framework/Graphics/Effect/Resources/SpriteEffect.ogl.mgfxo b/MonoGame.Framework/Graphics/Effect/Resources/SpriteEffect.ogl.mgfxo
index bc2eaf92ba7..49ce9d2c571 100644
Binary files a/MonoGame.Framework/Graphics/Effect/Resources/SpriteEffect.ogl.mgfxo and b/MonoGame.Framework/Graphics/Effect/Resources/SpriteEffect.ogl.mgfxo differ
diff --git a/MonoGame.Framework/Graphics/Effect/SkinnedEffect.cs b/MonoGame.Framework/Graphics/Effect/SkinnedEffect.cs
index da053c8cde8..a2d720fd3d3 100644
--- a/MonoGame.Framework/Graphics/Effect/SkinnedEffect.cs
+++ b/MonoGame.Framework/Graphics/Effect/SkinnedEffect.cs
@@ -37,8 +37,6 @@ public class SkinnedEffect : Effect, IEffectMatrices, IEffectLights, IEffectFog
EffectParameter worldViewProjParam;
EffectParameter bonesParam;
- int _shaderIndex = -1;
-
#endregion
#region Fields
@@ -485,7 +483,7 @@ void CacheEffectParameters(SkinnedEffect cloneSource)
///
/// Lazily computes derived parameter values immediately before applying the effect.
///
- protected internal override bool OnApply()
+ protected internal override void OnApply()
{
// Recompute the world+view+projection matrix or fog vector?
dirtyFlags = EffectHelpers.SetWorldViewProjAndFog(dirtyFlags, ref world, ref view, ref projection, ref worldView, fogEnabled, fogStart, fogEnd, worldViewProjParam, fogVectorParam);
@@ -530,15 +528,8 @@ protected internal override bool OnApply()
dirtyFlags &= ~EffectDirtyFlags.ShaderIndex;
- if (_shaderIndex != shaderIndex)
- {
- _shaderIndex = shaderIndex;
- CurrentTechnique = Techniques[_shaderIndex];
- return true;
- }
+ CurrentTechnique = Techniques[shaderIndex];
}
-
- return false;
}
diff --git a/MonoGame.Framework/Graphics/Effect/SpriteEffect.cs b/MonoGame.Framework/Graphics/Effect/SpriteEffect.cs
index 016ed5f36fb..2811ba72051 100644
--- a/MonoGame.Framework/Graphics/Effect/SpriteEffect.cs
+++ b/MonoGame.Framework/Graphics/Effect/SpriteEffect.cs
@@ -10,7 +10,7 @@ namespace Microsoft.Xna.Framework.Graphics
///
/// The default effect used by SpriteBatch.
///
- internal class SpriteEffect : Effect
+ public class SpriteEffect : Effect
{
#region Effect Parameters
@@ -59,16 +59,18 @@ void CacheEffectParameters()
///
/// Lazily computes derived parameter values immediately before applying the effect.
///
- protected internal override bool OnApply()
+ protected internal override void OnApply()
{
var viewport = GraphicsDevice.Viewport;
var projection = Matrix.CreateOrthographicOffCenter(0, viewport.Width, viewport.Height, 0, 0, 1);
- var halfPixelOffset = Matrix.CreateTranslation(-0.5f, -0.5f, 0);
+ var halfPixelOffset = Matrix.CreateTranslation(0, 0, 0);
- matrixParam.SetValue(halfPixelOffset * projection);
+ if (SpriteBatch.NeedsHalfPixelOffset){
+ halfPixelOffset += Matrix.CreateTranslation(-0.5f, -0.5f, 0);
+ }
- return false;
+ matrixParam.SetValue(halfPixelOffset * projection);
}
diff --git a/MonoGame.Framework/Graphics/GraphicsAdapter.DirectX.cs b/MonoGame.Framework/Graphics/GraphicsAdapter.DirectX.cs
new file mode 100644
index 00000000000..60fad0d84ae
--- /dev/null
+++ b/MonoGame.Framework/Graphics/GraphicsAdapter.DirectX.cs
@@ -0,0 +1,136 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using SharpDX.Direct3D;
+using SharpDX.DXGI;
+
+namespace Microsoft.Xna.Framework.Graphics
+{
+ partial class GraphicsAdapter
+ {
+ SharpDX.DXGI.Adapter1 _adapter;
+
+ private static void PlatformInitializeAdapters(out ReadOnlyCollection adapters)
+ {
+ var factory = new SharpDX.DXGI.Factory1();
+
+ var adapterCount = factory.GetAdapterCount();
+ var adapterList = new List(adapterCount);
+
+ for (var i = 0; i < adapterCount; i++)
+ {
+ var device = factory.GetAdapter1(i);
+
+ var monitorCount = device.GetOutputCount();
+ for (var j = 0; j < monitorCount; j++)
+ {
+ var monitor = device.GetOutput(j);
+
+ var adapter = CreateAdapter(device, monitor);
+ adapterList.Add(adapter);
+
+ monitor.Dispose();
+ }
+ }
+
+ factory.Dispose();
+
+ adapters = new ReadOnlyCollection(adapterList);
+ }
+
+ private static readonly Dictionary FormatTranslations = new Dictionary
+ {
+ { SharpDX.DXGI.Format.R8G8B8A8_UNorm, SurfaceFormat.Color },
+ { SharpDX.DXGI.Format.B8G8R8A8_UNorm, SurfaceFormat.Color },
+ { SharpDX.DXGI.Format.B5G6R5_UNorm, SurfaceFormat.Bgr565 },
+ };
+
+ private static GraphicsAdapter CreateAdapter(SharpDX.DXGI.Adapter1 device, SharpDX.DXGI.Output monitor)
+ {
+ var adapter = new GraphicsAdapter();
+ adapter._adapter = device;
+
+ adapter.DeviceName = monitor.Description.DeviceName.TrimEnd(new char[] {'\0'});
+ adapter.Description = device.Description1.Description.TrimEnd(new char[] {'\0'});
+ adapter.DeviceId = device.Description1.DeviceId;
+ adapter.Revision = device.Description1.Revision;
+ adapter.VendorId = device.Description1.VendorId;
+ adapter.SubSystemId = device.Description1.SubsystemId;
+ adapter.MonitorHandle = monitor.Description.MonitorHandle;
+
+#if WINDOWS_UAP
+ var desktopWidth = monitor.Description.DesktopBounds.Right - monitor.Description.DesktopBounds.Left;
+ var desktopHeight = monitor.Description.DesktopBounds.Bottom - monitor.Description.DesktopBounds.Top;
+#else
+ var desktopWidth = monitor.Description.DesktopBounds.Width;
+ var desktopHeight = monitor.Description.DesktopBounds.Height;
+#endif
+
+ var modes = new List();
+
+ foreach (var formatTranslation in FormatTranslations)
+ {
+ SharpDX.DXGI.ModeDescription[] displayModes;
+
+ // This can fail on headless machines, so just assume the desktop size
+ // is a valid mode and return that... so at least our unit tests work.
+ try
+ {
+ displayModes = monitor.GetDisplayModeList(formatTranslation.Key, 0);
+ }
+ catch (SharpDX.SharpDXException)
+ {
+ var mode = new DisplayMode(desktopWidth, desktopHeight, SurfaceFormat.Color);
+ modes.Add(mode);
+ adapter._currentDisplayMode = mode;
+ break;
+ }
+
+
+ foreach (var displayMode in displayModes)
+ {
+ var mode = new DisplayMode(displayMode.Width, displayMode.Height, formatTranslation.Value);
+
+ // Skip duplicate modes with the same width/height/formats.
+ if (modes.Contains(mode))
+ continue;
+
+ modes.Add(mode);
+
+ if (adapter._currentDisplayMode == null)
+ {
+ if (mode.Width == desktopWidth && mode.Height == desktopHeight && mode.Format == SurfaceFormat.Color)
+ adapter._currentDisplayMode = mode;
+ }
+ }
+ }
+
+ adapter._supportedDisplayModes = new DisplayModeCollection(modes);
+
+ if (adapter._currentDisplayMode == null) //(i.e. desktop mode wasn't found in the available modes)
+ adapter._currentDisplayMode = new DisplayMode(desktopWidth, desktopHeight, SurfaceFormat.Color);
+
+ return adapter;
+ }
+
+ private bool PlatformIsProfileSupported(GraphicsProfile graphicsProfile)
+ {
+ if(UseReferenceDevice)
+ return true;
+
+ switch(graphicsProfile)
+ {
+ case GraphicsProfile.Reach:
+ return SharpDX.Direct3D11.Device.IsSupportedFeatureLevel(_adapter, FeatureLevel.Level_9_1);
+ case GraphicsProfile.HiDef:
+ return SharpDX.Direct3D11.Device.IsSupportedFeatureLevel(_adapter, FeatureLevel.Level_10_0);
+ default:
+ throw new InvalidOperationException();
+ }
+ }
+ }
+}
diff --git a/MonoGame.Framework/Graphics/GraphicsAdapter.Legacy.cs b/MonoGame.Framework/Graphics/GraphicsAdapter.Legacy.cs
new file mode 100644
index 00000000000..6c68150f4f6
--- /dev/null
+++ b/MonoGame.Framework/Graphics/GraphicsAdapter.Legacy.cs
@@ -0,0 +1,407 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+
+#if MONOMAC
+#if PLATFORM_MACOS_LEGACY
+using MonoMac.AppKit;
+using MonoMac.Foundation;
+#else
+using AppKit;
+using Foundation;
+#endif
+#elif IOS
+using UIKit;
+#elif ANDROID
+using Android.Views;
+using Android.Runtime;
+#endif
+
+// NOTE: This is the legacy graphics adapter implementation
+// which should no longer be updated. All new development
+// should go into the new one.
+
+namespace Microsoft.Xna.Framework.Graphics
+{
+ public sealed class GraphicsAdapter : IDisposable
+ {
+ ///
+ /// Defines the driver type for graphics adapter. Usable only on DirectX platforms for now.
+ ///
+ public enum DriverType
+ {
+ ///
+ /// Hardware device been used for rendering. Maximum speed and performance.
+ ///
+ Hardware,
+ ///
+ /// Emulates the hardware device on CPU. Slowly, only for testing.
+ ///
+ Reference,
+ ///
+ /// Useful when acceleration does not work.
+ ///
+ FastSoftware
+ }
+
+ private static ReadOnlyCollection _adapters;
+
+ private DisplayModeCollection _supportedDisplayModes;
+
+
+#if MONOMAC
+ private NSScreen _screen;
+ internal GraphicsAdapter(NSScreen screen)
+ {
+ _screen = screen;
+ }
+#elif IOS
+ private UIScreen _screen;
+ internal GraphicsAdapter(UIScreen screen)
+ {
+ _screen = screen;
+ }
+#elif DESKTOPGL
+ int _displayIndex;
+#else
+ internal GraphicsAdapter()
+ {
+ }
+#endif
+
+ public void Dispose()
+ {
+ }
+
+ string _description = string.Empty;
+ public string Description { get { return _description; } private set { _description = value; } }
+
+ public DisplayMode CurrentDisplayMode
+ {
+ get
+ {
+#if MONOMAC
+ //Dummy values until MonoMac implements Quartz Display Services
+ SurfaceFormat format = SurfaceFormat.Color;
+
+ return new DisplayMode((int)_screen.Frame.Width,
+ (int)_screen.Frame.Height,
+ format);
+#elif IOS
+ return new DisplayMode((int)(_screen.Bounds.Width * _screen.Scale),
+ (int)(_screen.Bounds.Height * _screen.Scale),
+ SurfaceFormat.Color);
+#elif ANDROID
+ View view = ((AndroidGameWindow)Game.Instance.Window).GameView;
+ return new DisplayMode(view.Width, view.Height, SurfaceFormat.Color);
+#elif DESKTOPGL
+ var displayIndex = Sdl.Display.GetWindowDisplayIndex(SdlGameWindow.Instance.Handle);
+
+ Sdl.Display.Mode mode;
+ Sdl.Display.GetCurrentDisplayMode(displayIndex, out mode);
+
+ return new DisplayMode(mode.Width, mode.Height, SurfaceFormat.Color);
+#elif WINDOWS
+ using (var graphics = System.Drawing.Graphics.FromHwnd(IntPtr.Zero))
+ {
+ var dc = graphics.GetHdc();
+ int width = GetDeviceCaps(dc, HORZRES);
+ int height = GetDeviceCaps(dc, VERTRES);
+ graphics.ReleaseHdc(dc);
+ return new DisplayMode(width, height, SurfaceFormat.Color);
+ }
+#else
+ return new DisplayMode(800, 600, SurfaceFormat.Color);
+#endif
+ }
+ }
+
+ public static GraphicsAdapter DefaultAdapter
+ {
+ get { return Adapters[0]; }
+ }
+
+ public static ReadOnlyCollection Adapters
+ {
+ get
+ {
+ if (_adapters == null)
+ {
+#if MONOMAC
+ GraphicsAdapter[] tmpAdapters = new GraphicsAdapter[NSScreen.Screens.Length];
+ for (int i=0; i(tmpAdapters);
+#elif IOS
+ _adapters = new ReadOnlyCollection(
+ new [] {new GraphicsAdapter(UIScreen.MainScreen)});
+#else
+ _adapters = new ReadOnlyCollection(new[] { new GraphicsAdapter() });
+#endif
+ }
+
+ return _adapters;
+ }
+ }
+
+ ///
+ /// Used to request creation of the reference graphics device,
+ /// or the default hardware accelerated device (when set to false).
+ ///
+ ///
+ /// This only works on DirectX platforms where a reference graphics
+ /// device is available and must be defined before the graphics device
+ /// is created. It defaults to false.
+ ///
+ public static bool UseReferenceDevice
+ {
+ get { return UseDriverType == DriverType.Reference; }
+ set { UseDriverType = value ? DriverType.Reference : DriverType.Hardware; }
+ }
+
+ ///
+ /// Used to request creation of a specific kind of driver.
+ ///
+ ///
+ /// These values only work on DirectX platforms and must be defined before the graphics device
+ /// is created. by default.
+ ///
+ public static DriverType UseDriverType { get; set; }
+
+ /*
+ public bool QueryRenderTargetFormat(
+ GraphicsProfile graphicsProfile,
+ SurfaceFormat format,
+ DepthFormat depthFormat,
+ int multiSampleCount,
+ out SurfaceFormat selectedFormat,
+ out DepthFormat selectedDepthFormat,
+ out int selectedMultiSampleCount)
+ {
+ throw new NotImplementedException();
+ }
+
+ public string Description
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public int DeviceId
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public Guid DeviceIdentifier
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public string DeviceName
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public string DriverDll
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public Version DriverVersion
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public bool IsDefaultAdapter
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public bool IsWideScreen
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public IntPtr MonitorHandle
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public int Revision
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public int SubSystemId
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+ }
+ */
+
+#if DIRECTX && !WINDOWS_PHONE
+ private static readonly Dictionary FormatTranslations = new Dictionary
+ {
+ { SharpDX.DXGI.Format.R8G8B8A8_UNorm, SurfaceFormat.Color },
+ { SharpDX.DXGI.Format.B8G8R8A8_UNorm, SurfaceFormat.Color },
+ { SharpDX.DXGI.Format.B5G6R5_UNorm, SurfaceFormat.Bgr565 },
+ };
+#endif
+
+ public DisplayModeCollection SupportedDisplayModes
+ {
+ get
+ {
+ bool displayChanged = false;
+#if DESKTOPGL
+ var displayIndex = Sdl.Display.GetWindowDisplayIndex (SdlGameWindow.Instance.Handle);
+ displayChanged = displayIndex != _displayIndex;
+#endif
+ if (_supportedDisplayModes == null || displayChanged)
+ {
+ var modes = new List(new[] { CurrentDisplayMode, });
+
+#if DESKTOPGL
+ _displayIndex = displayIndex;
+ modes.Clear();
+
+ var modeCount = Sdl.Display.GetNumDisplayModes(displayIndex);
+
+ for (int i = 0;i < modeCount;i++)
+ {
+ Sdl.Display.Mode mode;
+ Sdl.Display.GetDisplayMode(displayIndex, i, out mode);
+
+ // We are only using one format, Color
+ // mode.Format gets the Color format from SDL
+ var displayMode = new DisplayMode(mode.Width, mode.Height, SurfaceFormat.Color);
+ if (!modes.Contains(displayMode))
+ modes.Add(displayMode);
+ }
+#elif DIRECTX && !WINDOWS_PHONE
+ var dxgiFactory = new SharpDX.DXGI.Factory1();
+ var adapter = dxgiFactory.GetAdapter(0);
+ var output = adapter.Outputs[0];
+
+ modes.Clear();
+ foreach (var formatTranslation in FormatTranslations)
+ {
+ var displayModes = output.GetDisplayModeList(formatTranslation.Key, 0);
+ foreach (var displayMode in displayModes)
+ {
+ var xnaDisplayMode = new DisplayMode(displayMode.Width, displayMode.Height, formatTranslation.Value);
+ if (!modes.Contains(xnaDisplayMode))
+ modes.Add(xnaDisplayMode);
+ }
+ }
+
+ output.Dispose();
+ adapter.Dispose();
+ dxgiFactory.Dispose();
+#endif
+ modes.Sort(delegate(DisplayMode a, DisplayMode b)
+ {
+ if (a == b) return 0;
+ if (a.Format <= b.Format && a.Width <= b.Width && a.Height <= b.Height) return -1;
+ else return 1;
+ });
+ _supportedDisplayModes = new DisplayModeCollection(modes);
+ }
+
+ return _supportedDisplayModes;
+ }
+ }
+
+ /*
+ public int VendorId
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+ }
+ */
+
+ ///
+ /// Gets a indicating whether
+ /// has a
+ /// Width:Height ratio corresponding to a widescreen .
+ /// Common widescreen modes include 16:9, 16:10 and 2:1.
+ ///
+ public bool IsWideScreen
+ {
+ get
+ {
+ // Common non-widescreen modes: 4:3, 5:4, 1:1
+ // Common widescreen modes: 16:9, 16:10, 2:1
+ // XNA does not appear to account for rotated displays on the desktop
+ const float limit = 4.0f / 3.0f;
+ var aspect = CurrentDisplayMode.AspectRatio;
+ return aspect > limit;
+ }
+ }
+
+ public bool IsProfileSupported(GraphicsProfile graphicsProfile)
+ {
+ if(UseReferenceDevice)
+ return true;
+
+ switch(graphicsProfile)
+ {
+ case GraphicsProfile.Reach:
+ return true;
+ case GraphicsProfile.HiDef:
+ bool result = true;
+ // TODO: check adapter capabilities...
+ return result;
+ default:
+ throw new InvalidOperationException();
+ }
+ }
+
+#if WINDOWS && !OPENGL
+ [System.Runtime.InteropServices.DllImport("gdi32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true, ExactSpelling = true)]
+ public static extern int GetDeviceCaps(IntPtr hDC, int nIndex);
+
+ private const int HORZRES = 8;
+ private const int VERTRES = 10;
+#endif
+ }
+}
\ No newline at end of file
diff --git a/MonoGame.Framework/Graphics/GraphicsAdapter.cs b/MonoGame.Framework/Graphics/GraphicsAdapter.cs
index 606b5512c9a..69e61729ad9 100644
--- a/MonoGame.Framework/Graphics/GraphicsAdapter.cs
+++ b/MonoGame.Framework/Graphics/GraphicsAdapter.cs
@@ -3,27 +3,11 @@
// file 'LICENSE.txt', which is part of this source code package.
using System;
-using System.Collections.Generic;
using System.Collections.ObjectModel;
-#if MONOMAC
-#if PLATFORM_MACOS_LEGACY
-using MonoMac.AppKit;
-using MonoMac.Foundation;
-#else
-using AppKit;
-using Foundation;
-#endif
-#elif IOS
-using UIKit;
-#elif ANDROID
-using Android.Views;
-using Android.Runtime;
-#endif
-
namespace Microsoft.Xna.Framework.Graphics
{
- public sealed class GraphicsAdapter : IDisposable
+ public sealed partial class GraphicsAdapter : IDisposable
{
///
/// Defines the driver type for graphics adapter. Usable only on DirectX platforms for now.
@@ -43,98 +27,31 @@ public enum DriverType
///
FastSoftware
}
-
- private static ReadOnlyCollection _adapters;
+
+ private static readonly ReadOnlyCollection _adapters;
private DisplayModeCollection _supportedDisplayModes;
-
-#if MONOMAC
- private NSScreen _screen;
- internal GraphicsAdapter(NSScreen screen)
- {
- _screen = screen;
- }
-#elif IOS
- private UIScreen _screen;
- internal GraphicsAdapter(UIScreen screen)
- {
- _screen = screen;
- }
-#else
- internal GraphicsAdapter()
- {
- }
-#endif
-
- public void Dispose()
- {
- }
-
- public DisplayMode CurrentDisplayMode
- {
- get
- {
-#if MONOMAC
- //Dummy values until MonoMac implements Quartz Display Services
- SurfaceFormat format = SurfaceFormat.Color;
-
- return new DisplayMode((int)_screen.Frame.Width,
- (int)_screen.Frame.Height,
- format);
-#elif IOS
- return new DisplayMode((int)(_screen.Bounds.Width * _screen.Scale),
- (int)(_screen.Bounds.Height * _screen.Scale),
- SurfaceFormat.Color);
-#elif ANDROID
- View view = ((AndroidGameWindow)Game.Instance.Window).GameView;
- return new DisplayMode(view.Width, view.Height, SurfaceFormat.Color);
-#elif DESKTOPGL
+ private DisplayMode _currentDisplayMode;
- return new DisplayMode(OpenTK.DisplayDevice.Default.Width, OpenTK.DisplayDevice.Default.Height, SurfaceFormat.Color);
-#elif WINDOWS
- using (var graphics = System.Drawing.Graphics.FromHwnd(IntPtr.Zero))
- {
- var dc = graphics.GetHdc();
- int width = GetDeviceCaps(dc, HORZRES);
- int height = GetDeviceCaps(dc, VERTRES);
- graphics.ReleaseHdc(dc);
- return new DisplayMode(width, height, SurfaceFormat.Color);
- }
-#else
- return new DisplayMode(800, 600, SurfaceFormat.Color);
-#endif
- }
- }
+ static GraphicsAdapter()
+ {
+ // NOTE: An adapter is a monitor+device combination, so we expect
+ // at lease one adapter per connected monitor.
+ PlatformInitializeAdapters(out _adapters);
+ // The first adapter is considered the default.
+ _adapters[0].IsDefaultAdapter = true;
+ }
+
public static GraphicsAdapter DefaultAdapter
{
- get { return Adapters[0]; }
+ get { return _adapters[0]; }
}
public static ReadOnlyCollection Adapters
{
- get
- {
- if (_adapters == null)
- {
-#if MONOMAC
- GraphicsAdapter[] tmpAdapters = new GraphicsAdapter[NSScreen.Screens.Length];
- for (int i=0; i(tmpAdapters);
-#elif IOS
- _adapters = new ReadOnlyCollection(
- new [] {new GraphicsAdapter(UIScreen.MainScreen)});
-#else
- _adapters = new ReadOnlyCollection(new[] {new GraphicsAdapter()});
-#endif
- }
-
- return _adapters;
- }
+ get { return _adapters; }
}
///
@@ -148,7 +65,7 @@ public static ReadOnlyCollection Adapters
///
public static bool UseReferenceDevice
{
- get { return UseDriverType==DriverType.Reference; }
+ get { return UseDriverType == DriverType.Reference; }
set { UseDriverType = value ? DriverType.Reference : DriverType.Hardware; }
}
@@ -161,237 +78,71 @@ public static bool UseReferenceDevice
///
public static DriverType UseDriverType { get; set; }
- /*
- public bool QueryRenderTargetFormat(
- GraphicsProfile graphicsProfile,
- SurfaceFormat format,
- DepthFormat depthFormat,
- int multiSampleCount,
- out SurfaceFormat selectedFormat,
- out DepthFormat selectedDepthFormat,
- out int selectedMultiSampleCount)
- {
- throw new NotImplementedException();
- }
+ public string Description { get; private set; }
- public string Description
- {
- get
- {
- throw new NotImplementedException();
- }
- }
+ public int DeviceId { get; private set; }
- public int DeviceId
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public Guid DeviceIdentifier
- {
- get
- {
- throw new NotImplementedException();
- }
- }
+ public string DeviceName { get; private set; }
- public string DeviceName
- {
- get
- {
- throw new NotImplementedException();
- }
- }
+ public int VendorId { get; private set; }
- public string DriverDll
- {
- get
- {
- throw new NotImplementedException();
- }
- }
+ public bool IsDefaultAdapter { get; private set; }
- public Version DriverVersion
- {
- get
- {
- throw new NotImplementedException();
- }
- }
+ public IntPtr MonitorHandle { get; private set; }
- public bool IsDefaultAdapter
- {
- get
- {
- throw new NotImplementedException();
- }
- }
+ public int Revision { get; private set; }
- public bool IsWideScreen
+ public int SubSystemId { get; private set; }
+
+ public DisplayModeCollection SupportedDisplayModes
{
- get
- {
- throw new NotImplementedException();
- }
+ get { return _supportedDisplayModes; }
}
- public IntPtr MonitorHandle
+ public DisplayMode CurrentDisplayMode
{
- get
- {
- throw new NotImplementedException();
- }
+ get { return _currentDisplayMode; }
}
- public int Revision
+ ///
+ /// Returns true if the is widescreen.
+ ///
+ ///
+ /// Common widescreen modes include 16:9, 16:10 and 2:1.
+ ///
+ public bool IsWideScreen
{
get
{
- throw new NotImplementedException();
+ // Seems like XNA treats aspect ratios above 16:10 as wide screen.
+ const float minWideScreenAspect = 16.0f / 10.0f;
+ return CurrentDisplayMode.AspectRatio >= minWideScreenAspect;
}
}
- public int SubSystemId
- {
- get
- {
- throw new NotImplementedException();
- }
- }
+ /*
+ public bool QueryRenderTargetFormat(
+ GraphicsProfile graphicsProfile,
+ SurfaceFormat format,
+ DepthFormat depthFormat,
+ int multiSampleCount,
+ out SurfaceFormat selectedFormat,
+ out DepthFormat selectedDepthFormat,
+ out int selectedMultiSampleCount)
+ {
+ throw new NotImplementedException();
+ }
*/
-
-#if DIRECTX && !WINDOWS_PHONE
- private static readonly Dictionary FormatTranslations = new Dictionary
- {
- { SharpDX.DXGI.Format.R8G8B8A8_UNorm, SurfaceFormat.Color },
- { SharpDX.DXGI.Format.B8G8R8A8_UNorm, SurfaceFormat.Color },
- { SharpDX.DXGI.Format.B5G6R5_UNorm, SurfaceFormat.Bgr565 },
- };
-#endif
-
- public DisplayModeCollection SupportedDisplayModes
- {
- get
- {
-
- if (_supportedDisplayModes == null)
- {
- var modes = new List(new[] { CurrentDisplayMode, });
-
-#if DESKTOPGL
-
- //IList displays = OpenTK.DisplayDevice.AvailableDisplays;
- var displays = new List();
-
- OpenTK.DisplayIndex[] displayIndices = {
- OpenTK.DisplayIndex.First,
- OpenTK.DisplayIndex.Second,
- OpenTK.DisplayIndex.Third,
- OpenTK.DisplayIndex.Fourth,
- OpenTK.DisplayIndex.Fifth,
- OpenTK.DisplayIndex.Sixth,
- };
-
- foreach(var displayIndex in displayIndices)
- {
- var currentDisplay = OpenTK.DisplayDevice.GetDisplay(displayIndex);
- if(currentDisplay!= null) displays.Add(currentDisplay);
- }
-
- if (displays.Count > 0)
- {
- modes.Clear();
- foreach (OpenTK.DisplayDevice display in displays)
- {
- foreach (OpenTK.DisplayResolution resolution in display.AvailableResolutions)
- {
- SurfaceFormat format = SurfaceFormat.Color;
- switch (resolution.BitsPerPixel)
- {
- case 32: format = SurfaceFormat.Color; break;
- case 16: format = SurfaceFormat.Bgr565; break;
- case 8: format = SurfaceFormat.Bgr565; break;
- default:
- break;
- }
- // Just report the 32 bit surfaces for now
- // Need to decide what to do about other surface formats
- if (format == SurfaceFormat.Color)
- {
- var displayMode = new DisplayMode(resolution.Width, resolution.Height, format);
- if (!modes.Contains(displayMode))
- modes.Add(displayMode);
- }
- }
-
- }
- }
-#elif DIRECTX && !WINDOWS_PHONE
- var dxgiFactory = new SharpDX.DXGI.Factory1();
- var adapter = dxgiFactory.GetAdapter(0);
- var output = adapter.Outputs[0];
-
- modes.Clear();
- foreach (var formatTranslation in FormatTranslations)
- {
- var displayModes = output.GetDisplayModeList(formatTranslation.Key, 0);
- foreach (var displayMode in displayModes)
- {
- var xnaDisplayMode = new DisplayMode(displayMode.Width, displayMode.Height, formatTranslation.Value);
- if (!modes.Contains(xnaDisplayMode))
- modes.Add(xnaDisplayMode);
- }
- }
-
- output.Dispose();
- adapter.Dispose();
- dxgiFactory.Dispose();
-#endif
- _supportedDisplayModes = new DisplayModeCollection(modes);
- }
-
- return _supportedDisplayModes;
- }
- }
- /*
- public int VendorId
+ public bool IsProfileSupported(GraphicsProfile graphicsProfile)
{
- get
- {
- throw new NotImplementedException();
- }
+ return PlatformIsProfileSupported(graphicsProfile);
}
- */
- ///
- /// Gets a indicating whether
- /// has a
- /// Width:Height ratio corresponding to a widescreen .
- /// Common widescreen modes include 16:9, 16:10 and 2:1.
- ///
- public bool IsWideScreen
+ public void Dispose()
{
- get
- {
- // Common non-widescreen modes: 4:3, 5:4, 1:1
- // Common widescreen modes: 16:9, 16:10, 2:1
- // XNA does not appear to account for rotated displays on the desktop
- const float limit = 4.0f / 3.0f;
- var aspect = CurrentDisplayMode.AspectRatio;
- return aspect > limit;
- }
+ // We don't keep any resources, so we have
+ // nothing to do... just here for XNA compatibility.
}
-
-#if WINDOWS && !OPENGL
- [System.Runtime.InteropServices.DllImport("gdi32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true, ExactSpelling = true)]
- public static extern int GetDeviceCaps(IntPtr hDC, int nIndex);
-
- private const int HORZRES = 8;
- private const int VERTRES = 10;
-#endif
}
}
diff --git a/MonoGame.Framework/Graphics/GraphicsCapabilities.DirectX.cs b/MonoGame.Framework/Graphics/GraphicsCapabilities.DirectX.cs
new file mode 100644
index 00000000000..a22c92ecbdb
--- /dev/null
+++ b/MonoGame.Framework/Graphics/GraphicsCapabilities.DirectX.cs
@@ -0,0 +1,52 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+namespace Microsoft.Xna.Framework.Graphics
+{
+ internal partial class GraphicsCapabilities
+ {
+ private void PlatformInitialize(GraphicsDevice device)
+ {
+ SupportsNonPowerOfTwo = device.GraphicsProfile == GraphicsProfile.HiDef;
+ SupportsTextureFilterAnisotropic = true;
+
+ SupportsDepth24 = true;
+ SupportsPackedDepthStencil = true;
+ SupportsDepthNonLinear = false;
+ SupportsTextureMaxLevel = true;
+
+ // Texture compression
+ SupportsDxt1 = true;
+ SupportsS3tc = true;
+
+ SupportsSRgb = true;
+
+ SupportsTextureArrays = device.GraphicsProfile == GraphicsProfile.HiDef;
+ SupportsDepthClamp = device.GraphicsProfile == GraphicsProfile.HiDef;
+ SupportsVertexTextures = device.GraphicsProfile == GraphicsProfile.HiDef;
+ }
+
+ private void PlatformInitializeAfterResources(GraphicsDevice device)
+ {
+ _maxMultiSampleCount = GetMaxMultiSampleCount(device);
+ }
+
+ private int GetMaxMultiSampleCount(GraphicsDevice device)
+ {
+ var format = SharpDXHelper.ToFormat(device.PresentationParameters.BackBufferFormat);
+ // Find the maximum supported level starting with the game's requested multisampling level
+ // and halving each time until reaching 0 (meaning no multisample support).
+ var qualityLevels = 0;
+ var maxLevel = MultiSampleCountLimit;
+ while (maxLevel > 0)
+ {
+ qualityLevels = device._d3dDevice.CheckMultisampleQualityLevels(format, maxLevel);
+ if (qualityLevels > 0)
+ break;
+ maxLevel /= 2;
+ }
+ return maxLevel;
+ }
+ }
+}
diff --git a/MonoGame.Framework/Graphics/GraphicsCapabilities.OpenGL.cs b/MonoGame.Framework/Graphics/GraphicsCapabilities.OpenGL.cs
new file mode 100644
index 00000000000..126d29749d0
--- /dev/null
+++ b/MonoGame.Framework/Graphics/GraphicsCapabilities.OpenGL.cs
@@ -0,0 +1,138 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+#if OPENGL
+#if MONOMAC
+#if PLATFORM_MACOS_LEGACY
+using MonoMac.OpenGL;
+using GetParamName = MonoMac.OpenGL.All;
+using GetPName = MonoMac.OpenGL.GetPName;
+#else
+using OpenTK.Graphics.OpenGL;
+using GetParamName = OpenTK.Graphics.OpenGL.All;
+using GetPName = OpenTK.Graphics.OpenGL.GetPName;
+#endif
+#elif GLES
+using OpenTK.Graphics.ES20;
+using GetParamName = OpenTK.Graphics.ES20.All;
+using GetPName = OpenTK.Graphics.ES20.GetPName;
+#else
+using OpenGL;
+using GetParamName = OpenGL.GetPName;
+#endif
+#endif
+
+namespace Microsoft.Xna.Framework.Graphics
+{
+
+ internal partial class GraphicsCapabilities
+ {
+ ///
+ /// True, if GL_ARB_framebuffer_object is supported; false otherwise.
+ ///
+ internal bool SupportsFramebufferObjectARB { get; private set; }
+
+ ///
+ /// True, if GL_EXT_framebuffer_object is supported; false otherwise.
+ ///
+ internal bool SupportsFramebufferObjectEXT { get; private set; }
+
+ ///
+ /// Gets the max texture anisotropy. This value typically lies
+ /// between 0 and 16, where 0 means anisotropic filtering is not
+ /// supported.
+ ///
+ internal int MaxTextureAnisotropy { get; private set; }
+
+ private void PlatformInitialize(GraphicsDevice device)
+ {
+#if GLES
+ SupportsNonPowerOfTwo = device._extensions.Contains("GL_OES_texture_npot") ||
+ device._extensions.Contains("GL_ARB_texture_non_power_of_two") ||
+ device._extensions.Contains("GL_IMG_texture_npot") ||
+ device._extensions.Contains("GL_NV_texture_npot_2D_mipmap");
+#else
+ // Unfortunately non PoT texture support is patchy even on desktop systems and we can't
+ // rely on the fact that GL2.0+ supposedly supports npot in the core.
+ // Reference: http://aras-p.info/blog/2012/10/17/non-power-of-two-textures/
+ SupportsNonPowerOfTwo = device._maxTextureSize >= 8192;
+#endif
+
+ SupportsTextureFilterAnisotropic = device._extensions.Contains("GL_EXT_texture_filter_anisotropic");
+
+#if GLES
+ SupportsDepth24 = device._extensions.Contains("GL_OES_depth24");
+ SupportsPackedDepthStencil = device._extensions.Contains("GL_OES_packed_depth_stencil");
+ SupportsDepthNonLinear = device._extensions.Contains("GL_NV_depth_nonlinear");
+ SupportsTextureMaxLevel = device._extensions.Contains("GL_APPLE_texture_max_level");
+#else
+ SupportsDepth24 = true;
+ SupportsPackedDepthStencil = true;
+ SupportsDepthNonLinear = false;
+ SupportsTextureMaxLevel = true;
+#endif
+ // Texture compression
+ SupportsS3tc = device._extensions.Contains("GL_EXT_texture_compression_s3tc") ||
+ device._extensions.Contains("GL_OES_texture_compression_S3TC") ||
+ device._extensions.Contains("GL_EXT_texture_compression_dxt3") ||
+ device._extensions.Contains("GL_EXT_texture_compression_dxt5");
+ SupportsDxt1 = SupportsS3tc || device._extensions.Contains("GL_EXT_texture_compression_dxt1");
+ SupportsPvrtc = device._extensions.Contains("GL_IMG_texture_compression_pvrtc");
+ SupportsEtc1 = device._extensions.Contains("GL_OES_compressed_ETC1_RGB8_texture");
+ SupportsAtitc = device._extensions.Contains("GL_ATI_texture_compression_atitc") ||
+ device._extensions.Contains("GL_AMD_compressed_ATC_texture");
+
+ // Framebuffer objects
+#if GLES
+ SupportsFramebufferObjectARB = true; // always supported on GLES 2.0+
+ SupportsFramebufferObjectEXT = false;
+#else
+ // if we're on GL 3.0+, frame buffer extensions are guaranteed to be present, but extensions may be missing
+ // it is then safe to assume that GL_ARB_framebuffer_object is present so that the standard function are loaded
+ SupportsFramebufferObjectARB = device.glMajorVersion >= 3 || device._extensions.Contains("GL_ARB_framebuffer_object");
+ SupportsFramebufferObjectEXT = device._extensions.Contains("GL_EXT_framebuffer_object");
+#endif
+ // Anisotropic filtering
+ int anisotropy = 0;
+ if (SupportsTextureFilterAnisotropic)
+ {
+#if __IOS__ || __TVOS__
+ GL.GetInteger ((GetPName)All.MaxTextureMaxAnisotropyExt, out anisotropy);
+#else
+ GL.GetInteger((GetPName)GetParamName.MaxTextureMaxAnisotropyExt, out anisotropy);
+#endif
+ GraphicsExtensions.CheckGLError();
+ }
+ MaxTextureAnisotropy = anisotropy;
+
+ // sRGB
+#if GLES
+ SupportsSRgb = device._extensions.Contains("GL_EXT_sRGB");
+#else
+ SupportsSRgb = device._extensions.Contains("GL_EXT_texture_sRGB") && device._extensions.Contains("GL_EXT_framebuffer_sRGB");
+#endif
+
+ // TODO: Implement OpenGL support for texture arrays
+ // once we can author shaders that use texture arrays.
+ SupportsTextureArrays = false;
+
+ SupportsDepthClamp = device._extensions.Contains("GL_ARB_depth_clamp");
+
+ SupportsVertexTextures = false; // For now, until we implement vertex textures in OpenGL.
+
+#if __IOS__ || __TVOS__
+ GL.GetInteger((GetPName)All.MaxSamplesApple, out _maxMultiSampleCount);
+#elif ANDROID
+ GL.GetInteger((GetPName) GetParamName.MaxSamplesExt, out _maxMultiSampleCount);
+#else
+ GL.GetInteger((GetPName)GetParamName.MaxSamples, out _maxMultiSampleCount);
+#endif
+ }
+
+ private void PlatformInitializeAfterResources(GraphicsDevice device)
+ {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/MonoGame.Framework/Graphics/GraphicsCapabilities.Web.cs b/MonoGame.Framework/Graphics/GraphicsCapabilities.Web.cs
new file mode 100644
index 00000000000..866dbc2f05c
--- /dev/null
+++ b/MonoGame.Framework/Graphics/GraphicsCapabilities.Web.cs
@@ -0,0 +1,21 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+
+namespace Microsoft.Xna.Framework.Graphics
+{
+ internal partial class GraphicsCapabilities
+ {
+ private void PlatformInitialize(GraphicsDevice device)
+ {
+ throw new NotImplementedException();
+ }
+
+ private void PlatformInitializeAfterResources(GraphicsDevice device)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/MonoGame.Framework/Graphics/GraphicsCapabilities.cs b/MonoGame.Framework/Graphics/GraphicsCapabilities.cs
index 28b8550004e..f3ab2cb3819 100644
--- a/MonoGame.Framework/Graphics/GraphicsCapabilities.cs
+++ b/MonoGame.Framework/Graphics/GraphicsCapabilities.cs
@@ -4,19 +4,6 @@
using System;
using System.Collections.Generic;
-#if OPENGL
-#if MONOMAC
-#if PLATFORM_MACOS_LEGACY
-using MonoMac.OpenGL;
-#else
-using OpenTK.Graphics.OpenGL;
-#endif
-#elif GLES
-using OpenTK.Graphics.ES20;
-#else
-using OpenTK.Graphics.OpenGL;
-#endif
-#endif
namespace Microsoft.Xna.Framework.Graphics
{
@@ -25,12 +12,19 @@ namespace Microsoft.Xna.Framework.Graphics
/// current graphics device. A very useful thread for investigating GL extenion names
/// http://stackoverflow.com/questions/3881197/opengl-es-2-0-extensions-on-android-devices
///
- internal class GraphicsCapabilities
+ internal partial class GraphicsCapabilities
{
- public GraphicsCapabilities(GraphicsDevice graphicsDevice)
+ internal void Initialize(GraphicsDevice device)
{
- Initialize(graphicsDevice);
+ PlatformInitialize(device);
}
+
+ // For figuring out some capabilities we need native resources for querying
+ internal void InitializeAfterResources(GraphicsDevice device)
+ {
+ PlatformInitializeAfterResources(device);
+ }
+
///
/// Whether the device fully supports non power-of-two textures, including
/// mip maps and wrap modes other than CLAMP_TO_EDGE
@@ -73,25 +67,6 @@ public GraphicsCapabilities(GraphicsDevice graphicsDevice)
///
internal bool SupportsAtitc { get; private set; }
-#if OPENGL
- ///
- /// True, if GL_ARB_framebuffer_object is supported; false otherwise.
- ///
- internal bool SupportsFramebufferObjectARB { get; private set; }
-
- ///
- /// True, if GL_EXT_framebuffer_object is supported; false otherwise.
- ///
- internal bool SupportsFramebufferObjectEXT { get; private set; }
-
- ///
- /// Gets the max texture anisotropy. This value typically lies
- /// between 0 and 16, where 0 means anisotropic filtering is not
- /// supported.
- ///
- internal int MaxTextureAnisotropy { get; private set; }
-#endif
-
internal bool SupportsTextureMaxLevel { get; private set; }
///
@@ -107,115 +82,13 @@ public GraphicsCapabilities(GraphicsDevice graphicsDevice)
internal bool SupportsVertexTextures { get; private set; }
- internal void Initialize(GraphicsDevice device)
- {
- SupportsNonPowerOfTwo = GetNonPowerOfTwo(device);
-
-#if OPENGL
- SupportsTextureFilterAnisotropic = device._extensions.Contains("GL_EXT_texture_filter_anisotropic");
-#else
- SupportsTextureFilterAnisotropic = true;
-#endif
-#if GLES
- SupportsDepth24 = device._extensions.Contains("GL_OES_depth24");
- SupportsPackedDepthStencil = device._extensions.Contains("GL_OES_packed_depth_stencil");
- SupportsDepthNonLinear = device._extensions.Contains("GL_NV_depth_nonlinear");
- SupportsTextureMaxLevel = device._extensions.Contains("GL_APPLE_texture_max_level");
-#else
- SupportsDepth24 = true;
- SupportsPackedDepthStencil = true;
- SupportsDepthNonLinear = false;
- SupportsTextureMaxLevel = true;
-#endif
-
- // Texture compression
-#if DIRECTX
- SupportsDxt1 = true;
- SupportsS3tc = true;
-#elif OPENGL
- SupportsS3tc = device._extensions.Contains("GL_EXT_texture_compression_s3tc") ||
- device._extensions.Contains("GL_OES_texture_compression_S3TC") ||
- device._extensions.Contains("GL_EXT_texture_compression_dxt3") ||
- device._extensions.Contains("GL_EXT_texture_compression_dxt5");
- SupportsDxt1 = SupportsS3tc || device._extensions.Contains("GL_EXT_texture_compression_dxt1");
- SupportsPvrtc = device._extensions.Contains("GL_IMG_texture_compression_pvrtc");
- SupportsEtc1 = device._extensions.Contains("GL_OES_compressed_ETC1_RGB8_texture");
- SupportsAtitc = device._extensions.Contains("GL_ATI_texture_compression_atitc") ||
- device._extensions.Contains("GL_AMD_compressed_ATC_texture");
-#endif
-
- // OpenGL framebuffer objects
-#if OPENGL
-#if GLES
- SupportsFramebufferObjectARB = true; // always supported on GLES 2.0+
- SupportsFramebufferObjectEXT = false;
-#else
- SupportsFramebufferObjectARB = device._extensions.Contains("GL_ARB_framebuffer_object");
- SupportsFramebufferObjectEXT = device._extensions.Contains("GL_EXT_framebuffer_object");
-#endif
-#endif
-
- // Anisotropic filtering
-#if OPENGL
- int anisotropy = 0;
- if (SupportsTextureFilterAnisotropic)
- {
- GL.GetInteger((GetPName)All.MaxTextureMaxAnisotropyExt, out anisotropy);
- GraphicsExtensions.CheckGLError();
- }
- MaxTextureAnisotropy = anisotropy;
-#endif
-
- // sRGB
-#if DIRECTX
- SupportsSRgb = true;
-#elif OPENGL
-#if GLES
- SupportsSRgb = device._extensions.Contains("GL_EXT_sRGB");
-#else
- SupportsSRgb = device._extensions.Contains("GL_EXT_texture_sRGB") && device._extensions.Contains("GL_EXT_framebuffer_sRGB");
-#endif
-#endif
-
-#if DIRECTX
- SupportsTextureArrays = device.GraphicsProfile == GraphicsProfile.HiDef;
-#elif OPENGL
- // TODO: Implement OpenGL support for texture arrays
- // once we can author shaders that use texture arrays.
- SupportsTextureArrays = false;
-#endif
-
-#if DIRECTX
- SupportsDepthClamp = device.GraphicsProfile == GraphicsProfile.HiDef;
-#elif OPENGL
- SupportsDepthClamp = device._extensions.Contains("GL_ARB_depth_clamp");
-#endif
-
-#if DIRECTX
- SupportsVertexTextures = device.GraphicsProfile == GraphicsProfile.HiDef;
-#elif OPENGL
- SupportsVertexTextures = false; // For now, until we implement vertex textures in OpenGL.
-#endif
- }
+ // The highest possible MSCount
+ private const int MultiSampleCountLimit = 32;
+ private int _maxMultiSampleCount;
- bool GetNonPowerOfTwo(GraphicsDevice device)
+ internal int MaxMultiSampleCount
{
-#if OPENGL
-#if GLES
- return device._extensions.Contains("GL_OES_texture_npot") ||
- device._extensions.Contains("GL_ARB_texture_non_power_of_two") ||
- device._extensions.Contains("GL_IMG_texture_npot") ||
- device._extensions.Contains("GL_NV_texture_npot_2D_mipmap");
-#else
- // Unfortunately non PoT texture support is patchy even on desktop systems and we can't
- // rely on the fact that GL2.0+ supposedly supports npot in the core.
- // Reference: http://aras-p.info/blog/2012/10/17/non-power-of-two-textures/
- return device._maxTextureSize >= 8192;
-#endif
-
-#else
- return device.GraphicsProfile == GraphicsProfile.HiDef;
-#endif
+ get { return _maxMultiSampleCount; }
}
}
}
\ No newline at end of file
diff --git a/MonoGame.Framework/Graphics/GraphicsContext.SDL.cs b/MonoGame.Framework/Graphics/GraphicsContext.SDL.cs
new file mode 100644
index 00000000000..acbb86b1085
--- /dev/null
+++ b/MonoGame.Framework/Graphics/GraphicsContext.SDL.cs
@@ -0,0 +1,87 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+
+namespace OpenGL
+{
+ public class GraphicsContext : IGraphicsContext, IDisposable
+ {
+ private IntPtr _context;
+ private IntPtr _winHandle;
+ private bool _disposed;
+
+ public int SwapInterval
+ {
+ get
+ {
+ return Sdl.GL.GetSwapInterval();
+ }
+ set
+ {
+ Sdl.GL.SetSwapInterval(value);
+ }
+ }
+
+ public bool IsDisposed
+ {
+ get { return _disposed; }
+ }
+
+ public GraphicsContext(IWindowInfo info)
+ {
+ if (_disposed)
+ return;
+
+ SetWindowHandle(info);
+ _context = Sdl.GL.CreateContext(_winHandle);
+
+ // GL entry points must be loaded after the GL context creation, otherwise some Windows drivers will return only GL 1.3 compatible functions
+ try
+ {
+ OpenGL.GL.LoadEntryPoints();
+ }
+ catch (EntryPointNotFoundException)
+ {
+ throw new PlatformNotSupportedException(
+ "MonoGame requires OpenGL 3.0 compatible drivers, or either ARB_framebuffer_object or EXT_framebuffer_object extensions." +
+ "Try updating your graphics drivers.");
+ }
+ }
+
+ public void MakeCurrent(IWindowInfo info)
+ {
+ if (_disposed)
+ return;
+
+ SetWindowHandle(info);
+ Sdl.GL.MakeCurrent(_winHandle, _context);
+ }
+
+ public void SwapBuffers()
+ {
+ if (_disposed)
+ return;
+
+ Sdl.GL.SwapWindow(_winHandle);
+ }
+
+ public void Dispose()
+ {
+ if (_disposed)
+ return;
+
+ Sdl.GL.DeleteContext(_context);
+ _disposed = true;
+ }
+
+ private void SetWindowHandle(IWindowInfo info)
+ {
+ if (info == null)
+ _winHandle = IntPtr.Zero;
+ else
+ _winHandle = info.Handle;
+ }
+ }
+}
diff --git a/MonoGame.Framework/Graphics/GraphicsDevice.DirectX.cs b/MonoGame.Framework/Graphics/GraphicsDevice.DirectX.cs
index 67e4df09394..ed7efbaad3a 100644
--- a/MonoGame.Framework/Graphics/GraphicsDevice.DirectX.cs
+++ b/MonoGame.Framework/Graphics/GraphicsDevice.DirectX.cs
@@ -11,9 +11,6 @@
using SharpDX;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
-#if WINDOWS_PHONE
-using MonoGame.Framework.WindowsPhone;
-#endif
#if WINDOWS_STOREAPP || WINDOWS_UAP
using Windows.UI.Xaml.Controls;
@@ -77,8 +74,8 @@ public partial class GraphicsDevice
SharpDX.Direct3D11.DepthStencilView _currentDepthStencilView;
private readonly Dictionary _userVertexBuffers = new Dictionary();
-
- private readonly Dictionary _userIndexBuffers = new Dictionary();
+ private DynamicIndexBuffer _userIndexBuffer16;
+ private DynamicIndexBuffer _userIndexBuffer32;
#if WINDOWS_STOREAPP || WINDOWS_UAP
@@ -100,7 +97,7 @@ internal float Dpi
#endif
- ///
+ ///
/// Returns a handle to internal device object. Valid only on DirectX platforms.
/// For usage, convert this to SharpDX.Direct3D11.Device.
///
@@ -214,6 +211,13 @@ internal void UpdateTarget(RenderTargetView renderTargetView)
}
}
}
+
+ internal void OnPresentationChanged()
+ {
+ // Display orientation is always portrait on WP8
+ PresentationParameters.DisplayOrientation = DisplayOrientation.Portrait;
+ }
+
#endif
#if WINDOWS_STOREAPP || WINDOWS_UAP
@@ -224,7 +228,7 @@ protected void CreateDeviceIndependentResources()
{
#if DEBUG
var debugLevel = SharpDX.Direct2D1.DebugLevel.Information;
-#else
+#else
var debugLevel = SharpDX.Direct2D1.DebugLevel.None;
#endif
// Dispose previous references.
@@ -551,6 +555,35 @@ internal void CreateSizeDependentResources()
_d2dContext.TextAntialiasMode = SharpDX.Direct2D1.TextAntialiasMode.Grayscale;
}
+ internal void OnPresentationChanged()
+ {
+ CreateSizeDependentResources();
+ ApplyRenderTargets(null);
+ }
+
+#endif
+
+ partial void PlatformValidatePresentationParameters(PresentationParameters presentationParameters)
+ {
+#if WINDOWS_UAP
+ if (presentationParameters.SwapChainPanel == null)
+ throw new ArgumentException("PresentationParameters.SwapChainPanel must not be null.");
+#elif WINDOWS_STOREAPP
+ if (presentationParameters.DeviceWindowHandle == IntPtr.Zero && presentationParameters.SwapChainBackgroundPanel == null)
+ throw new ArgumentException("PresentationParameters.DeviceWindowHandle or PresentationParameters.SwapChainBackgroundPanel must be not null.");
+#else
+ if (presentationParameters.DeviceWindowHandle == IntPtr.Zero)
+ throw new ArgumentException("PresentationParameters.DeviceWindowHandle must not be null.");
+#endif
+ }
+
+#if WINDOWS_STOREAPP || WINDOWS_UAP || WINDOWS_PHONE
+
+ private void SetMultiSamplingToMaximum(PresentationParameters presentationParameters, out int quality)
+ {
+ quality = (int)SharpDX.Direct3D11.StandardMultisampleQualityLevels.StandardMultisamplePattern;
+ }
+
#endif
#if WINDOWS
@@ -588,7 +621,7 @@ protected virtual void CreateDeviceResources()
supportedFeatureLevel = SharpDX.Direct3D11.Device.GetSupportedFeatureLevel();
}
catch (SharpDX.SharpDXException)
- {
+ {
// if GetSupportedFeatureLevel() fails, do not crash the initialization. Program can run without this.
}
@@ -612,7 +645,7 @@ protected virtual void CreateDeviceResources()
}
#if DEBUG
- try
+ try
{
#endif
// Create the Direct3D device.
@@ -620,7 +653,7 @@ protected virtual void CreateDeviceResources()
_d3dDevice = defaultDevice.QueryInterface();
#if DEBUG
}
- catch(SharpDXException)
+ catch (SharpDXException)
{
// Try again without the debug flag. This allows debug builds to run
// on machines that don't have the debug runtime installed.
@@ -634,7 +667,18 @@ protected virtual void CreateDeviceResources()
_d3dContext = _d3dDevice.ImmediateContext.QueryInterface();
}
- internal void CreateSizeDependentResources(bool useFullscreenParameter = false)
+ private int GetMultiSamplingQuality(Format format, int multiSampleCount)
+ {
+ return _d3dDevice.CheckMultisampleQualityLevels(format, multiSampleCount) - 1;
+ }
+
+ internal void SetHardwareFullscreen()
+ {
+ // This force to switch to fullscreen mode when hardware mode enabled(working in WindowsDX mode).
+ _swapChain.SetFullscreenState(PresentationParameters.IsFullScreen, null);
+ }
+
+ internal void CreateSizeDependentResources()
{
_d3dContext.OutputMerger.SetTargets((SharpDX.Direct3D11.DepthStencilView)null,
(SharpDX.Direct3D11.RenderTargetView)null);
@@ -671,60 +715,28 @@ internal void CreateSizeDependentResources(bool useFullscreenParameter = false)
return;
}
- var multisampleDesc = new SharpDX.DXGI.SampleDescription(1, 0);
- if (PresentationParameters.MultiSampleCount > 1)
- {
- //Find the maximum supported level coming down from 32, 16, 8, 4, 2, 1, 0
- var maxLevel = 32;
- while (maxLevel > 0)
- {
- if (_d3dDevice.CheckMultisampleQualityLevels(Format.R32G32B32A32_Typeless, maxLevel) > 0)
- break;
- maxLevel /= 2;
- }
-
- var targetLevel = PresentationParameters.MultiSampleCount;
- if (PresentationParameters.MultiSampleCount > maxLevel)
- {
- targetLevel = maxLevel;
- }
-
- multisampleDesc.Count = targetLevel;
- multisampleDesc.Quality = (int)SharpDX.Direct3D11.StandardMultisampleQualityLevels.StandardMultisamplePattern;
- }
-
// Use BGRA for the swap chain.
var format = PresentationParameters.BackBufferFormat == SurfaceFormat.Color ?
SharpDX.DXGI.Format.B8G8R8A8_UNorm :
SharpDXHelper.ToFormat(PresentationParameters.BackBufferFormat);
- int vSyncFrameLatency = PresentationParameters.PresentationInterval.GetFrameLatency();
+ var multisampleDesc = new SharpDX.DXGI.SampleDescription(1, 0);
+ if (PresentationParameters.MultiSampleCount > 1)
+ {
+ var quality = GetMultiSamplingQuality(format, PresentationParameters.MultiSampleCount);
+
+ multisampleDesc.Count = PresentationParameters.MultiSampleCount;
+ multisampleDesc.Quality = quality;
+ }
// If the swap chain already exists... update it.
if (_swapChain != null)
{
_swapChain.ResizeBuffers(2,
- PresentationParameters.BackBufferWidth,
- PresentationParameters.BackBufferHeight,
- format,
- SwapChainFlags.None);
-
- // This force to switch to fullscreen mode when hardware mode enabled(working in WindowsDX mode).
- if (useFullscreenParameter)
- {
- _swapChain.SetFullscreenState(PresentationParameters.IsFullScreen, null);
- }
-
- // Update Vsync setting.
- using (var dxgiDevice = _d3dDevice.QueryInterface())
- {
- // If VSync is disabled, Ensure that DXGI does not queue more than one frame at a time. This
- // both reduces latency and ensures that the application will only render
- // after each VSync, minimizing power consumption.
- // Setting latency to 0 (PresentInterval.Immediate) will result in the hardware default.
- // (normally 3)
- dxgiDevice.MaximumFrameLatency = vSyncFrameLatency;
- }
+ PresentationParameters.BackBufferWidth,
+ PresentationParameters.BackBufferHeight,
+ format,
+ SwapChainFlags.AllowModeSwitch);
}
// Otherwise, create a new swap chain.
@@ -742,7 +754,7 @@ internal void CreateSizeDependentResources(bool useFullscreenParameter = false)
Scaling = DisplayModeScaling.Unspecified,
#endif
Width = PresentationParameters.BackBufferWidth,
- Height = PresentationParameters.BackBufferHeight,
+ Height = PresentationParameters.BackBufferHeight,
},
OutputHandle = PresentationParameters.DeviceWindowHandle,
@@ -750,11 +762,11 @@ internal void CreateSizeDependentResources(bool useFullscreenParameter = false)
Usage = SharpDX.DXGI.Usage.RenderTargetOutput,
BufferCount = 2,
SwapEffect = SharpDXHelper.ToSwapEffect(PresentationParameters.PresentationInterval),
- IsWindowed = useFullscreenParameter ? PresentationParameters.IsFullScreen : true
+ IsWindowed = true
};
// Once the desired swap chain description is configured, it must be created on the same adapter as our D3D Device
-
+
// First, retrieve the underlying DXGI Device from the D3D Device.
// Creates the swap chain
using (var dxgiDevice = _d3dDevice.QueryInterface())
@@ -763,12 +775,9 @@ internal void CreateSizeDependentResources(bool useFullscreenParameter = false)
{
_swapChain = new SwapChain(dxgiFactory, dxgiDevice, desc);
dxgiFactory.MakeWindowAssociation(PresentationParameters.DeviceWindowHandle, WindowAssociationFlags.IgnoreAll);
- // If VSync is disabled, Ensure that DXGI does not queue more than one frame at a time. This
- // both reduces latency and ensures that the application will only render
- // after each VSync, minimizing power consumption.
- // Setting latency to 0 (PresentInterval.Immediate) will result in the hardware default.
- // (normally 3)
- dxgiDevice.MaximumFrameLatency = vSyncFrameLatency;
+ // To reduce latency, ensure that DXGI does not queue more than one frame at a time.
+ // Docs: https://msdn.microsoft.com/en-us/library/windows/desktop/ff471334(v=vs.85).aspx
+ dxgiDevice.MaximumFrameLatency = 1;
}
}
@@ -817,10 +826,16 @@ internal void CreateSizeDependentResources(bool useFullscreenParameter = false)
MaxDepth = 1.0f
};
}
-
+
+ internal void OnPresentationChanged()
+ {
+ CreateSizeDependentResources();
+ ApplyRenderTargets(null);
+ }
+
#endif // WINDOWS
- public void PlatformClear(ClearOptions options, Vector4 color, float depth, int stencil)
+ private void PlatformClear(ClearOptions options, Vector4 color, float depth, int stencil)
{
// Clear options for depth/stencil buffer if not attached.
if (_currentDepthStencilView != null)
@@ -845,10 +860,10 @@ public void PlatformClear(ClearOptions options, Vector4 color, float depth, int
#if WINDOWS_UAP
_d3dContext.ClearRenderTargetView(view, new RawColor4(color.X, color.Y, color.Z, color.W));
#else
- _d3dContext.ClearRenderTargetView(view, new Color4(color.X, color.Y, color.Z, color.W));
+ _d3dContext.ClearRenderTargetView(view, new Color4(color.X, color.Y, color.Z, color.W));
#endif
- }
- }
+ }
+ }
// Clear the depth/stencil render buffer.
SharpDX.Direct3D11.DepthStencilClearFlags flags = 0;
@@ -869,6 +884,14 @@ private void PlatformDispose()
SharpDX.Utilities.Dispose(ref _d3dDevice);
SharpDX.Utilities.Dispose(ref _d3dContext);
+ if (_userIndexBuffer16 != null)
+ _userIndexBuffer16.Dispose();
+ if (_userIndexBuffer32 != null)
+ _userIndexBuffer32.Dispose();
+
+ foreach (var vb in _userVertexBuffers.Values)
+ vb.Dispose();
+
#if WINDOWS_STOREAPP || WINDOWS_UAP
if (_swapChain != null)
@@ -910,8 +933,8 @@ private void PlatformDispose()
#endif // WINDOWS_STOREAPP
}
-
- public void PlatformPresent()
+
+ private void PlatformPresent()
{
#if WINDOWS_STOREAPP || WINDOWS_UAP
// The application may optionally specify "dirty" or "scroll" rects to improve efficiency
@@ -947,7 +970,7 @@ public void PlatformPresent()
try
{
- var syncInterval = PresentationParameters.PresentationInterval.GetFrameLatency();
+ var syncInterval = PresentationParameters.PresentationInterval.GetSyncInterval();
// The first argument instructs DXGI to block n VSyncs before presenting.
lock (_d3dContext)
@@ -975,9 +998,9 @@ private void PlatformSetViewport(ref Viewport value)
MaxDepth = _viewport.MaxDepth
};
#else
- var viewport = new SharpDX.ViewportF(_viewport.X, _viewport.Y, (float)_viewport.Width, (float)_viewport.Height, _viewport.MinDepth, _viewport.MaxDepth);
+ var viewport = new SharpDX.ViewportF(_viewport.X, _viewport.Y, (float)_viewport.Width, (float)_viewport.Height, _viewport.MinDepth, _viewport.MaxDepth);
#endif
- lock (_d3dContext)
+ lock (_d3dContext)
_d3dContext.Rasterizer.SetViewport(viewport);
}
}
@@ -1017,6 +1040,17 @@ private void PlatformApplyDefaultRenderTarget()
internal void PlatformResolveRenderTargets()
{
// Resolving MSAA render targets should be done here.
+
+ // Generate mipmaps.
+ for (var i = 0; i < _currentRenderTargetCount; i++)
+ {
+ var renderTargetBinding = _currentRenderTargetBindings[i];
+ if (renderTargetBinding.RenderTarget.LevelCount > 1)
+ {
+ lock (_d3dContext)
+ _d3dContext.GenerateMips(renderTargetBinding.RenderTarget.GetShaderResourceView());
+ }
+ }
}
private IRenderTarget PlatformApplyRenderTargets()
@@ -1113,20 +1147,49 @@ internal void PlatformBeginApplyState()
Debug.Assert(_d3dContext != null, "The d3d context is null!");
}
+ private void PlatformApplyBlend()
+ {
+ if (_blendFactorDirty || _blendStateDirty)
+ {
+ var state = _actualBlendState.GetDxState(this);
+ var factor = GetBlendFactor();
+ _d3dContext.OutputMerger.SetBlendState(state, factor);
+
+ _blendFactorDirty = false;
+ _blendStateDirty = false;
+ }
+ }
+
+#if WINDOWS_UAP
+ private SharpDX.Mathematics.Interop.RawColor4 GetBlendFactor()
+ {
+ return new SharpDX.Mathematics.Interop.RawColor4(
+ BlendFactor.R / 255.0f,
+ BlendFactor.G / 255.0f,
+ BlendFactor.B / 255.0f,
+ BlendFactor.A / 255.0f);
+ }
+#else
+ private Color4 GetBlendFactor()
+ {
+ return BlendFactor.ToColor4();
+ }
+#endif
+
internal void PlatformApplyState(bool applyShaders)
{
// NOTE: This code assumes _d3dContext has been locked by the caller.
- if ( _scissorRectangleDirty )
- {
- _d3dContext.Rasterizer.SetScissorRectangle(
- _scissorRectangle.X,
- _scissorRectangle.Y,
- _scissorRectangle.Right,
+ if (_scissorRectangleDirty)
+ {
+ _d3dContext.Rasterizer.SetScissorRectangle(
+ _scissorRectangle.X,
+ _scissorRectangle.Y,
+ _scissorRectangle.Right,
_scissorRectangle.Bottom);
- _scissorRectangleDirty = false;
- }
-
+ _scissorRectangleDirty = false;
+ }
+
// If we're not applying shaders then early out now.
if (!applyShaders)
return;
@@ -1137,7 +1200,7 @@ internal void PlatformApplyState(bool applyShaders)
{
_d3dContext.InputAssembler.SetIndexBuffer(
_indexBuffer.Buffer,
- _indexBuffer.IndexElementSize == IndexElementSize.SixteenBits ?
+ _indexBuffer.IndexElementSize == IndexElementSize.SixteenBits ?
SharpDX.DXGI.Format.R16_UInt : SharpDX.DXGI.Format.R32_UInt,
0);
}
@@ -1209,7 +1272,7 @@ internal void PlatformApplyState(bool applyShaders)
SamplerStates.PlatformSetSamplers(this);
}
- private int SetUserVertexBuffer(T[] vertexData, int vertexOffset, int vertexCount, VertexDeclaration vertexDecl)
+ private int SetUserVertexBuffer(T[] vertexData, int vertexOffset, int vertexCount, VertexDeclaration vertexDecl)
where T : struct
{
DynamicVertexBuffer buffer;
@@ -1249,17 +1312,33 @@ private int SetUserIndexBuffer(T[] indexData, int indexOffset, int indexCount
{
DynamicIndexBuffer buffer;
- var indexType = typeof(T);
- var indexSize = Marshal.SizeOf(indexType);
+ var indexSize = Utilities.ReflectionHelpers.SizeOf.Get();
var indexElementSize = indexSize == 2 ? IndexElementSize.SixteenBits : IndexElementSize.ThirtyTwoBits;
- if (!_userIndexBuffers.TryGetValue(indexElementSize, out buffer) || buffer.IndexCount < indexCount)
+ var requiredIndexCount = Math.Max(indexCount, 6000);
+ if (indexElementSize == IndexElementSize.SixteenBits)
{
- if (buffer != null)
- buffer.Dispose();
+ if (_userIndexBuffer16 == null || _userIndexBuffer16.IndexCount < requiredIndexCount)
+ {
+ if (_userIndexBuffer16 != null)
+ _userIndexBuffer16.Dispose();
- buffer = new DynamicIndexBuffer(this, indexElementSize, Math.Max(indexCount, 6000), BufferUsage.WriteOnly);
- _userIndexBuffers[indexElementSize] = buffer;
+ _userIndexBuffer16 = new DynamicIndexBuffer(this, indexElementSize, requiredIndexCount, BufferUsage.WriteOnly);
+ }
+
+ buffer = _userIndexBuffer16;
+ }
+ else
+ {
+ if (_userIndexBuffer32 == null || _userIndexBuffer32.IndexCount < requiredIndexCount)
+ {
+ if (_userIndexBuffer32 != null)
+ _userIndexBuffer32.Dispose();
+
+ _userIndexBuffer32 = new DynamicIndexBuffer(this, indexElementSize, requiredIndexCount, BufferUsage.WriteOnly);
+ }
+
+ buffer = _userIndexBuffer32;
}
var startIndex = buffer.UserOffset;
@@ -1367,27 +1446,27 @@ public void Flush()
{
_d3dContext.Flush();
}
-
+
private static GraphicsProfile PlatformGetHighestSupportedGraphicsProfile(GraphicsDevice graphicsDevice)
{
FeatureLevel featureLevel;
- try
+ try
{
- if (graphicsDevice == null || graphicsDevice._d3dDevice == null || graphicsDevice._d3dDevice.NativePointer == null)
- featureLevel = SharpDX.Direct3D11.Device.GetSupportedFeatureLevel();
- else
- featureLevel = graphicsDevice._d3dDevice.FeatureLevel;
+ if (graphicsDevice == null || graphicsDevice._d3dDevice == null || graphicsDevice._d3dDevice.NativePointer == null)
+ featureLevel = SharpDX.Direct3D11.Device.GetSupportedFeatureLevel();
+ else
+ featureLevel = graphicsDevice._d3dDevice.FeatureLevel;
}
catch (SharpDXException)
- {
- featureLevel = FeatureLevel.Level_9_1; //Minimum defined level
+ {
+ featureLevel = FeatureLevel.Level_9_1; //Minimum defined level
}
GraphicsProfile graphicsProfile;
if (featureLevel >= FeatureLevel.Level_10_0 || GraphicsAdapter.UseReferenceDevice)
graphicsProfile = GraphicsProfile.HiDef;
- else
+ else
graphicsProfile = GraphicsProfile.Reach;
return graphicsProfile;
@@ -1400,5 +1479,10 @@ internal void Trim()
dxgiDevice3.Trim();
}
#endif
+
+ private static Rectangle PlatformGetTitleSafeArea(int x, int y, int width, int height)
+ {
+ return new Rectangle(x, y, width, height);
+ }
}
}
diff --git a/MonoGame.Framework/Graphics/GraphicsDevice.OpenGL.FramebufferHelper.cs b/MonoGame.Framework/Graphics/GraphicsDevice.OpenGL.FramebufferHelper.cs
index ab131b8de36..ece2f32719c 100644
--- a/MonoGame.Framework/Graphics/GraphicsDevice.OpenGL.FramebufferHelper.cs
+++ b/MonoGame.Framework/Graphics/GraphicsDevice.OpenGL.FramebufferHelper.cs
@@ -20,7 +20,7 @@
#endif
#if (WINDOWS || DESKTOPGL) && !GLES
-using OpenTK.Graphics.OpenGL;
+using OpenGL;
#endif
@@ -34,9 +34,44 @@ namespace Microsoft.Xna.Framework.Graphics
// ARB_framebuffer_object implementation
partial class GraphicsDevice
{
-#if GLES
internal class FramebufferHelper
{
+ #region Singleton
+
+ private static FramebufferHelper _instance;
+
+ public static FramebufferHelper Create(GraphicsDevice gd)
+ {
+ if (gd.GraphicsCapabilities.SupportsFramebufferObjectARB)
+ {
+ _instance = new FramebufferHelper(gd);
+ }
+#if !(GLES || MONOMAC)
+ else if (gd.GraphicsCapabilities.SupportsFramebufferObjectEXT)
+ {
+ _instance = new FramebufferHelperEXT(gd);
+ }
+#endif
+ else
+ {
+ throw new PlatformNotSupportedException(
+ "MonoGame requires either ARB_framebuffer_object or EXT_framebuffer_object." +
+ "Try updating your graphics drivers.");
+ }
+
+ return _instance;
+ }
+
+ public static FramebufferHelper Get()
+ {
+ if (_instance == null)
+ throw new InvalidOperationException("The FramebufferHelper has not been created yet!");
+ return _instance;
+ }
+
+ #endregion
+
+#if GLES
public bool SupportsInvalidateFramebuffer { get; private set; }
public bool SupportsBlitFramebuffer { get; private set; }
@@ -296,19 +331,27 @@ internal virtual void CheckFramebufferStatus()
string message = "Framebuffer Incomplete.";
switch (status)
{
- case FramebufferErrorCode.FramebufferIncompleteAttachment: message = "Not all framebuffer attachment points are framebuffer attachment complete."; break;
- case FramebufferErrorCode.FramebufferIncompleteDimensions: message = "Not all attached images have the same width and height."; break;
- case FramebufferErrorCode.FramebufferIncompleteMissingAttachment: message = "No images are attached to the framebuffer."; break;
- case FramebufferErrorCode.FramebufferUnsupported: message = "The combination of internal formats of the attached images violates an implementation-dependent set of restrictions."; break;
+ case FramebufferErrorCode.FramebufferIncompleteAttachment:
+ message = "Not all framebuffer attachment points are framebuffer attachment complete.";
+ break;
+ case FramebufferErrorCode.FramebufferIncompleteDimensions:
+ message = "Not all attached images have the same width and height.";
+ break;
+ case FramebufferErrorCode.FramebufferIncompleteMissingAttachment:
+ message = "No images are attached to the framebuffer.";
+ break;
+ case FramebufferErrorCode.FramebufferUnsupported:
+ message =
+ "The combination of internal formats of the attached images violates an implementation-dependent set of restrictions.";
+ break;
}
throw new InvalidOperationException(message);
}
}
}
-#else
- internal class FramebufferHelper
- {
+#else // if GLES
+
public bool SupportsInvalidateFramebuffer { get; private set; }
public bool SupportsBlitFramebuffer { get; private set; }
diff --git a/MonoGame.Framework/Graphics/GraphicsDevice.OpenGL.cs b/MonoGame.Framework/Graphics/GraphicsDevice.OpenGL.cs
index 7f7fd830c1e..c6b24dd65d7 100644
--- a/MonoGame.Framework/Graphics/GraphicsDevice.OpenGL.cs
+++ b/MonoGame.Framework/Graphics/GraphicsDevice.OpenGL.cs
@@ -18,9 +18,7 @@
#endif
#if DESKTOPGL
-using OpenTK.Graphics;
-using OpenTK.Graphics.OpenGL;
-using GLPrimitiveType = OpenTK.Graphics.OpenGL.PrimitiveType;
+using OpenGL;
#endif
#if ANGLE
@@ -60,6 +58,8 @@ public partial class GraphicsDevice
internal FramebufferHelper framebufferHelper;
+ internal int glMajorVersion = 0;
+ internal int glMinorVersion = 0;
internal int glFramebuffer = 0;
internal int MaxVertexAttributes;
internal List _extensions = new List();
@@ -96,8 +96,28 @@ internal void SetVertexAttributeArray(bool[] attrs)
private void PlatformSetup()
{
#if DESKTOPGL || ANGLE
- GraphicsMode mode = GraphicsMode.Default;
- var wnd = (Game.Instance.Window as OpenTKGameWindow).Window.WindowInfo;
+
+ var windowInfo = new WindowInfo(SdlGameWindow.Instance.Handle);
+
+ if (Context == null || Context.IsDisposed)
+ {
+ Context = GL.CreateContext(windowInfo);
+ }
+
+ Context.MakeCurrent(windowInfo);
+ Context.SwapInterval = PresentationParameters.PresentationInterval.GetSwapInterval();
+
+ /*if (Threading.BackgroundContext == null)
+ {
+ Threading.BackgroundContext = GL.CreateContext(windowInfo);
+ Threading.WindowInfo = windowInfo;
+ Threading.BackgroundContext.MakeCurrent(null);
+ }*/
+
+ Context.MakeCurrent(windowInfo);
+
+ /*GraphicsMode mode = GraphicsMode.Default;
+ var wnd = OpenTK.Platform.Utilities.CreateSdl2WindowInfo(Game.Instance.Window.Handle);
#if GLES
// Create an OpenGL ES 2.0 context
@@ -164,7 +184,9 @@ private void PlatformSetup()
Threading.WindowInfo = wnd;
Threading.BackgroundContext.MakeCurrent(null);
}
- Context.MakeCurrent(wnd);
+ Context.MakeCurrent(wnd);*/
+
+
#endif
MaxTextureSlots = 16;
@@ -178,10 +200,55 @@ private void PlatformSetup()
GL.GetInteger(GetPName.MaxTextureSize, out _maxTextureSize);
GraphicsExtensions.CheckGLError();
+ SpriteBatch.NeedsHalfPixelOffset = true;
+
+ // try getting the context version
+ // GL_MAJOR_VERSION and GL_MINOR_VERSION are GL 3.0+ only, so we need to rely on the GL_VERSION string
+ // for non GLES this string always starts with the version number in the "major.minor" format, but can be followed by
+ // multiple vendor specific characters
+ // For GLES this string is formatted as: OpenGLES
+#if GLES
+ try
+ {
+ string version = GL.GetString(StringName.Version);
+ string[] versionSplit = version.Split(' ');
+ if(versionSplit.Length > 2 && versionSplit[0].Equals("OpenGL") && versionSplit[1].Equals("ES"))
+ {
+ glMajorVersion = Convert.ToInt32(versionSplit[2].Substring(0, 1));
+ glMinorVersion = Convert.ToInt32(versionSplit[2].Substring(2, 1));
+ }
+ else
+ {
+ glMajorVersion = 1;
+ glMinorVersion = 1;
+ }
+ }
+ catch (FormatException)
+ {
+ //if it fails we default to 1.1 context
+ glMajorVersion = 1;
+ glMinorVersion = 1;
+ }
+#else
+ try
+ {
+ string version = GL.GetString(StringName.Version);
+ glMajorVersion = Convert.ToInt32(version.Substring(0, 1));
+ glMinorVersion = Convert.ToInt32(version.Substring(2, 1));
+ }
+ catch (FormatException)
+ {
+ // if it fails, we assume to be on a 1.1 context
+ glMajorVersion = 1;
+ glMinorVersion = 1;
+ }
+#endif
+
#if !GLES
// Initialize draw buffer attachment array
int maxDrawBuffers;
- GL.GetInteger(GetPName.MaxDrawBuffers, out maxDrawBuffers);
+ GL.GetInteger(GetPName.MaxDrawBuffers, out maxDrawBuffers);
+ GraphicsExtensions.CheckGLError ();
_drawBuffers = new DrawBuffersEnum[maxDrawBuffers];
for (int i = 0; i < maxDrawBuffers; i++)
_drawBuffers[i] = (DrawBuffersEnum)(FramebufferAttachment.ColorAttachment0Ext + i);
@@ -196,22 +263,7 @@ List GetGLExtensions()
var extstring = GL.GetString(StringName.Extensions);
GraphicsExtensions.CheckGLError();
if (!string.IsNullOrEmpty(extstring))
- {
extensions.AddRange(extstring.Split(' '));
-#if DEBUG
-#if ANDROID
- Android.Util.Log.Debug("MonoGame", "Supported extensions:");
-#else
- System.Diagnostics.Debug.WriteLine("Supported extensions:");
-#endif
- foreach (string extension in extensions)
-#if ANDROID
- Android.Util.Log.Debug("MonoGame", extension);
-#else
- System.Diagnostics.Debug.WriteLine(extension);
-#endif
-#endif
- }
return extensions;
}
@@ -227,25 +279,10 @@ private void PlatformInitialize()
_programCache.Clear();
_shaderProgram = null;
- if (GraphicsCapabilities.SupportsFramebufferObjectARB)
- {
- this.framebufferHelper = new FramebufferHelper(this);
- }
- #if !(GLES || MONOMAC)
- else if (GraphicsCapabilities.SupportsFramebufferObjectEXT)
- {
- this.framebufferHelper = new FramebufferHelperEXT(this);
- }
- #endif
- else
- {
- throw new PlatformNotSupportedException(
- "MonoGame requires either ARB_framebuffer_object or EXT_framebuffer_object." +
- "Try updating your graphics drivers.");
- }
+ framebufferHelper = FramebufferHelper.Create(this);
- // Force reseting states
- this.BlendState.PlatformApplyState(this, true);
+ // Force resetting states
+ this.PlatformApplyBlend(true);
this.DepthStencilState.PlatformApplyState(this, true);
this.RasterizerState.PlatformApplyState(this, true);
@@ -308,11 +345,7 @@ public void PlatformClear(ClearOptions options, Vector4 color, float depth, int
{
if (depth != _lastClearDepth)
{
- #if GLES
- GL.ClearDepth (depth);
- #else
- GL.ClearDepth((double)depth);
- #endif
+ GL.ClearDepth(depth);
GraphicsExtensions.CheckGLError();
_lastClearDepth = depth;
}
@@ -339,13 +372,6 @@ private void PlatformDispose()
#if DESKTOPGL || ANGLE
Context.Dispose();
Context = null;
-
- if (Threading.BackgroundContext != null)
- {
- Threading.BackgroundContext.Dispose();
- Threading.BackgroundContext = null;
- Threading.WindowInfo = null;
- }
#endif
});
}
@@ -398,11 +424,8 @@ private void PlatformSetViewport(ref Viewport value)
else
GL.Viewport(value.X, PresentationParameters.BackBufferHeight - value.Y - value.Height, value.Width, value.Height);
GraphicsExtensions.LogGLError("GraphicsDevice.Viewport_set() GL.Viewport");
-#if GLES
+
GL.DepthRange(value.MinDepth, value.MaxDepth);
-#else
- GL.DepthRange((double)value.MinDepth, (double)value.MaxDepth);
-#endif
GraphicsExtensions.LogGLError("GraphicsDevice.Viewport_set() GL.DepthRange");
// In OpenGL we have to re-apply the special "posFixup"
@@ -472,7 +495,7 @@ public int GetHashCode(RenderTargetBinding[] array)
// FBO cache used to resolve MSAA rendertargets, we create 1 FBO per RenderTargetBinding combination
private Dictionary glResolveFramebuffers = new Dictionary(new RenderTargetBindingArrayComparer());
- internal void PlatformCreateRenderTarget(Texture renderTarget, int width, int height, bool mipMap, SurfaceFormat preferredFormat, DepthFormat preferredDepthFormat, int preferredMultiSampleCount, RenderTargetUsage usage)
+ internal void PlatformCreateRenderTarget(IRenderTarget renderTarget, int width, int height, bool mipMap, SurfaceFormat preferredFormat, DepthFormat preferredDepthFormat, int preferredMultiSampleCount, RenderTargetUsage usage)
{
var color = 0;
var depth = 0;
@@ -496,7 +519,8 @@ internal void PlatformCreateRenderTarget(Texture renderTarget, int width, int he
switch (preferredDepthFormat)
{
case DepthFormat.Depth16:
- depthInternalFormat = RenderbufferStorage.DepthComponent16; break;
+ depthInternalFormat = RenderbufferStorage.DepthComponent16;
+ break;
#if GLES
case DepthFormat.Depth24:
if (GraphicsCapabilities.SupportsDepth24)
@@ -522,8 +546,12 @@ internal void PlatformCreateRenderTarget(Texture renderTarget, int width, int he
}
break;
#else
- case DepthFormat.Depth24: depthInternalFormat = RenderbufferStorage.DepthComponent24; break;
- case DepthFormat.Depth24Stencil8: depthInternalFormat = RenderbufferStorage.Depth24Stencil8; break;
+ case DepthFormat.Depth24:
+ depthInternalFormat = RenderbufferStorage.DepthComponent24;
+ break;
+ case DepthFormat.Depth24Stencil8:
+ depthInternalFormat = RenderbufferStorage.Depth24Stencil8;
+ break;
#endif
}
@@ -545,37 +573,25 @@ internal void PlatformCreateRenderTarget(Texture renderTarget, int width, int he
}
}
- var renderTarget2D = renderTarget as RenderTarget2D;
- if (renderTarget2D != null)
- {
- if (color != 0)
- renderTarget2D.glColorBuffer = color;
- else
- renderTarget2D.glColorBuffer = renderTarget2D.glTexture;
- renderTarget2D.glDepthBuffer = depth;
- renderTarget2D.glStencilBuffer = stencil;
- }
+ if (color != 0)
+ renderTarget.GLColorBuffer = color;
else
- {
- throw new NotSupportedException();
- }
+ renderTarget.GLColorBuffer = renderTarget.GLTexture;
+ renderTarget.GLDepthBuffer = depth;
+ renderTarget.GLStencilBuffer = stencil;
}
- internal void PlatformDeleteRenderTarget(Texture renderTarget)
+ internal void PlatformDeleteRenderTarget(IRenderTarget renderTarget)
{
var color = 0;
var depth = 0;
var stencil = 0;
var colorIsRenderbuffer = false;
- var renderTarget2D = renderTarget as RenderTarget2D;
- if (renderTarget2D != null)
- {
- color = renderTarget2D.glColorBuffer;
- depth = renderTarget2D.glDepthBuffer;
- stencil = renderTarget2D.glStencilBuffer;
- colorIsRenderbuffer = color != renderTarget2D.glTexture;
- }
+ color = renderTarget.GLColorBuffer;
+ depth = renderTarget.GLDepthBuffer;
+ stencil = renderTarget.GLStencilBuffer;
+ colorIsRenderbuffer = color != renderTarget.GLTexture;
if (color != 0)
{
@@ -622,7 +638,7 @@ private void PlatformResolveRenderTargets()
return;
var renderTargetBinding = this._currentRenderTargetBindings[0];
- var renderTarget = renderTargetBinding.RenderTarget as RenderTarget2D;
+ var renderTarget = renderTargetBinding.RenderTarget as IRenderTarget;
if (renderTarget.MultiSampleCount > 0 && this.framebufferHelper.SupportsBlitFramebuffer)
{
var glResolveFramebuffer = 0;
@@ -632,38 +648,45 @@ private void PlatformResolveRenderTargets()
this.framebufferHelper.BindFramebuffer(glResolveFramebuffer);
for (var i = 0; i < this._currentRenderTargetCount; ++i)
{
- this.framebufferHelper.FramebufferTexture2D((int)(FramebufferAttachment.ColorAttachment0 + i), (int)renderTarget.glTarget, renderTarget.glTexture);
+ this.framebufferHelper.FramebufferTexture2D((int)(FramebufferAttachment.ColorAttachment0 + i), (int) renderTarget.GetFramebufferTarget(renderTargetBinding), renderTarget.GLTexture);
}
- this.glResolveFramebuffers.Add(this._currentRenderTargetBindings, glResolveFramebuffer);
+ this.glResolveFramebuffers.Add((RenderTargetBinding[])this._currentRenderTargetBindings.Clone(), glResolveFramebuffer);
}
else
{
this.framebufferHelper.BindFramebuffer(glResolveFramebuffer);
}
// The only fragment operations which affect the resolve are the pixel ownership test, the scissor test, and dithering.
- GL.Disable(EnableCap.ScissorTest);
- GraphicsExtensions.CheckGLError();
+ if (this._lastRasterizerState.ScissorTestEnable)
+ {
+ GL.Disable(EnableCap.ScissorTest);
+ GraphicsExtensions.CheckGLError();
+ }
var glFramebuffer = this.glFramebuffers[this._currentRenderTargetBindings];
this.framebufferHelper.BindReadFramebuffer(glFramebuffer);
for (var i = 0; i < this._currentRenderTargetCount; ++i)
{
renderTargetBinding = this._currentRenderTargetBindings[i];
- renderTarget = renderTargetBinding.RenderTarget as RenderTarget2D;
+ renderTarget = renderTargetBinding.RenderTarget as IRenderTarget;
this.framebufferHelper.BlitFramebuffer(i, renderTarget.Width, renderTarget.Height);
}
if (renderTarget.RenderTargetUsage == RenderTargetUsage.DiscardContents && this.framebufferHelper.SupportsInvalidateFramebuffer)
this.framebufferHelper.InvalidateReadFramebuffer();
- this._depthStencilStateDirty = true;
+ if (this._lastRasterizerState.ScissorTestEnable)
+ {
+ GL.Enable(EnableCap.ScissorTest);
+ GraphicsExtensions.CheckGLError();
+ }
}
for (var i = 0; i < this._currentRenderTargetCount; ++i)
{
renderTargetBinding = this._currentRenderTargetBindings[i];
- renderTarget = renderTargetBinding.RenderTarget as RenderTarget2D;
+ renderTarget = renderTargetBinding.RenderTarget as IRenderTarget;
if (renderTarget.LevelCount > 1)
{
- GL.BindTexture((TextureTarget)renderTarget.glTarget, renderTarget.glTexture);
+ GL.BindTexture((TextureTarget)renderTarget.GLTarget, renderTarget.GLTexture);
GraphicsExtensions.CheckGLError();
- this.framebufferHelper.GenerateMipmap((int)renderTarget.glTarget);
+ this.framebufferHelper.GenerateMipmap((int)renderTarget.GLTarget);
}
}
}
@@ -676,18 +699,18 @@ private IRenderTarget PlatformApplyRenderTargets()
this.framebufferHelper.GenFramebuffer(out glFramebuffer);
this.framebufferHelper.BindFramebuffer(glFramebuffer);
var renderTargetBinding = this._currentRenderTargetBindings[0];
- var renderTarget = renderTargetBinding.RenderTarget as RenderTarget2D;
- this.framebufferHelper.FramebufferRenderbuffer((int)FramebufferAttachment.DepthAttachment, renderTarget.glDepthBuffer, 0);
- this.framebufferHelper.FramebufferRenderbuffer((int)FramebufferAttachment.StencilAttachment, renderTarget.glStencilBuffer, 0);
+ var renderTarget = renderTargetBinding.RenderTarget as IRenderTarget;
+ this.framebufferHelper.FramebufferRenderbuffer((int)FramebufferAttachment.DepthAttachment, renderTarget.GLDepthBuffer, 0);
+ this.framebufferHelper.FramebufferRenderbuffer((int)FramebufferAttachment.StencilAttachment, renderTarget.GLStencilBuffer, 0);
for (var i = 0; i < this._currentRenderTargetCount; ++i)
{
renderTargetBinding = this._currentRenderTargetBindings[i];
- renderTarget = renderTargetBinding.RenderTarget as RenderTarget2D;
+ renderTarget = renderTargetBinding.RenderTarget as IRenderTarget;
var attachement = (int)(FramebufferAttachment.ColorAttachment0 + i);
- if (renderTarget.glColorBuffer != renderTarget.glTexture)
- this.framebufferHelper.FramebufferRenderbuffer(attachement, renderTarget.glColorBuffer, 0);
+ if (renderTarget.GLColorBuffer != renderTarget.GLTexture)
+ this.framebufferHelper.FramebufferRenderbuffer(attachement, renderTarget.GLColorBuffer, 0);
else
- this.framebufferHelper.FramebufferTexture2D(attachement, (int)renderTarget.glTarget, renderTarget.glTexture, 0, renderTarget.MultiSampleCount);
+ this.framebufferHelper.FramebufferTexture2D(attachement, (int)renderTarget.GetFramebufferTarget(renderTargetBinding), renderTarget.GLTexture, 0, renderTarget.MultiSampleCount);
}
#if DEBUG
@@ -733,7 +756,7 @@ private static GLPrimitiveType PrimitiveTypeGL(PrimitiveType primitiveType)
///
/// Activates the Current Vertex/Pixel shader pair into a program.
///
- private void ActivateShaderProgram()
+ private unsafe void ActivateShaderProgram()
{
// Lookup the shader program.
var shaderProgram = _programCache.GetProgram(VertexShader, PixelShader);
@@ -790,7 +813,10 @@ private void ActivateShaderProgram()
_posFixup[3] *= -1.0f;
}
- GL.Uniform4(posFixupLoc, 1, _posFixup);
+ fixed (float* floatPtr = _posFixup)
+ {
+ GL.Uniform4(posFixupLoc, 1, floatPtr);
+ }
GraphicsExtensions.CheckGLError();
}
@@ -799,6 +825,26 @@ internal void PlatformBeginApplyState()
Threading.EnsureUIThread();
}
+ private void PlatformApplyBlend(bool force = false)
+ {
+ _actualBlendState.PlatformApplyState(this, force);
+ ApplyBlendFactor(force);
+ }
+
+ private void ApplyBlendFactor(bool force)
+ {
+ if (force || BlendFactor != _lastBlendState.BlendFactor)
+ {
+ GL.BlendColor(
+ this.BlendFactor.R/255.0f,
+ this.BlendFactor.G/255.0f,
+ this.BlendFactor.B/255.0f,
+ this.BlendFactor.A/255.0f);
+ GraphicsExtensions.CheckGLError();
+ _lastBlendState.BlendFactor = this.BlendFactor;
+ }
+ }
+
internal void PlatformApplyState(bool applyShaders)
{
if ( _scissorRectangleDirty )
@@ -884,7 +930,8 @@ private void PlatformDrawIndexedPrimitives(PrimitiveType primitiveType, int base
var vertexDeclaration = _vertexBuffers.Get(0).VertexBuffer.VertexDeclaration;
var vertexOffset = (IntPtr)(vertexDeclaration.VertexStride * baseVertex);
- vertexDeclaration.Apply(_vertexShader, vertexOffset);
+ var programHash = _vertexShader.HashKey | _pixelShader.HashKey;
+ vertexDeclaration.Apply(_vertexShader, vertexOffset, programHash);
GL.DrawElements(target,
indexElementCount,
@@ -909,7 +956,8 @@ private void PlatformDrawUserPrimitives(PrimitiveType primitiveType, T[] vert
// Setup the vertex declaration to point at the VB data.
vertexDeclaration.GraphicsDevice = this;
- vertexDeclaration.Apply(_vertexShader, vbHandle.AddrOfPinnedObject());
+ var programHash = _vertexShader.HashKey | _pixelShader.HashKey;
+ vertexDeclaration.Apply(_vertexShader, vbHandle.AddrOfPinnedObject(), programHash);
//Draw
GL.DrawArrays(PrimitiveTypeGL(primitiveType),
@@ -925,7 +973,11 @@ private void PlatformDrawPrimitives(PrimitiveType primitiveType, int vertexStart
{
ApplyState(true);
- _vertexBuffers.Get(0).VertexBuffer.VertexDeclaration.Apply(_vertexShader, IntPtr.Zero);
+ var programHash = _vertexShader.HashKey | _pixelShader.HashKey;
+ _vertexBuffers.Get(0).VertexBuffer.VertexDeclaration.Apply(_vertexShader, IntPtr.Zero, programHash);
+
+ if (vertexStart < 0)
+ vertexStart = 0;
GL.DrawArrays(PrimitiveTypeGL(primitiveType),
vertexStart,
@@ -952,7 +1004,8 @@ private void PlatformDrawUserIndexedPrimitives(PrimitiveType primitiveType, T
// Setup the vertex declaration to point at the VB data.
vertexDeclaration.GraphicsDevice = this;
- vertexDeclaration.Apply(_vertexShader, vertexAddr);
+ var programHash = _vertexShader.HashKey | _pixelShader.HashKey;
+ vertexDeclaration.Apply(_vertexShader, vertexAddr, programHash);
//Draw
GL.DrawElements( PrimitiveTypeGL(primitiveType),
@@ -985,7 +1038,8 @@ private void PlatformDrawUserIndexedPrimitives(PrimitiveType primitiveType, T
// Setup the vertex declaration to point at the VB data.
vertexDeclaration.GraphicsDevice = this;
- vertexDeclaration.Apply(_vertexShader, vertexAddr);
+ var programHash = _vertexShader.HashKey | _pixelShader.HashKey;
+ vertexDeclaration.Apply(_vertexShader, vertexAddr, programHash);
//Draw
GL.DrawElements( PrimitiveTypeGL(primitiveType),
@@ -1008,5 +1062,21 @@ private static GraphicsProfile PlatformGetHighestSupportedGraphicsProfile(Graphi
{
return GraphicsProfile.HiDef;
}
+
+ private static Rectangle PlatformGetTitleSafeArea(int x, int y, int width, int height)
+ {
+ return new Rectangle(x, y, width, height);
+ }
+
+ internal void PlatformSetMultiSamplingToMaximum(PresentationParameters presentationParameters, out int quality)
+ {
+ presentationParameters.MultiSampleCount = 4;
+ quality = 0;
+ }
+
+ internal void OnPresentationChanged()
+ {
+ ApplyRenderTargets(null);
+ }
}
}
diff --git a/MonoGame.Framework/Graphics/GraphicsDevice.Web.cs b/MonoGame.Framework/Graphics/GraphicsDevice.Web.cs
index 2a1183378a1..776d73af19d 100644
--- a/MonoGame.Framework/Graphics/GraphicsDevice.Web.cs
+++ b/MonoGame.Framework/Graphics/GraphicsDevice.Web.cs
@@ -25,6 +25,10 @@ private void PlatformInitialize()
{
}
+ internal void OnPresentationChanged()
+ {
+ }
+
public void PlatformClear(ClearOptions options, Vector4 color, float depth, int stencil)
{
WebGL.gl.enable(WebGL.gl.DEPTH_TEST);
@@ -63,6 +67,10 @@ internal void PlatformBeginApplyState()
{
}
+ private void PlatformApplyBlend()
+ {
+ }
+
internal void PlatformApplyState(bool applyShaders)
{
}
@@ -95,5 +103,16 @@ private static GraphicsProfile PlatformGetHighestSupportedGraphicsProfile(Graphi
{
return GraphicsProfile.HiDef;
}
+
+ private static Rectangle PlatformGetTitleSafeArea(int x, int y, int width, int height)
+ {
+ return new Rectangle(x, y, width, height);
+ }
+
+ internal void PlatformSetMultiSamplingToMaximum(PresentationParameters presentationParameters, out int quality)
+ {
+ presentationParameters.MultiSampleCount = 0;
+ quality = 0;
+ }
}
}
diff --git a/MonoGame.Framework/Graphics/GraphicsDevice.cs b/MonoGame.Framework/Graphics/GraphicsDevice.cs
index f379d26ed8b..160dea0d369 100644
--- a/MonoGame.Framework/Graphics/GraphicsDevice.cs
+++ b/MonoGame.Framework/Graphics/GraphicsDevice.cs
@@ -6,6 +6,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
+using Microsoft.Xna.Framework.Utilities;
namespace Microsoft.Xna.Framework.Graphics
@@ -13,10 +14,12 @@ namespace Microsoft.Xna.Framework.Graphics
public partial class GraphicsDevice : IDisposable
{
private Viewport _viewport;
- private GraphicsProfile _graphicsProfile;
private bool _isDisposed;
+ private Color _blendFactor = Color.White;
+ private bool _blendFactorDirty;
+
private BlendState _blendState;
private BlendState _actualBlendState;
private bool _blendStateDirty;
@@ -53,6 +56,7 @@ public partial class GraphicsDevice : IDisposable
private readonly RenderTargetBinding[] _currentRenderTargetBindings = new RenderTargetBinding[4];
private int _currentRenderTargetCount;
+ private readonly RenderTargetBinding[] _tempRenderTargetBinding = new RenderTargetBinding[1];
internal GraphicsCapabilities GraphicsCapabilities { get; private set; }
@@ -118,14 +122,7 @@ private bool PixelShaderDirty
public event EventHandler ResourceDestroyed;
public event EventHandler Disposing;
- private bool SuppressEventHandlerWarningsUntilEventsAreProperlyImplemented()
- {
- return
- DeviceLost != null &&
- ResourceCreated != null &&
- ResourceDestroyed != null &&
- Disposing != null;
- }
+ internal event EventHandler PresentationChanged;
private int _maxVertexBufferSlots;
internal int MaxTextureSlots;
@@ -155,6 +152,16 @@ internal bool IsRenderTargetBound
}
}
+ internal DepthFormat ActiveDepthFormat
+ {
+ get
+ {
+ return IsRenderTargetBound
+ ? _currentRenderTargetBindings[0].DepthFormat
+ : PresentationParameters.DepthStencilFormat;
+ }
+ }
+
public GraphicsAdapter Adapter
{
get;
@@ -170,14 +177,8 @@ public GraphicsAdapter Adapter
public GraphicsMetrics Metrics { get { return _graphicsMetrics; } set { _graphicsMetrics = value; } }
internal GraphicsDevice(GraphicsDeviceInformation gdi)
+ : this(gdi.Adapter, gdi.GraphicsProfile, gdi.PresentationParameters)
{
- if (gdi.PresentationParameters == null)
- throw new ArgumentNullException("presentationParameters");
- PresentationParameters = gdi.PresentationParameters;
- Setup();
- GraphicsCapabilities = new GraphicsCapabilities(this);
- GraphicsProfile = gdi.GraphicsProfile;
- Initialize();
}
internal GraphicsDevice ()
@@ -185,7 +186,8 @@ internal GraphicsDevice ()
PresentationParameters = new PresentationParameters();
PresentationParameters.DepthStencilFormat = DepthFormat.Depth24;
Setup();
- GraphicsCapabilities = new GraphicsCapabilities(this);
+ GraphicsCapabilities = new GraphicsCapabilities();
+ GraphicsCapabilities.Initialize(this);
Initialize();
}
@@ -200,20 +202,35 @@ internal GraphicsDevice ()
///
public GraphicsDevice(GraphicsAdapter adapter, GraphicsProfile graphicsProfile, PresentationParameters presentationParameters)
{
- Adapter = adapter;
+ if (adapter == null)
+ throw new ArgumentNullException("adapter");
+ if (!adapter.IsProfileSupported(graphicsProfile))
+ throw new NoSuitableGraphicsDeviceException(String.Format("Adapter '{0}' does not support the {1} profile.", adapter.Description, graphicsProfile));
if (presentationParameters == null)
throw new ArgumentNullException("presentationParameters");
+ Adapter = adapter;
PresentationParameters = presentationParameters;
+ _graphicsProfile = graphicsProfile;
Setup();
- GraphicsCapabilities = new GraphicsCapabilities(this);
- GraphicsProfile = graphicsProfile;
+ GraphicsCapabilities = new GraphicsCapabilities();
+ GraphicsCapabilities.Initialize(this);
Initialize();
}
private void Setup()
{
- // Initialize the main viewport
- _viewport = new Viewport (0, 0,
+#if DEBUG
+ if (DisplayMode == null)
+ {
+ throw new Exception(
+ "Unable to determine the current display mode. This can indicate that the " +
+ "game is not configured to be HiDPI aware under Windows 10 or later. See " +
+ "https://github.com/MonoGame/MonoGame/issues/5040 for more information.");
+ }
+#endif
+
+ // Initialize the main viewport
+ _viewport = new Viewport (0, 0,
DisplayMode.Width, DisplayMode.Height);
_viewport.MaxDepth = 1.0f;
@@ -255,6 +272,7 @@ private void Setup()
internal void Initialize()
{
PlatformInitialize();
+ GraphicsCapabilities.InitializeAfterResources(this);
// Force set the default render states.
_blendStateDirty = _depthStencilStateDirty = _rasterizerStateDirty = true;
@@ -327,6 +345,25 @@ public RasterizerState RasterizerState
}
}
+ ///
+ /// The color used as blend factor when alpha blending.
+ ///
+ ///
+ /// When only changing BlendFactor, use this rather than to
+ /// only update BlendFactor so the whole BlendState does not have to be updated.
+ ///
+ public Color BlendFactor
+ {
+ get { return _blendFactor; }
+ set
+ {
+ if (_blendFactor == value)
+ return;
+ _blendFactor = value;
+ _blendFactorDirty = true;
+ }
+ }
+
public BlendState BlendState
{
get { return _blendState; }
@@ -359,6 +396,8 @@ public BlendState BlendState
_actualBlendState = newBlendState;
+ BlendFactor = _actualBlendState.BlendFactor;
+
_blendStateDirty = true;
}
}
@@ -399,11 +438,7 @@ internal void ApplyState(bool applyShaders)
{
PlatformBeginApplyState();
- if (_blendStateDirty)
- {
- _actualBlendState.PlatformApplyState(this);
- _blendStateDirty = false;
- }
+ PlatformApplyBlend();
if (_depthStencilStateDirty)
{
@@ -484,6 +519,9 @@ protected virtual void Dispose(bool disposing)
}
_isDisposed = true;
+
+ if (Disposing != null)
+ Disposing(this, EventArgs.Empty);
}
}
@@ -505,6 +543,10 @@ internal void RemoveResourceReference(WeakReference resourceReference)
public void Present()
{
+ // We cannot present with a RT set on the device.
+ if (_currentRenderTargetCount != 0)
+ throw new InvalidOperationException("Cannot call Present when a render target is active.");
+
_graphicsMetrics = new GraphicsMetrics();
PlatformPresent();
}
@@ -514,31 +556,34 @@ public void Present(Rectangle? sourceRectangle, Rectangle? destinationRectangle,
{
throw new NotImplementedException();
}
+ */
+
+ partial void PlatformValidatePresentationParameters(PresentationParameters presentationParameters);
public void Reset()
{
- // Manually resetting the device is not currently supported.
- throw new NotImplementedException();
- }
- */
+ PlatformValidatePresentationParameters(PresentationParameters);
-#if WINDOWS && DIRECTX
- public void Reset(PresentationParameters presentationParameters)
- {
- PresentationParameters = presentationParameters;
+ if (DeviceResetting != null)
+ DeviceResetting(this, EventArgs.Empty);
// Update the back buffer.
- CreateSizeDependentResources();
- ApplyRenderTargets(null);
+ OnPresentationChanged();
+
+ if (PresentationChanged != null)
+ PresentationChanged(this, EventArgs.Empty);
+ if (DeviceReset != null)
+ DeviceReset(this, EventArgs.Empty);
}
-#endif
- /*
- public void Reset(PresentationParameters presentationParameters, GraphicsAdapter graphicsAdapter)
+ public void Reset(PresentationParameters presentationParameters)
{
- throw new NotImplementedException();
+ if (presentationParameters == null)
+ throw new ArgumentNullException("presentationParameters");
+
+ PresentationParameters = presentationParameters;
+ Reset();
}
- */
///
/// Trigger the DeviceResetting event
@@ -609,22 +654,10 @@ public Viewport Viewport
}
}
+ private readonly GraphicsProfile _graphicsProfile;
public GraphicsProfile GraphicsProfile
{
- get
- {
- return _graphicsProfile;
- }
- internal set
- {
- //Check if Profile is supported.
- //TODO: [DirectX] Recreate the Device using the new
- // feature level each time the Profile changes.
- if(value > GetHighestSupportedGraphicsProfile(this))
- throw new NotSupportedException(String.Format("Could not find a graphics device that supports the {0} profile", value.ToString()));
- _graphicsProfile = value;
- GraphicsCapabilities.Initialize(this);
- }
+ get { return _graphicsProfile; }
}
public Rectangle ScissorRectangle
@@ -655,17 +688,27 @@ public int RenderTargetCount
public void SetRenderTarget(RenderTarget2D renderTarget)
{
if (renderTarget == null)
+ {
SetRenderTargets(null);
+ }
else
- SetRenderTargets(new RenderTargetBinding(renderTarget));
+ {
+ _tempRenderTargetBinding[0] = new RenderTargetBinding(renderTarget);
+ SetRenderTargets(_tempRenderTargetBinding);
+ }
}
public void SetRenderTarget(RenderTargetCube renderTarget, CubeMapFace cubeMapFace)
{
if (renderTarget == null)
- SetRenderTarget(null);
+ {
+ SetRenderTargets(null);
+ }
else
- SetRenderTargets(new RenderTargetBinding(renderTarget, cubeMapFace));
+ {
+ _tempRenderTargetBinding[0] = new RenderTargetBinding(renderTarget, cubeMapFace);
+ SetRenderTargets(_tempRenderTargetBinding);
+ }
}
public void SetRenderTargets(params RenderTargetBinding[] renderTargets)
@@ -712,7 +755,7 @@ public void SetRenderTargets(params RenderTargetBinding[] renderTargets)
{
unchecked
{
- _graphicsMetrics._targetCount += (ulong)renderTargetCount;
+ _graphicsMetrics._targetCount += renderTargetCount;
}
}
}
@@ -841,6 +884,7 @@ internal Shader VertexShader
return;
_vertexShader = value;
+ _vertexConstantBuffers.Clear();
_vertexShaderDirty = true;
}
}
@@ -855,6 +899,7 @@ internal Shader PixelShader
return;
_pixelShader = value;
+ _pixelConstantBuffers.Clear();
_pixelShaderDirty = true;
}
}
@@ -885,6 +930,13 @@ public void DrawIndexedPrimitives(PrimitiveType primitiveType, int baseVertex, i
DrawIndexedPrimitives(primitiveType, baseVertex, startIndex, primitiveCount);
}
+ ///
+ /// Draw geometry by indexing into the vertex buffer.
+ ///
+ /// The type of primitives in the index buffer.
+ /// Used to offset the vertex range indexed from the vertex buffer.
+ /// The index within the index buffer to start drawing from.
+ /// The number of primitives to render from the index buffer.
public void DrawIndexedPrimitives(PrimitiveType primitiveType, int baseVertex, int startIndex, int primitiveCount)
{
if (_vertexShader == null)
@@ -904,15 +956,34 @@ public void DrawIndexedPrimitives(PrimitiveType primitiveType, int baseVertex, i
unchecked
{
_graphicsMetrics._drawCount++;
- _graphicsMetrics._primitiveCount += (ulong)primitiveCount;
+ _graphicsMetrics._primitiveCount += primitiveCount;
}
}
+ ///
+ /// Draw primitives of the specified type from the data in an array of vertices without indexing.
+ ///
+ /// The type of the vertices.
+ /// The type of primitives to draw with the vertices.
+ /// An array of vertices to draw.
+ /// The index in the array of the first vertex that should be rendered.
+ /// The number of primitives to draw.
+ /// The will be found by getting
+ /// from an instance of and cached for subsequent calls.
public void DrawUserPrimitives(PrimitiveType primitiveType, T[] vertexData, int vertexOffset, int primitiveCount) where T : struct, IVertexType
{
DrawUserPrimitives(primitiveType, vertexData, vertexOffset, primitiveCount, VertexDeclarationCache.VertexDeclaration);
}
+ ///
+ /// Draw primitives of the specified type from the data in the given array of vertices without indexing.
+ ///
+ /// The type of the vertices.
+ /// The type of primitives to draw with the vertices.
+ /// An array of vertices to draw.
+ /// The index in the array of the first vertex that should be rendered.
+ /// The number of primitives to draw.
+ /// The layout of the vertices.
public void DrawUserPrimitives(PrimitiveType primitiveType, T[] vertexData, int vertexOffset, int primitiveCount, VertexDeclaration vertexDeclaration) where T : struct
{
if (vertexData == null)
@@ -940,10 +1011,16 @@ public void DrawUserPrimitives(PrimitiveType primitiveType, T[] vertexData, i
unchecked
{
_graphicsMetrics._drawCount++;
- _graphicsMetrics._primitiveCount += (ulong) primitiveCount;
+ _graphicsMetrics._primitiveCount += primitiveCount;
}
}
+ ///
+ /// Draw primitives of the specified type from the currently bound vertexbuffers without indexing.
+ ///
+ /// The type of primitives to draw.
+ /// Index of the vertex to start at.
+ /// The number of primitives to draw.
public void DrawPrimitives(PrimitiveType primitiveType, int vertexStart, int primitiveCount)
{
if (_vertexShader == null)
@@ -962,15 +1039,46 @@ public void DrawPrimitives(PrimitiveType primitiveType, int vertexStart, int pri
unchecked
{
_graphicsMetrics._drawCount++;
- _graphicsMetrics._primitiveCount += (ulong) primitiveCount;
+ _graphicsMetrics._primitiveCount += primitiveCount;
}
}
+ ///
+ /// Draw primitives of the specified type by indexing into the given array of vertices with 16-bit indices.
+ ///
+ /// The type of the vertices.
+ /// The type of primitives to draw with the vertices.
+ /// An array of vertices to draw.
+ /// The index in the array of the first vertex to draw.
+ /// The index in the array of indices of the first index to use
+ /// The number of primitives to draw.
+ /// The number of vertices to draw.
+ /// The index data.
+ /// The will be found by getting
+ /// from an instance of and cached for subsequent calls.
+ /// All indices in the vertex buffer are interpreted relative to the specified .
+ /// For example a value of zero in the array of indices points to the vertex at index
+ /// in the array of vertices.
public void DrawUserIndexedPrimitives(PrimitiveType primitiveType, T[] vertexData, int vertexOffset, int numVertices, short[] indexData, int indexOffset, int primitiveCount) where T : struct, IVertexType
{
DrawUserIndexedPrimitives(primitiveType, vertexData, vertexOffset, numVertices, indexData, indexOffset, primitiveCount, VertexDeclarationCache.VertexDeclaration);
}
+ ///
+ /// Draw primitives of the specified type by indexing into the given array of vertices with 16-bit indices.
+ ///
+ /// The type of the vertices.
+ /// The type of primitives to draw with the vertices.
+ /// An array of vertices to draw.
+ /// The index in the array of the first vertex to draw.
+ /// The index in the array of indices of the first index to use
+ /// The number of primitives to draw.
+ /// The number of vertices to draw.
+ /// The index data.
+ /// The layout of the vertices.
+ /// All indices in the vertex buffer are interpreted relative to the specified .
+ /// For example a value of zero in the array of indices points to the vertex at index
+ /// in the array of vertices.
public void DrawUserIndexedPrimitives(PrimitiveType primitiveType, T[] vertexData, int vertexOffset, int numVertices, short[] indexData, int indexOffset, int primitiveCount, VertexDeclaration vertexDeclaration) where T : struct
{
// These parameter checks are a duplicate of the checks in the int[] overload of DrawUserIndexedPrimitives.
@@ -1003,20 +1111,54 @@ public void DrawUserIndexedPrimitives(PrimitiveType primitiveType, T[] vertex
if (vertexDeclaration == null)
throw new ArgumentNullException("vertexDeclaration");
+ if (vertexDeclaration.VertexStride < ReflectionHelpers.SizeOf.Get())
+ throw new ArgumentOutOfRangeException("vertexDeclaration", "Vertex stride of vertexDeclaration should be at least as big as the stride of the actual vertices.");
+
PlatformDrawUserIndexedPrimitives(primitiveType, vertexData, vertexOffset, numVertices, indexData, indexOffset, primitiveCount, vertexDeclaration);
unchecked
{
_graphicsMetrics._drawCount++;
- _graphicsMetrics._primitiveCount += (ulong) primitiveCount;
+ _graphicsMetrics._primitiveCount += primitiveCount;
}
}
+ ///
+ /// Draw primitives of the specified type by indexing into the given array of vertices with 32-bit indices.
+ ///
+ /// The type of the vertices.
+ /// The type of primitives to draw with the vertices.
+ /// An array of vertices to draw.
+ /// The index in the array of the first vertex to draw.
+ /// The index in the array of indices of the first index to use
+ /// The number of primitives to draw.
+ /// The number of vertices to draw.
+ /// The index data.
+ /// The will be found by getting
+ /// from an instance of and cached for subsequent calls.
+ /// All indices in the vertex buffer are interpreted relative to the specified .
+ /// For example a value of zero in the array of indices points to the vertex at index
+ /// in the array of vertices.
public void DrawUserIndexedPrimitives(PrimitiveType primitiveType, T[] vertexData, int vertexOffset, int numVertices, int[] indexData, int indexOffset, int primitiveCount) where T : struct, IVertexType
{
DrawUserIndexedPrimitives(primitiveType, vertexData, vertexOffset, numVertices, indexData, indexOffset, primitiveCount, VertexDeclarationCache.VertexDeclaration);
}
+ ///
+ /// Draw primitives of the specified type by indexing into the given array of vertices with 32-bit indices.
+ ///
+ /// The type of the vertices.
+ /// The type of primitives to draw with the vertices.
+ /// An array of vertices to draw.
+ /// The index in the array of the first vertex to draw.
+ /// The index in the array of indices of the first index to use
+ /// The number of primitives to draw.
+ /// The number of vertices to draw.
+ /// The index data.
+ /// The layout of the vertices.
+ /// All indices in the vertex buffer are interpreted relative to the specified .
+ /// For example value of zero in the array of indices points to the vertex at index
+ /// in the array of vertices.
public void DrawUserIndexedPrimitives(PrimitiveType primitiveType, T[] vertexData, int vertexOffset, int numVertices, int[] indexData, int indexOffset, int primitiveCount, VertexDeclaration vertexDeclaration) where T : struct
{
// These parameter checks are a duplicate of the checks in the short[] overload of DrawUserIndexedPrimitives.
@@ -1049,17 +1191,46 @@ public void DrawUserIndexedPrimitives(PrimitiveType primitiveType, T[] vertex
if (vertexDeclaration == null)
throw new ArgumentNullException("vertexDeclaration");
+ if (vertexDeclaration.VertexStride < ReflectionHelpers.SizeOf.Get())
+ throw new ArgumentOutOfRangeException("vertexDeclaration", "Vertex stride of vertexDeclaration should be at least as big as the stride of the actual vertices.");
+
PlatformDrawUserIndexedPrimitives(primitiveType, vertexData, vertexOffset, numVertices, indexData, indexOffset, primitiveCount, vertexDeclaration);
unchecked
{
_graphicsMetrics._drawCount++;
- _graphicsMetrics._primitiveCount += (ulong) primitiveCount;
+ _graphicsMetrics._primitiveCount += primitiveCount;
}
}
+ ///
+ /// Draw instanced geometry from the bound vertex buffers and index buffer.
+ ///
+ /// The type of primitives in the index buffer.
+ /// Used to offset the vertex range indexed from the vertex buffer.
+ /// This is unused and remains here only for XNA API compatibility.
+ /// This is unused and remains here only for XNA API compatibility.
+ /// The index within the index buffer to start drawing from.
+ /// The number of primitives in a single instance.
+ /// The number of instances to render.
+ /// Note that minVertexIndex and numVertices are unused in MonoGame and will be ignored.
+ [Obsolete("Use DrawInstancedPrimitives(PrimitiveType primitiveType, int baseVertex, int startIndex, int primitiveCount, int instanceCount) instead. In future versions this method can be removed.")]
public void DrawInstancedPrimitives(PrimitiveType primitiveType, int baseVertex, int minVertexIndex,
int numVertices, int startIndex, int primitiveCount, int instanceCount)
+ {
+ DrawInstancedPrimitives(primitiveType, baseVertex, startIndex, primitiveCount, instanceCount);
+ }
+
+ ///
+ /// Draw instanced geometry from the bound vertex buffers and index buffer.
+ ///
+ /// The type of primitives in the index buffer.
+ /// Used to offset the vertex range indexed from the vertex buffer.
+ /// The index within the index buffer to start drawing from.
+ /// The number of primitives in a single instance.
+ /// The number of instances to render.
+ /// Draw geometry with data from multiple bound vertex streams at different frequencies.
+ public void DrawInstancedPrimitives(PrimitiveType primitiveType, int baseVertex, int startIndex, int primitiveCount, int instanceCount)
{
if (_vertexShader == null)
throw new InvalidOperationException("Vertex shader must be set before calling DrawInstancedPrimitives.");
@@ -1078,7 +1249,7 @@ public void DrawInstancedPrimitives(PrimitiveType primitiveType, int baseVertex,
unchecked
{
_graphicsMetrics._drawCount++;
- _graphicsMetrics._primitiveCount += (ulong)(primitiveCount * instanceCount);
+ _graphicsMetrics._primitiveCount += (primitiveCount * instanceCount);
}
}
@@ -1103,5 +1274,23 @@ internal static GraphicsProfile GetHighestSupportedGraphicsProfile(GraphicsDevic
{
return PlatformGetHighestSupportedGraphicsProfile(graphicsDevice);
}
+
+ // uniformly scales down the given rectangle by 10%
+ internal static Rectangle GetDefaultTitleSafeArea(int x, int y, int width, int height)
+ {
+ var marginX = (width + 19) / 20;
+ var marginY = (height + 19) / 20;
+ x += marginX;
+ y += marginY;
+
+ width -= marginX * 2;
+ height -= marginY * 2;
+ return new Rectangle(x, y, width, height);
+ }
+
+ internal static Rectangle GetTitleSafeArea(int x, int y, int width, int height)
+ {
+ return PlatformGetTitleSafeArea(x, y, width, height);
+ }
}
}
diff --git a/MonoGame.Framework/Graphics/GraphicsExtensions.cs b/MonoGame.Framework/Graphics/GraphicsExtensions.cs
index 3035ffb7aef..b88e8359395 100644
--- a/MonoGame.Framework/Graphics/GraphicsExtensions.cs
+++ b/MonoGame.Framework/Graphics/GraphicsExtensions.cs
@@ -1,16 +1,25 @@
-using System;
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
using System.Diagnostics;
#if OPENGL
#if MONOMAC
#if PLATFORM_MACOS_LEGACY
using MonoMac.OpenGL;
+using GLPixelFormat = MonoMac.OpenGL.All;
+using PixelFormat = MonoMac.OpenGL.PixelFormat;
#else
using OpenTK.Graphics.OpenGL;
+using GLPixelFormat = OpenTK.Graphics.OpenGL.All;
+using PixelFormat = OpenTK.Graphics.OpenGL.PixelFormat;
#endif
#elif DESKTOPGL
-using OpenTK.Graphics;
-using OpenTK.Graphics.OpenGL;
+using OpenGL;
+using GLPixelFormat = OpenGL.PixelFormat;
+using PixelFormat = OpenGL.PixelFormat;
#elif GLES
#if ANGLE
using OpenTK.Graphics;
@@ -20,6 +29,8 @@
using ColorPointerType = OpenTK.Graphics.ES20.All;
using NormalPointerType = OpenTK.Graphics.ES20.All;
using TexCoordPointerType = OpenTK.Graphics.ES20.All;
+using GLPixelFormat = OpenTK.Graphics.ES20.All;
+using PixelFormat = OpenTK.Graphics.ES20.PixelFormat;
#endif
#endif
@@ -28,19 +39,6 @@ namespace Microsoft.Xna.Framework.Graphics
static class GraphicsExtensions
{
#if OPENGL
- public static All OpenGL11(CullMode cull)
- {
- switch (cull)
- {
- case CullMode.CullClockwiseFace:
- return All.Cw;
- case CullMode.CullCounterClockwiseFace:
- return All.Ccw;
- default:
- throw new ArgumentException();
- }
- }
-
public static int OpenGLNumberOfElements(this VertexElementFormat elementFormat)
{
switch (elementFormat)
@@ -378,10 +376,10 @@ public static BlendingFactorSrc GetBlendFactorSrc (this Blend blend)
case Blend.InverseSourceAlpha:
return BlendingFactorSrc.OneMinusSrcAlpha;
case Blend.InverseSourceColor:
-#if MONOMAC || WINDOWS || DESKTOPGL
- return (BlendingFactorSrc)All.OneMinusSrcColor;
+#if MONOMAC
+ return (BlendingFactorSrc)All.OneMinusSrcColor;
#else
- return BlendingFactorSrc.OneMinusSrcColor;
+ return BlendingFactorSrc.OneMinusSrcColor;
#endif
case Blend.One:
return BlendingFactorSrc.One;
@@ -390,11 +388,11 @@ public static BlendingFactorSrc GetBlendFactorSrc (this Blend blend)
case Blend.SourceAlphaSaturation:
return BlendingFactorSrc.SrcAlphaSaturate;
case Blend.SourceColor:
-#if MONOMAC || WINDOWS || DESKTOPGL
- return (BlendingFactorSrc)All.SrcColor;
-#else
+ #if MONOMAC
+ return (BlendingFactorSrc)All.SrcColor;
+ #else
return BlendingFactorSrc.SrcColor;
-#endif
+ #endif
case Blend.Zero:
return BlendingFactorSrc.Zero;
default:
@@ -417,11 +415,7 @@ public static BlendingFactorDest GetBlendFactorDest (this Blend blend)
case Blend.InverseSourceAlpha:
return BlendingFactorDest.OneMinusSrcAlpha;
case Blend.InverseSourceColor:
-#if MONOMAC || WINDOWS
- return (BlendingFactorDest)All.OneMinusSrcColor;
-#else
return BlendingFactorDest.OneMinusSrcColor;
-#endif
case Blend.One:
return BlendingFactorDest.One;
case Blend.SourceAlpha:
@@ -429,11 +423,7 @@ public static BlendingFactorDest GetBlendFactorDest (this Blend blend)
// case Blend.SourceAlphaSaturation:
// return BlendingFactorDest.SrcAlphaSaturate;
case Blend.SourceColor:
-#if MONOMAC || WINDOWS
- return (BlendingFactorDest)All.SrcColor;
-#else
return BlendingFactorDest.SrcColor;
-#endif
case Blend.Zero:
return BlendingFactorDest.Zero;
default:
@@ -583,37 +573,37 @@ internal static void GetGLFormat (this SurfaceFormat format,
#if !IOS && !ANDROID && !ANGLE
case SurfaceFormat.Dxt1:
glInternalFormat = PixelInternalFormat.CompressedRgbS3tcDxt1Ext;
- glFormat = (PixelFormat)All.CompressedTextureFormats;
+ glFormat = (PixelFormat)GLPixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.Dxt1SRgb:
if (!supportsSRgb)
goto case SurfaceFormat.Dxt1;
glInternalFormat = PixelInternalFormat.CompressedSrgbS3tcDxt1Ext;
- glFormat = (PixelFormat) All.CompressedTextureFormats;
+ glFormat = (PixelFormat)GLPixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.Dxt1a:
glInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt1Ext;
- glFormat = (PixelFormat)All.CompressedTextureFormats;
+ glFormat = (PixelFormat)GLPixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.Dxt3:
glInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt3Ext;
- glFormat = (PixelFormat)All.CompressedTextureFormats;
+ glFormat = (PixelFormat)GLPixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.Dxt3SRgb:
if (!supportsSRgb)
goto case SurfaceFormat.Dxt3;
glInternalFormat = PixelInternalFormat.CompressedSrgbAlphaS3tcDxt3Ext;
- glFormat = (PixelFormat) All.CompressedTextureFormats;
+ glFormat = (PixelFormat)GLPixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.Dxt5:
glInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt5Ext;
- glFormat = (PixelFormat)All.CompressedTextureFormats;
+ glFormat = (PixelFormat)GLPixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.Dxt5SRgb:
if (!supportsSRgb)
goto case SurfaceFormat.Dxt5;
glInternalFormat = PixelInternalFormat.CompressedSrgbAlphaS3tcDxt5Ext;
- glFormat = (PixelFormat) All.CompressedTextureFormats;
+ glFormat = (PixelFormat)GLPixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.Single:
@@ -761,7 +751,7 @@ internal static void GetGLFormat (this SurfaceFormat format,
#endif // OPENGL
- public static int GetFrameLatency(this PresentInterval interval)
+ public static int GetSyncInterval(this PresentInterval interval)
{
switch (interval)
{
@@ -914,6 +904,7 @@ public static void CheckGLError()
#else
var error = GL.GetError();
#endif
+ //Console.WriteLine(error);
if (error != ErrorCode.NoError)
throw new MonoGameGLException("GL.GetError() returned " + error.ToString());
}
diff --git a/MonoGame.Framework/Graphics/GraphicsMetrics.cs b/MonoGame.Framework/Graphics/GraphicsMetrics.cs
index 44820c3df9a..feb30f34ef3 100644
--- a/MonoGame.Framework/Graphics/GraphicsMetrics.cs
+++ b/MonoGame.Framework/Graphics/GraphicsMetrics.cs
@@ -9,54 +9,54 @@ namespace Microsoft.Xna.Framework.Graphics
///
public struct GraphicsMetrics
{
- internal ulong _clearCount;
- internal ulong _drawCount;
- internal ulong _pixelShaderCount;
- internal ulong _primitiveCount;
- internal ulong _spriteCount;
- internal ulong _targetCount;
- internal ulong _textureCount;
- internal ulong _vertexShaderCount;
+ internal long _clearCount;
+ internal long _drawCount;
+ internal long _pixelShaderCount;
+ internal long _primitiveCount;
+ internal long _spriteCount;
+ internal long _targetCount;
+ internal long _textureCount;
+ internal long _vertexShaderCount;
///
/// Number of times Clear was called.
///
- public ulong ClearCount { get { return _clearCount; } }
+ public long ClearCount { get { return _clearCount; } }
///
/// Number of times Draw was called.
///
- public ulong DrawCount { get { return _drawCount; } }
+ public long DrawCount { get { return _drawCount; } }
///
/// Number of times the pixel shader was changed on the GPU.
///
- public ulong PixelShaderCount { get { return _pixelShaderCount; } }
+ public long PixelShaderCount { get { return _pixelShaderCount; } }
///
/// Number of rendered primitives.
///
- public ulong PrimitiveCount { get { return _primitiveCount; } }
+ public long PrimitiveCount { get { return _primitiveCount; } }
///
/// Number of sprites and text characters rendered via .
///
- public ulong SpriteCount { get { return _spriteCount; } }
+ public long SpriteCount { get { return _spriteCount; } }
///
/// Number of times a target was changed on the GPU.
///
- public ulong TargetCount {get { return _targetCount; } }
+ public long TargetCount {get { return _targetCount; } }
///
/// Number of times a texture was changed on the GPU.
///
- public ulong TextureCount { get { return _textureCount; } }
+ public long TextureCount { get { return _textureCount; } }
///
/// Number of times the vertex shader was changed on the GPU.
///
- public ulong VertexShaderCount { get { return _vertexShaderCount; } }
+ public long VertexShaderCount { get { return _vertexShaderCount; } }
///
/// Returns the difference between two sets of metrics.
diff --git a/MonoGame.Framework/Graphics/IGraphicsContext.cs b/MonoGame.Framework/Graphics/IGraphicsContext.cs
new file mode 100644
index 00000000000..85368981558
--- /dev/null
+++ b/MonoGame.Framework/Graphics/IGraphicsContext.cs
@@ -0,0 +1,16 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+
+namespace OpenGL
+{
+ public interface IGraphicsContext : IDisposable
+ {
+ int SwapInterval { get; set; }
+ bool IsDisposed { get; }
+ void MakeCurrent(IWindowInfo info);
+ void SwapBuffers();
+ }
+}
diff --git a/MonoGame.Framework/Graphics/IRenderTarget.cs b/MonoGame.Framework/Graphics/IRenderTarget.cs
index 4602a33ff60..23af78fc3f8 100644
--- a/MonoGame.Framework/Graphics/IRenderTarget.cs
+++ b/MonoGame.Framework/Graphics/IRenderTarget.cs
@@ -40,6 +40,21 @@
using SharpDX.Direct3D11;
#endif
+#if OPENGL
+#if MONOMAC && PLATFORM_MACOS_LEGACY
+using MonoMac.OpenGL;
+#endif
+#if (MONOMAC && !PLATFORM_MACOS_LEGACY)
+using OpenTK.Graphics.OpenGL;
+#endif
+#if GLES
+using OpenTK.Graphics.ES20;
+#else
+using OpenGL;
+#endif
+#endif
+
+
namespace Microsoft.Xna.Framework.Graphics
{
///
@@ -82,5 +97,17 @@ internal interface IRenderTarget
/// The . Can be .
DepthStencilView GetDepthStencilView();
#endif
+
+#if OPENGL
+ int GLTexture { get; }
+ TextureTarget GLTarget { get; }
+ int GLColorBuffer { get; set; }
+ int GLDepthBuffer { get; set; }
+ int GLStencilBuffer { get; set; }
+ int MultiSampleCount { get; }
+ int LevelCount { get; }
+
+ TextureTarget GetFramebufferTarget(RenderTargetBinding renderTargetBinding);
+#endif
}
}
diff --git a/MonoGame.Framework/Graphics/IWindowInfo.cs b/MonoGame.Framework/Graphics/IWindowInfo.cs
new file mode 100644
index 00000000000..c447483847c
--- /dev/null
+++ b/MonoGame.Framework/Graphics/IWindowInfo.cs
@@ -0,0 +1,13 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+
+namespace OpenGL
+{
+ public interface IWindowInfo
+ {
+ IntPtr Handle { get; }
+ }
+}
diff --git a/MonoGame.Framework/Graphics/Model.cs b/MonoGame.Framework/Graphics/Model.cs
index d8b1c92dd27..e42186185cd 100644
--- a/MonoGame.Framework/Graphics/Model.cs
+++ b/MonoGame.Framework/Graphics/Model.cs
@@ -53,6 +53,15 @@ internal Model()
/// A valid reference to .
/// The collection of bones.
/// The collection of meshes.
+ ///
+ /// is null.
+ ///
+ ///
+ /// is null.
+ ///
+ ///
+ /// is null.
+ ///
public Model(GraphicsDevice graphicsDevice, List bones, List meshes)
{
if (graphicsDevice == null)
@@ -163,6 +172,12 @@ public void CopyAbsoluteBoneTransformsTo(Matrix[] destinationBoneTransforms)
/// Copies bone transforms relative to bone from a given array to this model.
///
/// The array of prepared bone transform data.
+ ///
+ /// is null.
+ ///
+ ///
+ /// is invalid.
+ ///
public void CopyBoneTransformsFrom(Matrix[] sourceBoneTransforms)
{
if (sourceBoneTransforms == null)
@@ -181,6 +196,12 @@ public void CopyBoneTransformsFrom(Matrix[] sourceBoneTransforms)
/// Copies bone transforms relative to bone from this model to a given array.
///
/// The array receiving the transformed bones.
+ ///
+ /// is null.
+ ///
+ ///
+ /// is invalid.
+ ///
public void CopyBoneTransformsTo(Matrix[] destinationBoneTransforms)
{
if (destinationBoneTransforms == null)
diff --git a/MonoGame.Framework/Graphics/ModelEffectCollection.cs b/MonoGame.Framework/Graphics/ModelEffectCollection.cs
index e2b72253779..0cd55ca1f6a 100644
--- a/MonoGame.Framework/Graphics/ModelEffectCollection.cs
+++ b/MonoGame.Framework/Graphics/ModelEffectCollection.cs
@@ -9,7 +9,7 @@ namespace Microsoft.Xna.Framework.Graphics
// Represents a collection of effects associated with a model.
public sealed class ModelEffectCollection : ReadOnlyCollection
{
- public ModelEffectCollection(IList list)
+ internal ModelEffectCollection(IList list)
: base(list)
{
diff --git a/MonoGame.Framework/Graphics/ModelMesh.cs b/MonoGame.Framework/Graphics/ModelMesh.cs
index 3673f9f2fa1..e5426d96e7b 100644
--- a/MonoGame.Framework/Graphics/ModelMesh.cs
+++ b/MonoGame.Framework/Graphics/ModelMesh.cs
@@ -45,7 +45,7 @@ public ModelMesh(GraphicsDevice graphicsDevice, System.Collections.Generic.List<
//
// Summary:
// Gets a collection of effects associated with this mesh.
- public ModelEffectCollection Effects { get; set; }
+ public ModelEffectCollection Effects { get; internal set; }
//
// Summary:
// Gets the ModelMeshPart objects that make up this mesh. Each part of a mesh
@@ -84,7 +84,7 @@ public void Draw()
for (int j = 0; j < effect.CurrentTechnique.Passes.Count; j++)
{
effect.CurrentTechnique.Passes[j].Apply ();
- this.graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, part.VertexOffset, 0, part.NumVertices, part.StartIndex, part.PrimitiveCount);
+ graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, part.VertexOffset, part.StartIndex, part.PrimitiveCount);
}
}
}
diff --git a/MonoGame.Framework/Graphics/ModelMeshPart.cs b/MonoGame.Framework/Graphics/ModelMeshPart.cs
index 7cf1fb2f42e..337c253021a 100644
--- a/MonoGame.Framework/Graphics/ModelMeshPart.cs
+++ b/MonoGame.Framework/Graphics/ModelMeshPart.cs
@@ -38,7 +38,9 @@ public Effect Effect
// Set the new effect.
_effect = value;
- parent.Effects.Add(value);
+
+ if (_effect != null && !parent.Effects.Contains(_effect))
+ parent.Effects.Add(_effect);
}
}
diff --git a/MonoGame.Framework/Graphics/OcclusionQuery.OpenGL.cs b/MonoGame.Framework/Graphics/OcclusionQuery.OpenGL.cs
index 3cda195c55f..8b2ab61a0c6 100644
--- a/MonoGame.Framework/Graphics/OcclusionQuery.OpenGL.cs
+++ b/MonoGame.Framework/Graphics/OcclusionQuery.OpenGL.cs
@@ -10,7 +10,7 @@
using OpenTK.Graphics.OpenGL;
#endif
#elif DESKTOPGL
-using OpenTK.Graphics.OpenGL;
+using OpenGL;
#elif GLES
using OpenTK.Graphics.ES30;
#endif
diff --git a/MonoGame.Framework/Graphics/OpenGL.Android.cs b/MonoGame.Framework/Graphics/OpenGL.Android.cs
new file mode 100644
index 00000000000..a12e12862d2
--- /dev/null
+++ b/MonoGame.Framework/Graphics/OpenGL.Android.cs
@@ -0,0 +1,77 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+using System.Runtime.InteropServices;
+using System.Security;
+
+namespace OpenGL
+{
+ public partial class GL
+ {
+ // internal for Android is not used on other platforms
+ // it allows us to use either GLES or Full GL (if the GPU supports it)
+ internal delegate bool BindAPIDelegate (RenderApi api);
+ internal static BindAPIDelegate BindAPI;
+
+ static partial void LoadPlatformEntryPoints()
+ {
+ BindAPI = (BindAPIDelegate)Marshal.GetDelegateForFunctionPointer (EntryPointHelper.GetAddress ("eglBindAPI"), typeof(BindAPIDelegate));
+ var supportsFullGL = BindAPI (RenderApi.GL);
+ if (!supportsFullGL) {
+ BindAPI (RenderApi.ES);
+ BoundApi = RenderApi.ES;
+ }
+ }
+
+ private static IGraphicsContext PlatformCreateContext (IWindowInfo info)
+ {
+ return new GraphicsContext(info);
+ }
+ }
+
+ internal static class EntryPointHelper {
+
+ static IntPtr libES1 = DL.Open("/system/lib/libGLESv1_CM.so");
+ static IntPtr libES2 = DL.Open("/system/lib/libGLESv2.so");
+ static IntPtr libGL = DL.Open("/system/lib/libGL.so");
+
+ public static IntPtr GetAddress(String function)
+ {
+ if (GL.BoundApi == GL.RenderApi.ES && libES2 != IntPtr.Zero)
+ {
+ return DL.Symbol(libES2, function);
+ }
+ else if (GL.BoundApi == GL.RenderApi.GL && libGL != IntPtr.Zero)
+ {
+ return DL.Symbol(libGL, function);
+ }
+ return IntPtr.Zero;
+ }
+ }
+
+
+ internal class DL
+ {
+ internal enum DLOpenFlags
+ {
+ Lazy = 0x0001,
+ Now = 0x0002,
+ Global = 0x0100,
+ Local = 0x0000,
+ }
+
+ const string lib = "/system/lib/libdl.so";
+
+ [DllImport(lib, EntryPoint = "dlopen")]
+ internal static extern IntPtr Open(string filename, DLOpenFlags flags = DLOpenFlags.Lazy);
+
+ [DllImport(lib, EntryPoint = "dlclose")]
+ internal static extern int Close(IntPtr handle);
+
+ [DllImport(lib, EntryPoint = "dlsym")]
+ internal static extern IntPtr Symbol(IntPtr handle, String name);
+ }
+}
+
diff --git a/MonoGame.Framework/Graphics/OpenGL.Common.cs b/MonoGame.Framework/Graphics/OpenGL.Common.cs
new file mode 100644
index 00000000000..5decb358ccf
--- /dev/null
+++ b/MonoGame.Framework/Graphics/OpenGL.Common.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace OpenGL
+{
+ // Required to allow platforms other than iOS use the same code.
+ // just don't include this on iOS
+ [AttributeUsage (AttributeTargets.Delegate)]
+ public sealed class MonoNativeFunctionWrapper : Attribute {
+ }
+}
+
diff --git a/MonoGame.Framework/Graphics/OpenGL.SDL.cs b/MonoGame.Framework/Graphics/OpenGL.SDL.cs
new file mode 100644
index 00000000000..a9e04e6bbd4
--- /dev/null
+++ b/MonoGame.Framework/Graphics/OpenGL.SDL.cs
@@ -0,0 +1,49 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+using System.Runtime.InteropServices;
+using System.Security;
+
+namespace OpenGL
+{
+ public partial class GL
+ {
+ static partial void LoadPlatformEntryPoints()
+ {
+ BoundApi = RenderApi.GL;
+ }
+
+ private static IGraphicsContext PlatformCreateContext (IWindowInfo info)
+ {
+ return new GraphicsContext(info);
+ }
+ }
+
+ internal class EntryPointHelper {
+
+ private const string NativeLibName = "SDL2.dll";
+
+ [SuppressUnmanagedCodeSecurity]
+ [DllImport(NativeLibName, CallingConvention = CallingConvention.Cdecl,
+ EntryPoint = "SDL_GL_GetProcAddress", ExactSpelling = true)]
+ public static extern IntPtr GetProcAddress(IntPtr proc);
+ public static IntPtr GetAddress(string proc)
+ {
+ IntPtr p = Marshal.StringToHGlobalAnsi(proc);
+ try
+ {
+ var addr = GetProcAddress(p);
+ if (addr == IntPtr.Zero)
+ throw new EntryPointNotFoundException (proc);
+ return addr;
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(p);
+ }
+ }
+ }
+}
+
diff --git a/MonoGame.Framework/Graphics/OpenGL.cs b/MonoGame.Framework/Graphics/OpenGL.cs
new file mode 100644
index 00000000000..c221336b4c8
--- /dev/null
+++ b/MonoGame.Framework/Graphics/OpenGL.cs
@@ -0,0 +1,1391 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Runtime.CompilerServices;
+
+
+#if __IOS__
+using ObjCRuntime;
+#endif
+
+namespace OpenGL
+{
+ public enum BufferAccess {
+ ReadOnly = 0x88B8,
+ }
+
+ public enum BufferUsageHint {
+ StreamDraw = 0x88E0,
+ StaticDraw = 0x88E4,
+ }
+
+ public enum StencilFace {
+ Front = 0x0404,
+ Back = 0x0405,
+ }
+ public enum DrawBuffersEnum {
+ UnsignedShort,
+ UnsignedInt,
+ }
+
+ public enum ShaderType {
+ VertexShader = 0x8B31,
+ FragmentShader = 0x8B30,
+ }
+
+ public enum ShaderParameter {
+ LogLength = 0x8B84,
+ CompileStatus = 0x8B81,
+ SourceLength = 0x8B88,
+ }
+
+ public enum GetProgramParameterName {
+ LogLength = 0x8B84,
+ LinkStatus = 0x8B82,
+ }
+
+ public enum DrawElementsType {
+ UnsignedShort = 0x1403,
+ UnsignedInt = 0x1405,
+ }
+
+ public enum QueryTarget {
+ SamplesPassed = 0x8914,
+ }
+
+ public enum GetQueryObjectParam {
+ QueryResultAvailable = 0x8867,
+ QueryResult = 0x8866,
+ }
+
+ public enum GenerateMipmapTarget {
+ Texture1D = 0x0DE0,
+ Texture2D = 0x0DE1,
+ Texture3D = 0x806F,
+ TextureCubeMap = 0x8513,
+ Texture1DArray = 0x8C18,
+ Texture2DArray = 0x8C1A,
+ Texture2DMultisample = 0x9100,
+ Texture2DMultisampleArray = 0x9102,
+ }
+
+ public enum BlitFramebufferFilter {
+ Nearest = 0x2600,
+ }
+
+ public enum ReadBufferMode {
+ ColorAttachment0 = 0x8CE0,
+ }
+
+ public enum DrawBufferMode {
+ ColorAttachment0 = 0x8CE0,
+ }
+
+ public enum FramebufferErrorCode {
+ FramebufferUndefined = 0x8219,
+ FramebufferComplete = 0x8CD5,
+ FramebufferCompleteExt = 0x8CD5,
+ FramebufferIncompleteAttachment = 0x8CD6,
+ FramebufferIncompleteAttachmentExt = 0x8CD6,
+ FramebufferIncompleteMissingAttachment = 0x8CD7,
+ FramebufferIncompleteMissingAttachmentExt = 0x8CD7,
+ FramebufferIncompleteDimensionsExt = 0x8CD9,
+ FramebufferIncompleteFormatsExt = 0x8CDA,
+ FramebufferIncompleteDrawBuffer = 0x8CDB,
+ FramebufferIncompleteDrawBufferExt = 0x8CDB,
+ FramebufferIncompleteReadBuffer = 0x8CDC,
+ FramebufferIncompleteReadBufferExt = 0x8CDC,
+ FramebufferUnsupported = 0x8CDD,
+ FramebufferUnsupportedExt = 0x8CDD,
+ FramebufferIncompleteMultisample = 0x8D56,
+ FramebufferIncompleteLayerTargets = 0x8DA8,
+ FramebufferIncompleteLayerCount = 0x8DA9,
+ }
+
+ public enum BufferTarget {
+ ArrayBuffer = 0x8892,
+ ElementArrayBuffer = 0x8893,
+ }
+
+ public enum RenderbufferTarget {
+ Renderbuffer = 0x8D41,
+ RenderbufferExt = 0x8D41,
+ }
+
+ public enum FramebufferTarget {
+ Framebuffer = 0x8D40,
+ FramebufferExt = 0x8D40,
+ ReadFramebuffer = 0x8CA8,
+ }
+
+ public enum RenderbufferStorage {
+ Rgba8 = 0x8058,
+ DepthComponent16 = 0x81a5,
+ DepthComponent24 = 0x81a6,
+ Depth24Stencil8 = 0x88F0,
+ }
+
+ public enum EnableCap : int
+ {
+ PointSmooth = 0x0B10,
+ LineSmooth = 0x0B20,
+ CullFace = 0x0B44,
+ Lighting = 0x0B50,
+ ColorMaterial = 0x0B57,
+ Fog = 0x0B60,
+ DepthTest = 0x0B71,
+ StencilTest = 0x0B90,
+ Normalize = 0x0BA1,
+ AlphaTest = 0x0BC0,
+ Dither = 0x0BD0,
+ Blend = 0x0BE2,
+ ColorLogicOp = 0x0BF2,
+ ScissorTest = 0x0C11,
+ Texture2D = 0x0DE1,
+ PolygonOffsetFill = 0x8037,
+ RescaleNormal = 0x803A,
+ VertexArray = 0x8074,
+ NormalArray = 0x8075,
+ ColorArray = 0x8076,
+ TextureCoordArray = 0x8078,
+ Multisample = 0x809D,
+ SampleAlphaToCoverage = 0x809E,
+ SampleAlphaToOne = 0x809F,
+ SampleCoverage = 0x80A0,
+ }
+
+ public enum VertexPointerType {
+ Float = 0x1406,
+ Short = 0x1402,
+ }
+
+ public enum VertexAttribPointerType {
+ Float = 0x1406,
+ Short = 0x1402,
+ UnsignedByte = 0x1401,
+ HalfFloat = 0x140B,
+ }
+
+ public enum CullFaceMode {
+ Back = 0x0405,
+ Front = 0x0404,
+ }
+
+ public enum FrontFaceDirection {
+ Cw = 0x0900,
+ Ccw = 0x0901,
+ }
+
+ public enum MaterialFace {
+ FrontAndBack = 0x0408,
+ }
+
+ public enum PolygonMode {
+ Fill = 0x1B02,
+ Line = 0x1B01,
+ }
+
+ public enum ColorPointerType {
+ Float = 0x1406,
+ Short = 0x1402,
+ UnsignedShort = 0x1403,
+ UnsignedByte = 0x1401,
+ HalfFloat = 0x140B,
+ }
+
+ public enum NormalPointerType {
+ Byte = 0x1400,
+ Float = 0x1406,
+ Short = 0x1402,
+ UnsignedShort = 0x1403,
+ UnsignedByte = 0x1401,
+ HalfFloat = 0x140B,
+ }
+
+ public enum TexCoordPointerType {
+ Byte = 0x1400,
+ Float = 0x1406,
+ Short = 0x1402,
+ UnsignedShort = 0x1403,
+ UnsignedByte = 0x1401,
+ HalfFloat = 0x140B,
+ }
+
+ public enum BlendEquationMode {
+ FuncAdd = 0x8006,
+ Max = 0x8008, // ios MaxExt
+ Min = 0x8007, // ios MinExt
+ FuncReverseSubtract = 0x800B,
+ FuncSubtract = 0x800A,
+ }
+
+ public enum BlendingFactorSrc {
+ Zero = 0,
+ SrcColor = 0x0300,
+ OneMinusSrcColor = 0x0301,
+ SrcAlpha = 0x0302,
+ OneMinusSrcAlpha = 0x0303,
+ DstAlpha = 0x0304,
+ OneMinusDstAlpha = 0x0305,
+ DstColor = 0x0306,
+ OneMinusDstColor = 0x0307,
+ SrcAlphaSaturate = 0x0308,
+ ConstantColor = 0x8001,
+ OneMinusConstantColor = 0x8002,
+ ConstantAlpha = 0x8003,
+ OneMinusConstantAlpha = 0x8004,
+ One = 1,
+ }
+
+ public enum BlendingFactorDest {
+ Zero = 0,
+ SrcColor = 0x0300,
+ OneMinusSrcColor = 0x0301,
+ SrcAlpha = 0x0302,
+ OneMinusSrcAlpha = 0x0303,
+ DstAlpha = 0x0304,
+ OneMinusDstAlpha = 0x0305,
+ DstColor = 0X0306,
+ OneMinusDstColor = 0x0307,
+ SrcAlphaSaturate = 0x0308,
+ ConstantColor = 0x8001,
+ OneMinusConstantColor = 0x8002,
+ ConstantAlpha = 0x8003,
+ OneMinusConstantAlpha = 0x8004,
+ One = 1,
+ }
+
+ public enum DepthFunction {
+ Always = 0x0207,
+ Equal = 0x0202,
+ Greater = 0x0204,
+ Gequal = 0x0206,
+ Less = 0x0201,
+ Lequal = 0x0203,
+ Never = 0x0200,
+ Notequal = 0x0205,
+ }
+
+ public enum GetPName : int {
+ MaxTextureImageUnits = 0x8872,
+ MaxVertexAttribs = 0x8869,
+ MaxTextureSize = 0x0D33,
+ MaxDrawBuffers = 0x8824,
+ TextureBinding2D = 0x8069,
+ MaxTextureMaxAnisotropyExt = 0x84FF,
+ MaxSamples = 0x8D57,
+ }
+
+ public enum StringName {
+ Extensions = 0x1F03,
+ Version = 0x1F02,
+ }
+
+ public enum FramebufferAttachment {
+ ColorAttachment0 = 0x8CE0,
+ ColorAttachment0Ext = 0x8CE0,
+ DepthAttachment = 0x8D00,
+ StencilAttachment = 0x8D20,
+ }
+
+ public enum GLPrimitiveType {
+ Lines = 0x0001,
+ LineStrip = 0x0003,
+ Triangles = 0x0004,
+ TriangleStrip = 0x0005,
+ }
+
+ [Flags]
+ public enum ClearBufferMask
+ {
+ DepthBufferBit = 0x00000100,
+ StencilBufferBit = 0x00000400,
+ ColorBufferBit = 0x00004000,
+ }
+
+ public enum ErrorCode {
+ NoError = 0,
+ }
+
+ public enum TextureUnit {
+ Texture0 = 0x84C0,
+ }
+
+ public enum TextureTarget {
+ Texture2D = 0x0DE1,
+ Texture3D = 0x806F,
+ TextureCubeMap = 0x8513,
+ TextureCubeMapPositiveX = 0x8515,
+ TextureCubeMapPositiveY = 0x8517,
+ TextureCubeMapPositiveZ = 0x8519,
+ TextureCubeMapNegativeX = 0x8516,
+ TextureCubeMapNegativeY = 0x8518,
+ TextureCubeMapNegativeZ = 0x851A,
+ }
+
+ public enum PixelInternalFormat {
+ Rgba = 0x1908,
+ Rgb = 0x1907,
+ Rgba4 = 0x8056,
+ Luminance = 0x1909,
+ CompressedRgbS3tcDxt1Ext = 0x83F0,
+ CompressedSrgbS3tcDxt1Ext = 0x8C4C,
+ CompressedRgbaS3tcDxt1Ext = 0x83F1,
+ CompressedRgbaS3tcDxt3Ext = 0x83F2,
+ CompressedSrgbAlphaS3tcDxt3Ext = 0x8C4E,
+ CompressedRgbaS3tcDxt5Ext = 0x83F3,
+ CompressedSrgbAlphaS3tcDxt5Ext = 0x8C4F,
+ R32f = 0x822E,
+ Rg16f = 0x822F,
+ Rgba16f = 0x881A,
+ R16f = 0x822D,
+ Rg32f = 0x8230,
+ Rgba32f = 0x8814,
+ Rg8i = 0x8237,
+ Rgba8i = 0x8D8E,
+ Rg16ui = 0x823A,
+ Rgba16ui = 0x8D76,
+ Rgb10A2ui = 0x906F,
+ // PVRTC
+ CompressedRgbPvrtc2Bppv1Img = 0x8C01,
+ CompressedRgbPvrtc4Bppv1Img = 0x8C00,
+ CompressedRgbaPvrtc2Bppv1Img = 0x8C03,
+ CompressedRgbaPvrtc4Bppv1Img = 0x8C02,
+ // ATITC
+ AtcRgbaExplicitAlphaAmd = 0x8C93,
+ AtcRgbaInterpolatedAlphaAmd = 0x87EE,
+ // DXT
+
+ }
+
+ public enum PixelFormat {
+ Rgba = 0x1908,
+ Rgb = 0x1907,
+ Luminance = 0x1909,
+ CompressedTextureFormats = 0x86A3,
+ Red = 0x1903,
+ Rg = 0x8227,
+ }
+
+ public enum PixelType {
+ UnsignedByte = 0x1401,
+ UnsignedShort565 = 0x8363,
+ UnsignedShort4444 = 0x8033,
+ UnsignedShort5551 = 0x8034,
+ Float = 0x1406,
+ HalfFloat = 0x140B,
+ Byte = 0x1400,
+ UnsignedShort = 0x1403,
+ UnsignedInt1010102 = 0x8036,
+ }
+
+ public enum PixelStoreParameter {
+ UnpackAlignment = 0x0CF5,
+ }
+
+ public enum GLStencilFunction {
+ Always = 0x0207,
+ Equal = 0x0202,
+ Greater = 0x0204,
+ Gequal = 0x0206,
+ Less = 0x0201,
+ Lequal = 0x0203,
+ Never = 0x0200,
+ Notequal = 0x0205,
+ }
+
+ public enum StencilOp {
+ Keep = 0x1E00,
+ DecrWrap = 0x8508,
+ Decr = 0x1E03,
+ Incr = 0x1E02,
+ IncrWrap = 0x8507,
+ Invert = 0x150A,
+ Replace = 0x1E01,
+ Zero = 0,
+ }
+
+ public enum TextureParameterName {
+ TextureMaxAnisotropyExt = 0x84FE,
+ TextureMaxLevel = 0x813D,
+ TextureMinFilter = 0x2801,
+ TextureMagFilter = 0x2800,
+ TextureWrapS = 0x2802,
+ TextureWrapT = 0x2803,
+ TextureBorderColor = 0x1004,
+ TextureLodBias = 0x8501,
+ TextureCompareMode = 0x884C,
+ TextureCompareFunc = 0x884D,
+ GenerateMipmap = 0x8191,
+ }
+
+ public enum Bool {
+ True = 1,
+ False = 0,
+ }
+
+ public enum TextureMinFilter {
+ LinearMipmapNearest = 0x2701,
+ NearestMipmapLinear = 0x2702,
+ LinearMipmapLinear = 0x2703,
+ Linear = 0x2601,
+ NearestMipmapNearest = 0x2700,
+ Nearest = 0x2600,
+ }
+
+ public enum TextureMagFilter {
+ Linear = 0x2601,
+ Nearest = 0x2600,
+ }
+
+ public enum TextureCompareMode {
+ CompareRefToTexture = 0x884E,
+ None = 0,
+ }
+
+ public enum TextureWrapMode {
+ ClampToEdge = 0x812F,
+ Repeat = 0x2901,
+ MirroredRepeat = 0x8370,
+ //GLES
+ ClampToBorder = 0x812D,
+ }
+
+ public partial class ColorFormat {
+ public ColorFormat(int r, int g, int b, int a)
+ {
+ R = r;
+ G = g;
+ B = b;
+ A = a;
+ }
+
+ public int R { get; private set; }
+ public int G { get; private set; }
+ public int B { get; private set; }
+ public int A { get; private set; }
+ }
+
+ [CLSCompliant (false)]
+ public partial class GL
+ {
+ public enum RenderApi
+ {
+ ES = 12448,
+ GL = 12450,
+ }
+
+ public static RenderApi BoundApi = RenderApi.GL;
+
+ public partial class Ext
+ {
+ [System.Security.SuppressUnmanagedCodeSecurity ()]
+ [MonoNativeFunctionWrapper]
+ public delegate void GenRenderbuffersDelegate (int count, out int buffer);
+ public static GenRenderbuffersDelegate GenRenderbuffers;
+
+ [System.Security.SuppressUnmanagedCodeSecurity ()]
+ [MonoNativeFunctionWrapper]
+ public delegate void BindRenderbufferDelegate (RenderbufferTarget target, int buffer);
+ public static BindRenderbufferDelegate BindRenderbuffer;
+
+ [System.Security.SuppressUnmanagedCodeSecurity ()]
+ [MonoNativeFunctionWrapper]
+ public delegate void DeleteRenderbuffersDelegate (int count, ref int buffer);
+ public static DeleteRenderbuffersDelegate DeleteRenderbuffers;
+
+ [System.Security.SuppressUnmanagedCodeSecurity ()]
+ [MonoNativeFunctionWrapper]
+ public delegate void RenderbufferStorageMultisampleDelegate (RenderbufferTarget target, int sampleCount,
+ RenderbufferStorage storage, int width, int height);
+ public static RenderbufferStorageMultisampleDelegate RenderbufferStorageMultisample;
+
+ [System.Security.SuppressUnmanagedCodeSecurity ()]
+ [MonoNativeFunctionWrapper]
+ public delegate void GenFramebuffersDelegate (int count, out int buffer);
+ public static GenFramebuffersDelegate GenFramebuffers;
+
+ [System.Security.SuppressUnmanagedCodeSecurity ()]
+ [MonoNativeFunctionWrapper]
+ public delegate void BindFramebufferDelegate (FramebufferTarget target, int buffer);
+ public static BindFramebufferDelegate BindFramebuffer;
+
+ [System.Security.SuppressUnmanagedCodeSecurity ()]
+ [MonoNativeFunctionWrapper]
+ public delegate void DeleteFramebuffersDelegate (int count, ref int buffer);
+ public static DeleteFramebuffersDelegate DeleteFramebuffers;
+
+ [System.Security.SuppressUnmanagedCodeSecurity ()]
+ [MonoNativeFunctionWrapper]
+ public delegate void FramebufferTexture2DDelegate (FramebufferTarget target, FramebufferAttachment attachement,
+ TextureTarget textureTarget, int texture, int level);
+ public static FramebufferTexture2DDelegate FramebufferTexture2D;
+
+ [System.Security.SuppressUnmanagedCodeSecurity ()]
+ [MonoNativeFunctionWrapper]
+ public delegate void FramebufferRenderbufferDelegate (FramebufferTarget target, FramebufferAttachment attachement,
+ RenderbufferTarget renderBufferTarget, int buffer);
+ public static FramebufferRenderbufferDelegate FramebufferRenderbuffer;
+
+ [System.Security.SuppressUnmanagedCodeSecurity ()]
+ [MonoNativeFunctionWrapper]
+ public delegate void GenerateMipmapDelegate (GenerateMipmapTarget target);
+ public static GenerateMipmapDelegate GenerateMipmap;
+
+ [System.Security.SuppressUnmanagedCodeSecurity ()]
+ [MonoNativeFunctionWrapper]
+ public delegate void BlitFramebufferDelegate (int srcX0,
+ int srcY0,
+ int srcX1,
+ int srcY1,
+ int dstX0,
+ int dstY0,
+ int dstX1,
+ int dstY1,
+ ClearBufferMask mask,
+ BlitFramebufferFilter filter);
+ public static BlitFramebufferDelegate BlitFramebuffer;
+
+ [System.Security.SuppressUnmanagedCodeSecurity ()]
+ [MonoNativeFunctionWrapper]
+ public delegate FramebufferErrorCode CheckFramebufferStatusDelegate (FramebufferTarget target);
+ public static CheckFramebufferStatusDelegate CheckFramebufferStatus;
+ }
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void EnableVertexAttribArrayDelegate (int attrib);
+ public static EnableVertexAttribArrayDelegate EnableVertexAttribArray;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void DisableVertexAttribArrayDelegte (int attrib);
+ public static DisableVertexAttribArrayDelegte DisableVertexAttribArray;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void MakeCurrentDelegate(IntPtr window);
+ public static MakeCurrentDelegate MakeCurrent;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public unsafe delegate void GetIntegerDelegate(int param, [Out] int* data);
+ public static GetIntegerDelegate GetIntegerv;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ internal delegate IntPtr GetStringDelegate(StringName param);
+ internal static GetStringDelegate GetStringInternal;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void ClearDepthDelegate (float depth);
+ public static ClearDepthDelegate ClearDepth;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void DepthRangeDelegate (double min, double max);
+ public static DepthRangeDelegate DepthRange;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void ClearDelegate(ClearBufferMask mask);
+ public static ClearDelegate Clear;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void ClearColorDelegate(float red,float green,float blue,float alpha);
+ public static ClearColorDelegate ClearColor;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void ClearStencilDelegate(int stencil);
+ public static ClearStencilDelegate ClearStencil;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void ViewportDelegate(int x, int y, int w, int h);
+ public static ViewportDelegate Viewport;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate ErrorCode GetErrorDelegate();
+ public static GetErrorDelegate GetError;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void FlushDelegate();
+ public static FlushDelegate Flush;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void GenTexturesDelegte (int count, [Out] out int id);
+ public static GenTexturesDelegte GenTextures;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void BindTextureDelegate(TextureTarget target, int id);
+ public static BindTextureDelegate BindTexture;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate int EnableDelegate (EnableCap cap);
+ public static EnableDelegate Enable;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate int DisableDelegate (EnableCap cap);
+ public static DisableDelegate Disable;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void CullFaceDelegate(CullFaceMode mode);
+ public static CullFaceDelegate CullFace;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void FrontFaceDelegate(FrontFaceDirection direction);
+ public static FrontFaceDelegate FrontFace;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void PolygonModeDelegate (MaterialFace face, PolygonMode mode);
+ public static PolygonModeDelegate PolygonMode;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void PolygonOffsetDelegate (float slopeScaleDepthBias, float depthbias);
+ public static PolygonOffsetDelegate PolygonOffset;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void DrawBuffersDelegate (int count, DrawBuffersEnum[] buffers);
+ public static DrawBuffersDelegate DrawBuffers;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void UseProgramDelegate(int program);
+ public static UseProgramDelegate UseProgram;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public unsafe delegate void Uniform4fvDelegate (int location, int size, float* values);
+ public static Uniform4fvDelegate Uniform4fv;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void Uniform1iDelegate (int location, int value);
+ public static Uniform1iDelegate Uniform1i;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void ScissorDelegate(int x, int y, int width, int height);
+ public static ScissorDelegate Scissor;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void BindBufferDelegate(BufferTarget target, uint buffer);
+ public static BindBufferDelegate BindBuffer;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void DrawElementsDelegate (GLPrimitiveType primitiveType, int count, DrawElementsType elementType, IntPtr offset);
+ public static DrawElementsDelegate DrawElements;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void DrawArraysDelegate (GLPrimitiveType primitiveType, int offset, int count);
+ public static DrawArraysDelegate DrawArrays;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void GenRenderbuffersDelegate(int count, [Out] out int buffer);
+ public static GenRenderbuffersDelegate GenRenderbuffers;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void BindRenderbufferDelegate (RenderbufferTarget target, int buffer);
+ public static BindRenderbufferDelegate BindRenderbuffer;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void DeleteRenderbuffersDelegate(int count, [In] [Out] ref int buffer);
+ public static DeleteRenderbuffersDelegate DeleteRenderbuffers;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void RenderbufferStorageMultisampleDelegate(RenderbufferTarget target, int sampleCount,
+ RenderbufferStorage storage, int width, int height);
+ public static RenderbufferStorageMultisampleDelegate RenderbufferStorageMultisample;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void GenFramebuffersDelegate(int count, out int buffer);
+ public static GenFramebuffersDelegate GenFramebuffers;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void BindFramebufferDelegate (FramebufferTarget target, int buffer);
+ public static BindFramebufferDelegate BindFramebuffer;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void DeleteFramebuffersDelegate(int count, ref int buffer);
+ public static DeleteFramebuffersDelegate DeleteFramebuffers;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void FramebufferTexture2DDelegate(FramebufferTarget target, FramebufferAttachment attachement,
+ TextureTarget textureTarget, int texture, int level );
+ public static FramebufferTexture2DDelegate FramebufferTexture2D;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void FramebufferRenderbufferDelegate (FramebufferTarget target, FramebufferAttachment attachement,
+ RenderbufferTarget renderBufferTarget, int buffer);
+ public static FramebufferRenderbufferDelegate FramebufferRenderbuffer;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void GenerateMipmapDelegate (GenerateMipmapTarget target);
+ public static GenerateMipmapDelegate GenerateMipmap;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void ReadBufferDelegate (ReadBufferMode buffer);
+ public static ReadBufferDelegate ReadBuffer;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void DrawBufferDelegate (DrawBufferMode buffer);
+ public static DrawBufferDelegate DrawBuffer;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void BlitFramebufferDelegate (int srcX0,
+ int srcY0,
+ int srcX1,
+ int srcY1,
+ int dstX0,
+ int dstY0,
+ int dstX1,
+ int dstY1,
+ ClearBufferMask mask,
+ BlitFramebufferFilter filter);
+ public static BlitFramebufferDelegate BlitFramebuffer;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate FramebufferErrorCode CheckFramebufferStatusDelegate (FramebufferTarget target);
+ public static CheckFramebufferStatusDelegate CheckFramebufferStatus;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void TexParameterFloatDelegate (TextureTarget target, TextureParameterName name, float value);
+ public static TexParameterFloatDelegate TexParameterf;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public unsafe delegate void TexParameterFloatArrayDelegate (TextureTarget target, TextureParameterName name, float* values);
+ public static TexParameterFloatArrayDelegate TexParameterfv;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void TexParameterIntDelegate (TextureTarget target, TextureParameterName name, int value);
+ public static TexParameterIntDelegate TexParameteri;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void GenQueriesDelegate (int count, [Out] out int queryId);
+ public static GenQueriesDelegate GenQueries;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void BeginQueryDelegate (QueryTarget target, int queryId);
+ public static BeginQueryDelegate BeginQuery;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void EndQueryDelegate (QueryTarget target);
+ public static EndQueryDelegate EndQuery;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void GetQueryObjectDelegate(int queryId, GetQueryObjectParam getparam, [Out] out int ready);
+ public static GetQueryObjectDelegate GetQueryObject;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void DeleteQueriesDelegate(int count, [In] [Out] ref int queryId);
+ public static DeleteQueriesDelegate DeleteQueries;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void ActiveTextureDelegate (TextureUnit textureUnit);
+ public static ActiveTextureDelegate ActiveTexture;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate int CreateShaderDelegate (ShaderType type);
+ public static CreateShaderDelegate CreateShader;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public unsafe delegate void ShaderSourceDelegate(uint shaderId, int count, IntPtr code, int* length);
+ public static ShaderSourceDelegate ShaderSourceInternal;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void CompileShaderDelegate (int shaderId);
+ public static CompileShaderDelegate CompileShader;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public unsafe delegate void GetShaderDelegate(uint shaderId, uint parameter, int* value);
+ public static GetShaderDelegate GetShaderiv;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void GetShaderInfoLogDelegate(uint shader, int bufSize, IntPtr length, StringBuilder infoLog);
+ public static GetShaderInfoLogDelegate GetShaderInfoLogInternal;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate bool IsShaderDelegate(int shaderId);
+ public static IsShaderDelegate IsShader;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void DeleteShaderDelegate (int shaderId);
+ public static DeleteShaderDelegate DeleteShader;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate int GetAttribLocationDelegate(int programId, string name);
+ public static GetAttribLocationDelegate GetAttribLocation;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate int GetUniformLocationDelegate(int programId, string name);
+ public static GetUniformLocationDelegate GetUniformLocation;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate bool IsProgramDelegate (int programId);
+ public static IsProgramDelegate IsProgram;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void DeleteProgramDelegate (int programId);
+ public static DeleteProgramDelegate DeleteProgram;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate int CreateProgramDelegate();
+ public static CreateProgramDelegate CreateProgram;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void AttachShaderDelegate (int programId, int shaderId);
+ public static AttachShaderDelegate AttachShader;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void LinkProgramDelegate(int programId);
+ public static LinkProgramDelegate LinkProgram;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public unsafe delegate void GetProgramDelegate(int programId, uint name, int* linked);
+ public static GetProgramDelegate GetProgramiv;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void GetProgramInfoLogDelegate(uint program, int bufSize, IntPtr length, StringBuilder infoLog);
+ public static GetProgramInfoLogDelegate GetProgramInfoLogInternal;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void DetachShaderDelegate(int programId, int shaderId);
+ public static DetachShaderDelegate DetachShader;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void BlendColorDelegate(float r, float g, float b, float a);
+ public static BlendColorDelegate BlendColor;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void BlendEquationSeparateDelegate(BlendEquationMode colorMode, BlendEquationMode alphaMode);
+ public static BlendEquationSeparateDelegate BlendEquationSeparate;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void BlendFuncSeparateDelegate(BlendingFactorSrc colorSrc, BlendingFactorDest colorDst,
+ BlendingFactorSrc alphaSrc, BlendingFactorDest alphaDst);
+ public static BlendFuncSeparateDelegate BlendFuncSeparate;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void ColorMaskDelegate(bool r, bool g, bool b, bool a);
+ public static ColorMaskDelegate ColorMask;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void DepthFuncDelegate(DepthFunction function);
+ public static DepthFuncDelegate DepthFunc;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void DepthMaskDelegate (bool enabled);
+ public static DepthMaskDelegate DepthMask;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void StencilFuncSeparateDelegate (StencilFace face, GLStencilFunction function, int referenceStencil, int mask);
+ public static StencilFuncSeparateDelegate StencilFuncSeparate;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void StencilOpSeparateDelegate(StencilFace face, StencilOp stencilfail, StencilOp depthFail, StencilOp pass);
+ public static StencilOpSeparateDelegate StencilOpSeparate;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void StencilFuncDelegate(GLStencilFunction function, int referenceStencil, int mask);
+ public static StencilFuncDelegate StencilFunc;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void StencilOpDelegate (StencilOp stencilfail, StencilOp depthFail, StencilOp pass);
+ public static StencilOpDelegate StencilOp;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void StencilMaskDelegate(int mask);
+ public static StencilMaskDelegate StencilMask;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void CompressedTexImage2DDelegate(TextureTarget target, int level, PixelInternalFormat internalFormat,
+ int width, int height, int border, int size, IntPtr data);
+ public static CompressedTexImage2DDelegate CompressedTexImage2D;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void TexImage2DDelegate(TextureTarget target,int level, PixelInternalFormat internalFormat,
+ int width, int height, int border,PixelFormat format, PixelType pixelType, IntPtr data);
+ public static TexImage2DDelegate TexImage2D;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void CompressedTexSubImage2DDelegate (TextureTarget target, int level,
+ int x, int y, int width, int height, PixelInternalFormat format, int size, IntPtr data);
+ public static CompressedTexSubImage2DDelegate CompressedTexSubImage2D;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void TexSubImage2DDelegate (TextureTarget target, int level,
+ int x, int y, int width, int height, PixelFormat format, PixelType pixelType, IntPtr data);
+ public static TexSubImage2DDelegate TexSubImage2D;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void PixelStoreDelegate (PixelStoreParameter parameter, int size);
+ public static PixelStoreDelegate PixelStore;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void FinishDelegate();
+ public static FinishDelegate Finish;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ internal delegate void GetTexImageDelegate(TextureTarget target, int level, PixelFormat format, PixelType type, [Out] IntPtr pixels);
+ internal static GetTexImageDelegate GetTexImageInternal;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ internal delegate void GetCompressedTexImageDelegate(TextureTarget target, int level, [Out] IntPtr pixels);
+ internal static GetCompressedTexImageDelegate GetCompressedTexImageInternal;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void TexImage3DDelegate(TextureTarget target,int level, PixelInternalFormat internalFormat,
+ int width, int height, int depth, int border,PixelFormat format, PixelType pixelType, IntPtr data);
+ public static TexImage3DDelegate TexImage3D;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void TexSubImage3DDelegate (TextureTarget target, int level,
+ int x, int y, int z, int width, int height, int depth, PixelFormat format, PixelType pixelType, IntPtr data);
+ public static TexSubImage3DDelegate TexSubImage3D;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void DeleteTexturesDelegate(int count, ref int id);
+ public static DeleteTexturesDelegate DeleteTextures;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void GenBuffersDelegate(int count, out uint buffer);
+ public static GenBuffersDelegate GenBuffers;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void BufferDataDelegate(BufferTarget target, IntPtr size, IntPtr n, BufferUsageHint usage);
+ public static BufferDataDelegate BufferData;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate IntPtr MapBufferDelegate(BufferTarget target, BufferAccess access);
+ public static MapBufferDelegate MapBuffer;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void UnmapBufferDelegate(BufferTarget target);
+ public static UnmapBufferDelegate UnmapBuffer;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void BufferSubDataDelegate (BufferTarget target, IntPtr offset, IntPtr size, IntPtr data);
+ public static BufferSubDataDelegate BufferSubData;
+
+ [CLSCompliant (false)]
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void DeleteBuffersDelegate (int count, [In] [Out] ref uint buffer);
+ [CLSCompliant (false)]
+ public static DeleteBuffersDelegate DeleteBuffers;
+
+ [System.Security.SuppressUnmanagedCodeSecurity()]
+ [MonoNativeFunctionWrapper]
+ public delegate void VertexAttribPointerDelegate(int location, int elementCount, VertexAttribPointerType type, bool normalize,
+ int stride, IntPtr data);
+ public static VertexAttribPointerDelegate VertexAttribPointer;
+
+ public static int SwapInterval { get; set; }
+
+ public static void LoadEntryPoints()
+ {
+ LoadPlatformEntryPoints ();
+
+ if (Viewport == null)
+ Viewport = (ViewportDelegate)LoadEntryPoint("glViewport");
+ if (Scissor == null)
+ Scissor = (ScissorDelegate)LoadEntryPoint("glScissor");
+ GetError = (GetErrorDelegate)LoadEntryPoint("glGetError");
+
+ TexParameterf = (TexParameterFloatDelegate)LoadEntryPoint("glTexParameterf");
+ TexParameterfv = (TexParameterFloatArrayDelegate)LoadEntryPoint("glTexParameterfv");
+ TexParameteri = (TexParameterIntDelegate)LoadEntryPoint("glTexParameteri");
+
+ EnableVertexAttribArray = (EnableVertexAttribArrayDelegate)LoadEntryPoint("glEnableVertexAttribArray");
+ DisableVertexAttribArray = (DisableVertexAttribArrayDelegte)LoadEntryPoint("glDisableVertexAttribArray");
+ //MakeCurrent = (MakeCurrentDelegate)LoadEntryPoint("glMakeCurrent");
+ GetIntegerv = (GetIntegerDelegate)LoadEntryPoint("glGetIntegerv");
+ GetStringInternal = (GetStringDelegate)LoadEntryPoint("glGetString");
+ ClearDepth = (ClearDepthDelegate)LoadEntryPoint("glClearDepth");
+ DepthRange = (DepthRangeDelegate)LoadEntryPoint("glDepthRange");
+ Clear = (ClearDelegate)LoadEntryPoint("glClear");
+ ClearColor = (ClearColorDelegate)LoadEntryPoint("glClearColor");
+ ClearStencil = (ClearStencilDelegate)LoadEntryPoint("glClearStencil");
+ Flush = (FlushDelegate)LoadEntryPoint("glFlush");
+ GenTextures = (GenTexturesDelegte)LoadEntryPoint("glGenTextures");
+ BindTexture = (BindTextureDelegate)LoadEntryPoint("glBindTexture");
+
+ Enable = (EnableDelegate)LoadEntryPoint("glEnable");
+ Disable = (DisableDelegate)LoadEntryPoint("glDisable");
+ CullFace = (CullFaceDelegate)LoadEntryPoint("glCullFace");
+ FrontFace = (FrontFaceDelegate)LoadEntryPoint("glFrontFace");
+ PolygonMode = (PolygonModeDelegate)LoadEntryPoint("glPolygonMode");
+ PolygonOffset = (PolygonOffsetDelegate)LoadEntryPoint("glPolygonOffset");
+
+ BindBuffer = (BindBufferDelegate)LoadEntryPoint("glBindBuffer");
+ DrawBuffers = (DrawBuffersDelegate)LoadEntryPoint("glDrawBuffers");
+ DrawElements = (DrawElementsDelegate)LoadEntryPoint("glDrawElements");
+ DrawArrays = (DrawArraysDelegate)LoadEntryPoint("glDrawArrays");
+ Uniform1i = (Uniform1iDelegate)LoadEntryPoint("glUniform1i");
+ Uniform4fv = (Uniform4fvDelegate)LoadEntryPoint("glUniform4fv");
+
+ ReadBuffer = (ReadBufferDelegate)LoadEntryPoint("glReadBuffer");
+ DrawBuffer = (DrawBufferDelegate)LoadEntryPoint("glDrawBuffer");
+
+ // these are only in GL 3.0 or ARB_framebuffer_object, if they fail to load (and only if they do), we need to check if EXT_framebuffer_object is present as a fallback
+ try
+ {
+ GenRenderbuffers = (GenRenderbuffersDelegate)LoadEntryPoint("glGenRenderbuffers");
+ BindRenderbuffer = (BindRenderbufferDelegate)LoadEntryPoint("glBindRenderbuffer");
+ DeleteRenderbuffers = (DeleteRenderbuffersDelegate)LoadEntryPoint("glDeleteRenderbuffers");
+ GenFramebuffers = (GenFramebuffersDelegate)LoadEntryPoint("glGenFramebuffers");
+ BindFramebuffer = (BindFramebufferDelegate)LoadEntryPoint("glBindFramebuffer");
+ DeleteFramebuffers = (DeleteFramebuffersDelegate)LoadEntryPoint