/// <summary> /// Create OpenGL object. Standard object constructor for ProtoFX. /// </summary> /// <param name="block"></param> /// <param name="scene"></param> /// <param name="debugging"></param> public GLVertinput(Compiler.Block block, Dict scene, bool debugging) : base(block.Name, block.Anno) { var err = new CompileException($"vertinput '{Name}'"); // CREATE OPENGL OBJECT glname = GL.GenVertexArray(); GL.BindVertexArray(glname); int numAttr = 0; foreach (var cmd in block["attr"]) { Attach(numAttr++, cmd, scene, err | $"command '{cmd.Text}'"); } // if errors occurred throw exception if (err.HasErrors) { throw err; } // unbind object and check for errors GL.BindVertexArray(0); if (HasErrorOrGlError(err, block)) { throw err; } }
/// <summary> /// Create OpenGL object. Standard object constructor for ProtoFX. /// </summary> /// <param name="block"></param> /// <param name="scene"></param> /// <param name="debugging"></param> public GLSampler(Compiler.Block block, Dict scene, bool debugging) : base(block.Name, block.Anno) { var err = new CompileException($"sampler '{Name}'"); // PARSE ARGUMENTS Cmds2Fields(block, err); // CREATE OPENGL OBJECT glname = GL.GenSampler(); int mini = (int)Minfilter; int magi = (int)Magfilter; int wrapi = (int)Wrap; GL.SamplerParameterI(glname, SamplerParameterName.TextureMinFilter, ref mini); GL.SamplerParameterI(glname, SamplerParameterName.TextureMagFilter, ref magi); GL.SamplerParameterI(glname, SamplerParameterName.TextureWrapR, ref wrapi); GL.SamplerParameterI(glname, SamplerParameterName.TextureWrapS, ref wrapi); GL.SamplerParameterI(glname, SamplerParameterName.TextureWrapT, ref wrapi); HasErrorOrGlError(err, block); if (err.HasErrors) { throw err; } }
/// <summary> /// Create OpenGL object. Standard object constructor for ProtoFX. /// </summary> /// <param name="block"></param> /// <param name="scene"></param> /// <param name="debugging"></param> public GLVertoutput(Compiler.Block block, Dict scene, bool debugging) : base(block.Name, block.Anno) { var err = new CompileException($"vertoutput '{Name}'"); // PARSE ARGUMENTS Cmds2Fields(block, err); // CREATE OPENGL OBJECT glname = GL.GenTransformFeedback(); GL.BindTransformFeedback(TransformFeedbackTarget.TransformFeedback, glname); // parse commands int numbindings = 0; foreach (var cmd in block["buff"]) { Attach(numbindings++, cmd, scene, err | $"command '{cmd.Text}'"); } // if errors occurred throw exception if (err.HasErrors) { throw err; } // unbind object and check for errors GL.BindTransformFeedback(TransformFeedbackTarget.TransformFeedback, 0); if (HasErrorOrGlError(err, block)) { throw err; } }
/// <summary> /// Create a new external class instance by processing the specified compiler block. /// </summary> /// <param name="block"></param> /// <param name="cmd"></param> /// <param name="err"></param> /// <returns></returns> private object CreateInstance(Compiler.Block block, Compiler.Command cmd, Dict scene, CompileException err) { // check if the command is valid if (cmd.ArgCount < 2) { err.Add("'class' command must specify a class name.", block); return(null); } // create OpenGL name lookup dictionary var glNames = new Dictionary <string, int>(scene.Count); scene.Keys.ForEach(scene.Values, (k, v) => glNames.Add(k, v.glname)); // create main class from compiled files var classname = cmd[1].Text; var instance = CompilerResults.CompiledAssembly.CreateInstance( classname, false, BindingFlags.Default, null, new object[] { block.Name, ToDict(block), glNames }, CultureInfo.CurrentCulture, null); if (instance == null) { throw err.Add($"Main class '{classname}' could not be found.", cmd); } InvokeMethod <List <string> >(instance, "GetErrors")?.ForEach(msg => err.Add(msg, cmd)); return(instance); }
/// <summary> /// Create a new external class instance by processing the specified compiler block. /// </summary> /// <param name="block"></param> /// <param name="scene"></param> /// <param name="err"></param> /// <returns></returns> internal static object CreateInstance(Compiler.Block block, Dict scene, CompileException err) { // GET CLASS COMMAND var cmds = block["class"].ToList(); if (cmds.Count == 0) { err.Add("Instance must specify a 'class' command " + "(e.g., class csharp_name class_name).", block); return(null); } var cmd = cmds.First(); // check command if (cmd.ArgCount < 1) { err.Add("'class' command must specify a csharp object name.", cmd); return(null); } // FIND CSHARP CLASS DEFINITION var csharp = scene.GetValueOrDefault <GLCsharp>(cmd[0].Text); if (csharp == null) { err.Add($"Could not find csharp code '{cmd[0].Text}' of command '{cmd.Text}' ", cmd); return(null); } // INSTANTIATE CSHARP CLASS return(csharp.CreateInstance(block, cmd, scene, err)); }
/// <summary> /// Convert code block commands to dictionary. /// </summary> /// <param name="block"></param> /// <returns></returns> private Dictionary <string, string[]> ToDict(Compiler.Block block) { // convert to dictionary of string arrays var dict = new Dictionary <string, string[]>(); // add commands to dictionary block.ForEach(cmd => dict.Add(cmd.Name, cmd.Select(x => x.Text).ToArray())); return(dict); }
/// <summary> /// Check for compiler and OpenGL errors. /// </summary> /// <param name="err"></param> /// <param name="block"></param> /// <returns></returns> static protected bool HasErrorOrGlError(CompileException err, Compiler.Block block) { var errcode = GL.GetError(); if (errcode != ErrorCode.NoError) { err.Add($"OpenGL error '{errcode}' occurred.", block); return(true); } return(err.HasErrors); }
private GLShader Attach(Compiler.Block block, string shadername, Dict classes, CompileException err) { GLShader obj = null; // get shader from class list if (shadername != null && classes.TryGetValue(shadername, out obj, block, err)) { GL.UseProgramStages(glname, ShaderType2ShaderBit(obj.ShaderType), obj.glname); } return(obj); }
/// <summary> /// Create OpenGL object. Standard object constructor for ProtoFX. /// </summary> /// <param name="block"></param> /// <param name="scene"></param> /// <param name="debugging"></param> public GLCsharp(Compiler.Block block, Dict scene, bool debugging) : base(block.Name, block.Anno) { var err = new CompileException($"csharp '{Name}'"); // PARSE ARGUMENTS Cmds2Fields(block, err); // check for errors if (err.HasErrors) { throw err; } if (File == null || File.Length == 0) { return; } // LOAD ADDITIONAL ASSEMBLIES if (Assembly != null) { foreach (var assemblypath in Assembly) { try { System.Reflection.Assembly.LoadFrom(assemblypath); } catch (FileNotFoundException) { err.Add($"Assembly file '{assemblypath}' cound not be found.", block); } catch (FileLoadException) { err.Add($"Assembly '{assemblypath}' cound not be loaded.", block); } catch { err.Add($"Unknown exception when loading assembly '{assemblypath}'.", block); } } } // replace placeholders with actual path var dir = Path.GetDirectoryName(block.Filename) + Path.DirectorySeparatorChar; var filepath = ProcessPaths(dir, File); // COMPILE FILES CompilerResults = CompileFilesOrSource(filepath.ToArray(), Version, block, err); // check for errors if (err.HasErrors) { throw err; } }
/// <summary> /// Create OpenGL object. Standard object constructor for ProtoFX. /// </summary> /// <param name="block"></param> /// <param name="scene"></param> /// <param name="debugging"></param> public GLTech(Compiler.Block block, Dict scene, bool debugging) : base(block.Name, block.Anno, 309, debugging) { var err = new CompileException($"tech '{Name}'"); // PARSE COMMANDS ParsePasses(ref init, block, scene, err); ParsePasses(ref passes, block, scene, err); ParsePasses(ref uninit, block, scene, err); // IF THERE ARE ERRORS THROW AND EXCEPTION if (err.HasErrors) { throw err; } }
/// <summary> /// Parse commands in block. /// </summary> /// <param name="list"></param> /// <param name="block"></param> /// <param name="scene"></param> /// <param name="err"></param> private void ParsePasses(ref List <GLPass> list, Compiler.Block block, Dict scene, CompileException err) { var cmdName = ReferenceEquals(list, init) ? "init" : ReferenceEquals(list, passes) ? "pass" : "uninit"; foreach (var cmd in block[cmdName]) { if (scene.TryGetValue(cmd[0].Text, out GLPass pass, block, err | $"command '{cmd.Text}'")) { list.Add(pass); } } }
/// <summary> /// Create OpenGL object. Standard object constructor for ProtoFX. /// </summary> /// <param name="block"></param> /// <param name="scene"></param> /// <param name="debugging"></param> public GLBuffer(Compiler.Block block, Dict scene, bool debugging) : base(block.Name, block.Anno) { var err = new CompileException($"buffer '{block.Name}'"); // PARSE COMMANDS AND CONVERT THEM TO CLASS FIELDS Cmds2Fields(block, err); // PARSE COMMANDS var datalist = new List <byte[]>(); foreach (var cmd in block["txt"]) { datalist.Add(LoadText(cmd, scene, err | $"command {cmd.Name} 'txt'")); } foreach (var cmd in block["xml"]) { datalist.Add(LoadXml(cmd, scene, err | $"command {cmd.Name} 'xml'")); } // merge data into a single array var iter = datalist.Cat(); var data = iter.Take(Size == 0 ? iter.Count() : Size).ToArray(); if (Size == 0) { Size = data.Length; } // CONVERT DATA var clazz = block["class"].FirstOrDefault(); if (clazz != null && Size > 0) { var converter = GLCsharp.GetMethod(clazz, scene, err); data = (byte[])converter?.Invoke(null, new[] { data }); } // CREATE OPENGL OBJECT CreateBuffer(data); if (HasErrorOrGlError(err, block)) { throw err; } }
/// <summary> /// Create OpenGL object specifying the referenced scene objects directly. /// </summary> /// <param name="block"></param> /// <param name="scene"></param> /// <param name="glbuff"></param> /// <param name="glimg"></param> public GLTexture(Compiler.Block block, Dict scene, GLBuffer glbuff, GLImage glimg) : base(block.Name, block.Anno) { var err = new CompileException($"texture '{Name}'"); // PARSE ARGUMENTS Cmds2Fields(block, err); // set name glBuff = glbuff; glImg = glimg; // GET REFERENCES if (Buff != null) { scene.TryGetValue(Buff, out glBuff, block, err); } if (Img != null) { scene.TryGetValue(Img, out glImg, block, err); } if (glBuff != null && glImg != null) { err.Add("Only an image or a buffer can be bound to a texture object.", block); } if (glBuff == null && glImg == null) { err.Add("Ether an image or a buffer has to be bound to a texture object.", block); } // IF THERE ARE ERRORS THROW AND EXCEPTION if (err.HasErrors) { throw err; } // INCASE THIS IS A TEXTURE OBJECT Link(block.Filename, block.LineInFile, err); if (HasErrorOrGlError(err, block)) { throw err; } }
/// <summary> /// Process object block and try to convert commands to class internal fields. /// </summary> /// <param name="block"></param> /// <param name="err"></param> protected void Cmds2Fields(Compiler.Block block, CompileException err = null) { foreach (var cmd in block) { // check if the name is too short for a field if (cmd.Name.Length < 2) { continue; } // get field member info var member = GetFxField(cmd.Name); if (member != null) { // set value of field SetValue(this, member, member is FieldInfo ? (member as FieldInfo).FieldType : (member as PropertyInfo).PropertyType, cmd, err); } } }
/// <summary> /// Create class instance of a C# class compiled through GLCSharp. /// </summary> /// <param name="params">Input parameters for GLObject creation.</param> public GLInstance(Compiler.Block block, Dict scene, bool debugging) : base(block.Name, block.Anno) { var err = new CompileException($"instance '{Name}'"); // INSTANTIATE CSHARP CLASS FROM CODE BLOCK Instance = GLCsharp.CreateInstance(block, scene, err); if (err.HasErrors) { throw err; } // get Bind method from main class instance update = Instance.GetType().GetMethod("Update", new[] { typeof(int), typeof(int), typeof(int), typeof(int), typeof(int) }); // get Unbind method from main class instance endpass = Instance.GetType().GetMethod("EndPass", new[] { typeof(int) }); // get Delete method from main class instance delete = Instance.GetType().GetMethod("Delete"); // get all public methods and check whether // they can be used as event handlers for glControl var reference = scene.GetValueOrDefault <GLReference>(GraphicControl.nullname); var glControl = (GraphicControl)reference.Reference; var methods = Instance.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance); foreach (var method in methods) { var info = glControl.GetType().GetEvent(method.Name); if (info != null) { var csmethod = Delegate.CreateDelegate(info.EventHandlerType, Instance, method.Name); info.AddEventHandler(glControl, csmethod); } } }
/// <summary> /// Create OpenGL object. Standard object constructor for ProtoFX. /// </summary> /// <param name="block"></param> /// <param name="scene"></param> /// <param name="debugging"></param> public GLShader(Compiler.Block block, Dict scene, bool debugging) : base(block.Name, block.Anno) { var err = new CompileException($"shader '{Name}'"); // COMPILE AND LINK SHADER INTO A SHADER PROGRAM switch (Anno) { case "vert": ShaderType = ShaderType.VertexShader; break; case "tess": ShaderType = ShaderType.TessControlShader; break; case "eval": ShaderType = ShaderType.TessEvaluationShader; break; case "geom": ShaderType = ShaderType.GeometryShader; break; case "frag": ShaderType = ShaderType.FragmentShader; break; case "comp": ShaderType = ShaderType.ComputeShader; break; default: throw err.Add($"Shader type '{Anno}' is not supported.", block); } glname = GL.CreateShaderProgram(ShaderType, 1, new[] { block.Body }); // check for errors GL.GetProgram(glname, GetProgramParameterName.LinkStatus, out int status); if (status != 1) { err.Add($"\n{GL.GetProgramInfoLog(glname)}", block); } if (HasErrorOrGlError(err, block)) { throw err; } // CREATE CSHARP DEBUG CODE string code; CompilerResults rs; if (debugging) { code = Converter.Shader2Class(ShaderType, Name, block.Body, block.BodyIndex); rs = GLCsharp.CompileFilesOrSource(new[] { code }, null, block, err, new[] { Name }); if (rs.Errors.Count == 0) { DebugShader = (Shader)rs.CompiledAssembly.CreateInstance( $"App.Glsl.{Name}", false, BindingFlags.Default, null, new object[] { block.LineInFile }, CultureInfo.CurrentCulture, null); } } // check for errors if (err.HasErrors) { throw err; } }
/// <summary> /// Compile a list of files or a list of source code. /// </summary> /// <param name="code"></param> /// <param name="block"></param> /// <param name="err"></param> /// <returns></returns> internal static CompilerResults CompileFilesOrSource(string[] code, string version, Compiler.Block block, CompileException err, string[] tmpNames = null) { CompilerResults rs = null; try { var compilerParams = new CompilerParameters(); // set compicompiler parameters compilerParams.GenerateInMemory = true; compilerParams.GenerateExecutable = false; compilerParams.TempFiles = new TempFileCollection("tmp", false); #if DEBUG compilerParams.IncludeDebugInformation = true; compilerParams.CompilerOptions = "/define:DEBUG"; #else compilerParams.IncludeDebugInformation = false; #endif // add assemblies compilerParams.ReferencedAssemblies.Add( System.Reflection.Assembly.GetExecutingAssembly().Location); compilerParams.ReferencedAssemblies.AddRange( Properties.Resources.CSHARP_REFERENCES.Split('\n') .Select(s => s.Trim()).ToArray()); // select compiler version var provider = version != null ? new CSharpCodeProvider(new Dictionary <string, string> { { "CompilerVersion", version } }) : new CSharpCodeProvider(); var check = code.Count(x => IsFilename(x)); if (check == code.Length) { rs = provider.CompileAssemblyFromFile(compilerParams, code); } else if (check == 0) { #if DEBUG Directory.CreateDirectory("tmp"); var filenames = new string[code.Length]; for (int i = 0; i < filenames.Length; i++) { filenames[i] = $"tmp{Path.DirectorySeparatorChar}tmp_{tmpNames[i]}.cs"; if (System.IO.File.Exists(filenames[i])) { var tmp = System.IO.File.ReadAllText(filenames[i]); if (tmp != code[i]) { System.IO.File.WriteAllText(filenames[i], code[i]); } } else { System.IO.File.WriteAllText(filenames[i], code[i]); } } rs = provider.CompileAssemblyFromFile(compilerParams, filenames); #else rs = provider.CompileAssemblyFromSource(compilerParams, code); #endif } else { throw err.Add("Cannot mix filenames and source code" + "strings when compiling C# code.", block); } } catch (DirectoryNotFoundException ex) { throw err.Add(ex.Message, block); } catch (FileNotFoundException ex) { throw err.Add(ex.Message, block); } finally { // check for compiler errors if (rs?.Errors.Count != 0) { string msg = ""; foreach (var message in rs.Errors) { msg += $"\n{message}"; } err.Add(msg, block); } } return(rs); }
/// <summary> /// Create OpenGL object. Standard object constructor for ProtoFX. /// </summary> /// <param name="block"></param> /// <param name="scene"></param> /// <param name="debugging"></param> public GLTexture(Compiler.Block block, Dict scene, bool debugging) : this(block, scene, null, null) { }
public static CompileException Add(this CompileException err, string message, Compiler.Block block) { return(err.Add(message, block.Filename, block.LineInFile)); }
public static bool TryGetValue <T>(this Dict dict, string key, out T obj, Compiler.Block block, CompileException err) where T : GLObject { return(dict.TryGetValue(key, out obj, block.LineInFile, block.Filename, err)); }
/// <summary> /// Create OpenGL object. Standard object constructor for ProtoFX. /// </summary> /// <param name="block"></param> /// <param name="scene"></param> /// <param name="genDebugInfo"></param> public GLPass(Compiler.Block block, Dict scene, bool genDebugInfo) : base(block.Name, block.Anno, 309, genDebugInfo) { var err = new CompileException($"pass '{Name}'"); GenDebugInfo = genDebugInfo; /// PARSE COMMANDS AND CONVERT THEM TO CLASS FIELDS Cmds2Fields(block, err); /// PARSE COMMANDS foreach (var cmd in block) { // ignore class fields var field = GetType().GetField(cmd.Name, Instance | Public | NonPublic); var attr = field?.GetCustomAttributes(typeof(FxField), false); if (attr?.Length > 0) { continue; } using (var e = err | $"command '{cmd.Name}' line {cmd.LineInFile}") { switch (cmd.Name) { case "draw": ParseDrawCall(cmd, scene, e); break; case "compute": ParseComputeCall(cmd, scene, e); break; case "tex": ParseTexCmd(cmd, scene, e); break; case "img": ParseImgCmd(cmd, scene, e); break; case "samp": ParseSampCmd(cmd, scene, e); break; case "exec": ParseCsharpExec(cmd, scene, e); break; case "vertout": vertoutput = new Vertoutput(cmd, scene, e); break; case "fragout": scene.TryGetValue(cmd[0].Text, out fragoutput, cmd, e); break; default: ParseOpenGLCall(cmd, e); break; } } } if (err.HasErrors) { throw err; } /// CREATE OPENGL OBJECT if (Vert != null || Comp != null) { GL.CreateProgramPipelines(1, out glname); // Attach shader objects. // First try attaching a compute shader. If that // fails, try attaching the default shader pipeline. if ((glcomp = Attach(block, Comp, scene, err)) == null) { glvert = Attach(block, Vert, scene, err); gltess = Attach(block, Tess, scene, err); gleval = Attach(block, Eval, scene, err); glgeom = Attach(block, Geom, scene, err); glfrag = Attach(block, Frag, scene, err); } // get debug shaders if (GenDebugInfo) { if (glcomp != null) { dbgcomp = (CompShader)glcomp.DebugShader; } else { Shader prev = dbgvert = (VertShader)glvert.DebugShader; dbgtess = (TessShader)gltess?.DebugShader; dbgeval = (EvalShader)gleval?.DebugShader; dbggeom = (GeomShader)glgeom?.DebugShader; dbgfrag = (FragShader)glfrag?.DebugShader; if (dbgtess != null) { dbgtess.Prev = prev; prev = dbgtess; } if (dbgeval != null) { dbgeval.Prev = prev; prev = dbgeval; } if (dbggeom != null) { dbggeom.Prev = prev; prev = dbggeom; } } } } /// CHECK FOR ERRORS if (GL.GetError() != ErrorCode.NoError) { err.Add($"OpenGL error '{GL.GetError()}' occurred " + "during shader program creation.", block); } if (err.HasErrors) { throw err; } }