/// <summary> /// Compiles the ShaderProgram. This is done automatically when loading the ShaderProgram /// or when binding it. /// </summary> public void Compile() { DualityApp.GuardSingleThreadState(); if (this.glProgramId == 0) { return; } if (this.compiled) { return; } GL.LinkProgram(this.glProgramId); int result; GL.GetProgram(this.glProgramId, GetProgramParameterName.LinkStatus, out result); if (result == 0) { string infoLog = GL.GetProgramInfoLog(this.glProgramId); Log.Core.WriteError("Error linking shader program. InfoLog:{1}{0}", infoLog, Environment.NewLine); return; } this.compiled = true; // Collect variable infos from sub programs ShaderVarInfo[] geomVarArray = this.geom.IsAvailable ? this.geom.Res.VarInfo : null; ShaderVarInfo[] fragVarArray = this.frag.IsAvailable ? this.frag.Res.VarInfo : null; ShaderVarInfo[] vertVarArray = this.vert.IsAvailable ? this.vert.Res.VarInfo : null; var emptyShaderInfo = new ShaderVarInfo[0]; this.varInfo = (vertVarArray ?? emptyShaderInfo) .Union(fragVarArray ?? emptyShaderInfo) .Union(geomVarArray ?? emptyShaderInfo) .ToArray(); // Determine actual variable locations for (int i = 0; i < this.varInfo.Length; i++) { if (this.varInfo[i].scope == ShaderVarScope.Uniform) { this.varInfo[i].glVarLoc = GL.GetUniformLocation(this.glProgramId, this.varInfo[i].name); } else { this.varInfo[i].glVarLoc = GL.GetAttribLocation(this.glProgramId, this.varInfo[i].name); } } }
/// <summary> /// Compiles the shader. This is done automatically when loading the shader /// or attaching it to a <see cref="Duality.Resources.ShaderProgram"/>. /// </summary> public void Compile() { DualityApp.GuardSingleThreadState(); if (this.compiled) return; if (String.IsNullOrEmpty(this.source)) return; if (this.glShaderId == 0) this.glShaderId = GL.CreateShader(this.OglShaderType); GL.ShaderSource(this.glShaderId, this.source); GL.CompileShader(this.glShaderId); int result; GL.GetShader(this.glShaderId, ShaderParameter.CompileStatus, out result); if (result == 0) { string infoLog = GL.GetShaderInfoLog(this.glShaderId); Log.Core.WriteError("Error compiling {0}. InfoLog:\n{1}", this.OglShaderType, infoLog); return; } this.compiled = true; // Remove comments from source code before extracting variables string sourceWithoutComments; { const string blockComments = @"/\*(.*?)\*/"; const string lineComments = @"//(.*?)\r?\n"; const string strings = @"""((\\[^\n]|[^""\n])*)"""; const string verbatimStrings = @"@(""[^""]*"")+"; sourceWithoutComments = Regex.Replace(this.source, blockComments + "|" + lineComments + "|" + strings + "|" + verbatimStrings, me => { if (me.Value.StartsWith("/*") || me.Value.StartsWith("//")) return me.Value.StartsWith("//") ? Environment.NewLine : ""; // Keep the literal strings return me.Value; }, RegexOptions.Singleline); } // Scan remaining code chunk for variable declarations List<ShaderVarInfo> varInfoList = new List<ShaderVarInfo>(); string[] lines = sourceWithoutComments.Split(new[] {';','\n'}, StringSplitOptions.RemoveEmptyEntries); ShaderVarInfo varInfo = new ShaderVarInfo(); foreach (string t in lines) { string curLine = t.TrimStart(); if (curLine.StartsWith("uniform")) varInfo.scope = ShaderVarScope.Uniform; else if (curLine.StartsWith("attribute")) varInfo.scope = ShaderVarScope.Attribute; else continue; string[] curLineSplit = curLine.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries); switch (curLineSplit[1].ToUpper()) { case "FLOAT": varInfo.type = ShaderVarType.Float; break; case "VEC2": varInfo.type = ShaderVarType.Vec2; break; case "VEC3": varInfo.type = ShaderVarType.Vec3; break; case "VEC4": varInfo.type = ShaderVarType.Vec4; break; case "MAT2": varInfo.type = ShaderVarType.Mat2; break; case "MAT3": varInfo.type = ShaderVarType.Mat3; break; case "MAT4": varInfo.type = ShaderVarType.Mat4; break; case "INT": varInfo.type = ShaderVarType.Int; break; case "SAMPLER2D": varInfo.type = ShaderVarType.Sampler2D; break; } curLineSplit = curLineSplit[2].Split(new char[] {'[', ']'}, StringSplitOptions.RemoveEmptyEntries); varInfo.name = curLineSplit[0]; varInfo.arraySize = (curLineSplit.Length > 1) ? int.Parse(curLineSplit[1]) : 1; varInfo.glVarLoc = -1; varInfoList.Add(varInfo); } this.varInfo = varInfoList.ToArray(); }
/// <summary> /// Compiles the shader. This is done automatically when loading the shader /// or attaching it to a <see cref="Duality.Resources.ShaderProgram"/>. /// </summary> public void Compile() { DualityApp.GuardSingleThreadState(); if (this.compiled) { return; } if (String.IsNullOrEmpty(this.source)) { return; } if (this.glShaderId == 0) { this.glShaderId = GL.CreateShader(this.OglShaderType); } GL.ShaderSource(this.glShaderId, this.source); GL.CompileShader(this.glShaderId); int result; GL.GetShader(this.glShaderId, ShaderParameter.CompileStatus, out result); if (result == 0) { string infoLog = GL.GetShaderInfoLog(this.glShaderId); Log.Core.WriteError("Error compiling {0}. InfoLog:\n{1}", this.OglShaderType, infoLog); return; } this.compiled = true; // Remove comments from source code before extracting variables string sourceWithoutComments; { const string blockComments = @"/\*(.*?)\*/"; const string lineComments = @"//(.*?)\r?\n"; const string strings = @"""((\\[^\n]|[^""\n])*)"""; const string verbatimStrings = @"@(""[^""]*"")+"; sourceWithoutComments = Regex.Replace(this.source, blockComments + "|" + lineComments + "|" + strings + "|" + verbatimStrings, me => { if (me.Value.StartsWith("/*") || me.Value.StartsWith("//")) { return(me.Value.StartsWith("//") ? Environment.NewLine : ""); } // Keep the literal strings return(me.Value); }, RegexOptions.Singleline); } // Scan remaining code chunk for variable declarations List <ShaderVarInfo> varInfoList = new List <ShaderVarInfo>(); string[] lines = sourceWithoutComments.Split(new[] { ';', '\n' }, StringSplitOptions.RemoveEmptyEntries); ShaderVarInfo varInfo = new ShaderVarInfo(); foreach (string t in lines) { string curLine = t.TrimStart(); if (curLine.StartsWith("uniform")) { varInfo.scope = ShaderVarScope.Uniform; } else if (curLine.StartsWith("attribute")) { varInfo.scope = ShaderVarScope.Attribute; } else { continue; } string[] curLineSplit = curLine.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); switch (curLineSplit[1].ToUpper()) { case "FLOAT": varInfo.type = ShaderVarType.Float; break; case "VEC2": varInfo.type = ShaderVarType.Vec2; break; case "VEC3": varInfo.type = ShaderVarType.Vec3; break; case "VEC4": varInfo.type = ShaderVarType.Vec4; break; case "MAT2": varInfo.type = ShaderVarType.Mat2; break; case "MAT3": varInfo.type = ShaderVarType.Mat3; break; case "MAT4": varInfo.type = ShaderVarType.Mat4; break; case "INT": varInfo.type = ShaderVarType.Int; break; case "SAMPLER2D": varInfo.type = ShaderVarType.Sampler2D; break; } curLineSplit = curLineSplit[2].Split(new char[] { '[', ']' }, StringSplitOptions.RemoveEmptyEntries); varInfo.name = curLineSplit[0]; varInfo.arraySize = (curLineSplit.Length > 1) ? int.Parse(curLineSplit[1]) : 1; varInfo.glVarLoc = -1; varInfoList.Add(varInfo); } this.varInfo = varInfoList.ToArray(); }
protected override void OnUpdateFromObjects(object[] values) { base.OnUpdateFromObjects(values); if (values.Any(o => o != null)) { bool invokeSetter = false; IEnumerable<BatchInfo> batchInfos = null; DrawTechnique refTech = null; batchInfos = values.Cast<BatchInfo>(); refTech = batchInfos.NotNull().First().Technique.Res; // Retrieve data about shader variables ShaderVarInfo[] varInfoArray = null; if (refTech != null && refTech.Shader.IsAvailable) { varInfoArray = refTech.Shader.Res.VarInfo; } else { varInfoArray = new ShaderVarInfo[] { new ShaderVarInfo() }; varInfoArray[0].arraySize = 1; varInfoArray[0].glVarLoc = -1; varInfoArray[0].name = ShaderVarInfo.VarName_MainTex; varInfoArray[0].scope = ShaderVarScope.Uniform; varInfoArray[0].type = ShaderVarType.Sampler2D; } // Get rid of unused variables (This changes actual Resource data!) if (!this.ReadOnly) { foreach (BatchInfo info in batchInfos.NotNull()) { List<string> texRemoveSched = null; List<string> uniRemoveSched = null; if (info.Textures != null) { foreach (var pair in info.Textures) { if (!varInfoArray.Any(v => v.scope == ShaderVarScope.Uniform && v.type == ShaderVarType.Sampler2D && v.name == pair.Key)) { if (texRemoveSched == null) texRemoveSched = new List<string>(); texRemoveSched.Add(pair.Key); } } } if (info.Uniforms != null) { foreach (var pair in info.Uniforms) { if (!varInfoArray.Any(v => v.scope == ShaderVarScope.Uniform && v.type != ShaderVarType.Sampler2D && v.name == pair.Key)) { if (uniRemoveSched == null) uniRemoveSched = new List<string>(); uniRemoveSched.Add(pair.Key); } } } if (texRemoveSched != null) { foreach (string name in texRemoveSched) info.SetTexture(name, ContentRef<Texture>.Null); invokeSetter = true; } if (uniRemoveSched != null) { foreach (string name in uniRemoveSched) info.SetUniform(name, null); invokeSetter = true; } } } // Create BatchInfo variables according to Shader uniforms, if not existing yet if (!this.ReadOnly) { foreach (ShaderVarInfo varInfo in varInfoArray) { if (varInfo.scope != ShaderVarScope.Uniform) continue; // Set Texture variables if (varInfo.type == ShaderVarType.Sampler2D) { foreach (BatchInfo info in batchInfos.NotNull()) { if (info.GetTexture(varInfo.name).IsExplicitNull) { info.SetTexture(varInfo.name, Texture.White); invokeSetter = true; } } } // Set other uniform variables else { float[] uniformVal = varInfo.InitUniformData(); if (uniformVal != null) { foreach (BatchInfo info in batchInfos.NotNull()) { float[] oldVal = info.GetUniform(varInfo.name); if (oldVal == null) { info.SetUniform(varInfo.name, uniformVal); invokeSetter = true; } else if (oldVal.Length != uniformVal.Length) { for (int i = 0; i < Math.Min(oldVal.Length, uniformVal.Length); i++) uniformVal[i] = oldVal[i]; info.SetUniform(varInfo.name, uniformVal); invokeSetter = true; } } } } } } // Create editors according to existing variables var texDict = batchInfos.NotNull().First().Textures; var uniformDict = batchInfos.NotNull().First().Uniforms; Dictionary<string,PropertyEditor> oldEditors = new Dictionary<string,PropertyEditor>(this.shaderVarEditors); if (texDict != null) { foreach (var tex in texDict) { ShaderVarInfo varInfo = varInfoArray.FirstOrDefault(v => v.scope == ShaderVarScope.Uniform && v.name == tex.Key); if (!varInfo.IsEditorVisible) continue; string texName = varInfo.name; if (oldEditors.ContainsKey(texName)) oldEditors.Remove(texName); else { PropertyEditor e = this.ParentGrid.CreateEditor(typeof(ContentRef<Texture>), this); e.Getter = this.CreateTextureValueGetter(texName); e.Setter = !this.ReadOnly ? this.CreateTextureValueSetter(texName) : null; e.PropertyName = texName; this.shaderVarEditors[texName] = e; this.ParentGrid.ConfigureEditor(e); this.AddPropertyEditor(e); } } } if (uniformDict != null) { foreach (var uniform in uniformDict) { ShaderVarInfo varInfo = varInfoArray.FirstOrDefault(v => v.scope == ShaderVarScope.Uniform && v.name == uniform.Key); if (!varInfo.IsEditorVisible) continue; PropertyEditor e = this.CreateUniformEditor(varInfo); if (e != null) { if (oldEditors.ContainsValue(e)) oldEditors.Remove(varInfo.name); else { e.PropertyName = uniform.Key; this.shaderVarEditors[uniform.Key] = e; this.AddPropertyEditor(e); } } } } // Remove old editors that aren't needed anymore foreach (var pair in oldEditors) { if (this.shaderVarEditors[pair.Key] == pair.Value) this.shaderVarEditors.Remove(pair.Key); this.RemovePropertyEditor(pair.Value); } // If we actually changed (updated) data here, invoke the setter if (invokeSetter) { this.SetValues(batchInfos); if (!this.IsUpdating) this.PerformGetValue(); } } }
protected PropertyEditor CreateUniformEditor(ShaderVarInfo varInfo) { PropertyEditor oldEditor; this.shaderVarEditors.TryGetValue(varInfo.name, out oldEditor); List<EditorHintAttribute> configData = new List<EditorHintAttribute>(); if (varInfo.arraySize == 1) { if (varInfo.type == ShaderVarType.Float || varInfo.type == ShaderVarType.Int) { Type editType = typeof(float); if (varInfo.type == ShaderVarType.Int) editType = typeof(int); if (oldEditor != null && oldEditor.EditedType == editType) return oldEditor; else { PropertyEditor e = this.ParentGrid.CreateEditor(editType, this); if (varInfo.type == ShaderVarType.Int) { e.Getter = this.CreateUniformIntValueGetter(varInfo.name); e.Setter = !this.ReadOnly ? this.CreateUniformIntValueSetter(varInfo.name) : null; } else { e.Getter = this.CreateUniformFloatValueGetter(varInfo.name); e.Setter = !this.ReadOnly ? this.CreateUniformFloatValueSetter(varInfo.name) : null; configData.Add(new EditorHintIncrementAttribute(0.1f)); } this.ParentGrid.ConfigureEditor(e, configData); return e; } } else if (varInfo.type == ShaderVarType.Vec2) { if (oldEditor != null && oldEditor.EditedType == typeof(Vector2)) return oldEditor; else { PropertyEditor e = this.ParentGrid.CreateEditor(typeof(Vector2), this); e.Getter = this.CreateUniformVec2ValueGetter(varInfo.name); e.Setter = !this.ReadOnly ? this.CreateUniformVec2ValueSetter(varInfo.name) : null; configData.Add(new EditorHintIncrementAttribute(0.1f)); this.ParentGrid.ConfigureEditor(e, configData); return e; } } else if (varInfo.type == ShaderVarType.Vec3) { if (oldEditor != null && oldEditor.EditedType == typeof(Vector3)) return oldEditor; else { PropertyEditor e = this.ParentGrid.CreateEditor(typeof(Vector3), this); e.Getter = this.CreateUniformVec3ValueGetter(varInfo.name); e.Setter = !this.ReadOnly ? this.CreateUniformVec3ValueSetter(varInfo.name) : null; configData.Add(new EditorHintIncrementAttribute(0.1f)); this.ParentGrid.ConfigureEditor(e, configData); return e; } } else { if (oldEditor != null && oldEditor.EditedType == typeof(float[])) return oldEditor; else { PropertyEditor e = this.ParentGrid.CreateEditor(typeof(float[]), this); e.Getter = this.CreateUniformValueGetter(varInfo.name); e.Setter = !this.ReadOnly ? this.CreateUniformValueSetter(varInfo.name) : null; if (e is GroupedPropertyEditor) { (e as GroupedPropertyEditor).EditorAdded += this.UniformList_EditorAdded; } this.ParentGrid.ConfigureEditor(e, configData); return e; } } } else { Array oldValue = oldEditor != null ? (oldEditor as IListPropertyEditor).DisplayedValue as Array : null; Type oldElementType = oldValue != null ? oldValue.GetType().GetElementType() : null; int oldLen = oldValue != null ? oldValue.Length : -1; if (varInfo.type == ShaderVarType.Float || varInfo.type == ShaderVarType.Int) { Type editType = typeof(float); if (varInfo.type == ShaderVarType.Int) editType = typeof(int); if (oldLen == varInfo.arraySize && oldElementType == editType) return oldEditor; else { PropertyEditor e = this.ParentGrid.CreateEditor(editType.MakeArrayType(), this); e.Getter = this.CreateUniformValueGetter(varInfo.name); e.Setter = !this.ReadOnly ? this.CreateUniformValueSetter(varInfo.name) : null; e.ForceWriteBack = true; if (e is GroupedPropertyEditor) { if (varInfo.type == ShaderVarType.Float) (e as GroupedPropertyEditor).EditorAdded += this.UniformList_EditorAdded; } this.ParentGrid.ConfigureEditor(e, configData); return e; } } else if (varInfo.type == ShaderVarType.Vec2) { if (oldLen == varInfo.arraySize && oldElementType == typeof(Vector2)) return oldEditor; else { PropertyEditor e = this.ParentGrid.CreateEditor(typeof(Vector2[]), this); e.Getter = this.CreateUniformVec2ArrayValueGetter(varInfo.name); e.Setter = !this.ReadOnly ? this.CreateUniformVec2ArrayValueSetter(varInfo.name) : null; e.ForceWriteBack = true; if (e is GroupedPropertyEditor) { (e as GroupedPropertyEditor).EditorAdded += this.UniformList_EditorAdded; } this.ParentGrid.ConfigureEditor(e, configData); return e; } } else if (varInfo.type == ShaderVarType.Vec3) { if (oldLen == varInfo.arraySize && oldElementType == typeof(Vector3)) return oldEditor; else { PropertyEditor e = this.ParentGrid.CreateEditor(typeof(Vector3[]), this); e.Getter = this.CreateUniformVec3ArrayValueGetter(varInfo.name); e.Setter = !this.ReadOnly ? this.CreateUniformVec3ArrayValueSetter(varInfo.name) : null; e.ForceWriteBack = true; if (e is GroupedPropertyEditor) { (e as GroupedPropertyEditor).EditorAdded += this.UniformList_EditorAdded; } this.ParentGrid.ConfigureEditor(e, configData); return e; } } else { if (oldLen == varInfo.arraySize) return oldEditor; else { PropertyEditor e = this.ParentGrid.CreateEditor(typeof(float[][]), this); e.Getter = this.CreateUniformArrayValueGetter(varInfo.name, varInfo.arraySize); e.Setter = !this.ReadOnly ? this.CreateUniformArrayValueSetter(varInfo.name) : null; e.ForceWriteBack = true; if (e is GroupedPropertyEditor) { (e as GroupedPropertyEditor).EditorAdded += this.UniformList_EditorAdded; } this.ParentGrid.ConfigureEditor(e, configData); return e; } } } }
/// <summary> /// Compiles the ShaderProgram. This is done automatically when loading the ShaderProgram /// or when binding it. /// </summary> public void Compile() { DualityApp.GuardSingleThreadState(); if (this.glProgramId == 0) return; if (this.compiled) return; GL.LinkProgram(this.glProgramId); int result; GL.GetProgram(this.glProgramId, GetProgramParameterName.LinkStatus, out result); if (result == 0) { string infoLog = GL.GetProgramInfoLog(this.glProgramId); Log.Core.WriteError("Error linking shader program. InfoLog:{1}{0}", infoLog, Environment.NewLine); return; } this.compiled = true; // Collect variable infos from sub programs ShaderVarInfo[] geomVarArray = this.geom.IsAvailable ? this.geom.Res.VarInfo : null; ShaderVarInfo[] fragVarArray = this.frag.IsAvailable ? this.frag.Res.VarInfo : null; ShaderVarInfo[] vertVarArray = this.vert.IsAvailable ? this.vert.Res.VarInfo : null; var emptyShaderInfo = new ShaderVarInfo[0]; this.varInfo = (vertVarArray ?? emptyShaderInfo) .Union(fragVarArray ?? emptyShaderInfo) .Union(geomVarArray ?? emptyShaderInfo) .ToArray(); // Determine actual variable locations for (int i = 0; i < this.varInfo.Length; i++) { if (this.varInfo[i].scope == ShaderVarScope.Uniform) this.varInfo[i].glVarLoc = GL.GetUniformLocation(this.glProgramId, this.varInfo[i].name); else this.varInfo[i].glVarLoc = GL.GetAttribLocation(this.glProgramId, this.varInfo[i].name); } }