diff --git a/Blarg.GameFramework.SDL2/Blarg.GameFramework.SDL2.csproj b/Blarg.GameFramework.SDL2/Blarg.GameFramework.SDL2.csproj
index 25a7532..5db992a 100644
--- a/Blarg.GameFramework.SDL2/Blarg.GameFramework.SDL2.csproj
+++ b/Blarg.GameFramework.SDL2/Blarg.GameFramework.SDL2.csproj
@@ -49,6 +49,10 @@
+
+
+
+
diff --git a/Blarg.GameFramework.SDL2/CurrentOS.cs b/Blarg.GameFramework.SDL2/CurrentOS.cs
new file mode 100644
index 0000000..a01b34d
--- /dev/null
+++ b/Blarg.GameFramework.SDL2/CurrentOS.cs
@@ -0,0 +1,152 @@
+//
+// CurrentOS Class by blez
+// http://blez.wordpress.com/2012/09/17/determine-os-with-netmono/
+// Detects the current OS (Windows, Linux, MacOS)
+//
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+using System.IO;
+
+namespace Blarg.GameFramework
+{
+ public static class CurrentOS
+ {
+ public static bool IsWindows { get; private set; }
+ public static bool IsUnix { get; private set; }
+ public static bool IsMac { get; private set; }
+ public static bool IsLinux { get; private set; }
+ public static bool IsUnknown { get; private set; }
+ public static bool Is32bit { get; private set; }
+ public static bool Is64bit { get; private set; }
+ public static bool Is64BitProcess { get { return (IntPtr.Size == 8); } }
+ public static bool Is32BitProcess { get { return (IntPtr.Size == 4); } }
+ public static string Name { get; private set; }
+
+ [DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool IsWow64Process([In] IntPtr hProcess, [Out] out bool wow64Process);
+
+ private static bool Is64bitWindows
+ {
+ get
+ {
+ if ((Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor >= 1) || Environment.OSVersion.Version.Major >= 6)
+ {
+ using (Process p = Process.GetCurrentProcess())
+ {
+ bool retVal;
+ if (!IsWow64Process(p.Handle, out retVal))
+ return false;
+ return retVal;
+ }
+ }
+ else
+ return false;
+ }
+ }
+
+ static CurrentOS()
+ {
+ IsWindows = Path.DirectorySeparatorChar == '\\';
+ if (IsWindows)
+ {
+ Name = Environment.OSVersion.VersionString;
+
+ Name = Name.Replace("Microsoft ", "");
+ Name = Name.Replace(" ", " ");
+ Name = Name.Replace(" )", ")");
+ Name = Name.Trim();
+
+ Name = Name.Replace("NT 6.2", "8 %bit 6.2");
+ Name = Name.Replace("NT 6.1", "7 %bit 6.1");
+ Name = Name.Replace("NT 6.0", "Vista %bit 6.0");
+ Name = Name.Replace("NT 5.", "XP %bit 5.");
+ Name = Name.Replace("%bit", (Is64bitWindows ? "64bit" : "32bit"));
+
+ if (Is64bitWindows)
+ Is64bit = true;
+ else
+ Is32bit = true;
+ }
+ else
+ {
+ string UnixName = ReadProcessOutput("uname");
+ if (UnixName.Contains("Darwin"))
+ {
+ IsUnix = true;
+ IsMac = true;
+
+ Name = "MacOS X " + ReadProcessOutput("sw_vers", "-productVersion");
+ Name = Name.Trim();
+
+ string machine = ReadProcessOutput("uname", "-m");
+ if (machine.Contains("x86_64"))
+ Is64bit = true;
+ else
+ Is32bit = true;
+
+ Name += " " + (Is32bit ? "32bit" : "64bit");
+ }
+ else if (UnixName.Contains("Linux"))
+ {
+ IsUnix = true;
+ IsLinux = true;
+
+ Name = ReadProcessOutput("lsb_release", "-d");
+ Name = Name.Substring(Name.IndexOf(":") + 1);
+ Name = Name.Trim();
+
+ string machine = ReadProcessOutput("uname", "-m");
+ if (machine.Contains("x86_64"))
+ Is64bit = true;
+ else
+ Is32bit = true;
+
+ Name += " " + (Is32bit ? "32bit" : "64bit");
+ }
+ else if (UnixName != "")
+ IsUnix = true;
+ else
+ IsUnknown = true;
+ }
+ }
+
+ private static string ReadProcessOutput(string name)
+ {
+ return ReadProcessOutput(name, null);
+ }
+
+ private static string ReadProcessOutput(string name, string args)
+ {
+ try
+ {
+ Process p = new Process();
+ p.StartInfo.UseShellExecute = false;
+ p.StartInfo.RedirectStandardOutput = true;
+ if (args != null && args != "")
+ p.StartInfo.Arguments = " " + args;
+ p.StartInfo.FileName = name;
+ p.Start();
+ // Do not wait for the child process to exit before
+ // reading to the end of its redirected stream.
+ // p.WaitForExit();
+ // Read the output stream first and then wait.
+ string output = p.StandardOutput.ReadToEnd();
+ p.WaitForExit();
+ if (output == null)
+ output = "";
+ output = output.Trim();
+ return output;
+ }
+ catch
+ {
+ return "";
+ }
+ }
+ }
+}
+
diff --git a/Blarg.GameFramework.SDL2/SDLConfiguration.cs b/Blarg.GameFramework.SDL2/SDLConfiguration.cs
new file mode 100644
index 0000000..412dac5
--- /dev/null
+++ b/Blarg.GameFramework.SDL2/SDLConfiguration.cs
@@ -0,0 +1,36 @@
+using System;
+
+namespace Blarg.GameFramework
+{
+ public class SDLConfiguration : IPlatformConfiguration
+ {
+ public string Title;
+ public int Width;
+ public int Height;
+ public bool Fullscreen;
+ public bool Resizeable;
+
+ public bool glDoubleBuffer;
+ public int glDepthBufferSize;
+ public int glRedSize;
+ public int glGreenSize;
+ public int glBlueSize;
+ public int glAlphaSize;
+
+ public SDLConfiguration()
+ {
+ Title = "SDL Application";
+ Width = 854;
+ Height = 480;
+ Fullscreen = false;
+ Resizeable = false;
+ glDoubleBuffer = true;
+ glDepthBufferSize = 24;
+ glRedSize = 8;
+ glGreenSize = 8;
+ glBlueSize = 8;
+ glAlphaSize = 8;
+ }
+ }
+}
+
diff --git a/Blarg.GameFramework.SDL2/SDLLooper.cs b/Blarg.GameFramework.SDL2/SDLLooper.cs
index 7c102d9..af77bc1 100644
--- a/Blarg.GameFramework.SDL2/SDLLooper.cs
+++ b/Blarg.GameFramework.SDL2/SDLLooper.cs
@@ -1,84 +1,578 @@
using System;
+using System.Diagnostics;
using SDL2;
+using Blarg.GameFramework.Graphics;
using Blarg.GameFramework.Input;
using Blarg.GameFramework.IO;
namespace Blarg.GameFramework
{
- public class SDLLooper : ILooper
+ public class SDLLooper : BaseLooper
{
+ const string LOOPER_TAG = "SDLLOOPER";
+
bool _isSDLinited;
IntPtr _window;
IntPtr _glContext;
SDLKeyboard _keyboard;
SDLMouse _mouse;
SDLFileSystem _filesystem;
+ SDLWindow _windowInfo;
+
+ bool _isWindowActive;
+ bool _isPaused;
+ bool _isQuitting;
+
+ int _fps;
+ float _frameTime;
+ int _rendersPerSecond;
+ int _updatesPerSecond;
+ int _renderTime;
+ int _updateTime;
+
+ int _targetUpdatesPerSecond;
+ int _ticksPerUpdate;
+ float _fixedUpdateInterval;
+ float _fixedRenderInterval;
+ int _maxFrameSkip = 10;
+
+ public override int FPS
+ {
+ get { return _fps; }
+ }
+
+ public override float FrameTime
+ {
+ get { return _frameTime; }
+ }
+
+ public override int RendersPerSecond
+ {
+ get { return _rendersPerSecond; }
+ }
+
+ public override int UpdatesPerSecond
+ {
+ get { return _updatesPerSecond; }
+ }
+
+ public override int RenderTime
+ {
+ get { return _renderTime; }
+ }
+
+ public override int UpdateTime
+ {
+ get { return _updateTime; }
+ }
public SDLLooper()
{
+ Platform.Services = new SDLPlatformServices();
+ _windowInfo = new SDLWindow();
+
+ SetUpdateFrequency(60);
}
- public void Run(IGameApp gameApp)
+ public override void Run(IGameApp gameApp, IPlatformConfiguration config)
{
if (gameApp == null)
throw new ArgumentNullException("gameApp");
+ GameApp = gameApp;
- Platform.Services.Logger.Info("Looper", "Running...");
+ if (config == null)
+ throw new ArgumentNullException("config");
+ if (!(config is SDLConfiguration))
+ throw new ArgumentException("Must pass a SDLConfiguration object.", "config");
+
+ Platform.Services.Logger.Info(LOOPER_TAG, "Running...");
+
+ SDLConfiguration sdlConfig = (SDLConfiguration)config;
+
+ Platform.Services.Logger.Info(LOOPER_TAG, "Received SDL configuration:");
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tTitle: {0}", sdlConfig.Title);
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tWidth: {0}", sdlConfig.Width);
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tHeight: {0}", sdlConfig.Height);
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tFullscreen: {0}", sdlConfig.Fullscreen);
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tResizeable: {0}", sdlConfig.Resizeable);
+ Platform.Services.Logger.Info(LOOPER_TAG, "GL Doublebuffer: {0}", sdlConfig.glDoubleBuffer);
+ Platform.Services.Logger.Info(LOOPER_TAG, "GL Depth Buffer Size: {0}", sdlConfig.glDepthBufferSize);
+ Platform.Services.Logger.Info(LOOPER_TAG, "GL Red Size: {0}", sdlConfig.glRedSize);
+ Platform.Services.Logger.Info(LOOPER_TAG, "GL Green Size: {0}", sdlConfig.glGreenSize);
+ Platform.Services.Logger.Info(LOOPER_TAG, "GL Blue Size: {0}", sdlConfig.glBlueSize);
+ Platform.Services.Logger.Info(LOOPER_TAG, "GL Alpha Size: {0}", sdlConfig.glAlphaSize);
if (!InitSDL())
{
- Platform.Services.Logger.Error("Looper", "SDL initialization failed. Aborting.");
+ Platform.Services.Logger.Error(LOOPER_TAG, "SDL initialization failed. Aborting.");
return;
}
+
+ if (!InitSDLWindow(sdlConfig))
+ {
+ Platform.Services.Logger.Error(LOOPER_TAG, "SDL window creation failed. Aborting.");
+ return;
+ }
+
+ (Platform.Services as SDLPlatformServices).Keyboard = _keyboard;
+ (Platform.Services as SDLPlatformServices).Mouse = _mouse;
+ (Platform.Services as SDLPlatformServices).FileSystem = _filesystem;
+ (Platform.Services as SDLPlatformServices).GL = new PortableGL.SDL.SDLGL20();
+
+ OnNewContext();
+ OnResize(ScreenOrientation.Rotation0, _windowInfo.ClientRectangle);
+ OnLoad();
+
+ Platform.Services.Logger.Info(LOOPER_TAG, "Main loop starting.");
+ MainLoop();
+ Platform.Services.Logger.Info(LOOPER_TAG, "Main loop finished.");
+
+ OnUnload();
+ OnLostContext();
+
+ GameApp.Dispose();
+ GameApp = null;
+
+ ReleaseSDL();
}
private bool InitSDL()
{
- Platform.Services.Logger.Info("Looper", "SDL initialization starting.");
+ Platform.Services.Logger.Info(LOOPER_TAG, "SDL initialization starting.");
SDL.SDL_version sdlVersion;
SDL.SDL_VERSION(out sdlVersion);
- Platform.Services.Logger.Info("Looper", "SDL Runtime Version: {0}.{1}.{2}", sdlVersion.major, sdlVersion.minor, sdlVersion.patch);
- Platform.Services.Logger.Info("Looper", "SDL Linked Version: {0}.{1}.{2}", SDL.SDL_MAJOR_VERSION, SDL.SDL_MINOR_VERSION, SDL.SDL_PATCHLEVEL);
+ Platform.Services.Logger.Info(LOOPER_TAG, "SDL Runtime Version: {0}.{1}.{2}", sdlVersion.major, sdlVersion.minor, sdlVersion.patch);
+ Platform.Services.Logger.Info(LOOPER_TAG, "SDL Linked Version: {0}.{1}.{2}", SDL.SDL_MAJOR_VERSION, SDL.SDL_MINOR_VERSION, SDL.SDL_PATCHLEVEL);
if (SDL.SDL_Init(SDL.SDL_INIT_VIDEO | SDL.SDL_INIT_AUDIO | SDL.SDL_INIT_GAMECONTROLLER | SDL.SDL_INIT_JOYSTICK | SDL.SDL_INIT_TIMER) == -1)
{
- Platform.Services.Logger.Error("Looper", "SDL_Init() failed: {0}", SDL.SDL_GetError());
+ Platform.Services.Logger.Error(LOOPER_TAG, "SDL_Init() failed: {0}", SDL.SDL_GetError());
return false;
}
+ _isSDLinited = true;
_keyboard = new SDLKeyboard();
- Platform.Services.Logger.Info("Looper", "Keyboard input device ready.");
+ Platform.Services.Logger.Info(LOOPER_TAG, "Keyboard input device ready.");
_mouse = new SDLMouse();
- Platform.Services.Logger.Info("Looper", "Mouse input device ready.");
+ Platform.Services.Logger.Info(LOOPER_TAG, "Mouse input device ready.");
int numJoysticks = SDL.SDL_NumJoysticks();
- Platform.Services.Logger.Info("Looper", "{0} joystick input devices found.", numJoysticks);
+ Platform.Services.Logger.Info(LOOPER_TAG, "{0} joystick input devices found.", numJoysticks);
for (int i = 0; i < numJoysticks; ++i)
{
- Platform.Services.Logger.Info("Looper", "Joystick #{0}. {1}:", (i + 1), SDL.SDL_JoystickNameForIndex(i));
+ Platform.Services.Logger.Info(LOOPER_TAG, "Joystick #{0}. {1}:", (i + 1), SDL.SDL_JoystickNameForIndex(i));
IntPtr joystick = SDL.SDL_JoystickOpen(i);
if (joystick != IntPtr.Zero)
{
- Platform.Services.Logger.Info("Looper", "\tAxes: {0}", SDL.SDL_JoystickNumAxes(joystick));
- Platform.Services.Logger.Info("Looper", "\tBalls: {0}", SDL.SDL_JoystickNumBalls(joystick));
- Platform.Services.Logger.Info("Looper", "\tHats: {0}", SDL.SDL_JoystickNumHats(joystick));
- Platform.Services.Logger.Info("Looper", "\tButtons: {0}", SDL.SDL_JoystickNumButtons(joystick));
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tAxes: {0}", SDL.SDL_JoystickNumAxes(joystick));
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tBalls: {0}", SDL.SDL_JoystickNumBalls(joystick));
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tHats: {0}", SDL.SDL_JoystickNumHats(joystick));
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tButtons: {0}", SDL.SDL_JoystickNumButtons(joystick));
SDL.SDL_JoystickClose(joystick);
}
else
- Platform.Services.Logger.Warn("Looper", "\tMore information could not be obtained.");
+ Platform.Services.Logger.Warn(LOOPER_TAG, "\tMore information could not be obtained.");
}
_filesystem = new SDLFileSystem();
- Platform.Services.Logger.Info("Looper", "Filesystem access initialized.");
+ Platform.Services.Logger.Info(LOOPER_TAG, "Filesystem access initialized.");
- Platform.Services.Logger.Info("Looper", "SDL initialization finished.");
+ int numVideoDrivers = SDL.SDL_GetNumVideoDrivers();
+ Platform.Services.Logger.Info(LOOPER_TAG, "Video drivers present: {0}.", numVideoDrivers);
+ for (int i = 0; i < numVideoDrivers; ++i)
+ Platform.Services.Logger.Info(LOOPER_TAG, "\t{0}: {1}", (i + 1), SDL.SDL_GetVideoDriver(i));
+ Platform.Services.Logger.Info(LOOPER_TAG, "Currently using video driver: {0}", SDL.SDL_GetCurrentVideoDriver());
+
+ int numAudioDrivers = SDL.SDL_GetNumAudioDrivers();
+ Platform.Services.Logger.Info(LOOPER_TAG, "Audio drivers present: {0}", numAudioDrivers);
+ for (int i = 0; i < numAudioDrivers; ++i)
+ Platform.Services.Logger.Info(LOOPER_TAG, "\t{0}: {1}", (i + 1), SDL.SDL_GetAudioDriver(i));
+ Platform.Services.Logger.Info(LOOPER_TAG, "Currently using audio driver: {0}", SDL.SDL_GetCurrentAudioDriver());
+
+ Platform.Services.Logger.Info(LOOPER_TAG, "SDL initialization finished.");
return true;
}
+ private void SetUpdateFrequency(int targetFrequency)
+ {
+ _targetUpdatesPerSecond = targetFrequency;
+ _ticksPerUpdate = 1000 / _targetUpdatesPerSecond;
+ _fixedUpdateInterval = _ticksPerUpdate / 1000.0f;
+ }
+
+ private void MainLoop()
+ {
+ _isWindowActive = true;
+ _isPaused = false;
+ _isQuitting = false;
+
+ int numUpdatesThisFrame = 0;
+ int numLoops = 0;
+ int timeElapsed = 0;
+ bool isDirty = false;
+
+ bool isRunningSlowly = false;
+
+ int updateTime = 0;
+ int renderTime = 0;
+ int numUpdates = 0;
+ int numRenders = 0;
+
+ int nextUpdateAt = Environment.TickCount;
+ int currentTime = Environment.TickCount;
+
+ while (!_isQuitting)
+ {
+ if (_isPaused)
+ {
+ // we always want to be processing events, even when paused
+ DoEvents();
+ }
+ else
+ {
+ int newTime = Environment.TickCount;
+ int frameTime = newTime - currentTime;
+ currentTime = newTime;
+ timeElapsed += frameTime;
+
+ // every second recalculate the FPS
+ if (timeElapsed >= 1000)
+ {
+ _fps = numLoops;
+ _frameTime = 1000.0f / _fps;
+
+ _rendersPerSecond = numRenders;
+ _updatesPerSecond = numUpdates;
+ _renderTime = renderTime;
+ _updateTime = updateTime;
+
+ numUpdates = 0;
+ numRenders = 0;
+
+ numLoops = 0;
+ timeElapsed = 0;
+ }
+
+ // we're "running slowly" if we're more then one update behind
+ if (currentTime > nextUpdateAt + _ticksPerUpdate)
+ isRunningSlowly = true;
+ else
+ isRunningSlowly = false;
+
+ numUpdatesThisFrame = 0;
+ while (Environment.TickCount >= nextUpdateAt && numUpdatesThisFrame < _maxFrameSkip)
+ {
+ if (numUpdatesThisFrame > 0)
+ isRunningSlowly = true;
+
+ int before = Environment.TickCount;
+ DoEvents();
+ OnUpdate(_fixedUpdateInterval);
+ updateTime += Environment.TickCount - before;
+
+ ++numUpdatesThisFrame;
+ nextUpdateAt += _ticksPerUpdate;
+
+ ++numUpdates;
+
+ // just updated, so we need to render the new game state
+ isDirty = true;
+ }
+
+ if (isDirty && _isWindowActive)
+ {
+ int before = Environment.TickCount;
+ OnRender(_fixedRenderInterval); // TODO
+ SDL.SDL_GL_SwapWindow(_window);
+ renderTime += Environment.TickCount - before;
+
+ ++numRenders;
+
+ // don't render again until we hve something new to show
+ isDirty = false;
+ }
+
+ ++numLoops;
+ }
+ }
+ }
+
+ #region Window Management
+
+ private bool InitSDLWindow(SDLConfiguration config)
+ {
+ Platform.Services.Logger.Info(LOOPER_TAG, "SDL Window initialization starting.");
+
+ int flags = (int)SDL.SDL_WindowFlags.SDL_WINDOW_OPENGL | (int)SDL.SDL_WindowFlags.SDL_WINDOW_SHOWN;
+ if (config.Fullscreen)
+ flags |= (int)SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN;
+ if (config.Resizeable)
+ flags |= (int)SDL.SDL_WindowFlags.SDL_WINDOW_RESIZABLE;
+
+ if (!CreateWindow(config.Title, config.Width, config.Height, flags))
+ return false;
+
+ if (!CreateOpenGLContext(config))
+ return false;
+
+ Platform.Services.Logger.Info(LOOPER_TAG, "SDL Window initialization finished.");
+ return true;
+ }
+
+ private bool CreateWindow(string title, int width, int height, int flags)
+ {
+ Platform.Services.Logger.Info(LOOPER_TAG, "Attempting to set up new window with dimensions {0}x{1}.", width, height);
+ if (_window != IntPtr.Zero)
+ throw new InvalidOperationException("Cannot create new window before destorying existing one.");
+
+ _window = SDL.SDL_CreateWindow(title, SDL.SDL_WINDOWPOS_UNDEFINED, SDL.SDL_WINDOWPOS_UNDEFINED, width, height, (SDL.SDL_WindowFlags)flags);
+ if (_window == IntPtr.Zero)
+ {
+ Platform.Services.Logger.Error(LOOPER_TAG, "Window creation failed: {0}", SDL.SDL_GetError());
+ return false;
+ }
+
+ SetWindowInfo();
+
+ Platform.Services.Logger.Info(LOOPER_TAG, "Window creation succeeded.");
+ return true;
+ }
+
+ private bool DestroyWindow()
+ {
+ Platform.Services.Logger.Info(LOOPER_TAG, "Destroying window.");
+ if (_window == IntPtr.Zero)
+ {
+ Platform.Services.Logger.Warn(LOOPER_TAG, "No window currently exists, not doing anything.");
+ return true;
+ }
+
+ SDL.SDL_DestroyWindow(_window);
+ _window = IntPtr.Zero;
+
+ Platform.Services.Logger.Info(LOOPER_TAG, "Window destroyed.");
+ return true;
+ }
+
+ private void SetWindowInfo()
+ {
+ if (_window == IntPtr.Zero)
+ throw new InvalidOperationException("Cannot set window info for a non-existant window.");
+
+ int windowX;
+ int windowY;
+ SDL.SDL_GetWindowPosition(_window, out windowX, out windowY);
+
+ int clientWidth;
+ int clientHeight;
+ SDL.SDL_GetWindowSize(_window, out clientWidth, out clientHeight);
+
+ _windowInfo.ClientWidth = clientWidth;
+ _windowInfo.ClientHeight = clientHeight;
+ _windowInfo.ClientRectangle = new Rect(0, 0, clientWidth, clientHeight);
+
+ Platform.Services.Logger.Info(LOOPER_TAG, "Window content area set to {0}", _windowInfo.ClientRectangle);
+ }
+
+ #endregion
+
+ #region OpenGL Context Management
+
+ private bool CreateOpenGLContext(SDLConfiguration config)
+ {
+ Platform.Services.Logger.Info(LOOPER_TAG, "Attempting to create OpenGL context.");
+ if (_glContext != IntPtr.Zero)
+ throw new InvalidOperationException("Cannoy create new OpenGL context before destroying existing one.");
+ if (_window == IntPtr.Zero)
+ throw new InvalidOperationException("Cannot create an OpenGL context without an existing window.");
+
+ // minimum requirements
+ SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_DOUBLEBUFFER, config.glDoubleBuffer ? 1 : 0);
+ SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_DEPTH_SIZE, config.glDepthBufferSize);
+ SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_RED_SIZE, config.glRedSize);
+ SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_GREEN_SIZE, config.glGreenSize);
+ SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_BLUE_SIZE, config.glBlueSize);
+ SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_ALPHA_SIZE, config.glAlphaSize);
+
+ _glContext = SDL.SDL_GL_CreateContext(_window);
+ if (_glContext == IntPtr.Zero)
+ {
+ Platform.Services.Logger.Error(LOOPER_TAG, "OpenGL context creation failed: {0}", SDL.SDL_GetError());
+ return false;
+ }
+
+ Platform.Services.Logger.Info(LOOPER_TAG, "OpenGL context creation succeeded.");
+
+ Platform.Services.Logger.Info(LOOPER_TAG, "Setting OpenTK's OpenGL context and loading OpenGL extensions.");
+ OpenTK.Graphics.GraphicsContext.CurrentContext = _glContext;
+ OpenTK.Graphics.OpenGL.GL.LoadAll();
+
+ int redSize;
+ int greenSize;
+ int blueSize;
+ int alphaSize;
+ int bufferSize;
+ int doubleBuffer;
+ int depthSize;
+ int stencilSize;
+ int accumRedSize;
+ int accumGreenSize;
+ int accumBlueSize;
+ int accumAlphaSize;
+ int stereo;
+ int multisampleBuffers;
+ int multisampleSamples;
+ int acceleratedVisual;
+ int contextMajorVersion;
+ int contextMinorVersion;
+
+ SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_RED_SIZE, out redSize);
+ SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_GREEN_SIZE, out greenSize);
+ SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_BLUE_SIZE, out blueSize);
+ SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_ALPHA_SIZE, out alphaSize);
+ SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_BUFFER_SIZE, out bufferSize);
+ SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_DOUBLEBUFFER, out doubleBuffer);
+ SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_DEPTH_SIZE, out depthSize);
+ SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_STENCIL_SIZE, out stencilSize);
+ SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_ACCUM_RED_SIZE, out accumRedSize);
+ SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_ACCUM_GREEN_SIZE, out accumGreenSize);
+ SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_ACCUM_BLUE_SIZE, out accumBlueSize);
+ SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_ACCUM_ALPHA_SIZE, out accumAlphaSize);
+ SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_STEREO, out stereo);
+ SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_MULTISAMPLEBUFFERS, out multisampleBuffers);
+ SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_MULTISAMPLESAMPLES, out multisampleSamples);
+ SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_ACCELERATED_VISUAL, out acceleratedVisual);
+ SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, out contextMajorVersion);
+ SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, out contextMinorVersion);
+
+ Platform.Services.Logger.Info(LOOPER_TAG, "OpenGL context attributes:");
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_RED_SIZE: {0}", redSize);
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_GREEN_SIZE: {0}", greenSize);
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_BLUE_SIZE: {0}", blueSize);
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_ALPHA_SIZE: {0}", alphaSize);
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_BUFFER_SIZE: {0}", bufferSize);
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_DOUBLEBUFFER: {0}", doubleBuffer);
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_DEPTH_SIZE: {0}", depthSize);
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_STENCIL_SIZE: {0}", stencilSize);
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_ACCUM_RED_SIZE: {0}", accumRedSize);
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_ACCUM_GREEN_SIZE: {0}", accumGreenSize);
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_ACCUM_BLUE_SIZE: {0}", accumBlueSize);
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_ACCUM_ALPHA_SIZE: {0}", accumAlphaSize);
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_STEREO: {0}", stereo);
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_MULTISAMPLEBUFFERS: {0}", multisampleBuffers);
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_MULTISAMPLESAMPLES: {0}", multisampleSamples);
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_ACCELERATED_VISUAL: {0}", acceleratedVisual);
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_CONTEXT_MAJOR_VERSION: {0}", contextMajorVersion);
+ Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_CONTEXT_MINOR_VERSION: {0}", contextMinorVersion);
+
+ Platform.Services.Logger.Info(LOOPER_TAG, "Attempting to enable V-sync.");
+ if (SDL.SDL_GL_SetSwapInterval(1) != 0)
+ Platform.Services.Logger.Warn(LOOPER_TAG, "Could not set swap interval: {0}", SDL.SDL_GetError());
+ else
+ Platform.Services.Logger.Info(LOOPER_TAG, "Swap interval set successful.");
+
+ return true;
+ }
+
+ private bool DestroyOpenGLContext()
+ {
+ Platform.Services.Logger.Info(LOOPER_TAG, "Destroying OpenGL context.");
+ if (_glContext == IntPtr.Zero)
+ {
+ Platform.Services.Logger.Warn(LOOPER_TAG, "No OpenGL context currently exists, not doing anything.");
+ return true;
+ }
+
+ SDL.SDL_GL_DeleteContext(_glContext);
+ _glContext = IntPtr.Zero;
+
+ Platform.Services.Logger.Info(LOOPER_TAG, "OpenGL context destroyed.");
+ return true;
+ }
+
+ #endregion
+
+ #region Events
+
+ private void DoEvents()
+ {
+ SDL.SDL_Event e;
+ while (SDL.SDL_PollEvent(out e) != 0)
+ {
+ if (e.type == SDL.SDL_EventType.SDL_WINDOWEVENT)
+ {
+ switch (e.window.windowEvent)
+ {
+ case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_MINIMIZED:
+ Platform.Services.Logger.Info(LOOPER_TAG, "Window focus lost.");
+ Platform.Services.Logger.Info(LOOPER_TAG, "Window marked inactive.");
+ break;
+
+ case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_RESTORED:
+ Platform.Services.Logger.Info(LOOPER_TAG, "Window focus gained.");
+ Platform.Services.Logger.Info(LOOPER_TAG, "Window marked active.");
+ break;
+
+ case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_ENTER:
+ Platform.Services.Logger.Info(LOOPER_TAG, "Gained mouse focus.");
+ break;
+
+ case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_LEAVE:
+ Platform.Services.Logger.Info(LOOPER_TAG, "Lost mouse focus.");
+ break;
+
+ case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_GAINED:
+ Platform.Services.Logger.Info(LOOPER_TAG, "Gained input device focus.");
+ OnAppGainFocus();
+ break;
+
+ case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_LOST:
+ Platform.Services.Logger.Info(LOOPER_TAG, "Lost input device focus.");
+ OnAppLostFocus();
+ break;
+
+ case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_RESIZED:
+ Platform.Services.Logger.Info(LOOPER_TAG, "Window resized to {0}x{1}.", e.window.data1, e.window.data2);
+ Rect size = new Rect();
+ size.Right = e.window.data1;
+ size.Bottom = e.window.data2;
+ SetWindowInfo();
+ OnResize(ScreenOrientation.Rotation0, size);
+ break;
+ }
+ }
+ else
+ {
+ switch (e.type)
+ {
+ case SDL.SDL_EventType.SDL_QUIT:
+ Platform.Services.Logger.Info(LOOPER_TAG, "Event: SQL_QUIT");
+ _isQuitting = true;
+ break;
+
+ case SDL.SDL_EventType.SDL_KEYDOWN:
+ case SDL.SDL_EventType.SDL_KEYUP:
+ _keyboard.OnKeyEvent(e.key);
+ break;
+
+ case SDL.SDL_EventType.SDL_MOUSEMOTION:
+ _mouse.OnMotionEvent(e.motion);
+ break;
+
+ case SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN:
+ case SDL.SDL_EventType.SDL_MOUSEBUTTONUP:
+ _mouse.OnButtonEvent(e.button);
+ break;
+ }
+ }
+ }
+ }
+
+ #endregion
+
#region IDisposable
private void ReleaseSDL()
@@ -86,20 +580,14 @@ namespace Blarg.GameFramework
if (!_isSDLinited)
return;
- if (_glContext != IntPtr.Zero)
- {
- SDL.SDL_GL_DeleteContext(_glContext);
- _glContext = IntPtr.Zero;
- }
- if (_window != IntPtr.Zero)
- {
- SDL.SDL_DestroyWindow(_window);
- _window = IntPtr.Zero;
- }
+ Platform.Services.Logger.Info(LOOPER_TAG, "Releasing SDL.");
+ DestroyOpenGLContext();
+ DestroyWindow();
SDL.SDL_Quit();
+ _isSDLinited = false;
- Platform.Services.Logger.Info("Looper", "SDL shutdown.");
+ Platform.Services.Logger.Info(LOOPER_TAG, "SDL shutdown.");
}
~SDLLooper()
@@ -107,7 +595,7 @@ namespace Blarg.GameFramework
ReleaseSDL();
}
- public void Dispose()
+ public override void Dispose()
{
ReleaseSDL();
GC.SuppressFinalize(this);
diff --git a/Blarg.GameFramework.SDL2/SDLPlatformServices.cs b/Blarg.GameFramework.SDL2/SDLPlatformServices.cs
new file mode 100644
index 0000000..9417f62
--- /dev/null
+++ b/Blarg.GameFramework.SDL2/SDLPlatformServices.cs
@@ -0,0 +1,38 @@
+using System;
+using PortableGL;
+using Blarg.GameFramework.Input;
+using Blarg.GameFramework.IO;
+
+namespace Blarg.GameFramework
+{
+ public class SDLPlatformServices : IPlatformServices
+ {
+ public SDLPlatformServices()
+ {
+ if (CurrentOS.IsWindows)
+ OperatingSystem = PlatformOS.Windows;
+ else if (CurrentOS.IsLinux)
+ OperatingSystem = PlatformOS.Linux;
+ else if (CurrentOS.IsMac)
+ OperatingSystem = PlatformOS.MacOS;
+ else
+ throw new Exception("Unable to determine OS.");
+
+ Logger = new SDLLogger();
+ }
+
+ public PlatformOS OperatingSystem { get; private set; }
+ public PlatformType Type
+ {
+ get { return PlatformType.Desktop; }
+ }
+
+ public IPlatformLogger Logger { get; internal set; }
+ public IFileSystem FileSystem { get; internal set; }
+ public IKeyboard Keyboard { get; internal set; }
+ public IMouse Mouse { get; internal set; }
+ public ITouchScreen TouchScreen { get; internal set; }
+ public GL20 GL { get; internal set; }
+ }
+}
+
diff --git a/Blarg.GameFramework.SDL2/SDLWindow.cs b/Blarg.GameFramework.SDL2/SDLWindow.cs
new file mode 100644
index 0000000..71bd42d
--- /dev/null
+++ b/Blarg.GameFramework.SDL2/SDLWindow.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Blarg.GameFramework
+{
+ public class SDLWindow : IPlatformWindow
+ {
+ public Rect ClientRectangle { get; internal set; }
+ public int ClientWidth { get; internal set; }
+ public int ClientHeight { get; internal set; }
+ public int X { get; internal set; }
+ public int Y { get; internal set; }
+ public int Width { get; internal set; }
+ public int Height { get; internal set; }
+ }
+}
+
diff --git a/Blarg.GameFramework/BaseLooper.cs b/Blarg.GameFramework/BaseLooper.cs
new file mode 100644
index 0000000..f0a96a9
--- /dev/null
+++ b/Blarg.GameFramework/BaseLooper.cs
@@ -0,0 +1,91 @@
+using System;
+using Blarg.GameFramework.Graphics;
+
+namespace Blarg.GameFramework
+{
+ public abstract class BaseLooper : ILooper
+ {
+ IGameApp _gameApp;
+
+ public IGameApp GameApp
+ {
+ get
+ {
+ return _gameApp;
+ }
+ protected set
+ {
+ _gameApp = value;
+ }
+ }
+
+ public abstract int FPS { get; }
+ public abstract float FrameTime { get; }
+ public abstract int RendersPerSecond { get; }
+ public abstract int UpdatesPerSecond { get; }
+ public abstract int RenderTime { get; }
+ public abstract int UpdateTime { get; }
+
+ public abstract void Run(IGameApp gameApp, IPlatformConfiguration config);
+
+ protected void OnAppGainFocus()
+ {
+ GameApp.OnAppGainFocus();
+ }
+
+ protected void OnAppLostFocus()
+ {
+ GameApp.OnAppLostFocus();
+ }
+
+ protected void OnAppPause()
+ {
+ GameApp.OnAppPause();
+ }
+
+ protected void OnAppResume()
+ {
+ GameApp.OnAppResume();
+ }
+
+ protected void OnLoad()
+ {
+ GameApp.OnLoad();
+ }
+
+ protected void OnUnload()
+ {
+ GameApp.OnUnload();
+ }
+
+ protected void OnLostContext()
+ {
+ GameApp.OnLostContext();
+ }
+
+ protected void OnNewContext()
+ {
+ GameApp.OnNewContext();
+ }
+
+ protected void OnRender(float delta)
+ {
+ GameApp.OnRender(delta);
+ }
+
+ protected void OnResize(ScreenOrientation orientation, Rect size)
+ {
+ GameApp.OnResize(orientation, size);
+ }
+
+ protected void OnUpdate(float delta)
+ {
+ GameApp.OnUpdate(delta);
+ }
+
+ public virtual void Dispose()
+ {
+ }
+ }
+}
+
diff --git a/Blarg.GameFramework/Blarg.GameFramework.csproj b/Blarg.GameFramework/Blarg.GameFramework.csproj
index 0af8688..3f6e104 100644
--- a/Blarg.GameFramework/Blarg.GameFramework.csproj
+++ b/Blarg.GameFramework/Blarg.GameFramework.csproj
@@ -82,6 +82,9 @@
+
+
+
diff --git a/Blarg.GameFramework/IGameApp.cs b/Blarg.GameFramework/IGameApp.cs
index 8073c26..d9da921 100644
--- a/Blarg.GameFramework/IGameApp.cs
+++ b/Blarg.GameFramework/IGameApp.cs
@@ -3,13 +3,12 @@ using Blarg.GameFramework.Graphics;
namespace Blarg.GameFramework
{
- public interface IGameApp
+ public interface IGameApp : IDisposable
{
void OnAppGainFocus();
void OnAppLostFocus();
void OnAppPause();
void OnAppResume();
- bool OnInit();
void OnLoad();
void OnUnload();
void OnLostContext();
diff --git a/Blarg.GameFramework/ILooper.cs b/Blarg.GameFramework/ILooper.cs
index f794b61..507eb9c 100644
--- a/Blarg.GameFramework/ILooper.cs
+++ b/Blarg.GameFramework/ILooper.cs
@@ -4,7 +4,14 @@ namespace Blarg.GameFramework
{
public interface ILooper : IDisposable
{
- void Run(IGameApp gameApp);
+ int FPS { get; }
+ float FrameTime { get; }
+ int RendersPerSecond { get; }
+ int UpdatesPerSecond { get; }
+ int RenderTime { get; }
+ int UpdateTime { get; }
+
+ void Run(IGameApp gameApp, IPlatformConfiguration config);
}
}
diff --git a/Blarg.GameFramework/IPlatformConfiguration.cs b/Blarg.GameFramework/IPlatformConfiguration.cs
new file mode 100644
index 0000000..53e513c
--- /dev/null
+++ b/Blarg.GameFramework/IPlatformConfiguration.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Blarg.GameFramework
+{
+ public interface IPlatformConfiguration
+ {
+ }
+}
+
diff --git a/Blarg.GameFramework/IPlatformWindow.cs b/Blarg.GameFramework/IPlatformWindow.cs
new file mode 100644
index 0000000..7b7982f
--- /dev/null
+++ b/Blarg.GameFramework/IPlatformWindow.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Blarg.GameFramework
+{
+ public interface IPlatformWindow
+ {
+ Rect ClientRectangle { get; }
+ int ClientWidth { get; }
+ int ClientHeight { get; }
+ int X { get; }
+ int Y { get; }
+ int Width { get; }
+ int Height { get; }
+ }
+}
+
diff --git a/Blarg.GameFramework/Platform.cs b/Blarg.GameFramework/Platform.cs
index 37112dc..d7505fb 100644
--- a/Blarg.GameFramework/Platform.cs
+++ b/Blarg.GameFramework/Platform.cs
@@ -17,9 +17,21 @@ namespace Blarg.GameFramework
Desktop
}
- public static partial class Platform
+ public static class Platform
{
- public static IPlatformServices Services { get; private set; }
+ static IPlatformServices _services;
+
+ public static IPlatformServices Services
+ {
+ get { return _services; }
+ set
+ {
+ if (_services != null)
+ throw new InvalidOperationException();
+ else
+ _services = value;
+ }
+ }
}
}
diff --git a/Game.Core/Game.Core.csproj b/Game.Core/Game.Core.csproj
index 8a45ad8..bc2a2f9 100644
--- a/Game.Core/Game.Core.csproj
+++ b/Game.Core/Game.Core.csproj
@@ -35,9 +35,13 @@
+
+ ..\Libs\PortableGL.dll
+
+
diff --git a/Game.Core/GameApp.cs b/Game.Core/GameApp.cs
new file mode 100644
index 0000000..3337205
--- /dev/null
+++ b/Game.Core/GameApp.cs
@@ -0,0 +1,65 @@
+using System;
+using PortableGL;
+using Blarg.GameFramework;
+using Blarg.GameFramework.Graphics;
+
+namespace Game
+{
+ public class GameApp : IGameApp
+ {
+ public GameApp()
+ {
+ }
+
+ public void OnAppGainFocus()
+ {
+ }
+
+ public void OnAppLostFocus()
+ {
+ }
+
+ public void OnAppPause()
+ {
+ }
+
+ public void OnAppResume()
+ {
+ }
+
+ public void OnLoad()
+ {
+ }
+
+ public void OnUnload()
+ {
+ }
+
+ public void OnLostContext()
+ {
+ }
+
+ public void OnNewContext()
+ {
+ }
+
+ public void OnRender(float delta)
+ {
+ Platform.Services.GL.glClear(GL20.GL_DEPTH_BUFFER_BIT | GL20.GL_COLOR_BUFFER_BIT);
+ Platform.Services.GL.glClearColor(0.25f, 0.5f, 1.0f, 1.0f);
+ }
+
+ public void OnResize(ScreenOrientation orientation, Rect size)
+ {
+ }
+
+ public void OnUpdate(float delta)
+ {
+ }
+
+ public void Dispose()
+ {
+ }
+ }
+}
+
diff --git a/Game.SDL2/Game.SDL2.csproj b/Game.SDL2/Game.SDL2.csproj
index 5e8d065..cada48b 100644
--- a/Game.SDL2/Game.SDL2.csproj
+++ b/Game.SDL2/Game.SDL2.csproj
@@ -6,7 +6,7 @@
10.0.0
2.0
{4C73802F-6EB0-490C-BB32-64718B0FEEBA}
- Exe
+ WinExe
Game
Game.SDL2
@@ -18,7 +18,7 @@
DEBUG;
prompt
4
- true
+ false
full
diff --git a/Game.SDL2/Program.cs b/Game.SDL2/Program.cs
index 329ce39..5cbc3ef 100644
--- a/Game.SDL2/Program.cs
+++ b/Game.SDL2/Program.cs
@@ -1,12 +1,18 @@
using System;
+using Blarg.GameFramework;
namespace Game.SDL2
{
- class MainClass
+ public static class MainClass
{
public static void Main(string[] args)
{
- Console.WriteLine("Hello World!");
+ var game = new GameApp();
+ var config = new SDLConfiguration();
+ config.Title = "Test Game";
+
+ var looper = new SDLLooper();
+ looper.Run(game, config);
}
}
}