#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.Renderer.Sdl2 { public class Shader : IShader { readonly Dictionary samplers = new Dictionary(); readonly Dictionary textures = new Dictionary(); int program; public Shader(string name) { // Vertex shader string vertexCode; using (var file = new StreamReader(GlobalFileSystem.Open("glsl{0}{1}.vert".F(Path.DirectorySeparatorChar, name)))) vertexCode = file.ReadToEnd(); var vertexShader = GL.CreateShader(ShaderType.VertexShader); ErrorHandler.CheckGlError(); GL.ShaderSource(vertexShader, vertexCode); ErrorHandler.CheckGlError(); GL.CompileShader(vertexShader); ErrorHandler.CheckGlError(); int success; GL.GetShader(vertexShader, ShaderParameter.CompileStatus, out success); ErrorHandler.CheckGlError(); if (success == (int)All.False) throw new InvalidProgramException("Compile error in glsl{0}{1}.vert".F(Path.DirectorySeparatorChar, name)); // Fragment shader string fragmentCode; using (var file = new StreamReader(GlobalFileSystem.Open("glsl{0}{1}.frag".F(Path.DirectorySeparatorChar, name)))) fragmentCode = file.ReadToEnd(); var fragmentShader = GL.CreateShader(ShaderType.FragmentShader); ErrorHandler.CheckGlError(); GL.ShaderSource(fragmentShader, fragmentCode); ErrorHandler.CheckGlError(); GL.CompileShader(fragmentShader); ErrorHandler.CheckGlError(); GL.GetShader(vertexShader, ShaderParameter.CompileStatus, out success); ErrorHandler.CheckGlError(); if (success == (int)All.False) throw new InvalidProgramException("Compile error in glsl{0}{1}.frag".F(Path.DirectorySeparatorChar, 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(); GL.GetProgram(program, ProgramParameter.LinkStatus, out success); ErrorHandler.CheckGlError(); if (success == (int)All.False) throw new InvalidProgramException("Linking error in {0} shader".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(); } } }