untie the engine from SDL2 and MiniTK
This commit is contained in:
32
OpenRA.Platforms.Default/DefaultPlatform.cs
Normal file
32
OpenRA.Platforms.Default/DefaultPlatform.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Reflection;
|
||||
using OpenRA;
|
||||
|
||||
[assembly: Platform(typeof(OpenRA.Platforms.Default.DeviceFactory))]
|
||||
|
||||
namespace OpenRA.Platforms.Default
|
||||
{
|
||||
public class DeviceFactory : IDeviceFactory
|
||||
{
|
||||
public IGraphicsDevice CreateGraphics(Size size, WindowMode windowMode)
|
||||
{
|
||||
return new Sdl2GraphicsDevice(size, windowMode);
|
||||
}
|
||||
|
||||
public ISoundEngine CreateSound()
|
||||
{
|
||||
return new OpenAlSoundEngine();
|
||||
}
|
||||
}
|
||||
}
|
||||
72
OpenRA.Platforms.Default/ErrorHandler.cs
Normal file
72
OpenRA.Platforms.Default/ErrorHandler.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
|
||||
namespace OpenRA.Platforms.Default
|
||||
{
|
||||
public static class ErrorHandler
|
||||
{
|
||||
public static void CheckGlVersion()
|
||||
{
|
||||
var versionString = GL.GetString(StringName.Version);
|
||||
var version = versionString.Contains(" ") ? versionString.Split(' ')[0].Split('.') : versionString.Split('.');
|
||||
|
||||
var major = 0;
|
||||
if (version.Length > 0)
|
||||
int.TryParse(version[0], out major);
|
||||
|
||||
var minor = 0;
|
||||
if (version.Length > 1)
|
||||
int.TryParse(version[1], out minor);
|
||||
|
||||
Console.WriteLine("Detected OpenGL version: {0}.{1}".F(major, minor));
|
||||
if (major < 2)
|
||||
{
|
||||
WriteGraphicsLog("OpenRA requires OpenGL version 2.0 or greater and detected {0}.{1}".F(major, minor));
|
||||
throw new InvalidProgramException("OpenGL Version Error: See graphics.log for details.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void CheckGlError()
|
||||
{
|
||||
var n = GL.GetError();
|
||||
if (n != ErrorCode.NoError)
|
||||
{
|
||||
var error = "GL Error: {0}\n{1}".F(n, new StackTrace());
|
||||
WriteGraphicsLog(error);
|
||||
throw new InvalidOperationException("OpenGL Error: See graphics.log for details.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteGraphicsLog(string message)
|
||||
{
|
||||
Log.Write("graphics", message);
|
||||
Log.Write("graphics", "");
|
||||
Log.Write("graphics", "OpenGL Information:");
|
||||
Log.Write("graphics", "Vendor: {0}", GL.GetString(StringName.Vendor));
|
||||
if (GL.GetString(StringName.Vendor).Contains("Microsoft"))
|
||||
{
|
||||
var msg = "";
|
||||
msg += "Note: The default driver provided by Microsoft does not include full OpenGL support.\n";
|
||||
msg += "Please install the latest drivers from your graphics card manufacturer's website.\n";
|
||||
Log.Write("graphics", msg);
|
||||
}
|
||||
|
||||
Log.Write("graphics", "Renderer: {0}", GL.GetString(StringName.Renderer));
|
||||
Log.Write("graphics", "GL Version: {0}", GL.GetString(StringName.Version));
|
||||
Log.Write("graphics", "Shader Version: {0}", GL.GetString(StringName.ShadingLanguageVersion));
|
||||
Log.Write("graphics", "Available extensions:");
|
||||
Log.Write("graphics", GL.GetString(StringName.Extensions));
|
||||
}
|
||||
}
|
||||
}
|
||||
137
OpenRA.Platforms.Default/FrameBuffer.cs
Normal file
137
OpenRA.Platforms.Default/FrameBuffer.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
|
||||
namespace OpenRA.Platforms.Default
|
||||
{
|
||||
public sealed class FrameBuffer : IFrameBuffer
|
||||
{
|
||||
Texture texture;
|
||||
Size size;
|
||||
int framebuffer, depth;
|
||||
bool disposed;
|
||||
|
||||
public FrameBuffer(Size size)
|
||||
{
|
||||
this.size = size;
|
||||
if (!Exts.IsPowerOf2(size.Width) || !Exts.IsPowerOf2(size.Height))
|
||||
throw new InvalidDataException("Frame buffer size ({0}x{1}) must be a power of two".F(size.Width, size.Height));
|
||||
|
||||
GL.Ext.GenFramebuffers(1, out framebuffer);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, framebuffer);
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
// Color
|
||||
texture = new Texture();
|
||||
texture.SetEmpty(size.Width, size.Height);
|
||||
GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt, FramebufferAttachment.ColorAttachment0Ext, TextureTarget.Texture2D, texture.ID, 0);
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
// Depth
|
||||
GL.Ext.GenRenderbuffers(1, out depth);
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
GL.Ext.BindRenderbuffer(RenderbufferTarget.RenderbufferExt, depth);
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
GL.Ext.RenderbufferStorage(RenderbufferTarget.RenderbufferExt, RenderbufferStorage.DepthComponent, size.Width, size.Height);
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
GL.Ext.FramebufferRenderbuffer(FramebufferTarget.FramebufferExt, FramebufferAttachment.DepthAttachmentExt, RenderbufferTarget.RenderbufferExt, depth);
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
// Test for completeness
|
||||
var status = GL.Ext.CheckFramebufferStatus(FramebufferTarget.FramebufferExt);
|
||||
if (status != FramebufferErrorCode.FramebufferCompleteExt)
|
||||
{
|
||||
var error = "Error creating framebuffer: {0}\n{1}".F(status, new StackTrace());
|
||||
ErrorHandler.WriteGraphicsLog(error);
|
||||
throw new InvalidOperationException("OpenGL Error: See graphics.log for details.");
|
||||
}
|
||||
|
||||
// Restore default buffer
|
||||
GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
static int[] ViewportRectangle()
|
||||
{
|
||||
var v = new int[4];
|
||||
unsafe
|
||||
{
|
||||
fixed (int* ptr = &v[0])
|
||||
GL.GetInteger(GetPName.Viewport, ptr);
|
||||
}
|
||||
|
||||
ErrorHandler.CheckGlError();
|
||||
return v;
|
||||
}
|
||||
|
||||
int[] cv = new int[4];
|
||||
public void Bind()
|
||||
{
|
||||
// Cache viewport rect to restore when unbinding
|
||||
cv = ViewportRectangle();
|
||||
|
||||
GL.Flush();
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, framebuffer);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.Viewport(0, 0, size.Width, size.Height);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.ClearColor(0, 0, 0, 0);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void Unbind()
|
||||
{
|
||||
GL.Flush();
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.Viewport(cv[0], cv[1], cv[2], cv[3]);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public ITexture Texture { get { return texture; } }
|
||||
|
||||
~FrameBuffer()
|
||||
{
|
||||
Game.RunAfterTick(() => Dispose(false));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Game.RunAfterTick(() => Dispose(true));
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
disposed = true;
|
||||
if (disposing)
|
||||
texture.Dispose();
|
||||
GL.Ext.DeleteFramebuffers(1, ref framebuffer);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.Ext.DeleteRenderbuffers(1, ref depth);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
}
|
||||
}
|
||||
83
OpenRA.Platforms.Default/MultiTapDetection.cs
Normal file
83
OpenRA.Platforms.Default/MultiTapDetection.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Platforms.Default
|
||||
{
|
||||
public static class MultiTapDetection
|
||||
{
|
||||
static Cache<Keycode, TapHistory> keyHistoryCache =
|
||||
new Cache<Keycode, TapHistory>(_ => new TapHistory(DateTime.Now - TimeSpan.FromSeconds(1)));
|
||||
static Cache<byte, TapHistory> clickHistoryCache =
|
||||
new Cache<byte, TapHistory>(_ => new TapHistory(DateTime.Now - TimeSpan.FromSeconds(1)));
|
||||
|
||||
public static int DetectFromMouse(byte button, int2 xy)
|
||||
{
|
||||
return clickHistoryCache[button].GetTapCount(xy);
|
||||
}
|
||||
|
||||
public static int InfoFromMouse(byte button)
|
||||
{
|
||||
return clickHistoryCache[button].LastTapCount();
|
||||
}
|
||||
|
||||
public static int DetectFromKeyboard(Keycode key)
|
||||
{
|
||||
return keyHistoryCache[key].GetTapCount(int2.Zero);
|
||||
}
|
||||
|
||||
public static int InfoFromKeyboard(Keycode key)
|
||||
{
|
||||
return keyHistoryCache[key].LastTapCount();
|
||||
}
|
||||
}
|
||||
|
||||
class TapHistory
|
||||
{
|
||||
public Pair<DateTime, int2> FirstRelease, SecondRelease, ThirdRelease;
|
||||
|
||||
public TapHistory(DateTime now)
|
||||
{
|
||||
FirstRelease = SecondRelease = ThirdRelease = Pair.New(now, int2.Zero);
|
||||
}
|
||||
|
||||
static bool CloseEnough(Pair<DateTime, int2> a, Pair<DateTime, int2> b)
|
||||
{
|
||||
return a.First - b.First < TimeSpan.FromMilliseconds(250)
|
||||
&& (a.Second - b.Second).Length < 4;
|
||||
}
|
||||
|
||||
public int GetTapCount(int2 xy)
|
||||
{
|
||||
FirstRelease = SecondRelease;
|
||||
SecondRelease = ThirdRelease;
|
||||
ThirdRelease = Pair.New(DateTime.Now, xy);
|
||||
|
||||
if (!CloseEnough(ThirdRelease, SecondRelease))
|
||||
return 1;
|
||||
if (!CloseEnough(SecondRelease, FirstRelease))
|
||||
return 2;
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
public int LastTapCount()
|
||||
{
|
||||
if (!CloseEnough(ThirdRelease, SecondRelease))
|
||||
return 1;
|
||||
if (!CloseEnough(SecondRelease, FirstRelease))
|
||||
return 2;
|
||||
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
369
OpenRA.Platforms.Default/OpenAlSoundEngine.cs
Normal file
369
OpenRA.Platforms.Default/OpenAlSoundEngine.cs
Normal file
@@ -0,0 +1,369 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.FileSystem;
|
||||
using OpenRA.GameRules;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
using OpenTK;
|
||||
using OpenTK.Audio.OpenAL;
|
||||
|
||||
namespace OpenRA.Platforms.Default
|
||||
{
|
||||
class OpenAlSoundEngine : ISoundEngine
|
||||
{
|
||||
public SoundDevice[] AvailableDevices()
|
||||
{
|
||||
var defaultDevices = new[]
|
||||
{
|
||||
new SoundDevice("Default", null, "Default Output"),
|
||||
new SoundDevice("Null", null, "Output Disabled")
|
||||
};
|
||||
|
||||
var physicalDevices = PhysicalDevices().Select(d => new SoundDevice("Default", d, d));
|
||||
return defaultDevices.Concat(physicalDevices).ToArray();
|
||||
}
|
||||
|
||||
class PoolSlot
|
||||
{
|
||||
public bool IsActive;
|
||||
public int FrameStarted;
|
||||
public WPos Pos;
|
||||
public bool IsRelative;
|
||||
public ISoundSource Sound;
|
||||
}
|
||||
|
||||
const int MaxInstancesPerFrame = 3;
|
||||
const int GroupDistance = 2730;
|
||||
const int GroupDistanceSqr = GroupDistance * GroupDistance;
|
||||
const int PoolSize = 32;
|
||||
|
||||
float volume = 1f;
|
||||
Dictionary<int, PoolSlot> sourcePool = new Dictionary<int, PoolSlot>();
|
||||
|
||||
static string[] QueryDevices(string label, AlcGetStringList type)
|
||||
{
|
||||
// Clear error bit
|
||||
AL.GetError();
|
||||
|
||||
var devices = Alc.GetString(IntPtr.Zero, type).ToArray();
|
||||
if (AL.GetError() != ALError.NoError)
|
||||
{
|
||||
Log.Write("sound", "Failed to query OpenAL device list using {0}", label);
|
||||
return new string[] { };
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
static string[] PhysicalDevices()
|
||||
{
|
||||
// Returns all devices under Windows Vista and newer
|
||||
if (Alc.IsExtensionPresent(IntPtr.Zero, "ALC_ENUMERATE_ALL_EXT"))
|
||||
return QueryDevices("ALC_ENUMERATE_ALL_EXT", AlcGetStringList.AllDevicesSpecifier);
|
||||
|
||||
if (Alc.IsExtensionPresent(IntPtr.Zero, "ALC_ENUMERATION_EXT"))
|
||||
return QueryDevices("ALC_ENUMERATION_EXT", AlcGetStringList.DeviceSpecifier);
|
||||
|
||||
return new string[] { };
|
||||
}
|
||||
|
||||
public OpenAlSoundEngine()
|
||||
{
|
||||
Console.WriteLine("Using OpenAL sound engine");
|
||||
|
||||
if (Game.Settings.Sound.Device != null)
|
||||
Console.WriteLine("Using device `{0}`", Game.Settings.Sound.Device);
|
||||
else
|
||||
Console.WriteLine("Using default device");
|
||||
|
||||
var dev = Alc.OpenDevice(Game.Settings.Sound.Device);
|
||||
if (dev == IntPtr.Zero)
|
||||
{
|
||||
Console.WriteLine("Failed to open device. Falling back to default");
|
||||
dev = Alc.OpenDevice(null);
|
||||
if (dev == IntPtr.Zero)
|
||||
throw new InvalidOperationException("Can't create OpenAL device");
|
||||
}
|
||||
|
||||
var ctx = Alc.CreateContext(dev, (int[])null);
|
||||
if (ctx == ContextHandle.Zero)
|
||||
throw new InvalidOperationException("Can't create OpenAL context");
|
||||
Alc.MakeContextCurrent(ctx);
|
||||
|
||||
for (var i = 0; i < PoolSize; i++)
|
||||
{
|
||||
var source = 0;
|
||||
AL.GenSources(1, out source);
|
||||
if (0 != AL.GetError())
|
||||
{
|
||||
Log.Write("sound", "Failed generating OpenAL source {0}", i);
|
||||
return;
|
||||
}
|
||||
|
||||
sourcePool.Add(source, new PoolSlot() { IsActive = false });
|
||||
}
|
||||
}
|
||||
|
||||
int GetSourceFromPool()
|
||||
{
|
||||
foreach (var kvp in sourcePool)
|
||||
{
|
||||
if (!kvp.Value.IsActive)
|
||||
{
|
||||
sourcePool[kvp.Key].IsActive = true;
|
||||
return kvp.Key;
|
||||
}
|
||||
}
|
||||
|
||||
var freeSources = new List<int>();
|
||||
foreach (var key in sourcePool.Keys)
|
||||
{
|
||||
int state;
|
||||
AL.GetSource(key, ALGetSourcei.SourceState, out state);
|
||||
if (state != (int)ALSourceState.Playing && state != (int)ALSourceState.Paused)
|
||||
freeSources.Add(key);
|
||||
}
|
||||
|
||||
if (freeSources.Count == 0)
|
||||
return -1;
|
||||
|
||||
foreach (var i in freeSources)
|
||||
sourcePool[i].IsActive = false;
|
||||
|
||||
sourcePool[freeSources[0]].IsActive = true;
|
||||
|
||||
return freeSources[0];
|
||||
}
|
||||
|
||||
public ISoundSource AddSoundSourceFromMemory(byte[] data, int channels, int sampleBits, int sampleRate)
|
||||
{
|
||||
return new OpenAlSoundSource(data, channels, sampleBits, sampleRate);
|
||||
}
|
||||
|
||||
public ISound Play2D(ISoundSource sound, bool loop, bool relative, WPos pos, float volume, bool attenuateVolume)
|
||||
{
|
||||
if (sound == null)
|
||||
{
|
||||
Log.Write("sound", "Attempt to Play2D a null `ISoundSource`");
|
||||
return null;
|
||||
}
|
||||
|
||||
var currFrame = Game.LocalTick;
|
||||
var atten = 1f;
|
||||
|
||||
// Check if max # of instances-per-location reached:
|
||||
if (attenuateVolume)
|
||||
{
|
||||
int instances = 0, activeCount = 0;
|
||||
foreach (var s in sourcePool.Values)
|
||||
{
|
||||
if (!s.IsActive)
|
||||
continue;
|
||||
if (s.IsRelative != relative)
|
||||
continue;
|
||||
|
||||
++activeCount;
|
||||
if (s.Sound != sound)
|
||||
continue;
|
||||
if (currFrame - s.FrameStarted >= 5)
|
||||
continue;
|
||||
|
||||
// Too far away to count?
|
||||
var lensqr = (s.Pos - pos).LengthSquared;
|
||||
if (lensqr >= GroupDistanceSqr)
|
||||
continue;
|
||||
|
||||
// If we are starting too many instances of the same sound within a short time then stop this one:
|
||||
if (++instances == MaxInstancesPerFrame)
|
||||
return null;
|
||||
}
|
||||
|
||||
// Attenuate a little bit based on number of active sounds:
|
||||
atten = 0.66f * ((PoolSize - activeCount * 0.5f) / PoolSize);
|
||||
}
|
||||
|
||||
var source = GetSourceFromPool();
|
||||
if (source == -1)
|
||||
return null;
|
||||
|
||||
var slot = sourcePool[source];
|
||||
slot.Pos = pos;
|
||||
slot.FrameStarted = currFrame;
|
||||
slot.Sound = sound;
|
||||
slot.IsRelative = relative;
|
||||
return new OpenAlSound(source, ((OpenAlSoundSource)sound).Buffer, loop, relative, pos, volume * atten);
|
||||
}
|
||||
|
||||
public float Volume
|
||||
{
|
||||
get { return volume; }
|
||||
set { AL.Listener(ALListenerf.Gain, volume = value); }
|
||||
}
|
||||
|
||||
public void PauseSound(ISound sound, bool paused)
|
||||
{
|
||||
if (sound == null)
|
||||
return;
|
||||
|
||||
var key = ((OpenAlSound)sound).Source;
|
||||
int state;
|
||||
AL.GetSource(key, ALGetSourcei.SourceState, out state);
|
||||
if (state == (int)ALSourceState.Playing && paused)
|
||||
AL.SourcePause(key);
|
||||
else if (state == (int)ALSourceState.Paused && !paused)
|
||||
AL.SourcePlay(key);
|
||||
}
|
||||
|
||||
public void SetAllSoundsPaused(bool paused)
|
||||
{
|
||||
foreach (var key in sourcePool.Keys)
|
||||
{
|
||||
int state;
|
||||
AL.GetSource(key, ALGetSourcei.SourceState, out state);
|
||||
if (state == (int)ALSourceState.Playing && paused)
|
||||
AL.SourcePause(key);
|
||||
else if (state == (int)ALSourceState.Paused && !paused)
|
||||
AL.SourcePlay(key);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSoundVolume(float volume, ISound music, ISound video)
|
||||
{
|
||||
var sounds = sourcePool.Select(s => s.Key).Where(b =>
|
||||
{
|
||||
int state;
|
||||
AL.GetSource(b, ALGetSourcei.SourceState, out state);
|
||||
return (state == (int)ALSourceState.Playing || state == (int)ALSourceState.Paused) &&
|
||||
(music == null || b != ((OpenAlSound)music).Source) &&
|
||||
(video == null || b != ((OpenAlSound)video).Source);
|
||||
});
|
||||
|
||||
foreach (var s in sounds)
|
||||
AL.Source(s, ALSourcef.Gain, volume);
|
||||
}
|
||||
|
||||
public void StopSound(ISound sound)
|
||||
{
|
||||
if (sound == null)
|
||||
return;
|
||||
|
||||
var key = ((OpenAlSound)sound).Source;
|
||||
int state;
|
||||
AL.GetSource(key, ALGetSourcei.SourceState, out state);
|
||||
if (state == (int)ALSourceState.Playing || state == (int)ALSourceState.Paused)
|
||||
AL.SourceStop(key);
|
||||
}
|
||||
|
||||
public void StopAllSounds()
|
||||
{
|
||||
foreach (var key in sourcePool.Keys)
|
||||
{
|
||||
int state;
|
||||
AL.GetSource(key, ALGetSourcei.SourceState, out state);
|
||||
if (state == (int)ALSourceState.Playing || state == (int)ALSourceState.Paused)
|
||||
AL.SourceStop(key);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetListenerPosition(WPos position)
|
||||
{
|
||||
// Move the listener out of the plane so that sounds near the middle of the screen aren't too positional
|
||||
AL.Listener(ALListener3f.Position, position.X, position.Y, position.Z + 2133);
|
||||
|
||||
var orientation = new[] { 0f, 0f, 1f, 0f, -1f, 0f };
|
||||
AL.Listener(ALListenerfv.Orientation, ref orientation);
|
||||
AL.Listener(ALListenerf.EfxMetersPerUnit, .01f);
|
||||
}
|
||||
}
|
||||
|
||||
class OpenAlSoundSource : ISoundSource
|
||||
{
|
||||
public readonly int Buffer;
|
||||
|
||||
static ALFormat MakeALFormat(int channels, int bits)
|
||||
{
|
||||
if (channels == 1)
|
||||
return bits == 16 ? ALFormat.Mono16 : ALFormat.Mono8;
|
||||
else
|
||||
return bits == 16 ? ALFormat.Stereo16 : ALFormat.Stereo8;
|
||||
}
|
||||
|
||||
public OpenAlSoundSource(byte[] data, int channels, int sampleBits, int sampleRate)
|
||||
{
|
||||
AL.GenBuffers(1, out Buffer);
|
||||
AL.BufferData(Buffer, MakeALFormat(channels, sampleBits), data, data.Length, sampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
class OpenAlSound : ISound
|
||||
{
|
||||
public readonly int Source = -1;
|
||||
float volume = 1f;
|
||||
|
||||
public OpenAlSound(int source, int buffer, bool looping, bool relative, WPos pos, float volume)
|
||||
{
|
||||
if (source == -1)
|
||||
return;
|
||||
|
||||
Source = source;
|
||||
Volume = volume;
|
||||
|
||||
AL.Source(source, ALSourcef.Pitch, 1f);
|
||||
AL.Source(source, ALSource3f.Position, pos.X, pos.Y, pos.Z);
|
||||
AL.Source(source, ALSource3f.Velocity, 0f, 0f, 0f);
|
||||
AL.Source(source, ALSourcei.Buffer, buffer);
|
||||
AL.Source(source, ALSourceb.Looping, looping);
|
||||
AL.Source(source, ALSourceb.SourceRelative, relative);
|
||||
|
||||
AL.Source(source, ALSourcef.ReferenceDistance, 6826);
|
||||
AL.Source(source, ALSourcef.MaxDistance, 136533);
|
||||
AL.SourcePlay(source);
|
||||
}
|
||||
|
||||
public float Volume
|
||||
{
|
||||
get
|
||||
{
|
||||
return volume;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (Source != -1)
|
||||
AL.Source(Source, ALSourcef.Gain, volume = value);
|
||||
}
|
||||
}
|
||||
|
||||
public float SeekPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
int pos;
|
||||
AL.GetSource(Source, ALGetSourcei.SampleOffset, out pos);
|
||||
return pos / 22050f;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Playing
|
||||
{
|
||||
get
|
||||
{
|
||||
int state;
|
||||
AL.GetSource(Source, ALGetSourcei.SourceState, out state);
|
||||
return state == (int)ALSourceState.Playing;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
64
OpenRA.Platforms.Default/OpenRA.Platforms.Default.csproj
Normal file
64
OpenRA.Platforms.Default/OpenRA.Platforms.Default.csproj
Normal file
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>10.0.0</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{33D03738-C154-4028-8EA8-63A3C488A651}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>OpenRA.Platforms.Default</RootNamespace>
|
||||
<AssemblyName>OpenRA.Platforms.Default</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<OutputPath>..\</OutputPath>
|
||||
<DefineConstants>TRACE;DEBUG</DefineConstants>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>..\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Optimize>true</Optimize>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="Eluant">
|
||||
<HintPath>..\thirdparty\download\Eluant.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SDL2-CS">
|
||||
<HintPath>..\thirdparty\download\SDL2-CS.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="DefaultPlatform.cs" />
|
||||
<Compile Include="Sdl2GraphicsDevice.cs" />
|
||||
<Compile Include="Sdl2Input.cs" />
|
||||
<Compile Include="Shader.cs" />
|
||||
<Compile Include="ErrorHandler.cs" />
|
||||
<Compile Include="FrameBuffer.cs" />
|
||||
<Compile Include="MultiTapDetection.cs" />
|
||||
<Compile Include="Texture.cs" />
|
||||
<Compile Include="VertexBuffer.cs" />
|
||||
<Compile Include="OpenAlSoundEngine.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj">
|
||||
<Project>{0DFB103F-2962-400F-8C6D-E2C28CCBA633}</Project>
|
||||
<Name>OpenRA.Game</Name>
|
||||
<Private>False</Private>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
360
OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs
Normal file
360
OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs
Normal file
@@ -0,0 +1,360 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using OpenRA;
|
||||
using OpenRA.Graphics;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using SDL2;
|
||||
|
||||
namespace OpenRA.Platforms.Default
|
||||
{
|
||||
public sealed class Sdl2GraphicsDevice : IGraphicsDevice
|
||||
{
|
||||
Size size;
|
||||
Sdl2Input input;
|
||||
IntPtr context, window;
|
||||
bool disposed;
|
||||
|
||||
public Size WindowSize { get { return size; } }
|
||||
|
||||
public Sdl2GraphicsDevice(Size windowSize, WindowMode windowMode)
|
||||
{
|
||||
Console.WriteLine("Using SDL 2 with OpenGL renderer");
|
||||
|
||||
size = windowSize;
|
||||
|
||||
SDL.SDL_Init(SDL.SDL_INIT_NOPARACHUTE | SDL.SDL_INIT_VIDEO);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_RED_SIZE, 8);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_GREEN_SIZE, 8);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_BLUE_SIZE, 8);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_ALPHA_SIZE, 0);
|
||||
|
||||
SDL.SDL_DisplayMode display;
|
||||
SDL.SDL_GetCurrentDisplayMode(0, out display);
|
||||
|
||||
Console.WriteLine("Desktop resolution: {0}x{1}", display.w, display.h);
|
||||
if (size.Width == 0 && size.Height == 0)
|
||||
{
|
||||
Console.WriteLine("No custom resolution provided, using desktop resolution");
|
||||
size = new Size(display.w, display.h);
|
||||
}
|
||||
|
||||
Console.WriteLine("Using resolution: {0}x{1}", size.Width, size.Height);
|
||||
|
||||
window = SDL.SDL_CreateWindow("OpenRA", SDL.SDL_WINDOWPOS_CENTERED, SDL.SDL_WINDOWPOS_CENTERED, size.Width, size.Height, SDL.SDL_WindowFlags.SDL_WINDOW_OPENGL);
|
||||
|
||||
if (Game.Settings.Game.LockMouseWindow)
|
||||
GrabWindowMouseFocus();
|
||||
else
|
||||
ReleaseWindowMouseFocus();
|
||||
|
||||
if (windowMode == WindowMode.Fullscreen)
|
||||
SDL.SDL_SetWindowFullscreen(window, (uint)SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN);
|
||||
else if (windowMode == WindowMode.PseudoFullscreen)
|
||||
{
|
||||
// Work around a visual glitch in OSX: the window is offset
|
||||
// partially offscreen if the dock is at the left of the screen
|
||||
if (Platform.CurrentPlatform == PlatformType.OSX)
|
||||
SDL.SDL_SetWindowPosition(window, 0, 0);
|
||||
|
||||
SDL.SDL_SetWindowFullscreen(window, (uint)SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
SDL.SDL_SetHint(SDL.SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
|
||||
}
|
||||
|
||||
context = SDL.SDL_GL_CreateContext(window);
|
||||
SDL.SDL_GL_MakeCurrent(window, context);
|
||||
|
||||
GL.LoadAll();
|
||||
ErrorHandler.CheckGlVersion();
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
if (SDL.SDL_GL_ExtensionSupported("GL_EXT_framebuffer_object") == SDL.SDL_bool.SDL_FALSE)
|
||||
{
|
||||
ErrorHandler.WriteGraphicsLog("OpenRA requires the OpenGL extension GL_EXT_framebuffer_object.\n"
|
||||
+ "Please try updating your GPU driver to the latest version provided by the manufacturer.");
|
||||
throw new InvalidProgramException("Missing OpenGL extension GL_EXT_framebuffer_object. See graphics.log for details.");
|
||||
}
|
||||
|
||||
GL.EnableClientState(ArrayCap.VertexArray);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.EnableClientState(ArrayCap.TextureCoordArray);
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
SDL.SDL_SetModState(0);
|
||||
input = new Sdl2Input();
|
||||
}
|
||||
|
||||
public IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new SDL2HardwareCursor(size, data, hotspot);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidDataException("Failed to create hardware cursor `{0}`".F(name), ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetHardwareCursor(IHardwareCursor cursor)
|
||||
{
|
||||
var c = cursor as SDL2HardwareCursor;
|
||||
if (c == null)
|
||||
SDL.SDL_ShowCursor(0);
|
||||
else
|
||||
{
|
||||
SDL.SDL_ShowCursor(1);
|
||||
SDL.SDL_SetCursor(c.Cursor);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class SDL2HardwareCursor : IHardwareCursor
|
||||
{
|
||||
public IntPtr Cursor { get; private set; }
|
||||
IntPtr surface;
|
||||
|
||||
public SDL2HardwareCursor(Size size, byte[] data, int2 hotspot)
|
||||
{
|
||||
try
|
||||
{
|
||||
surface = SDL.SDL_CreateRGBSurface(0, size.Width, size.Height, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
|
||||
if (surface == IntPtr.Zero)
|
||||
throw new InvalidDataException("Failed to create surface: {0}".F(SDL.SDL_GetError()));
|
||||
|
||||
var sur = (SDL2.SDL.SDL_Surface)Marshal.PtrToStructure(surface, typeof(SDL2.SDL.SDL_Surface));
|
||||
Marshal.Copy(data, 0, sur.pixels, data.Length);
|
||||
|
||||
// This call very occasionally fails on Windows, but often works when retried.
|
||||
for (var retries = 0; retries < 3 && Cursor == IntPtr.Zero; retries++)
|
||||
Cursor = SDL.SDL_CreateColorCursor(surface, hotspot.X, hotspot.Y);
|
||||
if (Cursor == IntPtr.Zero)
|
||||
throw new InvalidDataException("Failed to create cursor: {0}".F(SDL.SDL_GetError()));
|
||||
}
|
||||
catch
|
||||
{
|
||||
Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
~SDL2HardwareCursor()
|
||||
{
|
||||
Game.RunAfterTick(() => Dispose(false));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Game.RunAfterTick(() => Dispose(true));
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
void Dispose(bool disposing)
|
||||
{
|
||||
if (Cursor != IntPtr.Zero)
|
||||
{
|
||||
SDL.SDL_FreeCursor(Cursor);
|
||||
Cursor = IntPtr.Zero;
|
||||
}
|
||||
|
||||
if (surface != IntPtr.Zero)
|
||||
{
|
||||
SDL.SDL_FreeSurface(surface);
|
||||
surface = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
disposed = true;
|
||||
if (context != IntPtr.Zero)
|
||||
{
|
||||
SDL.SDL_GL_DeleteContext(context);
|
||||
context = IntPtr.Zero;
|
||||
}
|
||||
|
||||
if (window != IntPtr.Zero)
|
||||
{
|
||||
SDL.SDL_DestroyWindow(window);
|
||||
window = IntPtr.Zero;
|
||||
}
|
||||
|
||||
SDL.SDL_Quit();
|
||||
}
|
||||
|
||||
static BeginMode ModeFromPrimitiveType(PrimitiveType pt)
|
||||
{
|
||||
switch (pt)
|
||||
{
|
||||
case PrimitiveType.PointList: return BeginMode.Points;
|
||||
case PrimitiveType.LineList: return BeginMode.Lines;
|
||||
case PrimitiveType.TriangleList: return BeginMode.Triangles;
|
||||
case PrimitiveType.QuadList: return BeginMode.Quads;
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void DrawPrimitives(PrimitiveType pt, int firstVertex, int numVertices)
|
||||
{
|
||||
GL.DrawArrays(ModeFromPrimitiveType(pt), firstVertex, numVertices);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
GL.ClearColor(0, 0, 0, 1);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void EnableDepthBuffer()
|
||||
{
|
||||
GL.Clear(ClearBufferMask.DepthBufferBit);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.Enable(EnableCap.DepthTest);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void DisableDepthBuffer()
|
||||
{
|
||||
GL.Disable(EnableCap.DepthTest);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void SetBlendMode(BlendMode mode)
|
||||
{
|
||||
GL.BlendEquation(BlendEquationMode.FuncAdd);
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case BlendMode.None:
|
||||
GL.Disable(EnableCap.Blend);
|
||||
break;
|
||||
case BlendMode.Alpha:
|
||||
GL.Enable(EnableCap.Blend);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.BlendFunc(BlendingFactorSrc.One, BlendingFactorDest.OneMinusSrcAlpha);
|
||||
break;
|
||||
case BlendMode.Additive:
|
||||
case BlendMode.Subtractive:
|
||||
GL.Enable(EnableCap.Blend);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.BlendFunc(BlendingFactorSrc.One, BlendingFactorDest.One);
|
||||
if (mode == BlendMode.Subtractive)
|
||||
{
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.BlendEquation(BlendEquationMode.FuncReverseSubtract);
|
||||
}
|
||||
|
||||
break;
|
||||
case BlendMode.Multiply:
|
||||
GL.Enable(EnableCap.Blend);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.BlendFunc(BlendingFactorSrc.DstColor, BlendingFactorDest.OneMinusSrcAlpha);
|
||||
ErrorHandler.CheckGlError();
|
||||
break;
|
||||
case BlendMode.Multiplicative:
|
||||
GL.Enable(EnableCap.Blend);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.BlendFunc(BlendingFactorSrc.Zero, BlendingFactorDest.SrcColor);
|
||||
break;
|
||||
case BlendMode.DoubleMultiplicative:
|
||||
GL.Enable(EnableCap.Blend);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.BlendFunc(BlendingFactorSrc.DstColor, BlendingFactorDest.SrcColor);
|
||||
break;
|
||||
}
|
||||
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void GrabWindowMouseFocus()
|
||||
{
|
||||
SDL.SDL_SetWindowGrab(window, SDL.SDL_bool.SDL_TRUE);
|
||||
}
|
||||
|
||||
public void ReleaseWindowMouseFocus()
|
||||
{
|
||||
SDL.SDL_SetWindowGrab(window, SDL.SDL_bool.SDL_FALSE);
|
||||
}
|
||||
|
||||
public void EnableScissor(int left, int top, int width, int height)
|
||||
{
|
||||
if (width < 0)
|
||||
width = 0;
|
||||
|
||||
if (height < 0)
|
||||
height = 0;
|
||||
|
||||
GL.Scissor(left, size.Height - (top + height), width, height);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.Enable(EnableCap.ScissorTest);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void DisableScissor()
|
||||
{
|
||||
GL.Disable(EnableCap.ScissorTest);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void SetLineWidth(float width)
|
||||
{
|
||||
GL.LineWidth(width);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public Bitmap TakeScreenshot()
|
||||
{
|
||||
var rect = new Rectangle(Point.Empty, size);
|
||||
var bitmap = new Bitmap(rect.Width, rect.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||
var data = bitmap.LockBits(rect,
|
||||
System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||
|
||||
GL.PushClientAttrib(ClientAttribMask.ClientPixelStoreBit);
|
||||
|
||||
GL.PixelStore(PixelStoreParameter.PackRowLength, data.Stride / 4f);
|
||||
GL.PixelStore(PixelStoreParameter.PackAlignment, 1);
|
||||
|
||||
GL.ReadPixels(rect.X, rect.Y, rect.Width, rect.Height, PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
|
||||
GL.Finish();
|
||||
|
||||
GL.PopClientAttrib();
|
||||
|
||||
bitmap.UnlockBits(data);
|
||||
|
||||
// OpenGL standard defines the origin in the bottom left corner which is why this is upside-down by default.
|
||||
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public void Present() { SDL.SDL_GL_SwapWindow(window); }
|
||||
public void PumpInput(IInputHandler inputHandler) { input.PumpInput(inputHandler); }
|
||||
public string GetClipboardText() { return input.GetClipboardText(); }
|
||||
public IVertexBuffer<Vertex> CreateVertexBuffer(int size) { return new VertexBuffer<Vertex>(size); }
|
||||
public ITexture CreateTexture() { return new Texture(); }
|
||||
public ITexture CreateTexture(Bitmap bitmap) { return new Texture(bitmap); }
|
||||
public IFrameBuffer CreateFrameBuffer(Size s) { return new FrameBuffer(s); }
|
||||
public IShader CreateShader(string name) { return new Shader(name); }
|
||||
}
|
||||
}
|
||||
180
OpenRA.Platforms.Default/Sdl2Input.cs
Normal file
180
OpenRA.Platforms.Default/Sdl2Input.cs
Normal file
@@ -0,0 +1,180 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using SDL2;
|
||||
|
||||
namespace OpenRA.Platforms.Default
|
||||
{
|
||||
public class Sdl2Input
|
||||
{
|
||||
MouseButton lastButtonBits = (MouseButton)0;
|
||||
|
||||
public string GetClipboardText() { return SDL.SDL_GetClipboardText(); }
|
||||
|
||||
static MouseButton MakeButton(byte b)
|
||||
{
|
||||
return b == SDL.SDL_BUTTON_LEFT ? MouseButton.Left
|
||||
: b == SDL.SDL_BUTTON_RIGHT ? MouseButton.Right
|
||||
: b == SDL.SDL_BUTTON_MIDDLE ? MouseButton.Middle
|
||||
: 0;
|
||||
}
|
||||
|
||||
static Modifiers MakeModifiers(int raw)
|
||||
{
|
||||
return ((raw & (int)SDL.SDL_Keymod.KMOD_ALT) != 0 ? Modifiers.Alt : 0)
|
||||
| ((raw & (int)SDL.SDL_Keymod.KMOD_CTRL) != 0 ? Modifiers.Ctrl : 0)
|
||||
| ((raw & (int)SDL.SDL_Keymod.KMOD_LGUI) != 0 ? Modifiers.Meta : 0)
|
||||
| ((raw & (int)SDL.SDL_Keymod.KMOD_RGUI) != 0 ? Modifiers.Meta : 0)
|
||||
| ((raw & (int)SDL.SDL_Keymod.KMOD_SHIFT) != 0 ? Modifiers.Shift : 0);
|
||||
}
|
||||
|
||||
public void PumpInput(IInputHandler inputHandler)
|
||||
{
|
||||
var mods = MakeModifiers((int)SDL.SDL_GetModState());
|
||||
var scrollDelta = 0;
|
||||
inputHandler.ModifierKeys(mods);
|
||||
MouseInput? pendingMotion = null;
|
||||
|
||||
SDL.SDL_Event e;
|
||||
while (SDL.SDL_PollEvent(out e) != 0)
|
||||
{
|
||||
switch (e.type)
|
||||
{
|
||||
case SDL.SDL_EventType.SDL_QUIT:
|
||||
Game.Exit();
|
||||
break;
|
||||
|
||||
case SDL.SDL_EventType.SDL_WINDOWEVENT:
|
||||
{
|
||||
switch (e.window.windowEvent)
|
||||
{
|
||||
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
Game.HasInputFocus = false;
|
||||
break;
|
||||
|
||||
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||
Game.HasInputFocus = true;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN:
|
||||
{
|
||||
if (pendingMotion != null)
|
||||
{
|
||||
inputHandler.OnMouseInput(pendingMotion.Value);
|
||||
pendingMotion = null;
|
||||
}
|
||||
|
||||
var button = MakeButton(e.button.button);
|
||||
lastButtonBits |= button;
|
||||
|
||||
var pos = new int2(e.button.x, e.button.y);
|
||||
|
||||
inputHandler.OnMouseInput(new MouseInput(
|
||||
MouseInputEvent.Down, button, scrollDelta, pos, mods,
|
||||
MultiTapDetection.DetectFromMouse(e.button.button, pos)));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL.SDL_EventType.SDL_MOUSEBUTTONUP:
|
||||
{
|
||||
if (pendingMotion != null)
|
||||
{
|
||||
inputHandler.OnMouseInput(pendingMotion.Value);
|
||||
pendingMotion = null;
|
||||
}
|
||||
|
||||
var button = MakeButton(e.button.button);
|
||||
lastButtonBits &= ~button;
|
||||
|
||||
var pos = new int2(e.button.x, e.button.y);
|
||||
inputHandler.OnMouseInput(new MouseInput(
|
||||
MouseInputEvent.Up, button, scrollDelta, pos, mods,
|
||||
MultiTapDetection.InfoFromMouse(e.button.button)));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL.SDL_EventType.SDL_MOUSEMOTION:
|
||||
{
|
||||
pendingMotion = new MouseInput(
|
||||
MouseInputEvent.Move, lastButtonBits, scrollDelta,
|
||||
new int2(e.motion.x, e.motion.y), mods, 0);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL.SDL_EventType.SDL_MOUSEWHEEL:
|
||||
{
|
||||
int x, y;
|
||||
SDL.SDL_GetMouseState(out x, out y);
|
||||
scrollDelta = e.wheel.y;
|
||||
inputHandler.OnMouseInput(new MouseInput(MouseInputEvent.Scroll, MouseButton.None, scrollDelta, new int2(x, y), Modifiers.None, 0));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL.SDL_EventType.SDL_TEXTINPUT:
|
||||
{
|
||||
var rawBytes = new byte[SDL.SDL_TEXTINPUTEVENT_TEXT_SIZE];
|
||||
unsafe { Marshal.Copy((IntPtr)e.text.text, rawBytes, 0, SDL.SDL_TEXTINPUTEVENT_TEXT_SIZE); }
|
||||
inputHandler.OnTextInput(Encoding.UTF8.GetString(rawBytes, 0, Array.IndexOf(rawBytes, (byte)0)));
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL.SDL_EventType.SDL_KEYDOWN:
|
||||
case SDL.SDL_EventType.SDL_KEYUP:
|
||||
{
|
||||
var keyCode = (Keycode)e.key.keysym.sym;
|
||||
var type = e.type == SDL.SDL_EventType.SDL_KEYDOWN ?
|
||||
KeyInputEvent.Down : KeyInputEvent.Up;
|
||||
|
||||
var tapCount = e.type == SDL.SDL_EventType.SDL_KEYDOWN ?
|
||||
MultiTapDetection.DetectFromKeyboard(keyCode) :
|
||||
MultiTapDetection.InfoFromKeyboard(keyCode);
|
||||
|
||||
var keyEvent = new KeyInput
|
||||
{
|
||||
Event = type,
|
||||
Key = keyCode,
|
||||
Modifiers = mods,
|
||||
UnicodeChar = (char)e.key.keysym.sym,
|
||||
MultiTapCount = tapCount
|
||||
};
|
||||
|
||||
// Special case workaround for windows users
|
||||
if (e.key.keysym.sym == SDL.SDL_Keycode.SDLK_F4 && mods.HasModifier(Modifiers.Alt) &&
|
||||
Platform.CurrentPlatform == PlatformType.Windows)
|
||||
Game.Exit();
|
||||
else
|
||||
inputHandler.OnKeyInput(keyEvent);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pendingMotion != null)
|
||||
{
|
||||
inputHandler.OnMouseInput(pendingMotion.Value);
|
||||
pendingMotion = null;
|
||||
}
|
||||
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
}
|
||||
}
|
||||
198
OpenRA.Platforms.Default/Shader.cs
Normal file
198
OpenRA.Platforms.Default/Shader.cs
Normal file
@@ -0,0 +1,198 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using OpenRA.FileSystem;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
|
||||
namespace OpenRA.Platforms.Default
|
||||
{
|
||||
public class Shader : IShader
|
||||
{
|
||||
readonly Dictionary<string, int> samplers = new Dictionary<string, int>();
|
||||
readonly Dictionary<int, ITexture> textures = new Dictionary<int, ITexture>();
|
||||
int program;
|
||||
|
||||
protected int CompileShaderObject(ShaderType type, string name)
|
||||
{
|
||||
string ext = type == ShaderType.VertexShader ? "vert" : "frag";
|
||||
string filename = "glsl{0}{1}.{2}".F(Path.DirectorySeparatorChar, name, ext);
|
||||
string code;
|
||||
using (var file = new StreamReader(GlobalFileSystem.Open(filename)))
|
||||
code = file.ReadToEnd();
|
||||
|
||||
var shader = GL.CreateShader(type);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.ShaderSource(shader, code);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.CompileShader(shader);
|
||||
ErrorHandler.CheckGlError();
|
||||
int success;
|
||||
GL.GetShader(shader, ShaderParameter.CompileStatus, out success);
|
||||
ErrorHandler.CheckGlError();
|
||||
if (success == (int)All.False)
|
||||
{
|
||||
int len;
|
||||
GL.GetShader(shader, ShaderParameter.InfoLogLength, out len);
|
||||
var log = new StringBuilder(len);
|
||||
unsafe
|
||||
{
|
||||
GL.GetShaderInfoLog(shader, len, null, log);
|
||||
}
|
||||
|
||||
Log.Write("graphics", "GL Info Log:\n{0}", log.ToString());
|
||||
throw new InvalidProgramException("Compile error in shader object '{0}'".F(filename));
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
public Shader(string name)
|
||||
{
|
||||
var vertexShader = CompileShaderObject(ShaderType.VertexShader, name);
|
||||
var fragmentShader = CompileShaderObject(ShaderType.FragmentShader, name);
|
||||
|
||||
// Assemble program
|
||||
program = GL.CreateProgram();
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.AttachShader(program, vertexShader);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.AttachShader(program, fragmentShader);
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
GL.LinkProgram(program);
|
||||
ErrorHandler.CheckGlError();
|
||||
int success;
|
||||
GL.GetProgram(program, ProgramParameter.LinkStatus, out success);
|
||||
ErrorHandler.CheckGlError();
|
||||
if (success == (int)All.False)
|
||||
{
|
||||
int len;
|
||||
GL.GetProgram(program, ProgramParameter.InfoLogLength, out len);
|
||||
var log = new StringBuilder(len);
|
||||
unsafe
|
||||
{
|
||||
GL.GetProgramInfoLog(program, len, null, log);
|
||||
}
|
||||
|
||||
Log.Write("graphics", "GL Info Log:\n{0}", log.ToString());
|
||||
throw new InvalidProgramException("Link error in shader program '{0}'".F(name));
|
||||
}
|
||||
|
||||
GL.UseProgram(program);
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
int numUniforms;
|
||||
GL.GetProgram(program, ProgramParameter.ActiveUniforms, out numUniforms);
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
var nextTexUnit = 0;
|
||||
for (var i = 0; i < numUniforms; i++)
|
||||
{
|
||||
int length, size;
|
||||
ActiveUniformType type;
|
||||
var sb = new StringBuilder(128);
|
||||
GL.GetActiveUniform(program, i, 128, out length, out size, out type, sb);
|
||||
var sampler = sb.ToString();
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
if (type == ActiveUniformType.Sampler2D)
|
||||
{
|
||||
samplers.Add(sampler, nextTexUnit);
|
||||
|
||||
var loc = GL.GetUniformLocation(program, sampler);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.Uniform1(loc, nextTexUnit);
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
nextTexUnit++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Render(Action a)
|
||||
{
|
||||
GL.UseProgram(program);
|
||||
|
||||
// bind the textures
|
||||
foreach (var kv in textures)
|
||||
{
|
||||
GL.ActiveTexture(TextureUnit.Texture0 + kv.Key);
|
||||
GL.BindTexture(TextureTarget.Texture2D, ((Texture)kv.Value).ID);
|
||||
}
|
||||
|
||||
ErrorHandler.CheckGlError();
|
||||
a();
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void SetTexture(string name, ITexture t)
|
||||
{
|
||||
if (t == null)
|
||||
return;
|
||||
|
||||
int texUnit;
|
||||
if (samplers.TryGetValue(name, out texUnit))
|
||||
textures[texUnit] = t;
|
||||
}
|
||||
|
||||
public void SetVec(string name, float x)
|
||||
{
|
||||
GL.UseProgram(program);
|
||||
ErrorHandler.CheckGlError();
|
||||
var param = GL.GetUniformLocation(program, name);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.Uniform1(param, x);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void SetVec(string name, float x, float y)
|
||||
{
|
||||
GL.UseProgram(program);
|
||||
ErrorHandler.CheckGlError();
|
||||
var param = GL.GetUniformLocation(program, name);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.Uniform2(param, x, y);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void SetVec(string name, float[] vec, int length)
|
||||
{
|
||||
var param = GL.GetUniformLocation(program, name);
|
||||
ErrorHandler.CheckGlError();
|
||||
switch (length)
|
||||
{
|
||||
case 1: GL.Uniform1(param, 1, vec); break;
|
||||
case 2: GL.Uniform2(param, 1, vec); break;
|
||||
case 3: GL.Uniform3(param, 1, vec); break;
|
||||
case 4: GL.Uniform4(param, 1, vec); break;
|
||||
default: throw new InvalidDataException("Invalid vector length");
|
||||
}
|
||||
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void SetMatrix(string name, float[] mtx)
|
||||
{
|
||||
if (mtx.Length != 16)
|
||||
throw new InvalidDataException("Invalid 4x4 matrix");
|
||||
|
||||
GL.UseProgram(program);
|
||||
ErrorHandler.CheckGlError();
|
||||
var param = GL.GetUniformLocation(program, name);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.UniformMatrix4(param, 1, false, mtx);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
}
|
||||
}
|
||||
204
OpenRA.Platforms.Default/Texture.cs
Normal file
204
OpenRA.Platforms.Default/Texture.cs
Normal file
@@ -0,0 +1,204 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
|
||||
namespace OpenRA.Platforms.Default
|
||||
{
|
||||
public sealed class Texture : ITexture
|
||||
{
|
||||
int texture;
|
||||
TextureScaleFilter scaleFilter;
|
||||
Size size;
|
||||
|
||||
public int ID { get { return texture; } }
|
||||
|
||||
public Size Size { get { return size; } }
|
||||
|
||||
bool disposed;
|
||||
|
||||
public TextureScaleFilter ScaleFilter
|
||||
{
|
||||
get
|
||||
{
|
||||
return scaleFilter;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (scaleFilter == value)
|
||||
return;
|
||||
|
||||
scaleFilter = value;
|
||||
PrepareTexture();
|
||||
}
|
||||
}
|
||||
|
||||
public Texture()
|
||||
{
|
||||
GL.GenTextures(1, out texture);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public Texture(Bitmap bitmap)
|
||||
{
|
||||
GL.GenTextures(1, out texture);
|
||||
ErrorHandler.CheckGlError();
|
||||
SetData(bitmap);
|
||||
}
|
||||
|
||||
void PrepareTexture()
|
||||
{
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.BindTexture(TextureTarget.Texture2D, texture);
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
var filter = scaleFilter == TextureScaleFilter.Linear ? (int)TextureMinFilter.Linear : (int)TextureMinFilter.Nearest;
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, filter);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, filter);
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (float)TextureWrapMode.ClampToEdge);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (float)TextureWrapMode.ClampToEdge);
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureBaseLevel, 0);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMaxLevel, 0);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void SetData(byte[] colors, int width, int height)
|
||||
{
|
||||
if (!Exts.IsPowerOf2(width) || !Exts.IsPowerOf2(height))
|
||||
throw new InvalidDataException("Non-power-of-two array {0}x{1}".F(width, height));
|
||||
|
||||
size = new Size(width, height);
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* ptr = &colors[0])
|
||||
{
|
||||
var intPtr = new IntPtr((void*)ptr);
|
||||
PrepareTexture();
|
||||
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba8, width, height,
|
||||
0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, intPtr);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// An array of RGBA
|
||||
public void SetData(uint[,] colors)
|
||||
{
|
||||
var width = colors.GetUpperBound(1) + 1;
|
||||
var height = colors.GetUpperBound(0) + 1;
|
||||
|
||||
if (!Exts.IsPowerOf2(width) || !Exts.IsPowerOf2(height))
|
||||
throw new InvalidDataException("Non-power-of-two array {0}x{1}".F(width, height));
|
||||
|
||||
size = new Size(width, height);
|
||||
unsafe
|
||||
{
|
||||
fixed (uint* ptr = &colors[0, 0])
|
||||
{
|
||||
var intPtr = new IntPtr((void*)ptr);
|
||||
PrepareTexture();
|
||||
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba8, width, height,
|
||||
0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, intPtr);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetData(Bitmap bitmap)
|
||||
{
|
||||
var allocatedBitmap = false;
|
||||
if (!Exts.IsPowerOf2(bitmap.Width) || !Exts.IsPowerOf2(bitmap.Height))
|
||||
{
|
||||
bitmap = new Bitmap(bitmap, bitmap.Size.NextPowerOf2());
|
||||
allocatedBitmap = true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
size = new Size(bitmap.Width, bitmap.Height);
|
||||
var bits = bitmap.LockBits(bitmap.Bounds(),
|
||||
ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||
|
||||
PrepareTexture();
|
||||
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba8, bits.Width, bits.Height,
|
||||
0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, bits.Scan0); // TODO: weird strides
|
||||
ErrorHandler.CheckGlError();
|
||||
bitmap.UnlockBits(bits);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (allocatedBitmap)
|
||||
bitmap.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] GetData()
|
||||
{
|
||||
var data = new byte[4 * size.Width * size.Height];
|
||||
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.BindTexture(TextureTarget.Texture2D, texture);
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* ptr = &data[0])
|
||||
{
|
||||
var intPtr = new IntPtr((void*)ptr);
|
||||
GL.GetTexImage(TextureTarget.Texture2D, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, intPtr);
|
||||
}
|
||||
}
|
||||
|
||||
ErrorHandler.CheckGlError();
|
||||
return data;
|
||||
}
|
||||
|
||||
public void SetEmpty(int width, int height)
|
||||
{
|
||||
if (!Exts.IsPowerOf2(width) || !Exts.IsPowerOf2(height))
|
||||
throw new InvalidDataException("Non-power-of-two array {0}x{1}".F(width, height));
|
||||
|
||||
size = new Size(width, height);
|
||||
PrepareTexture();
|
||||
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba8, width, height,
|
||||
0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, IntPtr.Zero);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
~Texture()
|
||||
{
|
||||
Game.RunAfterTick(() => Dispose(false));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Game.RunAfterTick(() => Dispose(true));
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
disposed = true;
|
||||
GL.DeleteTextures(1, ref texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
90
OpenRA.Platforms.Default/VertexBuffer.cs
Normal file
90
OpenRA.Platforms.Default/VertexBuffer.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
|
||||
namespace OpenRA.Platforms.Default
|
||||
{
|
||||
public sealed class VertexBuffer<T> : IVertexBuffer<T>
|
||||
where T : struct
|
||||
{
|
||||
static readonly int VertexSize = Marshal.SizeOf(typeof(T));
|
||||
int buffer;
|
||||
bool disposed;
|
||||
|
||||
public VertexBuffer(int size)
|
||||
{
|
||||
GL.GenBuffers(1, out buffer);
|
||||
ErrorHandler.CheckGlError();
|
||||
Bind();
|
||||
GL.BufferData(BufferTarget.ArrayBuffer,
|
||||
new IntPtr(VertexSize * size),
|
||||
new T[size],
|
||||
BufferUsageHint.DynamicDraw);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void SetData(T[] data, int length)
|
||||
{
|
||||
SetData(data, 0, length);
|
||||
}
|
||||
|
||||
public void SetData(T[] data, int start, int length)
|
||||
{
|
||||
Bind();
|
||||
GL.BufferSubData(BufferTarget.ArrayBuffer,
|
||||
new IntPtr(VertexSize * start),
|
||||
new IntPtr(VertexSize * length),
|
||||
data);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void SetData(IntPtr data, int start, int length)
|
||||
{
|
||||
Bind();
|
||||
GL.BufferSubData(BufferTarget.ArrayBuffer,
|
||||
new IntPtr(VertexSize * start),
|
||||
new IntPtr(VertexSize * length),
|
||||
data);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void Bind()
|
||||
{
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, buffer);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.VertexPointer(3, VertexPointerType.Float, VertexSize, IntPtr.Zero);
|
||||
ErrorHandler.CheckGlError();
|
||||
GL.TexCoordPointer(4, TexCoordPointerType.Float, VertexSize, new IntPtr(12));
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
~VertexBuffer()
|
||||
{
|
||||
Game.RunAfterTick(() => Dispose(false));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Game.RunAfterTick(() => Dispose(true));
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
disposed = true;
|
||||
GL.DeleteBuffers(1, ref buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user