From 2710007ec2cdd2ec5c63e8bf37e7750f051ef822 Mon Sep 17 00:00:00 2001 From: David Allemang Date: Fri, 24 Feb 2017 21:22:13 -0500 Subject: [PATCH] More OO-like. a bit easier to read/use things --- hexworld/{Program.cs => Driver.cs} | 2 +- hexworld/Game.cs | 119 +++++++++++----------------- hexworld/Properties/AssemblyInfo.cs | 2 +- hexworld/Util/Extensions.cs | 24 ++++++ hexworld/Util/GLObject.cs | 22 +++++ hexworld/Util/Program.cs | 63 +++++++++++++++ hexworld/Util/Shader.cs | 39 +++++++++ hexworld/Util/ShaderException.cs | 30 +++++++ hexworld/Util/Texture.cs | 74 +++++++++++++++++ hexworld/Util/VBO.cs | 36 +++++++++ hexworld/hexworld.csproj | 10 ++- hexworld/s.fs.glsl | 2 +- 12 files changed, 348 insertions(+), 75 deletions(-) rename hexworld/{Program.cs => Driver.cs} (93%) create mode 100644 hexworld/Util/Extensions.cs create mode 100644 hexworld/Util/GLObject.cs create mode 100644 hexworld/Util/Program.cs create mode 100644 hexworld/Util/Shader.cs create mode 100644 hexworld/Util/ShaderException.cs create mode 100644 hexworld/Util/Texture.cs create mode 100644 hexworld/Util/VBO.cs diff --git a/hexworld/Program.cs b/hexworld/Driver.cs similarity index 93% rename from hexworld/Program.cs rename to hexworld/Driver.cs index c4946bc..69aacd5 100644 --- a/hexworld/Program.cs +++ b/hexworld/Driver.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace hexworld { - public class Program + public class Driver { public static void Main(string[] args) { diff --git a/hexworld/Game.cs b/hexworld/Game.cs index 76a2e2a..31c6356 100644 --- a/hexworld/Game.cs +++ b/hexworld/Game.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using hexworld.Util; using OpenTK; using OpenTK.Graphics.OpenGL4; using PixelFormat = OpenTK.Graphics.OpenGL4.PixelFormat; @@ -36,31 +37,32 @@ namespace hexworld private readonly Vert[] _verts = { // +X - new Vert(+1, +1, -1, 1.0f, 0.5f), new Vert(+1, +1, +1, 1.0f, 0.0f), new Vert(+1, -1, +1, 0.5f, 0.0f), - new Vert(+1, -1, +1, 0.5f, 0.0f), new Vert(+1, -1, -1, 0.5f, 0.5f), new Vert(+1, +1, -1, 1.0f, 0.5f), + new Vert(+.5f, +.5f, -.5f, 1.0f, 0.5f), new Vert(+.5f, +.5f, +.5f, 1.0f, 0.0f), new Vert(+.5f, -.5f, +.5f, 0.5f, 0.0f), + new Vert(+.5f, -.5f, +.5f, 0.5f, 0.0f), new Vert(+.5f, -.5f, -.5f, 0.5f, 0.5f), new Vert(+.5f, +.5f, -.5f, 1.0f, 0.5f), // -X - new Vert(-1, +1, +1, 0.5f, 0.0f), new Vert(-1, +1, -1, 0.5f, 0.5f), new Vert(-1, -1, -1, 1.0f, 0.5f), - new Vert(-1, -1, -1, 1.0f, 0.5f), new Vert(-1, -1, +1, 1.0f, 0.0f), new Vert(-1, +1, +1, 0.5f, 0.0f), + new Vert(-.5f, +.5f, +.5f, 0.5f, 0.0f), new Vert(-.5f, +.5f, -.5f, 0.5f, 0.5f), new Vert(-.5f, -.5f, -.5f, 1.0f, 0.5f), + new Vert(-.5f, -.5f, -.5f, 1.0f, 0.5f), new Vert(-.5f, -.5f, +.5f, 1.0f, 0.0f), new Vert(-.5f, +.5f, +.5f, 0.5f, 0.0f), // +Y - new Vert(+1, +1, -1, 0.5f, 0.5f), new Vert(-1, +1, -1, 1.0f, 0.5f), new Vert(-1, +1, +1, 1.0f, 0.0f), - new Vert(-1, +1, +1, 1.0f, 0.0f), new Vert(+1, +1, +1, 0.5f, 0.0f), new Vert(+1, +1, -1, 0.5f, 0.5f), + new Vert(+.5f, +.5f, -.5f, 0.5f, 0.5f), new Vert(-.5f, +.5f, -.5f, 1.0f, 0.5f), new Vert(-.5f, +.5f, +.5f, 1.0f, 0.0f), + new Vert(-.5f, +.5f, +.5f, 1.0f, 0.0f), new Vert(+.5f, +.5f, +.5f, 0.5f, 0.0f), new Vert(+.5f, +.5f, -.5f, 0.5f, 0.5f), // -Y - new Vert(+1, -1, +1, 1.0f, 0.0f), new Vert(-1, -1, +1, 0.5f, 0.0f), new Vert(-1, -1, -1, 0.5f, 0.5f), - new Vert(-1, -1, -1, 0.5f, 0.5f), new Vert(+1, -1, -1, 1.0f, 0.5f), new Vert(+1, -1, +1, 1.0f, 0.0f), + new Vert(+.5f, -.5f, +.5f, 1.0f, 0.0f), new Vert(-.5f, -.5f, +.5f, 0.5f, 0.0f), new Vert(-.5f, -.5f, -.5f, 0.5f, 0.5f), + new Vert(-.5f, -.5f, -.5f, 0.5f, 0.5f), new Vert(+.5f, -.5f, -.5f, 1.0f, 0.5f), new Vert(+.5f, -.5f, +.5f, 1.0f, 0.0f), // +Z - new Vert(+1, +1, +1, 0.5f, 0.0f), new Vert(-1, +1, +1, 0.0f, 0.0f), new Vert(-1, -1, +1, 0.0f, 0.5f), - new Vert(-1, -1, +1, 0.0f, 0.5f), new Vert(+1, -1, +1, 0.5f, 0.5f), new Vert(+1, +1, +1, 0.5f, 0.0f), + new Vert(+.5f, +.5f, +.5f, 0.5f, 0.0f), new Vert(-.5f, +.5f, +.5f, 0.0f, 0.0f), new Vert(-.5f, -.5f, +.5f, 0.0f, 0.5f), + new Vert(-.5f, -.5f, +.5f, 0.0f, 0.5f), new Vert(+.5f, -.5f, +.5f, 0.5f, 0.5f), new Vert(+.5f, +.5f, +.5f, 0.5f, 0.0f), // -Z - new Vert(+1, +1, -1, 0.5f, 0.5f), new Vert(-1, +1, -1, 0.0f, 0.5f), new Vert(-1, -1, -1, 0.0f, 1.0f), - new Vert(-1, -1, -1, 0.0f, 1.0f), new Vert(+1, -1, -1, 0.5f, 1.0f), new Vert(+1, +1, -1, 0.5f, 0.5f), + new Vert(+.5f, +.5f, -.5f, 0.5f, 0.5f), new Vert(-.5f, +.5f, -.5f, 0.0f, 0.5f), new Vert(-.5f, -.5f, -.5f, 0.0f, 1.0f), + new Vert(-.5f, -.5f, -.5f, 0.0f, 1.0f), new Vert(+.5f, -.5f, -.5f, 0.5f, 1.0f), new Vert(+.5f, +.5f, -.5f, 0.5f, 0.5f), }; private Matrix4 _view = Matrix4.Identity; private Matrix4 _proj = Matrix4.Identity; - private int _pgm; - private int _texLoc; - private int _tex2Loc; + private Program _pgm; + + private Texture _tex1; + private Texture _tex2; public HexWindow(int width, int height) : base(width, height) @@ -71,6 +73,7 @@ namespace hexworld Y = (DisplayDevice.Default.Height - Height) / 2; } + protected override void OnUpdateFrame(FrameEventArgs e) { base.OnUpdateFrame(e); @@ -84,64 +87,33 @@ namespace hexworld { base.OnLoad(e); - var vertsVbo = GL.GenBuffer(); - GL.BindBuffer(BufferTarget.ArrayBuffer, vertsVbo); - GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr) (_verts.Length * Vert.SizeInBytes), _verts, - BufferUsageHint.StaticDraw); + var vbo = new VBO(); + vbo.Data(_verts); - var vs = GL.CreateShader(ShaderType.VertexShader); - GL.ShaderSource(vs, File.ReadAllText("s.vs.glsl")); - GL.CompileShader(vs); - Console.Out.WriteLine("vs:" + GL.GetShaderInfoLog(vs)); + var vs = new Shader(ShaderType.VertexShader) {Source = File.ReadAllText("s.vs.glsl")}; + if (!vs.Compile()) + Console.Out.WriteLine($"vs: {vs.Log}"); - var fs = GL.CreateShader(ShaderType.FragmentShader); - GL.ShaderSource(fs, File.ReadAllText("s.fs.glsl")); - GL.CompileShader(fs); - Console.Out.WriteLine("fs: " + GL.GetShaderInfoLog(fs)); + var fs = new Shader(ShaderType.FragmentShader) {Source = File.ReadAllText("s.fs.glsl")}; + if (!fs.Compile()) + Console.Out.WriteLine($"fs: {fs.Log}"); - _pgm = GL.CreateProgram(); - GL.AttachShader(_pgm, vs); - GL.AttachShader(_pgm, fs); - GL.LinkProgram(_pgm); - Console.Out.WriteLine("pgm: " + GL.GetProgramInfoLog(_pgm)); + _pgm = new Program(); + _pgm.Attach(vs); + _pgm.Attach(fs); + if (!_pgm.Link()) + Console.Out.WriteLine($"pgm: {_pgm.Log}"); - GL.BindBuffer(BufferTarget.ArrayBuffer, vertsVbo); + vbo.Bind(); GL.EnableVertexAttribArray(0); GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, Vert.SizeInBytes, 0); GL.EnableVertexAttribArray(1); GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, Vert.SizeInBytes, Vector3.SizeInBytes); + VBO.Unbind(); - GL.BindBuffer(BufferTarget.ArrayBuffer, 0); + _tex1 = Texture.FromBitmap(new Bitmap("tex.png")); - var texBmp = new Bitmap("tex.png"); - _texLoc = GL.GenTexture(); - GL.BindTexture(TextureTarget.Texture2D, _texLoc); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, - (int) TextureMinFilter.Nearest); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, - (int) TextureMagFilter.Nearest); - var texData = texBmp.LockBits(new Rectangle(0, 0, texBmp.Width, texBmp.Height), - ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, texBmp.Width, texBmp.Height, 0, - PixelFormat.Bgra, PixelType.UnsignedByte, texData.Scan0); - texBmp.UnlockBits(texData); - GL.BindTexture(TextureTarget.Texture2D, 0); - - GL.BindBuffer(BufferTarget.ArrayBuffer, 0); - - var tex2Bmp = new Bitmap("tex2.png"); - _tex2Loc = GL.GenTexture(); - GL.BindTexture(TextureTarget.Texture2D, _tex2Loc); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, - (int) TextureMinFilter.Nearest); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, - (int) TextureMagFilter.Nearest); - var tex2Data = tex2Bmp.LockBits(new Rectangle(0, 0, tex2Bmp.Width, tex2Bmp.Height), - ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, tex2Bmp.Width, tex2Bmp.Height, 0, - PixelFormat.Bgra, PixelType.UnsignedByte, tex2Data.Scan0); - tex2Bmp.UnlockBits(tex2Data); - GL.BindTexture(TextureTarget.Texture2D, 0); + _tex2 = Texture.FromBitmap(new Bitmap("tex2.png")); } protected override void OnRenderFrame(FrameEventArgs e) @@ -152,19 +124,24 @@ namespace hexworld GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); GL.Enable(EnableCap.DepthTest); - GL.UseProgram(_pgm); - GL.ActiveTexture(TextureUnit.Texture0); - GL.BindTexture(TextureTarget.Texture2D, _tex2Loc); - GL.Uniform1(GL.GetUniformLocation(_pgm, "tex"), 0); + _pgm.Use(); - GL.UniformMatrix4(GL.GetUniformLocation(_pgm, "view"), false, ref _view); - GL.UniformMatrix4(GL.GetUniformLocation(_pgm, "proj"), false, ref _proj); + _tex1.Bind(0); + _tex2.Bind(1); + + GL.Uniform1(_pgm.GetUniform("tex"), 0); + + GL.UniformMatrix4(_pgm.GetUniform("view"), false, ref _view); + GL.UniformMatrix4(_pgm.GetUniform("proj"), false, ref _proj); GL.DrawArrays(PrimitiveType.Triangles, 0, _verts.Length); - GL.ActiveTexture(TextureUnit.Texture0); - GL.BindTexture(TextureTarget.Texture2D, 0); + GL.Uniform1(_pgm.GetUniform("tex"), 1); + _view = Matrix4.CreateTranslation(0, 2, 0) * _view; + GL.UniformMatrix4(_pgm.GetUniform("view"), false, ref _view); + + GL.DrawArrays(PrimitiveType.Triangles, 0, _verts.Length); SwapBuffers(); } diff --git a/hexworld/Properties/AssemblyInfo.cs b/hexworld/Properties/AssemblyInfo.cs index dad37ab..df4a9be 100644 --- a/hexworld/Properties/AssemblyInfo.cs +++ b/hexworld/Properties/AssemblyInfo.cs @@ -19,7 +19,7 @@ using System.Runtime.InteropServices; // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] -// The following GUID is for the ID of the typelib if this project is exposed to COM +// The following GUID is for the Id of the typelib if this project is exposed to COM [assembly: Guid("ad9ed057-fb47-44cb-8839-22924b409706")] // Version information for an assembly consists of the following four values: diff --git a/hexworld/Util/Extensions.cs b/hexworld/Util/Extensions.cs new file mode 100644 index 0000000..0ab84c4 --- /dev/null +++ b/hexworld/Util/Extensions.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using OpenTK.Graphics.OpenGL4; + +namespace hexworld.Util +{ + public static class Extensions + { + public static byte[] ToBytes(this T[] arr) where T : struct + { + var size = Marshal.SizeOf()*arr.Length; + var bytes = new byte[size]; + var ptr = Marshal.AllocHGlobal(size); + Marshal.StructureToPtr(arr, ptr, false); + Marshal.Copy(ptr, bytes, 0, size); + Marshal.FreeHGlobal(ptr); + return bytes; + } + } +} \ No newline at end of file diff --git a/hexworld/Util/GLObject.cs b/hexworld/Util/GLObject.cs new file mode 100644 index 0000000..5710727 --- /dev/null +++ b/hexworld/Util/GLObject.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenTK.Graphics.OpenGL4; + +namespace hexworld.Util +{ + public abstract class GLObject + { + public uint Id { get; protected set; } + + protected GLObject(uint id) + { + Id = id; + } + + public static explicit operator uint(GLObject o) => o.Id; + public static explicit operator int(GLObject o) => (int) o.Id; + } +} \ No newline at end of file diff --git a/hexworld/Util/Program.cs b/hexworld/Util/Program.cs new file mode 100644 index 0000000..74dda95 --- /dev/null +++ b/hexworld/Util/Program.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenTK.Graphics.OpenGL4; + +namespace hexworld.Util +{ + public class Program : GLObject + { + private Dictionary uniforms = new Dictionary(); + private Dictionary attributes = new Dictionary(); + + public string Log => GL.GetProgramInfoLog((int) Id); + + public Program() + : base((uint) GL.CreateProgram()) + { + } + + public void Attach(Shader shader) => GL.AttachShader(Id, shader.Id); + + public bool Link() + { + uniforms.Clear(); + attributes.Clear(); + GL.LinkProgram(Id); + GL.GetProgram(Id, GetProgramParameterName.LinkStatus, out int success); + if (success == 0) return false; + + GL.GetProgram(Id, GetProgramParameterName.ActiveUniforms, out int uniformcount); + for (var i = 0; i < uniformcount; i++) + { + var sb = new StringBuilder(256); + GL.GetActiveUniformName((int) Id, i, sb.Capacity, out int length, sb); + uniforms[sb.ToString()] = i; + } + + GL.GetProgram(Id, GetProgramParameterName.ActiveAttributes, out int attributecount); + for (var i = 0; i < attributecount; i++) + { + var sb = new StringBuilder(256); + GL.GetActiveAttrib((int) Id, i, sb.Capacity, out int length, out int size, + out ActiveAttribType type, sb); + attributes[sb.ToString()] = i; + } + + return true; + } + + public int GetUniform(string name) + { + if (!uniforms.TryGetValue(name, out int id)) + throw new ShaderException($"Shader Program {Id} does not contain uniform '{name}'"); + else return id; + } + + public void Use() => GL.UseProgram(Id); + + public static void UseDefault() => GL.UseProgram(0); + } +} \ No newline at end of file diff --git a/hexworld/Util/Shader.cs b/hexworld/Util/Shader.cs new file mode 100644 index 0000000..9e046a9 --- /dev/null +++ b/hexworld/Util/Shader.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenTK.Graphics.OpenGL4; + +namespace hexworld.Util +{ + public class Shader : GLObject + { + public readonly ShaderType Type; + + public string Source + { + get + { + var sb = new StringBuilder(1024); + GL.GetShaderSource(Id, sb.Capacity, out int length, sb); + return sb.ToString(); + } + set { GL.ShaderSource((int) Id, value); } + } + + public string Log => GL.GetShaderInfoLog((int) Id); + + public Shader(ShaderType type) + : base((uint) GL.CreateShader(type)) + { + Type = type; + } + + public bool Compile() + { + GL.CompileShader(Id); + GL.GetShader(Id, ShaderParameter.CompileStatus, out int success); + return success != 0; + } + } +} \ No newline at end of file diff --git a/hexworld/Util/ShaderException.cs b/hexworld/Util/ShaderException.cs new file mode 100644 index 0000000..d5d99ef --- /dev/null +++ b/hexworld/Util/ShaderException.cs @@ -0,0 +1,30 @@ +using System; +using System.Runtime.Serialization; + +namespace hexworld.Util +{ + [Serializable] + public class ShaderException : Exception + { + public ShaderException() + { + } + + public ShaderException(string message) + : base(message) + { + } + + public ShaderException(string message, Exception inner) + : base(message, inner) + { + } + + protected ShaderException( + SerializationInfo info, + StreamingContext context) + : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/hexworld/Util/Texture.cs b/hexworld/Util/Texture.cs new file mode 100644 index 0000000..12f21c0 --- /dev/null +++ b/hexworld/Util/Texture.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenTK.Graphics.OpenGL4; +using PixelFormat = OpenTK.Graphics.OpenGL4.PixelFormat; + +namespace hexworld.Util +{ + // todo: this structure doesn't fit GL at all. need to fix it. + // need to reconcile ActiveTexture and BindTexture + public class Texture : GLObject + { + public TextureTarget Target; + + public Texture(TextureTarget target = TextureTarget.Texture2D) + : base((uint) GL.GenTexture()) + { + Target = target; + } + + public void Bind() + { + GL.BindTexture(Target, Id); + } + + public void Bind(TextureUnit unit) + { + GL.ActiveTexture(unit); + Bind(); + } + + public void Bind(int unit) + { + GL.ActiveTexture(TextureUnit.Texture0 + unit); + Bind(); + } + + public void Unbind() + { + GL.BindTexture(Target, 0); + } + + public void Unbind(TextureUnit unit) + { + GL.ActiveTexture(unit); + Unbind(); + } + + public void Unbind(int unit) + { + GL.ActiveTexture(TextureUnit.Texture0 + unit); + Unbind(); + } + + public static Texture FromBitmap(Bitmap bmp) + { + var tex = new Texture(TextureTarget.Texture2D); + tex.Bind(); + GL.TexParameter(tex.Target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Nearest); + GL.TexParameter(tex.Target, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Nearest); + var data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, + System.Drawing.Imaging.PixelFormat.Format32bppArgb); + GL.TexImage2D(tex.Target, 0, PixelInternalFormat.Rgba, bmp.Width, bmp.Height, 0, PixelFormat.Bgra, + PixelType.UnsignedByte, data.Scan0); + bmp.UnlockBits(data); + tex.Unbind(); + return tex; + } + } +} \ No newline at end of file diff --git a/hexworld/Util/VBO.cs b/hexworld/Util/VBO.cs new file mode 100644 index 0000000..500c6b9 --- /dev/null +++ b/hexworld/Util/VBO.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using OpenTK.Graphics.OpenGL4; + +namespace hexworld.Util +{ + public class VBO : GLObject + { + public VBO() + : base((uint) GL.GenBuffer()) + { + } + + public void Bind() + { + GL.BindBuffer(BufferTarget.ArrayBuffer, Id); + } + + public void Data(T[] data, BufferUsageHint usage = BufferUsageHint.StaticDraw) where T : struct + { + Bind(); + var size = Marshal.SizeOf(); + GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(size * data.Length), data, usage); + Unbind(); + } + + public static void Unbind() + { + GL.BindBuffer(BufferTarget.ArrayBuffer, 0); + } + } +} \ No newline at end of file diff --git a/hexworld/hexworld.csproj b/hexworld/hexworld.csproj index fe96bd2..5be51dd 100644 --- a/hexworld/hexworld.csproj +++ b/hexworld/hexworld.csproj @@ -22,6 +22,7 @@ DEBUG;TRACE prompt 4 + false AnyCPU @@ -51,8 +52,15 @@ - + + + + + + + + diff --git a/hexworld/s.fs.glsl b/hexworld/s.fs.glsl index 40028a0..8039d90 100644 --- a/hexworld/s.fs.glsl +++ b/hexworld/s.fs.glsl @@ -6,5 +6,5 @@ uniform sampler2D tex; void main () { - gl_FragColor = texture(tex, vTexCoord) + vec4(.1); + gl_FragColor = texture(tex, vTexCoord); } \ No newline at end of file