af8a8f17f6
Start on base for BLO. These will probably not be usable for awhile and is wip.. Currently aiming to support more varied layouts so this can help improve the code base. Add BRFNT and BCFNT support. All merged as BXFNT class. Fix bflim 3ds with LA4 textures. Fix loading/saving part panes with property user data. Fix texture coordinates to default centered UVs for layout panes with no textures. Cleanup some files and directories.
354 lines
13 KiB
C#
354 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using OpenTK.Graphics.OpenGL;
|
|
using OpenTK;
|
|
using Toolbox.Library;
|
|
|
|
namespace LayoutBXLYT
|
|
{
|
|
public class BxlytShader : IDisposable
|
|
{
|
|
public bool Compiled = false;
|
|
|
|
public int program;
|
|
private int vertexShader;
|
|
private int fragmentShader;
|
|
|
|
private Dictionary<string, int> attributes = new Dictionary<string, int>();
|
|
private Dictionary<string, int> uniforms = new Dictionary<string, int>();
|
|
private int activeAttributeCount;
|
|
|
|
public void LoadShaders()
|
|
{
|
|
Compile();
|
|
}
|
|
|
|
public void Enable()
|
|
{
|
|
GL.UseProgram(program);
|
|
}
|
|
|
|
public void Disable()
|
|
{
|
|
GL.UseProgram(0);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
GL.DeleteProgram(program);
|
|
}
|
|
|
|
public virtual string VertexShader
|
|
{
|
|
get
|
|
{
|
|
StringBuilder vert = new StringBuilder();
|
|
vert.AppendLine("uniform mat4 rotationMatrix;");
|
|
vert.AppendLine("void main()");
|
|
vert.AppendLine("{");
|
|
{
|
|
vert.AppendLine("gl_FrontColor = gl_Color;");
|
|
vert.AppendLine("gl_Position = gl_ModelViewProjectionMatrix * rotationMatrix * gl_Vertex;");
|
|
}
|
|
vert.AppendLine("}");
|
|
return vert.ToString();
|
|
}
|
|
}
|
|
public virtual string FragmentShader
|
|
{
|
|
get
|
|
{
|
|
StringBuilder vert = new StringBuilder();
|
|
vert.AppendLine("uniform vec4 color;");
|
|
vert.AppendLine("void main()");
|
|
vert.AppendLine("{");
|
|
{
|
|
vert.AppendLine("gl_FragColor = gl_Color * color;");
|
|
}
|
|
vert.AppendLine("}");
|
|
return vert.ToString();
|
|
}
|
|
}
|
|
|
|
//For non material panes
|
|
public void SetBasic(BasePane pane, Color color)
|
|
{
|
|
var rotationMatrix = pane.GetRotationMatrix();
|
|
SetMatrix("rotationMatrix", ref rotationMatrix);
|
|
SetColor("color", color);
|
|
}
|
|
|
|
public void SetVec4(string name, Vector4 value)
|
|
{
|
|
if (uniforms.ContainsKey(name))
|
|
GL.Uniform4(uniforms[name], value);
|
|
}
|
|
|
|
public void SetVec2(string name, Vector2 value)
|
|
{
|
|
if (uniforms.ContainsKey(name))
|
|
GL.Uniform2(uniforms[name], value);
|
|
}
|
|
|
|
public void SetFloat(string name, float value)
|
|
{
|
|
if (uniforms.ContainsKey(name))
|
|
GL.Uniform1(uniforms[name], value);
|
|
}
|
|
|
|
public void SetInt(string name, int value)
|
|
{
|
|
if (uniforms.ContainsKey(name))
|
|
GL.Uniform1(uniforms[name], value);
|
|
}
|
|
|
|
public void SetBool(string name, bool value)
|
|
{
|
|
int intValue = value == true ? 1 : 0;
|
|
|
|
if (uniforms.ContainsKey(name))
|
|
GL.Uniform1(uniforms[name], intValue);
|
|
}
|
|
|
|
public void SetColor(string name, Color color)
|
|
{
|
|
if (uniforms.ContainsKey(name))
|
|
GL.Uniform4(uniforms[name], color);
|
|
}
|
|
|
|
public void SetMatrix(string name, ref Matrix4 value)
|
|
{
|
|
if (uniforms.ContainsKey(name))
|
|
GL.UniformMatrix4(uniforms[name], false, ref value);
|
|
}
|
|
|
|
public int this[string name]
|
|
{
|
|
get { return uniforms[name]; }
|
|
}
|
|
|
|
private void LoadAttributes(int program)
|
|
{
|
|
attributes.Clear();
|
|
|
|
GL.GetProgram(program, GetProgramParameterName.ActiveAttributes, out activeAttributeCount);
|
|
for (int i = 0; i < activeAttributeCount; i++)
|
|
{
|
|
int size = 0;
|
|
ActiveAttribType type;
|
|
|
|
string name = GL.GetActiveAttrib(program, i, out size, out type);
|
|
int location = GL.GetAttribLocation(program, name);
|
|
|
|
// Overwrite existing vertex attributes.
|
|
attributes[name] = location;
|
|
}
|
|
}
|
|
|
|
public void EnableVertexAttributes()
|
|
{
|
|
foreach (KeyValuePair<string, int> attrib in attributes)
|
|
GL.EnableVertexAttribArray(attrib.Value);
|
|
}
|
|
|
|
public void DisableVertexAttributes()
|
|
{
|
|
foreach (KeyValuePair<string, int> attrib in attributes)
|
|
GL.DisableVertexAttribArray(attrib.Value);
|
|
}
|
|
|
|
public int GetAttribute(string name)
|
|
{
|
|
if (string.IsNullOrEmpty(name) || !attributes.ContainsKey(name))
|
|
return -1;
|
|
else
|
|
return attributes[name];
|
|
}
|
|
|
|
private void LoadUniorms(int program)
|
|
{
|
|
uniforms.Clear();
|
|
|
|
GL.GetProgram(program, GetProgramParameterName.ActiveUniforms, out activeAttributeCount);
|
|
for (int i = 0; i < activeAttributeCount; i++)
|
|
{
|
|
int size = 0;
|
|
ActiveUniformType type;
|
|
string name = GL.GetActiveUniform(program, i, out size, out type);
|
|
int location = GL.GetUniformLocation(program, name);
|
|
|
|
// Overwrite existing vertex attributes.
|
|
uniforms[name] = location;
|
|
}
|
|
}
|
|
|
|
public void Compile()
|
|
{
|
|
program = CompileShaders();
|
|
|
|
LoadAttributes(program);
|
|
LoadUniorms(program);
|
|
OnCompiled();
|
|
|
|
Compiled = true;
|
|
}
|
|
|
|
public virtual void OnCompiled() { }
|
|
|
|
private int CompileShaders()
|
|
{
|
|
vertexShader = GL.CreateShader(ShaderType.VertexShader);
|
|
GL.ShaderSource(vertexShader, VertexShader);
|
|
GL.CompileShader(vertexShader);
|
|
|
|
fragmentShader = GL.CreateShader(ShaderType.FragmentShader);
|
|
GL.ShaderSource(fragmentShader, FragmentShader);
|
|
GL.CompileShader(fragmentShader);
|
|
|
|
var program = GL.CreateProgram();
|
|
GL.AttachShader(program, vertexShader);
|
|
GL.AttachShader(program, fragmentShader);
|
|
GL.LinkProgram(program);
|
|
|
|
var info = GL.GetProgramInfoLog(program);
|
|
if (!string.IsNullOrWhiteSpace(info))
|
|
{
|
|
if (Toolbox.Library.Runtime.DumpShadersDEBUG)
|
|
{
|
|
if (!System.IO.Directory.Exists("ShaderDump"))
|
|
System.IO.Directory.CreateDirectory("ShaderDump");
|
|
|
|
System.IO.File.WriteAllText($"ShaderDump/ShaderError_VS[{vertexShader}]_FS[{fragmentShader}].txt",
|
|
info + VertexShader + FragmentShader);
|
|
}
|
|
|
|
|
|
Console.WriteLine($"GL.LinkProgram had info log: {info}");
|
|
}
|
|
|
|
GL.DetachShader(program, vertexShader);
|
|
GL.DetachShader(program, fragmentShader);
|
|
GL.DeleteShader(vertexShader);
|
|
GL.DeleteShader(fragmentShader);
|
|
return program;
|
|
}
|
|
|
|
public static void LoadDefaultBlending()
|
|
{
|
|
GL.Enable(EnableCap.Blend);
|
|
GL.Enable(EnableCap.AlphaTest);
|
|
GL.AlphaFunc(AlphaFunction.Always, 0f);
|
|
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
|
|
GL.BlendEquation(BlendEquationMode.FuncAdd);
|
|
GL.Disable(EnableCap.ColorLogicOp);
|
|
GL.LogicOp(LogicOp.Noop);
|
|
}
|
|
|
|
public static void LoadTextureUniforms(BxlytShader shader, BxlytMaterial material,
|
|
Dictionary<string, STGenericTexture> textures)
|
|
{
|
|
shader.SetInt("hasTexture0", 0);
|
|
shader.SetInt("hasTexture1", 0);
|
|
shader.SetInt("hasTexture2", 0);
|
|
shader.SetInt("textures0", 0);
|
|
shader.SetInt("textures1", 0);
|
|
shader.SetInt("textures2", 0);
|
|
|
|
BindTextureUniforms(shader, material);
|
|
|
|
if (material.TextureMaps.Length > 0 || Runtime.LayoutEditor.Shading == Runtime.LayoutEditor.DebugShading.UVTestPattern)
|
|
GL.Enable(EnableCap.Texture2D);
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
//Default UVs as centered
|
|
var matTranslate = Matrix4.CreateTranslation(0 / 1 - 0.5f, 0 / 1 - 0.5f, 0);
|
|
shader.SetMatrix(String.Format("textureTransforms[{0}]", i), ref matTranslate);
|
|
}
|
|
|
|
int id = 1;
|
|
for (int i = 0; i < material.TextureMaps.Length; i++)
|
|
{
|
|
string TexName = material.TextureMaps[i].Name;
|
|
|
|
if (material.animController.TexturePatterns.ContainsKey((LTPTarget)i))
|
|
TexName = material.animController.TexturePatterns[(LTPTarget)i];
|
|
|
|
if (textures.ContainsKey(TexName))
|
|
{
|
|
GL.ActiveTexture(TextureUnit.Texture0 + id);
|
|
shader.SetInt($"textures{i}", id);
|
|
bool isBinded = BxlytToGL.BindGLTexture(material.TextureMaps[i], textures[TexName]);
|
|
if (isBinded)
|
|
shader.SetInt($"hasTexture{i}", 1);
|
|
|
|
var scale = new Syroot.Maths.Vector2F(1, 1);
|
|
float rotate = 0;
|
|
var translate = new Syroot.Maths.Vector2F(0, 0);
|
|
|
|
int index = i;
|
|
|
|
if (material.TextureTransforms.Length > index)
|
|
{
|
|
var transform = material.TextureTransforms[index];
|
|
scale = transform.Scale;
|
|
rotate = transform.Rotate;
|
|
translate = transform.Translate;
|
|
|
|
foreach (var animItem in material.animController.TextureSRTS)
|
|
{
|
|
switch (animItem.Key)
|
|
{
|
|
case LTSTarget.ScaleS: scale.X = animItem.Value; break;
|
|
case LTSTarget.ScaleT: scale.Y = animItem.Value; break;
|
|
case LTSTarget.Rotate: rotate = animItem.Value; break;
|
|
case LTSTarget.TranslateS: translate.X = animItem.Value; break;
|
|
case LTSTarget.TranslateT: translate.Y = animItem.Value; break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
var matScale = Matrix4.CreateScale(scale.X, scale.Y, 1.0f);
|
|
var matRotate = Matrix4.CreateFromAxisAngle(new Vector3(0, 0, 1), MathHelper.DegreesToRadians(rotate));
|
|
var matTranslate = Matrix4.CreateTranslation(
|
|
translate.X / scale.X - 0.5f,
|
|
translate.Y / scale.Y - 0.5f, 0);
|
|
|
|
Matrix4 matTransform = matRotate * matTranslate * matScale;
|
|
shader.SetMatrix(String.Format("textureTransforms[{0}]", i), ref matTransform);
|
|
|
|
id++;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void BindTextureUniforms(BxlytShader shader, BxlytMaterial material)
|
|
{
|
|
//Do uv test pattern
|
|
GL.ActiveTexture(TextureUnit.Texture10);
|
|
GL.Uniform1(GL.GetUniformLocation(shader.program, "uvTestPattern"), 10);
|
|
GL.BindTexture(TextureTarget.Texture2D, RenderTools.uvTestPattern.RenderableTex.TexID);
|
|
|
|
if (material.TextureMaps.Length > 0)
|
|
{
|
|
var tex = material.TextureMaps[0];
|
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, BxlytToGL.ConvertTextureWrap(tex.WrapModeU));
|
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, BxlytToGL.ConvertTextureWrap(tex.WrapModeV));
|
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, BxlytToGL.ConvertMagFilterMode(tex.MaxFilterMode));
|
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, BxlytToGL.ConvertMinFilterMode(tex.MinFilterMode));
|
|
}
|
|
else
|
|
{
|
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureParameterName.ClampToEdge);
|
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureParameterName.ClampToEdge);
|
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
|
|
}
|
|
}
|
|
}
|
|
}
|