diff --git a/Makefile b/Makefile index 2cfca502f1..688fbf1f40 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ CSC = gmcs CSFLAGS = -nologo -warn:4 -debug:+ -debug:full -optimize- -codepage:utf8 -unsafe DEFINE = DEBUG;TRACE -PROGRAMS = fileformats rcg game ra cnc seqed editor ralint filex tsbuild utility +PROGRAMS = fileformats rcg rgl game ra cnc seqed editor ralint filex tsbuild utility prefix = /usr/local datarootdir = $(prefix)/share datadir = $(datarootdir) @@ -25,8 +25,15 @@ rcg_DEPS = $(fileformats_TARGET) $(game_TARGET) rcg_LIBS = $(COMMON_LIBS) System.Windows.Forms.dll \ thirdparty/Tao/Tao.Cg.dll thirdparty/Tao/Tao.OpenGl.dll thirdparty/Tao/Tao.Sdl.dll \ $(rcg_DEPS) $(game_TARGET) +rgl_SRCS = $(shell find OpenRA.Renderer.Gl/ -iname '*.cs') +rgl_TARGET = OpenRA.Renderer.Gl.dll +rgl_KIND = library +rgl_DEPS = $(fileformats_TARGET) $(game_TARGET) +rgl_LIBS = $(COMMON_LIBS) System.Windows.Forms.dll \ + thirdparty/Tao/Tao.Cg.dll thirdparty/Tao/Tao.OpenGl.dll thirdparty/Tao/Tao.Sdl.dll \ + $(rgl_DEPS) $(game_TARGET) -game_SRCS = $(shell find OpenRA.Game/ -iname '*.cs') +game_SRCS = $(shell find OpenRA.Game/ -iname '*.cs') game_TARGET = OpenRA.Game.exe game_KIND = winexe game_DEPS = $(fileformats_TARGET) @@ -91,7 +98,7 @@ utility_LIBS = $(COMMON_LIBS) $(utility_DEPS) .SUFFIXES: .PHONY: clean all game tool default mods mod_ra mod_cnc install uninstall editor_res editor tsbuild ralint seqed filex utility -game: $(fileformats_TARGET) $(rcg_TARGET) $(game_TARGET) $(ra_TARGET) $(cnc_TARGET) $(utility_TARGET) +game: $(fileformats_TARGET) $(rcg_TARGET) $(rgl_TARGET) $(game_TARGET) $(ra_TARGET) $(cnc_TARGET) $(utility_TARGET) clean: @-rm *.exe *.dll *.mdb mods/**/*.dll mods/**/*.mdb *.resources diff --git a/OpenRA.Renderer.Cg/OpenRA.Renderer.Cg.csproj b/OpenRA.Renderer.Cg/OpenRA.Renderer.Cg.csproj index 3ebcbf77bb..b6522cb733 100644 --- a/OpenRA.Renderer.Cg/OpenRA.Renderer.Cg.csproj +++ b/OpenRA.Renderer.Cg/OpenRA.Renderer.Cg.csproj @@ -8,8 +8,8 @@ {67CF1A10-C5F6-48FA-B1A7-FE83BE4CE2CC} Library Properties - OpenRA.GlRenderer - OpenRA.Gl + OpenRA.Renderer.Cg + OpenRA.Renderer.Cg v3.5 512 diff --git a/OpenRA.Renderer.Cg/Properties/AssemblyInfo.cs b/OpenRA.Renderer.Cg/Properties/AssemblyInfo.cs index d8fc8da918..cb3698539a 100644 --- a/OpenRA.Renderer.Cg/Properties/AssemblyInfo.cs +++ b/OpenRA.Renderer.Cg/Properties/AssemblyInfo.cs @@ -5,11 +5,11 @@ using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("OpenRA.Gl")] +[assembly: AssemblyTitle("OpenRA.Renderer.Cg")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("OpenRA.Gl")] +[assembly: AssemblyProduct("OpenRA.Renderer.Cg")] [assembly: AssemblyCopyright("Copyright © Microsoft 2010")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/OpenRA.Renderer.Gl/GraphicsDevice.cs b/OpenRA.Renderer.Gl/GraphicsDevice.cs new file mode 100755 index 0000000000..70d98d8b1c --- /dev/null +++ b/OpenRA.Renderer.Gl/GraphicsDevice.cs @@ -0,0 +1,317 @@ +#region Copyright & License Information +/* +* Copyright 2007-2010 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 LICENSE. +*/ +#endregion + +using System; +using System.Drawing; +using System.IO; +using OpenRA.FileFormats.Graphics; +using Tao.Cg; +using Tao.OpenGl; +using Tao.Sdl; + +[assembly: Renderer(typeof(OpenRA.Renderer.Glsl.GraphicsDevice))] + +namespace OpenRA.Renderer.Glsl +{ + public class GraphicsDevice : IGraphicsDevice + { + Size windowSize; + internal IntPtr cgContext; + internal int vertexProfile, fragmentProfile; + + IntPtr surf; + + public Size WindowSize { get { return windowSize; } } + + public enum GlError + { + GL_NO_ERROR = Gl.GL_NO_ERROR, + GL_INVALID_ENUM = Gl.GL_INVALID_ENUM, + GL_INVALID_VALUE = Gl.GL_INVALID_VALUE, + GL_STACK_OVERFLOW = Gl.GL_STACK_OVERFLOW, + GL_STACK_UNDERFLOW = Gl.GL_STACK_UNDERFLOW, + GL_OUT_OF_MEMORY = Gl.GL_OUT_OF_MEMORY, + GL_TABLE_TOO_LARGE = Gl.GL_TABLE_TOO_LARGE, + } + + internal static void CheckGlError() + { + var n = Gl.glGetError(); + if( n != Gl.GL_NO_ERROR ) + throw new InvalidOperationException( "GL Error: " + ( (GlError)n ).ToString() ); + } + + public GraphicsDevice( int width, int height, WindowMode window, bool vsync ) + { + Sdl.SDL_Init( Sdl.SDL_INIT_NOPARACHUTE | Sdl.SDL_INIT_VIDEO ); + Sdl.SDL_GL_SetAttribute( Sdl.SDL_GL_DOUBLEBUFFER, 1 ); + Sdl.SDL_GL_SetAttribute( Sdl.SDL_GL_RED_SIZE, 8 ); + Sdl.SDL_GL_SetAttribute( Sdl.SDL_GL_GREEN_SIZE, 8 ); + Sdl.SDL_GL_SetAttribute( Sdl.SDL_GL_BLUE_SIZE, 8 ); + Sdl.SDL_GL_SetAttribute( Sdl.SDL_GL_ALPHA_SIZE, 0 ); + + int windowFlags = 0; + switch( window ) + { + case WindowMode.Fullscreen: + windowFlags |= Sdl.SDL_FULLSCREEN; + break; + case WindowMode.PseudoFullscreen: + // pseudo-fullscreen only reliably works on windows; fall back to fullscreen for everyone else + windowFlags |= ( Environment.OSVersion.Platform == PlatformID.Win32NT ) ? Sdl.SDL_NOFRAME : Sdl.SDL_FULLSCREEN; + Environment.SetEnvironmentVariable( "SDL_VIDEO_WINDOW_POS", "0,0" ); + break; + default: + break; + } + + surf = Sdl.SDL_SetVideoMode( width, height, 0, Sdl.SDL_OPENGL | windowFlags ); + + Sdl.SDL_WM_SetCaption( "OpenRA", "OpenRA" ); + Sdl.SDL_ShowCursor( 0 ); + Sdl.SDL_EnableUNICODE( 1 ); + Sdl.SDL_EnableKeyRepeat( Sdl.SDL_DEFAULT_REPEAT_DELAY, Sdl.SDL_DEFAULT_REPEAT_INTERVAL ); + + CheckGlError(); + + windowSize = new Size( width, height ); + + cgContext = Tao.Cg.Cg.cgCreateContext(); + + Tao.Cg.Cg.cgSetErrorCallback( CgErrorCallback ); + + Tao.Cg.CgGl.cgGLRegisterStates( cgContext ); + Tao.Cg.CgGl.cgGLSetManageTextureParameters( cgContext, true ); + vertexProfile = CgGl.cgGLGetLatestProfile( CgGl.CG_GL_VERTEX ); + fragmentProfile = CgGl.cgGLGetLatestProfile( CgGl.CG_GL_FRAGMENT ); + + //Console.WriteLine("VP Profile: " + vertexProfile); + //Console.WriteLine("FP Profile: " + fragmentProfile); + + Gl.glEnableClientState( Gl.GL_VERTEX_ARRAY ); + CheckGlError(); + Gl.glEnableClientState( Gl.GL_TEXTURE_COORD_ARRAY ); + CheckGlError(); + + Sdl.SDL_SetModState( 0 ); // i have had enough. + } + + static Tao.Cg.Cg.CGerrorCallbackFuncDelegate CgErrorCallback = () => + { + var err = Tao.Cg.Cg.cgGetError(); + var str = Tao.Cg.Cg.cgGetErrorString( err ); + throw new InvalidOperationException( + string.Format( "CG Error: {0}: {1}", err, str ) ); + }; + + public void EnableScissor( int left, int top, int width, int height ) + { + if( width < 0 ) width = 0; + if( height < 0 ) height = 0; + Gl.glScissor( left, windowSize.Height - ( top + height ), width, height ); + CheckGlError(); + Gl.glEnable( Gl.GL_SCISSOR_TEST ); + CheckGlError(); + } + + public void DisableScissor() + { + Gl.glDisable( Gl.GL_SCISSOR_TEST ); + CheckGlError(); + } + + public void Clear( Color c ) + { + Gl.glClearColor( 0, 0, 0, 0 ); + CheckGlError(); + Gl.glClear( Gl.GL_COLOR_BUFFER_BIT ); + CheckGlError(); + } + + MouseButton lastButtonBits = (MouseButton)0; + + 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 & Sdl.KMOD_ALT ) != 0 ? Modifiers.Alt : 0 ) + | ( ( raw & Sdl.KMOD_CTRL ) != 0 ? Modifiers.Ctrl : 0 ) + | ( ( raw & Sdl.KMOD_SHIFT ) != 0 ? Modifiers.Shift : 0 ); + } + + bool HandleSpecialKey( KeyInput k ) + { + switch( k.VirtKey ) + { + case Sdl.SDLK_F13: + var path = Environment.GetFolderPath( Environment.SpecialFolder.Personal ) + + Path.DirectorySeparatorChar + DateTime.UtcNow.ToString( "OpenRA-yyyy-MM-ddThhmmssZ" ) + ".bmp"; + Sdl.SDL_SaveBMP( surf, path ); + return true; + + case Sdl.SDLK_F4: + if( k.Modifiers.HasModifier( Modifiers.Alt ) ) + { + OpenRA.Game.Exit(); + return true; + } + return false; + + default: + return false; + } + } + + public void Present( IInputHandler inputHandler ) + { + Sdl.SDL_GL_SwapBuffers(); + Game.HasInputFocus = 0 != ( Sdl.SDL_GetAppState() & Sdl.SDL_APPINPUTFOCUS ); + + var mods = MakeModifiers( Sdl.SDL_GetModState() ); + inputHandler.ModifierKeys( mods ); + MouseInput? pendingMotion = null; + + Sdl.SDL_Event e; + while( Sdl.SDL_PollEvent( out e ) != 0 ) + { + switch( e.type ) + { + case Sdl.SDL_QUIT: + OpenRA.Game.Exit(); + break; + + case Sdl.SDL_MOUSEBUTTONDOWN: + { + if( pendingMotion != null ) + { + inputHandler.OnMouseInput( pendingMotion.Value ); + pendingMotion = null; + } + + var button = MakeButton( e.button.button ); + lastButtonBits |= button; + + inputHandler.OnMouseInput( new MouseInput( + MouseInputEvent.Down, button, new int2( e.button.x, e.button.y ), mods ) ); + } break; + + case Sdl.SDL_MOUSEBUTTONUP: + { + if( pendingMotion != null ) + { + inputHandler.OnMouseInput( pendingMotion.Value ); + pendingMotion = null; + } + + var button = MakeButton( e.button.button ); + lastButtonBits &= ~button; + + inputHandler.OnMouseInput( new MouseInput( + MouseInputEvent.Up, button, new int2( e.button.x, e.button.y ), mods ) ); + } break; + + case Sdl.SDL_MOUSEMOTION: + { + pendingMotion = new MouseInput( + MouseInputEvent.Move, + lastButtonBits, + new int2( e.motion.x, e.motion.y ), + mods ); + } break; + + case Sdl.SDL_KEYDOWN: + { + var keyEvent = new KeyInput + { + Event = KeyInputEvent.Down, + Modifiers = mods, + KeyChar = (char)e.key.keysym.unicode, + KeyName = Sdl.SDL_GetKeyName( e.key.keysym.sym ), + VirtKey = e.key.keysym.sym + }; + + if( !HandleSpecialKey( keyEvent ) ) + inputHandler.OnKeyInput( keyEvent ); + } break; + + case Sdl.SDL_KEYUP: + { + var keyEvent = new KeyInput + { + Event = KeyInputEvent.Up, + Modifiers = mods, + KeyChar = (char)e.key.keysym.unicode, + KeyName = Sdl.SDL_GetKeyName( e.key.keysym.sym ), + VirtKey = e.key.keysym.sym + }; + + inputHandler.OnKeyInput( keyEvent ); + } break; + } + } + + if( pendingMotion != null ) + { + inputHandler.OnMouseInput( pendingMotion.Value ); + pendingMotion = null; + } + + CheckGlError(); + } + + public void DrawIndexedPrimitives( PrimitiveType pt, Range vertices, Range indices ) + { + Gl.glDrawElements( ModeFromPrimitiveType( pt ), indices.End - indices.Start, + Gl.GL_UNSIGNED_SHORT, new IntPtr( indices.Start * 2 ) ); + CheckGlError(); + } + + public void DrawIndexedPrimitives( PrimitiveType pt, int numVerts, int numPrimitives ) + { + Gl.glDrawElements( ModeFromPrimitiveType( pt ), numPrimitives * IndicesPerPrimitive( pt ), + Gl.GL_UNSIGNED_SHORT, IntPtr.Zero ); + CheckGlError(); + } + + static int ModeFromPrimitiveType( PrimitiveType pt ) + { + switch( pt ) + { + case PrimitiveType.PointList: return Gl.GL_POINTS; + case PrimitiveType.LineList: return Gl.GL_LINES; + case PrimitiveType.TriangleList: return Gl.GL_TRIANGLES; + } + throw new NotImplementedException(); + } + + static int IndicesPerPrimitive( PrimitiveType pt ) + { + switch( pt ) + { + case PrimitiveType.PointList: return 1; + case PrimitiveType.LineList: return 2; + case PrimitiveType.TriangleList: return 3; + } + throw new NotImplementedException(); + } + + public IVertexBuffer CreateVertexBuffer( int size ) { return new VertexBuffer( this, size ); } + public IIndexBuffer CreateIndexBuffer( int size ) { return new IndexBuffer( this, size ); } + public ITexture CreateTexture() { return new Texture( this ); } + public ITexture CreateTexture( Bitmap bitmap ) { return new Texture( this, bitmap ); } + public IShader CreateShader( string name ) { return new Shader( this, name ); } + } +} diff --git a/OpenRA.Renderer.Gl/IndexBuffer.cs b/OpenRA.Renderer.Gl/IndexBuffer.cs new file mode 100644 index 0000000000..8a4553ca57 --- /dev/null +++ b/OpenRA.Renderer.Gl/IndexBuffer.cs @@ -0,0 +1,61 @@ +#region Copyright & License Information +/* + * Copyright 2007-2010 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 LICENSE. + */ +#endregion + +using System; +using OpenRA.FileFormats.Graphics; +using Tao.OpenGl; + +namespace OpenRA.Renderer.Glsl +{ + public class IndexBuffer : IIndexBuffer, IDisposable + { + int buffer; + + public IndexBuffer(GraphicsDevice dev, int size) + { + Gl.glGenBuffers(1, out buffer); + GraphicsDevice.CheckGlError(); + Bind(); + Gl.glBufferData(Gl.GL_ELEMENT_ARRAY_BUFFER, + new IntPtr(2 * size), + new ushort[ size ], + Gl.GL_DYNAMIC_DRAW); + GraphicsDevice.CheckGlError(); + } + + public void SetData(ushort[] data, int length) + { + Bind(); + Gl.glBufferSubData(Gl.GL_ELEMENT_ARRAY_BUFFER, + IntPtr.Zero, + new IntPtr(2 * length), + data); + GraphicsDevice.CheckGlError(); + } + + public void Bind() + { + Gl.glBindBuffer(Gl.GL_ELEMENT_ARRAY_BUFFER, buffer); + GraphicsDevice.CheckGlError(); + } + + bool disposed; + public void Dispose() + { + if (disposed) return; + GC.SuppressFinalize(this); + Gl.glDeleteBuffers(1, ref buffer); + GraphicsDevice.CheckGlError(); + disposed = true; + } + + //~IndexBuffer() { Dispose(); } + } +} diff --git a/OpenRA.Renderer.Gl/OpenRA.Renderer.Gl.csproj b/OpenRA.Renderer.Gl/OpenRA.Renderer.Gl.csproj new file mode 100644 index 0000000000..169b93611e --- /dev/null +++ b/OpenRA.Renderer.Gl/OpenRA.Renderer.Gl.csproj @@ -0,0 +1,78 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {E9C01A96-C3E2-4772-825B-A740AC513D31} + Library + OpenRA.Renderer.Glsl + OpenRA.Renderer.Gl + v3.5 + + + true + full + false + bin\Debug + DEBUG + prompt + 4 + false + true + + + none + false + bin\Release + prompt + 4 + true + false + + + + + False + ..\thirdparty\Tao\Tao.OpenGl.dll + + + False + ..\thirdparty\Tao\Tao.Sdl.dll + + + 3.5 + + + + + + + False + ..\thirdparty\Tao\Tao.Cg.dll + + + + + + + + + + + + + + + + + {BDAEAB25-991E-46A7-AF1E-4F0E03358DAA} + OpenRA.FileFormats + + + {0DFB103F-2962-400F-8C6D-E2C28CCBA633} + OpenRA.Game + + + \ No newline at end of file diff --git a/OpenRA.Renderer.Gl/Properties/AssemblyInfo.cs b/OpenRA.Renderer.Gl/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..cd1665bf81 --- /dev/null +++ b/OpenRA.Renderer.Gl/Properties/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("OpenRA.Renderer.Gl")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/OpenRA.Renderer.Gl/Shader.cs b/OpenRA.Renderer.Gl/Shader.cs new file mode 100644 index 0000000000..6033f294e2 --- /dev/null +++ b/OpenRA.Renderer.Gl/Shader.cs @@ -0,0 +1,87 @@ +#region Copyright & License Information +/* + * Copyright 2007-2010 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 LICENSE. + */ +#endregion + +using System; +using System.IO; +using OpenRA.FileFormats; +using OpenRA.FileFormats.Graphics; +using Tao.Cg; + +namespace OpenRA.Renderer.Glsl +{ + public class Shader : IShader + { + IntPtr effect; + IntPtr technique; + GraphicsDevice dev; + + public Shader(GraphicsDevice dev, string name) + { + this.dev = dev; + string code; + using (var file = new StreamReader(FileSystem.Open("cg{0}{1}.fx".F(Path.DirectorySeparatorChar, name)))) + code = file.ReadToEnd(); + effect = Tao.Cg.Cg.cgCreateEffect(dev.cgContext, code, null); + + if (effect == IntPtr.Zero) + { + var err = Tao.Cg.Cg.cgGetErrorString(Tao.Cg.Cg.cgGetError()); + var results = Tao.Cg.Cg.cgGetLastListing(dev.cgContext); + throw new InvalidOperationException( + string.Format("Cg compile failed ({0}):\n{1}", err, results)); + } + + technique = Tao.Cg.Cg.cgGetFirstTechnique(effect); + if (technique == IntPtr.Zero) + throw new InvalidOperationException("No techniques"); + while (Tao.Cg.Cg.cgValidateTechnique(technique) == 0) + { + technique = Tao.Cg.Cg.cgGetNextTechnique(technique); + if (technique == IntPtr.Zero) + throw new InvalidOperationException("No valid techniques"); + } + } + + public void Render(Action a) + { + Tao.Cg.CgGl.cgGLEnableProfile(dev.vertexProfile); + Tao.Cg.CgGl.cgGLEnableProfile(dev.fragmentProfile); + + var pass = Tao.Cg.Cg.cgGetFirstPass(technique); + while (pass != IntPtr.Zero) + { + Tao.Cg.Cg.cgSetPassState(pass); + a(); + Tao.Cg.Cg.cgResetPassState(pass); + pass = Tao.Cg.Cg.cgGetNextPass(pass); + } + + Tao.Cg.CgGl.cgGLDisableProfile(dev.fragmentProfile); + Tao.Cg.CgGl.cgGLDisableProfile(dev.vertexProfile); + } + + public void SetValue(string name, ITexture t) + { + var texture = (Texture)t; + var param = Tao.Cg.Cg.cgGetNamedEffectParameter(effect, name); + if (param != IntPtr.Zero && texture != null) + Tao.Cg.CgGl.cgGLSetupSampler(param, texture.texture); + } + + public void SetValue(string name, float x, float y) + { + var param = Tao.Cg.Cg.cgGetNamedEffectParameter(effect, name); + if (param != IntPtr.Zero) + Tao.Cg.CgGl.cgGLSetParameter2f(param, x, y); + } + + public void Commit() { } + } +} diff --git a/OpenRA.Renderer.Gl/Texture.cs b/OpenRA.Renderer.Gl/Texture.cs new file mode 100644 index 0000000000..db4f2078ec --- /dev/null +++ b/OpenRA.Renderer.Gl/Texture.cs @@ -0,0 +1,132 @@ +#region Copyright & License Information +/* + * Copyright 2007-2010 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 LICENSE. + */ +#endregion + +using System.Drawing; +using System.Drawing.Imaging; +using OpenRA.FileFormats.Graphics; +using Tao.OpenGl; +using System.IO; +using System; + +namespace OpenRA.Renderer.Glsl +{ + public class Texture : ITexture + { + internal int texture; + + public Texture(GraphicsDevice dev) + { + Gl.glGenTextures(1, out texture); + GraphicsDevice.CheckGlError(); + } + + public Texture(GraphicsDevice dev, Bitmap bitmap) + { + Gl.glGenTextures(1, out texture); + GraphicsDevice.CheckGlError(); + SetData(bitmap); + } + + public void SetData(byte[] colors, int width, int height) + { + if (!IsPowerOf2(width) || !IsPowerOf2(height)) + throw new InvalidDataException("Non-power-of-two array {0}x{1}".F(width, height)); + + unsafe + { + fixed (byte* ptr = &colors[0]) + { + IntPtr intPtr = new IntPtr((void*)ptr); + + Gl.glBindTexture(Gl.GL_TEXTURE_2D, texture); + GraphicsDevice.CheckGlError(); + Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_BASE_LEVEL, 0); + GraphicsDevice.CheckGlError(); + Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAX_LEVEL, 0); + GraphicsDevice.CheckGlError(); + Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA8, width, height, + 0, Gl.GL_BGRA, Gl.GL_UNSIGNED_BYTE, intPtr); + GraphicsDevice.CheckGlError(); + } + } + } + + // An array of RGBA + public void SetData(uint[,] colors) + { + int width = colors.GetUpperBound(1) + 1; + int height = colors.GetUpperBound(0) + 1; + + if (!IsPowerOf2(width) || !IsPowerOf2(height)) + throw new InvalidDataException("Non-power-of-two array {0}x{1}".F(width,height)); + + unsafe + { + fixed (uint* ptr = &colors[0,0]) + { + IntPtr intPtr = new IntPtr((void *) ptr); + + Gl.glBindTexture(Gl.GL_TEXTURE_2D, texture); + GraphicsDevice.CheckGlError(); + Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_BASE_LEVEL, 0); + GraphicsDevice.CheckGlError(); + Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAX_LEVEL, 0); + GraphicsDevice.CheckGlError(); + Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA8, width, height, + 0, Gl.GL_BGRA, Gl.GL_UNSIGNED_BYTE, intPtr); + GraphicsDevice.CheckGlError(); + } + } + } + + public void SetData(Bitmap bitmap) + { + if (!IsPowerOf2(bitmap.Width) || !IsPowerOf2(bitmap.Height)) + { + //throw new InvalidOperationException( "non-power-of-2-texture" ); + bitmap = new Bitmap(bitmap, new Size(NextPowerOf2(bitmap.Width), NextPowerOf2(bitmap.Height))); + } + + Gl.glBindTexture(Gl.GL_TEXTURE_2D, texture); + GraphicsDevice.CheckGlError(); + + var bits = bitmap.LockBits( + new Rectangle(0, 0, bitmap.Width, bitmap.Height), + ImageLockMode.ReadOnly, + PixelFormat.Format32bppArgb); + + Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_BASE_LEVEL, 0); + GraphicsDevice.CheckGlError(); + Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAX_LEVEL, 0); + GraphicsDevice.CheckGlError(); + Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA8, bits.Width, bits.Height, + 0, Gl.GL_BGRA, Gl.GL_UNSIGNED_BYTE, bits.Scan0); // todo: weird strides + GraphicsDevice.CheckGlError(); + + bitmap.UnlockBits(bits); + } + + bool IsPowerOf2(int v) + { + return (v & (v - 1)) == 0; + } + + int NextPowerOf2(int v) + { + --v; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + ++v; + return v; + } + } +} diff --git a/OpenRA.Renderer.Gl/VertexBuffer.cs b/OpenRA.Renderer.Gl/VertexBuffer.cs new file mode 100644 index 0000000000..70564f4c13 --- /dev/null +++ b/OpenRA.Renderer.Gl/VertexBuffer.cs @@ -0,0 +1,67 @@ +#region Copyright & License Information +/* + * Copyright 2007-2010 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 LICENSE. + */ +#endregion + +using System; +using System.Runtime.InteropServices; +using OpenRA.FileFormats.Graphics; +using Tao.OpenGl; + +namespace OpenRA.Renderer.Glsl +{ + public class VertexBuffer : IVertexBuffer, IDisposable + where T : struct + { + int buffer; + + public VertexBuffer(GraphicsDevice dev, int size) + { + Gl.glGenBuffers(1, out buffer); + GraphicsDevice.CheckGlError(); + Bind(); + Gl.glBufferData(Gl.GL_ARRAY_BUFFER, + new IntPtr(Marshal.SizeOf(typeof(T)) * size), + new T[ size ], + Gl.GL_DYNAMIC_DRAW); + GraphicsDevice.CheckGlError(); + } + + public void SetData(T[] data, int length) + { + Bind(); + Gl.glBufferSubData(Gl.GL_ARRAY_BUFFER, + IntPtr.Zero, + new IntPtr(Marshal.SizeOf(typeof(T)) * length), + data); + GraphicsDevice.CheckGlError(); + } + + public void Bind() + { + Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, buffer); + GraphicsDevice.CheckGlError(); + Gl.glVertexPointer(3, Gl.GL_FLOAT, Marshal.SizeOf(typeof(T)), IntPtr.Zero); + GraphicsDevice.CheckGlError(); + Gl.glTexCoordPointer(4, Gl.GL_FLOAT, Marshal.SizeOf(typeof(T)), new IntPtr(12)); + GraphicsDevice.CheckGlError(); + } + + bool disposed; + public void Dispose() + { + if (disposed) return; + GC.SuppressFinalize(this); + Gl.glDeleteBuffers(1, ref buffer); + GraphicsDevice.CheckGlError(); + disposed = true; + } + + //~VertexBuffer() { Dispose(); } + } +} diff --git a/OpenRA.sln b/OpenRA.sln index 27ccee5034..76ef1e41e9 100644 --- a/OpenRA.sln +++ b/OpenRA.sln @@ -31,6 +31,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRA.Utility", "OpenRA.Ut EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRA.Renderer.Null", "OpenRA.Renderer.Null\OpenRA.Renderer.Null.csproj", "{0C4AEC1A-E7D5-4114-8CCD-3EEC82872981}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRA.Renderer.Gl", "OpenRA.Renderer.Gl\OpenRA.Renderer.Gl.csproj", "{E9C01A96-C3E2-4772-825B-A740AC513D31}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -118,6 +120,14 @@ Global {BDAEAB25-991E-46A7-AF1E-4F0E03358DAA}.Release|Any CPU.Build.0 = Release|Any CPU {BDAEAB25-991E-46A7-AF1E-4F0E03358DAA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {BDAEAB25-991E-46A7-AF1E-4F0E03358DAA}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {E9C01A96-C3E2-4772-825B-A740AC513D31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E9C01A96-C3E2-4772-825B-A740AC513D31}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E9C01A96-C3E2-4772-825B-A740AC513D31}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {E9C01A96-C3E2-4772-825B-A740AC513D31}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {E9C01A96-C3E2-4772-825B-A740AC513D31}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E9C01A96-C3E2-4772-825B-A740AC513D31}.Release|Any CPU.Build.0 = Release|Any CPU + {E9C01A96-C3E2-4772-825B-A740AC513D31}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {E9C01A96-C3E2-4772-825B-A740AC513D31}.Release|Mixed Platforms.Build.0 = Release|Any CPU {F33337BE-CB69-4B24-850F-07D23E408DDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F33337BE-CB69-4B24-850F-07D23E408DDF}.Debug|Any CPU.Build.0 = Debug|Any CPU {F33337BE-CB69-4B24-850F-07D23E408DDF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU