public MaterialEntry(EndianBinaryReader er) { Name = er.ReadString(Encoding.ASCII, 20).Replace("\0", ""); BufferColor = er.ReadColor8(); ConstColors = new Color[6]; ConstColors[0] = er.ReadColor8(); ConstColors[1] = er.ReadColor8(); ConstColors[2] = er.ReadColor8(); ConstColors[3] = er.ReadColor8(); ConstColors[4] = er.ReadColor8(); ConstColors[5] = er.ReadColor8(); Flags = er.ReadUInt32(); //Material Flag: // 0-1: Nr texMap // 2-3: Nr texMatrix // 4-5: Nr texCoordGen // 6-8: Nr tevStage // 9: Has alphaCompare // 10: Has blendMode // 11: Use Texture Only // 12: Separate Blend Mode // 14: Has Indirect Parameter //15-16: Nr projectionTexGenParameter // 17: Has Font Shadow Parameter TexMaps = new TexMap[Flags & 3]; for (int i = 0; i < (Flags & 3); i++) { TexMaps[i] = new TexMap(er); } TexMatrices = new TexMatrix[(Flags >> 2) & 3]; for (int i = 0; i < ((Flags >> 2) & 3); i++) { TexMatrices[i] = new TexMatrix(er); } TexCoordGens = new TexCoordGen[(Flags >> 4) & 3]; for (int i = 0; i < ((Flags >> 4) & 3); i++) { TexCoordGens[i] = new TexCoordGen(er); } TevStages = new TevStage[(Flags >> 6) & 7]; for (int i = 0; i < ((Flags >> 6) & 7); i++) { TevStages[i] = new TevStage(er); } if (((Flags >> 9) & 1) == 1) { AlphaTest = new AlphaCompare(er); } if (((Flags >> 10) & 1) == 1) { ColorBlendMode = new BlendMode(er); } if (((Flags >> 12) & 1) == 1) { AlphaBlendMode = new BlendMode(er); } //Some more things }
private static string GenerateTexGens(StringBuilder stream, Material mat) { for (int i = 0; i < mat.NumTexGens; i++) { TexCoordGen gen = mat.TexGens[i]; stream.AppendLine($"\t// TexGen { i } - Type: { gen.Type }, Source: { gen.Source }, Matrix: { gen.TexMatrixSource }"); stream.AppendLine($"\tv_Tex{ i } = { GenerateTexGenPost(gen) };\n"); } return(stream.ToString()); }
private static TexCoordGen ReadTexCoordGen(EndianBinaryReader stream) { var retVal = new TexCoordGen { Type = (GXTexGenType)stream.ReadByte(), Source = (GXTexGenSrc)stream.ReadByte(), TexMatrixSource = (GXTexMatrix)stream.ReadByte() }; Trace.Assert(stream.ReadByte() == 0xFF); // Padding return(retVal); }
private static string GenerateTexGenPost(TexCoordGen gen) { StringBuilder stream = new StringBuilder(); string gen_source = GenerateTexGenSource(gen.Source); if (gen.Type == GXTexGenType.SRTG) { return($"vec3({ gen_source }.xy, 1.0)"); } else if (gen.Type == GXTexGenType.Matrix2x4) { return($"vec3({ GenerateTexGenMatrixMult(gen, gen_source) }.xy, 1.0)"); } else if (gen.Type == GXTexGenType.Matrix3x4) { return(GenerateTexGenMatrixMult(gen, gen_source)); } return(stream.ToString()); }
private static string GenerateTexGenMatrixMult(TexCoordGen gen, string src) { if (false) { } else { if (gen.TexMatrixSource == GXTexMatrix.Identity) { return($"{ src }.xyz"); } else if (gen.TexMatrixSource >= GXTexMatrix.TexMtx0) { int id = (gen.TexMatrixSource - GXTexMatrix.TexMtx0) / 3; return($"(mat4x4(TexMatrices[{ id }]) * { src })"); } else { return(src); } } }
public MaterialEntry(EndianBinaryReader er) { Name = er.ReadString(Encoding.ASCII, 20).Replace("\0", ""); BufferColor = er.ReadColor8(); ConstColors = new Color[6]; ConstColors[0] = er.ReadColor8(); ConstColors[1] = er.ReadColor8(); ConstColors[2] = er.ReadColor8(); ConstColors[3] = er.ReadColor8(); ConstColors[4] = er.ReadColor8(); ConstColors[5] = er.ReadColor8(); Flags = er.ReadUInt32(); //Material Flag: // 0-1: Nr texMap // 2-3: Nr texMatrix // 4-5: Nr texCoordGen // 6-8: Nr tevStage // 9: Has alphaCompare // 10: Has blendMode // 11: Use Texture Only // 12: Separate Blend Mode // 14: Has Indirect Parameter //15-16: Nr projectionTexGenParameter // 17: Has Font Shadow Parameter TexMaps = new TexMap[Flags & 3]; for (int i = 0; i < (Flags & 3); i++) { TexMaps[i] = new TexMap(er); } TexMatrices = new TexMatrix[(Flags >> 2) & 3]; for (int i = 0; i < ((Flags >> 2) & 3); i++) { TexMatrices[i] = new TexMatrix(er); } TexCoordGens = new TexCoordGen[(Flags >> 4) & 3]; for (int i = 0; i < ((Flags >> 4) & 3); i++) { TexCoordGens[i] = new TexCoordGen(er); } TevStages = new TevStage[(Flags >> 6) & 7]; for (int i = 0; i < ((Flags >> 6) & 7); i++) { TevStages[i] = new TevStage(er); } if (((Flags >> 9) & 1) == 1) AlphaTest = new AlphaCompare(er); if (((Flags >> 10) & 1) == 1) ColorBlendMode = new BlendMode(er); if (((Flags >> 12) & 1) == 1) AlphaBlendMode = new BlendMode(er); //Some more things }
public static bool GenerateVertexShader(Shader shader, Material mat) { StringBuilder stream = new StringBuilder(); // Shader Header stream.AppendLine("#version 330 core"); stream.AppendLine(); // Input Format stream.AppendLine("// Input"); if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Position)) { stream.AppendLine("in vec3 RawPosition;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Normal)) { stream.AppendLine("in vec3 RawNormal;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Color0)) { stream.AppendLine("in vec4 RawColor0;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Color1)) { stream.AppendLine("in vec4 RawColor1;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex0)) { stream.AppendLine("in vec2 RawTex0;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex1)) { stream.AppendLine("in vec2 RawTex1;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex2)) { stream.AppendLine("in vec2 RawTex2;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex3)) { stream.AppendLine("in vec2 RawTex3;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex4)) { stream.AppendLine("in vec2 RawTex4;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex5)) { stream.AppendLine("in vec2 RawTex5;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex6)) { stream.AppendLine("in vec2 RawTex6;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex7)) { stream.AppendLine("in vec2 RawTex7;"); } stream.AppendLine(); // Output Format stream.AppendLine("// Output"); if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Normal)) { stream.AppendLine("out vec3 Normal;"); } for (int i = 0; i < mat.NumChannelControls; i++) { stream.AppendLine(string.Format("out vec4 Color{0};", i)); } for (int texGen = 0; texGen < mat.NumTexGens; texGen++) { if (mat.TexGenInfos[texGen] != null) { stream.AppendLine(string.Format("out vec3 Tex{0};", texGen)); } } // Uniforms stream.AppendLine(); stream.AppendLine("// Uniforms"); stream.AppendLine( " uniform mat4 ModelMtx;\n" + " uniform mat4 ViewMtx;\n" + " uniform mat4 ProjMtx;\n" + "\n" + " uniform mat4 TexMtx[10];\n" + " uniform mat4 PostMtx[20];\n" + " uniform vec4 COLOR0_Amb;\n" + " uniform vec4 COLOR0_Mat;\n" + " uniform vec4 COLOR1_Amb;\n" + " uniform vec4 COLOR1_Mat;\n" + "\n" + "struct GXLight\n" + "{\n" + " vec4 Position;\n" + " vec4 Direction;\n" + " vec4 Color;\n" + " vec4 DistAtten;\n" + " vec4 AngleAtten;\n" + "};\n" + "\n" + " GXLight Lights[8];\n" + "\n" + "uniform int NumLights;\n" + "uniform vec4 ambLightColor;\n"); // Main Shader Code stream.AppendLine("// Main"); stream.AppendLine("void main()"); stream.AppendLine("{"); stream.AppendLine(" mat4 MVP = ProjMtx * ViewMtx * ModelMtx;"); stream.AppendLine(" mat4 MV = ViewMtx * ModelMtx;"); if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Position)) { stream.AppendLine(" gl_Position = MVP * vec4(RawPosition, 1);"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Normal)) { stream.AppendLine(" Normal = normalize(RawNormal.xyz * inverse(transpose(mat3(MV))));"); } //if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Color0)) // stream.AppendLine(" Color0 = RawColor0;"); //if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Color1)) //stream.AppendLine(" Color1 = RawColor1;"); stream.AppendLine(); stream.AppendLine(" // Ambient Colors & Material Colors"); // Add the Ambient Colors for the Material for (int a = 0; a < mat.AmbientColors.Length; a++) { stream.AppendLine(string.Format(" vec4 ambColor{0} = vec4({1}, {2}, {3}, {4});", a, mat.AmbientColors[a].R, mat.AmbientColors[a].G, mat.AmbientColors[a].B, mat.AmbientColors[a].A)); } // Add in the Material Colors for (int m = 0; m < mat.MaterialColors.Length; m++) { stream.AppendLine(string.Format(" vec4 matColor{0} = vec4({1}, {2}, {3}, {4});", m, mat.MaterialColors[m].R, mat.MaterialColors[m].G, mat.MaterialColors[m].B, mat.MaterialColors[m].A)); } stream.AppendLine(); stream.AppendLine(string.Format(" // ChanCtrl's - {0} count", mat.NumChannelControls)); // Channel Controllers // A vertex can have up to two color channels (RGBA each) which gives us four possible channels: // color0, color1, alpha0, alpha1 // Each channel has an associated ambient color/alpha and a material color/alpha. These can come // from vertex colors or existing amb/mat registers. for (int chanSel = 0; chanSel < mat.NumChannelControls; chanSel++) { ChanCtrl chanInfo = mat.ChannelControls[chanSel]; string chanTarget, ambColor, matColor, ambLight, diffLight; string swizzle, chan; bool alpha; // Todo: Is this really a fixed order? switch (chanSel) { case /* Color0 */ 0: chan = "0"; swizzle = ".rgb"; alpha = false; break; case /* Alpha0 */ 1: chan = "0"; swizzle = ".a"; alpha = true; break; case /* Color1 */ 2: chan = "1"; swizzle = ".rgb"; alpha = false; break; case /* Alpha1 */ 3: chan = "1"; swizzle = ".a"; alpha = true; break; default: WLog.Warning(LogCategory.TEVShaderGenerator, shader, "Unknown vertex output color channel {0}, skipping.", chanSel); continue; } chanTarget = string.Format("Color{0}{1}", chan, swizzle); ambColor = (chanInfo.AmbientSrc == GXColorSrc.Vertex ? "RawColor" : "ambColor") + chan + swizzle; matColor = (chanInfo.MaterialSrc == GXColorSrc.Vertex ? "RawColor" : "matColor") + chan + swizzle; ambLight = "ambLightColor" + swizzle; diffLight = GetLightCalcString(chanInfo, alpha); //Color{0}.rgb = ambient * ambLightColor * light stream.AppendLine(string.Format(" Color{0} = vec4(1, 1, 1, 1);", chan)); if (chanInfo.Enable) { stream.AppendLine(string.Format(" {0} = {1} * {2} + {3} * {4};", chanTarget, ambColor, ambLight, matColor, diffLight)); } else { stream.AppendLine(string.Format(" {0} = {1};", chanTarget, matColor)); } stream.AppendLine(); stream.AppendLine(); } // Texture Coordinate Generation stream.AppendLine(string.Format(" // TexGen - {0} count", mat.NumTexGens)); for (int i = 0; i < mat.NumTexGens; i++) { if (mat.TexGenInfos[i] == null) { continue; } TexCoordGen texGen = mat.TexGenInfos[i]; string texGenSrc; switch (texGen.Source) { case GXTexGenSrc.Position: texGenSrc = "RawPosition"; break; case GXTexGenSrc.Normal: texGenSrc = "RawNormal"; break; case GXTexGenSrc.Color0: texGenSrc = "Color0"; break; case GXTexGenSrc.Color1: texGenSrc = "Color1"; break; case GXTexGenSrc.Tex0: texGenSrc = "RawTex0"; break; // Should Tex0 be TEXTURE 0? Or is it TEX0 = Input TEX0, while TEXCOORD0 = Output TEX0? case GXTexGenSrc.Tex1: texGenSrc = "RawTex1"; break; case GXTexGenSrc.Tex2: texGenSrc = "RawTex2"; break; case GXTexGenSrc.Tex3: texGenSrc = "RawTex3"; break; case GXTexGenSrc.Tex4: texGenSrc = "RawTex4"; break; case GXTexGenSrc.Tex5: texGenSrc = "RawTex5"; break; case GXTexGenSrc.Tex6: texGenSrc = "RawTex6"; break; case GXTexGenSrc.Tex7: texGenSrc = "RawTex7"; break; case GXTexGenSrc.TexCoord0: texGenSrc = "Tex0"; break; case GXTexGenSrc.TexCoord1: texGenSrc = "Tex1"; break; case GXTexGenSrc.TexCoord2: texGenSrc = "Tex2"; break; case GXTexGenSrc.TexCoord3: texGenSrc = "Tex3"; break; case GXTexGenSrc.TexCoord4: texGenSrc = "Tex4"; break; case GXTexGenSrc.TexCoord5: texGenSrc = "Tex5"; break; case GXTexGenSrc.TexCoord6: texGenSrc = "Tex6"; break; case GXTexGenSrc.Tangent: case GXTexGenSrc.Binormal: default: WLog.Warning(LogCategory.TEVShaderGenerator, shader, "Unsupported TexGenSrc: {0}, defaulting to TEXCOORD0.", texGen.Source); texGenSrc = "Tex0"; break; } if (texGen.TexMatrixSource == GXTexMatrix.Identity) { switch (texGen.Type) { case GXTexGenType.Matrix2x4: stream.AppendLine(string.Format(" Tex{0} = vec3({1}.xy, 0);", i, texGenSrc)); break; case GXTexGenType.Matrix3x4: stream.AppendLine(string.Format(" float3 uvw = {0}.xyz;", texGenSrc)); stream.AppendLine(string.Format(" Tex{0} = vec3((uvw / uvw.z).xy,0);", i)); break; case GXTexGenType.SRTG: stream.AppendLine(string.Format(" Tex{0} = vec3({1}.rg, 0);", i, texGenSrc)); break; case GXTexGenType.Bump0: case GXTexGenType.Bump1: case GXTexGenType.Bump2: case GXTexGenType.Bump3: case GXTexGenType.Bump4: case GXTexGenType.Bump5: case GXTexGenType.Bump6: case GXTexGenType.Bump7: default: WLog.Warning(LogCategory.TEVShaderGenerator, shader, "Unsupported TexMatrixSource: {0}, Defaulting to Matrix2x4", texGen.TexMatrixSource); stream.AppendLine(string.Format(" Tex{0} = vec3({1}.xy, 0);", i, texGenSrc)); break; } } else { // Convert to TexMtx0 to TexMtx9 int matIndex = ((int)texGen.TexMatrixSource - 30) / 3; switch (texGen.Type) { default: WLog.Warning(LogCategory.TEVShaderGenerator, shader, "Unsupported TexMatrixSource"); break; } } } stream.AppendLine("}"); stream.AppendLine(); // Compile the Vertex Shader and return whether it compiled sucesfully or not. Directory.CreateDirectory("ShaderDump"); System.IO.File.WriteAllText("ShaderDump/" + mat.Name + "_vert_output", stream.ToString()); return(shader.CompileSource(stream.ToString(), OpenTK.Graphics.OpenGL.ShaderType.VertexShader)); }
private void GenerateTexGenXFCommands(Material mat) { XFCommand texGensCommand = new XFCommand(XFRegister.SETTEXMTXINFO); XFCommand dtTexGensCommand = new XFCommand(XFRegister.SETPOSMTXINFO); for (int i = 0; i < 8; i++) { if (mat.TexCoord1Gens[i] == null) { continue; } TexCoordGen texgen = mat.TexCoord1Gens[i].Value; XFCommandArgument texGenArg = new XFCommandArgument(); texGenArg.SetBits(0, 1, 1); texGenArg.SetBits(0, 2, 1); texGenArg.SetBits(5, 12, 3); texGenArg.SetBits(0, 15, 3); if (texgen.Type == TexGenType.Matrix3x4) { texGenArg.SetFlag(true, 1); } switch (texgen.Source) { case TexGenSrc.Position: case TexGenSrc.Normal: case TexGenSrc.Binormal: case TexGenSrc.Tangent: texGenArg.SetFlag(true, 2); break; } switch (texgen.Type) { case TexGenType.Bump0: case TexGenType.Bump1: case TexGenType.Bump2: case TexGenType.Bump3: case TexGenType.Bump4: case TexGenType.Bump5: case TexGenType.Bump6: case TexGenType.Bump7: texGenArg.SetBits(1, 4, 2); texGenArg.SetBits(texgen.Source - TexGenSrc.Tex0, 12, 3); texGenArg.SetBits(texgen.Type - TexGenType.Bump0, 15, 3); texGenArg.SetBits(5, 7, 3); break; case TexGenType.SRTG: texGenArg.SetBits(2, 7, 3); if (texgen.Source == TexGenSrc.Color0) { texGenArg.SetBits(2, 4, 2); } else if (texgen.Source == TexGenSrc.Color1) { texGenArg.SetBits(3, 4, 2); } break; default: texGenArg.SetBits(0, 4, 2); if (texgen.Source == TexGenSrc.Position || texgen.Source == TexGenSrc.Normal) { texGenArg.SetBits((int)texgen.Source, 7, 3); } else { texGenArg.SetBits((int)texgen.Source + 1, 7, 3); } break; } XFCommandArgument dtTexGenArg = new XFCommandArgument(); dtTexGenArg.SetFlag(false, 8); dtTexGenArg.SetBits(61, 0, 6); texGensCommand.Args.Add(texGenArg); dtTexGensCommand.Args.Add(dtTexGenArg); } if (texGensCommand.Args.Count > 0) { XFCommands.Add(texGensCommand); XFCommands.Add(dtTexGensCommand); } }
public Material(ref BinaryStream s) { name = Encoding.ASCII.GetString(s.ReadBytes(28)).Replace("\0", ""); flag = s.ReadUInt32(); unknown = s.ReadUInt32(); byte[] black = s.ReadBytes(4), white = s.ReadBytes(4); blackColor = Color.FromArgb(black[3], black[0], black[1], black[2]); whiteColor = Color.FromArgb(white[3], white[0], black[1], black[2]); texMapCount = (uint)(flag & 0x03); texSRTCount = (uint)((flag >> 2) & 0x03); texCoordGenCount = (uint)((flag >> 4) & 0x03); tevStageCount = (uint)((flag >> 6) & 0x07); hasAlphaCompare = ((flag >> 9) & 0x01) == 1; hasBlendMode = ((flag >> 10) & 0x01) == 1; useTextureOnly = ((flag >> 11) & 0x01) == 1; seperateBlendMode = ((flag >> 12) & 0x01) == 1; hasIndirectParameter = ((flag >> 14) & 0x01) == 1; projectionTexGenParameterCount = (uint)((flag >> 15) & 0x03); hasFontShadowParameter = ((flag >> 17) & 0x01) == 1; thresholingAlphaInterpolation = ((flag >> 18) & 0x01) == 1; texMaps = new TexMap[texMapCount]; for (int i = 0; i < texMapCount; i++) { texMaps[i] = new TexMap(ref s); } texSRTs = new TexSRT[texSRTCount]; for (int i = 0; i < texSRTCount; i++) { texSRTs[i] = new TexSRT(ref s); } texCoords = new TexCoordGen[texCoordGenCount]; for (int i = 0; i < texCoordGenCount; i++) { texCoords[i] = new TexCoordGen(ref s); } tevStages = new TevStage[tevStageCount]; for (int i = 0; i < tevStageCount; i++) { tevStages[i] = new TevStage(ref s); } if (hasAlphaCompare) { alphaCompare = new AlphaCompare(ref s); } if (hasBlendMode) { blendMode = new BlendMode(ref s); } if (seperateBlendMode) { blendAlpha = new BlendMode(ref s); } if (hasIndirectParameter) { indirectParameter = new IndirectParameter(ref s); } projectionTexGenParameters = new ProjectionTexGenParameters[projectionTexGenParameterCount]; for (int i = 0; i < projectionTexGenParameterCount; i++) { projectionTexGenParameters[i] = new ProjectionTexGenParameters(ref s); } if (hasFontShadowParameter) { fontShadowParameter = new FontShadowParameter(ref s); } //System.Windows.Forms.MessageBox.Show($"[{s.BaseStream.Position}] mat end"); }
public static string GenerateVertexShader(Material mat, MAT3 data) { StringBuilder stream = new StringBuilder(); // Shader Header stream.AppendLine("// Automatically Generated File. All changes will be lost."); stream.AppendLine("#version 330 core"); stream.AppendLine(); // Examine the attributes the mesh has so we can ensure the shader knows about the incoming data. // I don't think this is technically right, but meh. stream.AppendLine("// Per-Vertex Input"); if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Position)) { stream.AppendLine("in vec3 RawPosition;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Normal)) { stream.AppendLine("in vec3 RawNormal;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Color0)) { stream.AppendLine("in vec4 RawColor0;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Color1)) { stream.AppendLine("in vec4 RawColor1;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex0)) { stream.AppendLine("in vec2 RawTex0;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex1)) { stream.AppendLine("in vec2 RawTex1;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex2)) { stream.AppendLine("in vec2 RawTex2;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex3)) { stream.AppendLine("in vec2 RawTex3;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex4)) { stream.AppendLine("in vec2 RawTex4;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex5)) { stream.AppendLine("in vec2 RawTex5;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex6)) { stream.AppendLine("in vec2 RawTex6;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex7)) { stream.AppendLine("in vec2 RawTex7;"); } stream.AppendLine(); stream.AppendLine("// Output (Interpolated)"); stream.AppendLine(); // TEV uses up to 4 channels to accumulate the result of Per-Vertex Lighting/Material/Ambient lighting. // Color0, Alpha0, Color1, and Alpha1 are the four possible channel names. stream.AppendFormat("// NumChannelControls: {0}\n", mat.NumChannelControls); stream.AppendFormat("out vec4 colors_0;\n"); stream.AppendFormat("out vec4 colors_1;\n"); stream.AppendLine(); // TEV can generate up to 16 (?) sets of Texture Coordinates by taking an incoming data value (UV, POS, NRM, BINRM, TNGT) and transforming it by a matrix. stream.AppendFormat("// NumTexGens: {0}\n", mat.NumTexGensIndex); for (int i = 0; i < mat.NumTexGensIndex; i++) { stream.AppendFormat("out vec3 TexGen{0};\n", i); } stream.AppendLine(); // Declare shader Uniforms coming in from the CPU. stream.AppendLine("// Uniforms"); stream.AppendLine ( "uniform mat4 ModelMtx;\n" + "uniform mat4 ViewMtx;\n" + "uniform mat4 ProjMtx;\n" + "\n" + "uniform mat4 TexMtx[10];\n" + "uniform mat4 PostMtx[20];\n" + "uniform vec4 COLOR0_Amb;\n" + "uniform vec4 COLOR0_Mat;\n" + "uniform vec4 COLOR1_Mat;\n" + "uniform vec4 COLOR1_Amb;\n" + "\n" + "struct GXLight\n" + "{\n" + " vec4 Position;\n" + " vec4 Direction;\n" + " vec4 Color;\n" + " vec4 CosAtten; //AngleAtten\n" + // 1.875000, 0, 0 ? " vec4 DistAtten;\n" + // 1.875000, 0, 0 ? "};\n" + "\n" + "layout(std140) uniform LightBlock\n" + "{\n\tGXLight Lights[8];\n};\n" ); stream.AppendLine(); // Main Shader Code stream.AppendLine("// Main Vertex Shader"); stream.AppendLine("void main()\n{"); stream.AppendLine("\tmat4 MVP = ProjMtx * ViewMtx * ModelMtx;"); stream.AppendLine("\tmat4 MV = ViewMtx * ModelMtx;"); if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Position)) { stream.AppendLine("\tgl_Position = MVP * vec4(RawPosition, 1);"); stream.AppendLine("\tvec4 worldPos = ModelMtx * vec4(RawPosition, 1);"); } stream.AppendLine(); // Do Color Channel Fixups if (mat.NumChannelControls < 2) { if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Color1)) { stream.AppendFormat("\tcolors_1 = RawColor1;\n"); } else { stream.AppendFormat("\tcolors_1 = vec4(1, 1, 1, 1);\n"); } } stream.AppendLine(); // TEV Channel Colors. // A vertex can have two colors each (Color0, Color1) and each color has two channels - RGB and A. This gives us // up to 4 channels, color0, color1, alpha0, and alpha1. Channels are associated with an ambient color/alpha which can // come from a variety of sources - vertex colors, or special ambient and material registers. The register colors // are set in GX via the command: GXSetChanAmbColor(GXChanneLID chan, GXColor amb_color), and GXSetChanMatColor(GXChannelID chan, GXColor mat_color); // Now, the source for each channel can be controlled by another command: // GXSetChanCtrl(GXCHannelID chan, bool enable, GXColorSrc amb_src, GXColorSrc mat_src, GXLightID light_mask, GXDiffuseFn diff_fn, GXAttnFn attn_fn); // // If the lighting channel is disabled, then the material color for that channel is passed through unmodified. The mat_src parameter specifies if the // material color comes from the Vertex Color, or from the Material Register. If the channel is enabled, then lighting needs to be computed for each light // enabled in the light_mask. stream.AppendLine("\tvec4 ambColor = vec4(1,1,1,1);\n\tvec4 matColor = vec4(1,1,1,1);\n\tvec4 lightAccum = vec4(0,0,0,0);\n\tvec4 lightFunc;"); stream.AppendLine("\tvec3 ldir; float dist; float dist2; float attn;"); // Declaring these all anyways in case we use lighting. if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Normal)) { stream.AppendLine("\tvec3 _norm0 = RawNormal.xyz;"); } else { stream.AppendLine("\tvec3 _norm0 = vec3(0.0, 0.0, 0.0);"); } stream.AppendFormat("\t// {0} Channel Controller(s).\n", mat.NumChannelControls); for (int i = 0; i < mat.NumChannelControls; i++) { ColorChannelControl channelControl = mat.ColorChannelControls[i]; stream.AppendFormat("\t// Channel Control: {0} - LightingEnabled: {1} MaterialSrc: {2} LightMask: {3} DiffuseFn: {4} AttenuationFn: {5} AmbientSrc: {6}\n", i, channelControl.LightingEnabled, channelControl.MaterialSrc, channelControl.LitMask, channelControl.DiffuseFunction, channelControl.AttenuationFunction, channelControl.AmbientSrc); string swizzle, channel; switch (i) { case /* Color0 */ 0: channel = "0"; swizzle = ".rgb"; break; case /* Alpha0 */ 1: channel = "0"; swizzle = ".a"; break; case /* Color1 */ 2: channel = "1"; swizzle = ".rgb"; break; case /* Alpha1 */ 3: channel = "1"; swizzle = ".a"; break; // ToDo: This is wrong. There's a maximum of 2 color channels default: Console.WriteLine("Unknown Color Channel Control Index: {0}", i); continue; } bool isAlphaChannel = i % 2 != 0; string channelTarget = string.Format("colors_{0}", channel); bool ambSrcVtx = channelControl.AmbientSrc == GXColorSrc.Vertex; bool matSrcVtx = channelControl.MaterialSrc == GXColorSrc.Vertex; string ambColorSrc = string.Format("{0}{1}", (ambSrcVtx ? "RawColor" : "COLOR"), channel + (ambSrcVtx ? "" : "_Amb")); string matColorSrc = string.Format("{0}{1}", (matSrcVtx ? "RawColor" : "COLOR"), channel + (matSrcVtx ? "" : "_Mat")); stream.AppendFormat("\tambColor = {0};\n", ambColorSrc); stream.AppendFormat("\tmatColor = {0};\n", matColorSrc); for (int l = 0; l < 8; l++) { bool isLit = channelControl.LitMask.HasFlag((GXLightMask)(1 << l)); if (isLit) { stream.AppendFormat("\t// ChannelControl: {0} Light: {1}\n", i, l); GenerateLightVertexShader(stream, channelControl, l, swizzle, isAlphaChannel ? 1 : 3); } } if (channelControl.LightingEnabled) { stream.AppendLine("\tvec4 illum = clamp(ambColor + lightAccum, 0, 1);"); } stream.AppendFormat("\tlightFunc = {0};\n", channelControl.LightingEnabled ? "illum" : "vec4(1.0, 1.0, 1.0, 1.0)"); stream.AppendFormat("\t{0}{1} = (matColor * lightFunc){1};\n", channelTarget, swizzle); // Not sure if this is right, but if a single color channel is enabled then the alpha component of color_0 never gets assigned // and then something tries to use it and it's empty instead of being the ambSrc/matSrc alpha. if (mat.NumChannelControls == 1 || mat.NumChannelControls == 3) { // ToDo: https://github.com/dolphin-emu/dolphin/blob/master/Source/Core/VideoCommon/LightingShaderGen.h#L184 looks like a better implementation stream.AppendLine("\t// Doing an unknown fixup. There's only one color channel enabled, so we never write to the alpha of the color_*, and thus it never gets initialized."); stream.AppendFormat("\t{0}.a = matColor.a;\n", channelTarget); } } // TEV "TexGen" Texture Coordinate Generation // TEV can generate texture coordinates on the fly from a variety of sources. The various ways all follow the form of: // dst_coord = func(src_param, mtx) - that is, the destination coordinate is generated by multiplying an input source by a 2x4 or 3x4 matrix. // The input coordinates can come from one of the following locations: TEX0-7, POS, NRM, BINRM, TANGENT. // GX has a default set of texture matrices (GXTexMtx enum). stream.AppendFormat("\t// {0} Texture Coordinate Generators.\n", mat.NumTexGensIndex); stream.Append("\tvec4 coord;\n"); for (int i = 0; i < mat.NumTexGensIndex; i++) { TexCoordGen texGen = mat.TexGenInfoIndexes[i]; stream.AppendFormat("\t// TexGen: {0} Type: {1} Source: {2} TexMatrixIndex: {3}\n", i, texGen.Type, texGen.Source, texGen.TexMatrixSource); stream.AppendLine("\t{"); // False scope block so we can re-declare variables string texGenSource; switch (texGen.Source) { case GXTexGenSrc.Position: texGenSource = "vec4(RawPosition.xyz, 1.0)"; break; case GXTexGenSrc.Normal: texGenSource = "vec4(_norm0.xyz, 1.0)"; break; case GXTexGenSrc.Color0: texGenSource = "colors_0"; break; case GXTexGenSrc.Color1: texGenSource = "colors_1"; break; case GXTexGenSrc.Binormal: texGenSource = "vec4(RawBinormal.xyz, 1.0)"; break; case GXTexGenSrc.Tangent: texGenSource = "vec4(RawTangent.xyz, 1.0)"; break; case GXTexGenSrc.Tex0: case GXTexGenSrc.Tex1: case GXTexGenSrc.Tex2: case GXTexGenSrc.Tex3: case GXTexGenSrc.Tex4: case GXTexGenSrc.Tex5: case GXTexGenSrc.Tex6: case GXTexGenSrc.Tex7: texGenSource = string.Format("vec4(RawTex{0}.xy, 1.0, 1.0)", ((int)texGen.Source - (int)GXTexGenSrc.Tex0)); break; // This implies using a texture coordinate set already generated by TEV. case GXTexGenSrc.TexCoord0: case GXTexGenSrc.TexCoord1: case GXTexGenSrc.TexCoord2: case GXTexGenSrc.TexCoord3: case GXTexGenSrc.TexCoord4: case GXTexGenSrc.TexCoord5: case GXTexGenSrc.TexCoord6: texGenSource = string.Format("vec4(TexGen{0}.xy, 1.0, 1.0)", ((int)texGen.Source - (int)GXTexGenSrc.TexCoord0)); break; default: Console.WriteLine("Unsupported TexGenSrc: {0}, defaulting to TexCoord0.", texGen.Source); texGenSource = "RawTex0"; break; } stream.AppendFormat("\t\tcoord = {0};\n", texGenSource); TexMatrixProjection matrixProj = TexMatrixProjection.TexProj_ST; if (texGen.TexMatrixSource != GXTexMatrix.Identity) { matrixProj = mat.TexMatrixIndexes[(((int)texGen.TexMatrixSource) - 30) / 3].Projection; } // TEV Texture Coordinate generation takes the general form: // dst_coord = func(src_param, mtx), where func is GXTexGenType, src_param is GXTexGenSrc, and mtx is GXTexMtx. string destCoord = string.Format("TexGen{0}", i); switch (texGen.Type) { case GXTexGenType.Matrix3x4: case GXTexGenType.Matrix2x4: //if(mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.TexMtxId)) //{ // stream.AppendFormat("int temp = {0}.z\n", destCoord); // if (texMtx.Projection == TexMatrixProjection.TexProj_STQ) // stream.AppendFormat("{0}.xyz = vec3(dot({1}, TexMtx[temp]), dot({1}, TexMtx[temp+1]), dot({1}, TexMtx[temp+2]));\n", destCoord, texGenSource); // else // stream.AppendFormat("{0}.xyz = vec3(dot({1}, TexMtx[temp]), dot({1}, TexMtx[temp+1]), 1);\n", destCoord, texGenSource); //} //else { if (texGen.TexMatrixSource != GXTexMatrix.Identity) { if (matrixProj == TexMatrixProjection.TexProj_STQ) { stream.AppendFormat("\t\t{0}.xyz = vec3(dot(coord, TexMtx[{1}][0]), dot(coord, TexMtx[{1}][1]), dot(coord, TexMtx[{1}][2]));\n", destCoord, i); //3x4 } else { stream.AppendFormat("\t\t{0}.xyz = vec3(dot(coord, TexMtx[{1}][0]), dot(coord, TexMtxs[{1}][1]), 1);\n", destCoord, i); //2x4 } } else { stream.AppendFormat("\t\t{0} = coord.xyz;\n", destCoord); } } break; case GXTexGenType.SRTG: stream.AppendFormat("\t{0} = vec3({1}.rg, 1);\n", destCoord, texGenSource); break; case GXTexGenType.Bump0: case GXTexGenType.Bump1: case GXTexGenType.Bump2: case GXTexGenType.Bump3: case GXTexGenType.Bump4: case GXTexGenType.Bump5: case GXTexGenType.Bump6: case GXTexGenType.Bump7: // Transform the light dir into tangent space. // ldir = normalize(Lights[{0}.Position.xyz - RawPosition.xyz);\n {0} = "texInfo.embosslightshift"; // destCoord = TexGen{0} + float3(dot(ldir, _norm0), dot(ldir, RawBinormal), 0.0);\n", {0} = i, {1} = "texInfo.embosssourceshift"; default: Console.WriteLine("Unsupported TexGenType: {0}", texGen.Type); break; } // Dual Tex Transforms if (mat.PostTexMatrixIndexes.Length > 0) { //TexMatrix postTexMtx = mat.PostTexMatrixIndexes // ToDo: Should this just be... i? lol Console.WriteLine("PostMtx transforms are... not really anything supported?"); //TexMatrix postTexMtx = mat.PostTexMatrixIndexes[((int)texGen.TexMatrixSource) - 30]; //int postIndex = postTexMtx. mat.PostTexMatrixIndexes[i]; //stream.AppendFormat("float4 P0 = PostMtx[{0}];\n", postIndex); //stream.AppendFormat("float4 P1 = PostMtx[{0}];\n", postIndex + 1); //stream.AppendFormat("float4 P2 = PostMtx[{0}];\n", postIndex + 2); //stream.AppendFormat("{0}.xyz = vec3(dot(P0.xyz, {0}.xyz) + P0.w, dot(P1.xyz, {0}.xyz) + P1.w, dot(P2.xyz, {0}.xyz) + P2.w);\n", destCoord); } stream.AppendLine("\t}"); // End of false-scope block. } // Append the tail end of our shader file. stream.AppendLine("}"); stream.AppendLine(); return(stream.ToString()); }