public void IdentityTest() { // Arrange // Act MAT3 m = MAT3.Identity(); // Assert Assert.AreEqual(1.0, m[0][0]); Assert.AreEqual(0.0, m[0][1]); Assert.AreEqual(0.0, m[0][2]); Assert.AreEqual(0.0, m[1][0]); Assert.AreEqual(1.0, m[1][1]); Assert.AreEqual(0.0, m[1][2]); Assert.AreEqual(0.0, m[2][0]); Assert.AreEqual(0.0, m[2][1]); Assert.AreEqual(1.0, m[2][2]); }
public void InvertTest() { // Arrange MAT3 m = new MAT3( new VEC3[3] { new VEC3(3, 2, 1), new VEC3(4, 5, 6), new VEC3(7, 5, 9) }); // Act bool inverted = MAT3.Invert(in m, out MAT3 inverse_m); // Assert Assert.IsTrue(inverted); MAT3 identity = MAT3.Multiply(m, inverse_m); Assert.IsTrue(identity.IsIdentity); }
public Model(EndianBinaryReader reader, Arguments args) { int j3d2Magic = reader.ReadInt32(); int modelMagic = reader.ReadInt32(); if (j3d2Magic != 0x4A334432) { throw new Exception("Model was not a BMD or BDL! (J3D2 magic not found)"); } if ((modelMagic != 0x62646C34) && (modelMagic != 0x626D6433)) { throw new Exception("Model was not a BMD or BDL! (Model type was not bmd3 or bdl4)"); } int modelSize = reader.ReadInt32(); int sectionCount = reader.ReadInt32(); // Skip the dummy section, SVR3 reader.Skip(16); Scenegraph = new INF1(reader, 32); VertexData = new VTX1(reader, (int)reader.BaseStream.Position); SkinningEnvelopes = new EVP1(reader, (int)reader.BaseStream.Position); PartialWeightData = new DRW1(reader, (int)reader.BaseStream.Position); Joints = new JNT1(reader, (int)reader.BaseStream.Position); SkinningEnvelopes.SetInverseBindMatrices(Joints.FlatSkeleton); Shapes = SHP1.Create(reader, (int)reader.BaseStream.Position); Shapes.SetVertexWeights(SkinningEnvelopes, PartialWeightData); Materials = new MAT3(reader, (int)reader.BaseStream.Position); SkipMDL3(reader); Textures = new TEX1(reader, (int)reader.BaseStream.Position); Materials.SetTextureNames(Textures); Materials.DumpMaterials(Path.GetDirectoryName(args.input_path)); foreach (Geometry.Shape shape in Shapes.Shapes) { packetCount += shape.Packets.Count; } vertexCount = VertexData.Attributes.Positions.Count; }
private static void WriteAlphaTest(StringBuilder stream, Material mat, MAT3 data) { string[] alphaRef = new[] { "alphaRef.r", "alphaRef.g" }; AlphaTest aTest = mat.AlphaTest; stream.AppendFormat("\t// Alpha Test: Compare A: {0} Reference A: {1} Op: {2} Compare B: {3} Reference B: {4}\n", aTest.Comp0, aTest.Reference0, aTest.Operation, aTest.Comp1, aTest.Reference1); stream.Append("\tif(!( "); stream.AppendFormat(m_tevAlphaFuncTable[(int)aTest.Comp0], (aTest.Reference0 / 255f)); // LHS stream.AppendFormat("{0}", m_tevAlphaFuncLogicTable[(int)aTest.Operation]); // Logic Operation stream.AppendFormat(m_tevAlphaFuncTable[(int)aTest.Comp1], (aTest.Reference1 / 255f)); stream.Append("))\n\t{\n"); stream.Append("\t\tdiscard;\n"); stream.Append("\t\treturn;\n"); stream.Append("\t}\n"); }
public Model(Scene scene, Arguments args) { VertexData = new VTX1(scene); Joints = new JNT1(scene, VertexData); Scenegraph = new INF1(scene, Joints); Textures = new TEX1(scene, args); SkinningEnvelopes = new EVP1(); SkinningEnvelopes.SetInverseBindMatrices(scene, Joints.FlatSkeleton); PartialWeightData = new DRW1(scene, Joints.BoneNameIndices); Shapes = SHP1.Create(scene, Joints.BoneNameIndices, VertexData.Attributes, SkinningEnvelopes, PartialWeightData); Materials = new MAT3(scene, Textures, Shapes); foreach (Geometry.Shape shape in Shapes.Shapes) { packetCount += shape.Packets.Count; } vertexCount = VertexData.Attributes.Positions.Count; }
public Model(Scene scene, Arguments args) { EnsureOneMaterialPerMeshAndOneMeshPerMaterial(scene); SortMeshesByObjectNames(scene); if (args.rotate_model) { RotateModel(scene); } VertexData = new VTX1(scene); Joints = new JNT1(scene, VertexData); Textures = new TEX1(scene, args); SkinningEnvelopes = new EVP1(); SkinningEnvelopes.SetInverseBindMatrices(scene, Joints.FlatSkeleton); PartialWeightData = new DRW1(scene, Joints.BoneNameIndices); Shapes = SHP1.Create(scene, Joints.BoneNameIndices, VertexData.Attributes, SkinningEnvelopes, PartialWeightData, args.tristrip_mode); Materials = new MAT3(scene, Textures, Shapes, args); if (args.output_bdl) { MatDisplayList = new MDL3(Materials.m_Materials, Textures.Textures); } Scenegraph = new INF1(scene, Joints); foreach (Geometry.Shape shape in Shapes.Shapes) { packetCount += shape.Packets.Count; } vertexCount = VertexData.Attributes.Positions.Count; }
private void LoadMAT3FromStream(EndianBinaryReader reader, long tagStart) { m_mat3Section = new MAT3(); m_mat3Section.LoadMAT3FromStream(reader, tagStart); }
internal static void MAT3identity(ref MAT3 a) { MAT3identity_Internal(ref a); }
private static extern void MAT3identity_Internal( ref MAT3 a);
public Model( Scene scene, string modelDirectory, List <Materials.Material> mat_presets = null, TristripOption triopt = TristripOption.DoNotTriStrip, bool flipAxis = false, bool fixNormals = false, string additionalTexPath = null) { Assimp.Node root = null; for (int i = 0; i < scene.RootNode.ChildCount; i++) { if (scene.RootNode.Children[i].Name.ToLowerInvariant() == "skeleton_root") { if (scene.RootNode.Children[i].ChildCount == 0) { throw new System.Exception("skeleton_root has no children! If you are making a rigged model, make sure skeleton_root contains the root of your skeleton."); } root = scene.RootNode.Children[i].Children[0]; break; } } foreach (Mesh mesh in scene.Meshes) { if (mesh.HasBones && root == null) { throw new System.Exception("Model uses bones but the skeleton root has not been found! Make sure your skeleton is inside a dummy object called 'skeleton_root'."); } } Matrix3x3 rotateXminus90 = Matrix3x3.FromRotationX((float)(-(1 / 2.0) * Math.PI)); Matrix3x3 rotateXplus90 = Matrix3x3.FromRotationX((float)((1 / 2.0) * Math.PI)); Matrix3x3 rotateYminus90 = Matrix3x3.FromRotationY((float)(-(1 / 2.0) * Math.PI)); Matrix3x3 rotateYplus90 = Matrix3x3.FromRotationY((float)((1 / 2.0) * Math.PI)); Matrix3x3 rotateZminus90 = Matrix3x3.FromRotationZ((float)(-(1 / 2.0) * Math.PI)); Matrix3x3 rotateZplus90 = Matrix3x3.FromRotationZ((float)((1 / 2.0) * Math.PI)); if (flipAxis) { Console.WriteLine("Rotating the model..."); int i = 0; Matrix4x4 rotate = Matrix4x4.FromRotationX((float)(-(1 / 2.0) * Math.PI)); //rotate = Matrix4x4.FromRotationZ((float)(-(1 / 2.0) * Math.PI)); Matrix4x4 rotateinv = rotate; //Matrix3x3 rotvec = rotateZplus90 * rotateXplus90; //Matrix3x3 rotvec = rotateYplus90*rotateYplus90 * rotateZplus90 * rotateZplus90* rotateXplus90* rotateXplus90; rotateinv.Inverse(); //rotate = Matrix4x4.FromRotationY((float)(-(1 / 2.0) * Math.PI)); //rotate = Matrix4x4.FromRotationZ((float)(-(1 / 2.0) * Math.PI)); Matrix4x4 rotateC = Matrix4x4.FromRotationX((float)(-(1 / 2.0) * Math.PI)); Matrix4x4 trans; if (root != null) { // Rotate rigged mesh foreach (Mesh mesh in scene.Meshes) { Console.WriteLine(mesh.Name); Console.WriteLine(String.Format("Does it have bones? {0}", mesh.HasBones)); Matrix3x3[] weightedmats = new Matrix3x3[mesh.Normals.Count]; foreach (Assimp.Bone bone in mesh.Bones) { bone.OffsetMatrix = rotateinv * bone.OffsetMatrix; /*Matrix3x3 invbind = bone.OffsetMatrix; * //bind.Inverse(); * //List<int> vertices = new List<VertexWeight>(); * * foreach (Assimp.VertexWeight weight in bone.VertexWeights) { * Matrix3x3 weightedcurrentmat = new Matrix3x3( * weight.Weight * invbind.A1, weight.Weight * invbind.A2, weight.Weight * invbind.A1, * weight.Weight * invbind.B1, weight.Weight * invbind.B2, weight.Weight * invbind.B3, * weight.Weight * invbind.C1, weight.Weight * invbind.C2, weight.Weight * invbind.C3); * * if (weightedmats[weight.VertexID] == null) { * weightedmats[weight.VertexID] = weightedcurrentmat; * } * else { * Matrix3x3 existingmat = weightedmats[weight.VertexID]; * weightedmats[weight.VertexID] = new Matrix3x3( * existingmat.A1 + invbind.A1, existingmat.A2 + invbind.A2, existingmat.A3 + invbind.A3, * existingmat.B1 + invbind.B1, existingmat.B2 + invbind.B2, existingmat.B3 + invbind.B3, * existingmat.C1 + invbind.C1, existingmat.C2 + invbind.C2, existingmat.C3 + invbind.C3); * } * }*/ //Matrix4x4 bindMat = bone.OffsetMatrix; //bindMat.Inverse(); //trans = /*bone.OffsetMatrix = root.Transform * bone.OffsetMatrix; * Matrix4x4 newtransform = root.Transform * rotate; * newtransform.Inverse(); * bone.OffsetMatrix = newtransform * bone.OffsetMatrix;*/ } for (i = 0; i < mesh.VertexCount; i++) { Vector3D vertex = mesh.Vertices[i]; vertex.Set(vertex.X, vertex.Z, -vertex.Y); mesh.Vertices[i] = vertex; } for (i = 0; i < mesh.Normals.Count; i++) { Vector3D norm = mesh.Normals[i]; norm.Set(norm.X, norm.Z, -norm.Y); //Matrix3x3 invbind = weightedmats[i]; //invbind.Inverse(); //norm = invbind * norm; mesh.Normals[i] = norm; } } } else { // Rotate static mesh foreach (Mesh mesh in scene.Meshes) { for (i = 0; i < mesh.VertexCount; i++) { Vector3D vertex = mesh.Vertices[i]; vertex.Set(vertex.X, vertex.Z, -vertex.Y); mesh.Vertices[i] = vertex; } for (i = 0; i < mesh.Normals.Count; i++) { Vector3D norm = mesh.Normals[i]; norm.Set(norm.X, norm.Z, -norm.Y); mesh.Normals[i] = norm; } } } if (root != null) { List <Assimp.Node> allnodes = new List <Assimp.Node>(); List <Assimp.Node> processnodes = new List <Assimp.Node>(); processnodes.Add(root); root.Transform = root.Transform * rotate; /*while (processnodes.Count > 0) { * Assimp.Node current = processnodes[0]; * processnodes.RemoveAt(0); * * current.Transform = current.Transform * rotate; * * foreach (Assimp.Node child in current.Children) { * processnodes.Add(child); * } * }*/ } } // On rigged models we attempt to fix normals for shading to work properly (when using materials) // That works by multiplying the normal for a vertex with the inverse bind matrices that have an effect on the vertex. // Seems to work semi-well, might need to look over this at a later point again though. Console.WriteLine(String.Format("fixNormals is {0}", fixNormals)); if (fixNormals && root != null) { Console.WriteLine("Fixing the normals on the rigged mesh..."); foreach (Mesh mesh in scene.Meshes) { List <Tuple <float, Matrix3x3> >[] weightedmats = new List <Tuple <float, Matrix3x3> > [mesh.Normals.Count];//new Matrix3x3[mesh.Normals.Count]; foreach (Assimp.Bone bone in mesh.Bones) { Matrix3x3 invbind = bone.OffsetMatrix; foreach (Assimp.VertexWeight weight in bone.VertexWeights) { if (weightedmats[weight.VertexID] == null) { weightedmats[weight.VertexID] = new List <Tuple <float, Matrix3x3> >(); } weightedmats[weight.VertexID].Add(Tuple.Create(weight.Weight, invbind)); /*Matrix3x3 weightedcurrentmat = new Matrix3x3( * weight.Weight * invbind.A1, weight.Weight * invbind.A2, weight.Weight * invbind.A1, * weight.Weight * invbind.B1, weight.Weight * invbind.B2, weight.Weight * invbind.B3, * weight.Weight * invbind.C1, weight.Weight * invbind.C2, weight.Weight * invbind.C3); * * if (weightedmats[weight.VertexID] == null) { * weightedmats[weight.VertexID] = weightedcurrentmat; * } * else { * Matrix3x3 existingmat = weightedmats[weight.VertexID]; * weightedmats[weight.VertexID] = new Matrix3x3( * existingmat.A1 + invbind.A1, existingmat.A2 + invbind.A2, existingmat.A3 + invbind.A3, * existingmat.B1 + invbind.B1, existingmat.B2 + invbind.B2, existingmat.B3 + invbind.B3, * existingmat.C1 + invbind.C1, existingmat.C2 + invbind.C2, existingmat.C3 + invbind.C3); * }*/ } } for (int i = 0; i < mesh.Normals.Count; i++) { if (weightedmats[i] == null) { continue; // means that index hasn't been weighted to so we can't do anything? } //weightedmats[i].Sort((x, y) => y.Item1.CompareTo(x.Item1)); Vector3D norm = mesh.Normals[i]; Matrix3x3 invbind = ScalarMultiply3x3(weightedmats[i][0].Item1, weightedmats[i][0].Item2); for (int j = 1; j < weightedmats[i].Count; j++) { invbind = AddMatrix3x3( invbind, ScalarMultiply3x3(weightedmats[i][j].Item1, weightedmats[i][j].Item2) ); } norm = invbind * norm; mesh.Normals[i] = norm; } } } // We check if the model mixes weighted and unweighted vertices. // If that is the case, we throw an exception here. If we don't, // an exception will be thrown later on that is less helpful to the user. if (true) { bool usesWeights = false; foreach (Mesh mesh in scene.Meshes) { bool[] weightedmats = new bool[mesh.VertexCount]; foreach (Assimp.Bone bone in mesh.Bones) { foreach (Assimp.VertexWeight weight in bone.VertexWeights) { weightedmats[weight.VertexID] = true; } } for (uint i = 0; i < mesh.VertexCount; i++) { if (weightedmats[i] == true) { usesWeights = true; } else if (usesWeights) { throw new System.Exception("Model has a mixture of weighted and unweighted vertices! Please weight all vertices to at least one bone."); } } } } VertexData = new VTX1(scene); Joints = new JNT1(scene, VertexData); Scenegraph = new INF1(scene, Joints); Textures = new TEX1(scene, Path.GetDirectoryName(modelDirectory)); SkinningEnvelopes = new EVP1(); SkinningEnvelopes.SetInverseBindMatrices(scene, Joints.FlatSkeleton); //SkinningEnvelopes.AddInverseBindMatrices(Joints.FlatSkeleton); PartialWeightData = new DRW1(scene, Joints.BoneNameIndices); Shapes = SHP1.Create(scene, Joints.BoneNameIndices, VertexData.Attributes, SkinningEnvelopes, PartialWeightData, triopt); Materials = new MAT3(scene, Textures, Shapes, mat_presets); if (additionalTexPath == null) { Materials.LoadAdditionalTextures(Textures, Path.GetDirectoryName(modelDirectory)); } else { Materials.LoadAdditionalTextures(Textures, additionalTexPath); } Materials.MapTextureNamesToIndices(Textures); foreach (Geometry.Shape shape in Shapes.Shapes) { packetCount += shape.Packets.Count; } vertexCount = VertexData.Attributes.Positions.Count; }
private static void WriteStage(StringBuilder stream, int stageIndex, Material mat, MAT3 data) { // Basic TEV Operation: // Inputs: [0 <= A, B, C <= 255] [-1024 <= D <= 1023] // Lerp between A and B using C as the interpolation factor. Optionally negate the result // using op. Input D and a bias (0, +0.5, -0.5) are added to the result. Then a constant // scale (1, 2, 4, 0.5) is applied. Result is optionally clamped before being written to // an output buffer. TevStage tevStage = mat.TevStages[stageIndex]; TevOrder tevOrder = mat.TevOrders[stageIndex]; stream.AppendFormat("\t// TEV Stage {0}\n", stageIndex); stream.AppendFormat("\t// Unknown0: {0} ColorInA: {1} ColorInB: {2} ColorInC: {3} ColorInD: {4} ColorOp: {5} ColorBias: {6} ColorScale: {7} ColorClamp: {8} ColorRegId: {9}\n", tevStage.Unknown0, tevStage.ColorIn[0], tevStage.ColorIn[1], tevStage.ColorIn[2], tevStage.ColorIn[3], tevStage.ColorOp, tevStage.ColorBias, tevStage.ColorScale, tevStage.ColorClamp, tevStage.ColorRegister); stream.AppendFormat("\t// AlphaInA: {0} AlphaInB: {1} AlphaInC: {2} AlphaInD: {3} AlphaOp: {4} AlphaBias: {5} AlphaScale: {6} AlphaClamp: {7} AlphaRegId: {8} Unknown1: {9}\n", tevStage.AlphaIn[0], tevStage.AlphaIn[1], tevStage.AlphaIn[2], tevStage.AlphaIn[3], tevStage.AlphaOp, tevStage.AlphaBias, tevStage.AlphaScale, tevStage.AlphaClamp, tevStage.AlphaRegister, tevStage.Unknown1); stream.AppendFormat("\t// Tev Order TexCoordId: {0} TexMap: {1} ChannelId: {2}\n", tevOrder.TexCoordId, tevOrder.TexMap, tevOrder.ChannelId); TevSwapMode swapMode = mat.TevSwapModes[stageIndex]; TevSwapModeTable rasSwapTable = mat.TevSwapModeTables[swapMode.RasSel]; TevSwapModeTable texSwapTable = mat.TevSwapModeTables[swapMode.TexSel]; stream.AppendFormat("\t// TEV Swap Mode: RasSel: {0} TexSel: {1}\n", swapMode.RasSel, swapMode.TexSel); stream.AppendFormat("\t// Ras Swap Table: R: {0} G: {1} B: {2} A: {3}\n", rasSwapTable.R, rasSwapTable.G, rasSwapTable.B, rasSwapTable.A); stream.AppendFormat("\t// Tex Swap Table: R: {0} G: {1} B: {2} A: {3}\n", texSwapTable.R, texSwapTable.G, texSwapTable.B, texSwapTable.A); int texcoord = (int)tevOrder.TexCoordId; bool bHasTexCoord = (int)tevOrder.TexCoordId < mat.NumTexGens; // Build a Swap Mode for swapping texture color input/rasterized color input to the TEV Stage. string[] rasSwapModeTable = new string[4]; string swapColors = "rgba"; for (int i = 0; i < 4; i++) { char[] swapTable = new char[4]; swapTable[0] = swapColors[rasSwapTable.R]; swapTable[1] = swapColors[rasSwapTable.G]; swapTable[2] = swapColors[rasSwapTable.B]; swapTable[3] = swapColors[rasSwapTable.A]; rasSwapModeTable[i] = new string(swapTable); } string[] texSwapModeTable = new string[4]; for (int i = 0; i < 4; i++) { char[] swapTable = new char[4]; swapTable[0] = swapColors[texSwapTable.R]; swapTable[1] = swapColors[texSwapTable.G]; swapTable[2] = swapColors[texSwapTable.B]; swapTable[3] = swapColors[texSwapTable.A]; texSwapModeTable[i] = new string(swapTable); } // ToDo: Implement Indirect Stages // If our TEV Stage uses rasterized alpha or color inputs if (tevStage.ColorIn[0] == GXCombineColorInput.RasAlpha || tevStage.ColorIn[0] == GXCombineColorInput.RasColor || tevStage.ColorIn[1] == GXCombineColorInput.RasAlpha || tevStage.ColorIn[1] == GXCombineColorInput.RasColor || tevStage.ColorIn[2] == GXCombineColorInput.RasAlpha || tevStage.ColorIn[2] == GXCombineColorInput.RasColor || tevStage.ColorIn[3] == GXCombineColorInput.RasAlpha || tevStage.ColorIn[3] == GXCombineColorInput.RasColor || tevStage.AlphaIn[0] == GXCombineAlphaInput.RasAlpha || tevStage.AlphaIn[1] == GXCombineAlphaInput.RasAlpha || tevStage.AlphaIn[2] == GXCombineAlphaInput.RasAlpha || tevStage.AlphaIn[3] == GXCombineAlphaInput.RasAlpha) { stream.AppendFormat("\t// TEV Swap Mode\n"); stream.AppendFormat("\trastemp = {0}.{1};\n", m_tevRasTable[(int)tevOrder.ChannelId], rasSwapModeTable[rasSwapTable.A]); // ToDo: No idea if this works. } if (tevOrder.TexCoordId != GXTexCoordSlot.Null) { int texmap = tevOrder.TexMap; if (true /* !bHasIndStage*/) { if (bHasTexCoord) { stream.AppendFormat("\ttevcoord.xy = TexGen{0}.xy;\n", texcoord); } else { stream.AppendFormat("\ttevcoord.xy = vec2(0, 0);\n"); } } string texswap = texSwapModeTable[texSwapTable.A]; // Again, no idea if this works. stream.Append("\ttextemp = "); SampleTexture(stream, "vec2(tevcoord.xy)", texswap, texmap); } else { stream.AppendFormat("\ttextemp = vec4(1,1,1,1); // tevOrder specified no texture!\n"); } if (tevStage.ColorIn[0] == GXCombineColorInput.Konst || tevStage.ColorIn[1] == GXCombineColorInput.Konst || tevStage.ColorIn[2] == GXCombineColorInput.Konst || tevStage.ColorIn[3] == GXCombineColorInput.Konst || tevStage.AlphaIn[0] == GXCombineAlphaInput.Konst || tevStage.AlphaIn[1] == GXCombineAlphaInput.Konst || tevStage.AlphaIn[2] == GXCombineAlphaInput.Konst || tevStage.AlphaIn[3] == GXCombineAlphaInput.Konst) { GXKonstColorSel kc = mat.KonstColorSelectors[stageIndex]; GXKonstAlphaSel ka = mat.KonstAlphaSelectors[stageIndex]; stream.AppendFormat("\t// KonstColorSel: {0} KonstAlphaSel: {1}\n", kc, ka); stream.AppendFormat("\tkonsttemp = vec4({0}, {1});\n", m_tevKSelTableC[(int)kc], m_tevKSelTableA[(int)ka]); } stream.AppendFormat("\ttevin_a = vec4({0}, {1});\n", m_tevCInputTable[(int)tevStage.ColorIn[0]], m_tevAInputTable[(int)tevStage.AlphaIn[0]]); stream.AppendFormat("\ttevin_b = vec4({0}, {1});\n", m_tevCInputTable[(int)tevStage.ColorIn[1]], m_tevAInputTable[(int)tevStage.AlphaIn[1]]); stream.AppendFormat("\ttevin_c = vec4({0}, {1});\n", m_tevCInputTable[(int)tevStage.ColorIn[2]], m_tevAInputTable[(int)tevStage.AlphaIn[2]]); stream.AppendFormat("\ttevin_d = vec4({0}, {1});\n", m_tevCInputTable[(int)tevStage.ColorIn[3]], m_tevAInputTable[(int)tevStage.AlphaIn[3]]); // COLOR COMBINER stream.AppendFormat("\t// Color Combine\n"); stream.AppendFormat("\t{0} = clamp(", m_tevCOutputTable[(byte)tevStage.ColorRegister]); if (tevStage.ColorOp == GXTevOp.Add || tevStage.ColorOp == GXTevOp.Sub) { WriteTevRegular(stream, "rgb", tevStage.ColorBias, tevStage.ColorOp, tevStage.ColorScale, tevStage.ColorClamp); } else { string[] opTable = new string[] { "((tevin_a.r > tevin_b.r) ? tevin_c.rgb : vec3(0,0,0))", // GXTevOp::Comp_R8_GT "((tevin_a.r == tevin_b.r) ? tevin_c.rgb : vec3(0,0,0))", // GXTevOp::Comp_R8_EQ "((dot(tevin_a.rgb, comp16) > dot(tevin_b.rgb, comp16)) ? tevin_c.rgb : vec3(0,0,0))", // GXTevOp::Comp_GR16_GT "((dot(tevin_a.rgb, comp16) == dot(tevin_b.rgb, comp16)) ? tevin_c.rgb : vec3(0,0,0))", // GXTevOp::Comp_GR16_EQ "((dot(tevin_a.rgb, comp24) > dot(tevin_b.rgb, comp24)) ? tevin_c.rgb : vec3(0,0,0))", // GXTevOp::Comp_GR24_GT "((dot(tevin_a.rgb, comp24) == dot(tevin_b.rgb, comp24)) ? tevin_c.rgb : vec3(0,0,0))", // GXTevOp::Comp_GR24_EQ "(max(sign(tevin_a.rgb - tevin_b.rgb), vec3(0,0,0)) * tevin_c.rgb)", // GXTevOp::Comp_RGB8_GT "((vec3(1,1,1) - sign(abs(tevin_a.rgb - tevin_b.rgb))) * tevin_c.rgb)", // GXTevOp::Comp_RGB8_EQ }; int index = (int)tevStage.ColorOp - 8; stream.AppendFormat("tevin_d.rgb + {0}", opTable[index]); } if (tevStage.ColorClamp) { stream.AppendFormat(", vec3(0,0,0), vec3(1,1,1))"); } else { stream.AppendFormat(", vec3(-1024, -1024, -1024), vec3(1023, 1023, 1023))"); } stream.AppendFormat(";\n"); // ALPHA COMBINER stream.AppendFormat("\t// Alpha Combine\n"); stream.AppendFormat("\t{0} = clamp(", m_tevAOutputTable[(byte)tevStage.AlphaRegister]); if (tevStage.AlphaOp == GXTevOp.Add || tevStage.AlphaOp == GXTevOp.Sub) { WriteTevRegular(stream, "a", tevStage.AlphaBias, tevStage.AlphaOp, tevStage.AlphaScale, tevStage.AlphaClamp); } else { string[] opTable = new string[] { "((tevin_a.r > tevin_b.r) ? tevin_c.a : 0)", // GXTevOp::Comp_R8_GT "((tevin_a.r == tevin_b.r) ? tevin_c.a : 0)", // GXTevOp::Comp_R8_EQ "((dot(tevin_a.rgb, comp16) > dot(tevin_b.rgb, comp16)) ? tevin_c.a : 0)", // GXTevOp::Comp_GR16_GT "((dot(tevin_a.rgb, comp16) == dot(tevin_b.rgb, comp16)) ? tevin_c.a : 0)", // GXTevOp::Comp_GR16_EQ "((dot(tevin_a.rgb, comp24) > dot(tevin_b.rgb, comp24)) ? tevin_c.a : 0)", // GXTevOp::Comp_GR24_GT "((dot(tevin_a.rgb, comp24) == dot(tevin_b.rgb, comp24)) ? tevin_c.a : 0)", // GXTevOp::Comp_GR24_EQ "((tevin_a.a > tevin_b.a) ? tevin_c.a : 0)", // GXTevOp::Comp_RGB8_GT "((tevin_a.a == tevin_b.a) ? tevin_c.a : 0)", // GXTevOp::Comp_RGB8_EQ }; int index = (int)tevStage.AlphaOp - 8; stream.AppendFormat("tevin_d.a + {0}", opTable[index]); } if (tevStage.AlphaClamp) { stream.AppendFormat(", 0, 1)"); } else { stream.AppendFormat(", -1024, 1023)"); } stream.AppendFormat(";\n"); stream.AppendLine(); }
public static string GenerateFragmentShader(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(); // Configure inputs to match our outputs from VS //if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Position)) //stream.AppendLine("in vec3 Position;"); //if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Normal)) //stream.AppendLine("in vec3 Normal;"); //for (int i = 0; i < data.NumChannelControls[mat.NumChannelControlsIndex]; i++) stream.AppendLine("in vec4 colors_0;"); stream.AppendLine("in vec4 colors_1;"); for (int texGen = 0; texGen < mat.NumTexGens; texGen++) { stream.AppendLine(string.Format("in vec3 TexGen{0};", texGen)); } stream.AppendLine(); // Texture Inputs stream.AppendFormat("uniform sampler2D Texture[8];\n"); stream.Append( "layout(std140) uniform PSBlock\n{" + "\tvec4 color[4];\n" + "\tvec4 kColor[4];\n" + "\tvec4 TexDimension[8];" + "\tvec4 FogColor;" + "};\n\n"); // Final Output stream.AppendLine("// Final Output"); stream.AppendLine("out vec4 PixelColor;\n\n"); // Main Function stream.Append("void main()\n{\n"); stream.Append("\tvec4 c0 = color[0], c1 = color[1], c2 = color[2], prev = color[3];\n" + "\tvec4 rastemp = vec4(0,0,0,0), textemp = vec4(0,0,0,0), konsttemp = vec4(0,0,0,0);\n" + "\tvec3 comp16 = vec3(1/256, 256/256, 0), comp24 = vec3(1/256, 256/256, (256*256)/256);\n" + // Uhh "\tfloat alphabump=0;\n" + "\tvec3 tevcoord=vec3(0,0,0);\n" + //"\tvec2 wrappedcoord=vec2(0,0), tempcoord=vec2(0,0);\n" + "\tvec4 tevin_a = vec4(0, 0, 0, 0), tevin_b = vec4(0, 0, 0, 0), tevin_c = vec4(0, 0, 0, 0), tevin_d = vec4(0, 0, 0, 0);\n"); // Cannot assign to input variables in GLSL so we copy them to a local instance instead. stream.AppendFormat("\tvec4 col0 = colors_0;\n"); stream.AppendFormat("\tvec4 col1 = colors_1;\n"); stream.AppendLine(); // Write up to 16 TEV Stage Operations for (int i = 0; i < mat.NumTevStages; i++) { WriteStage(stream, i, mat, data); } // Alpha Compare WriteAlphaTest(stream, mat, data); WriteFog(stream, mat, data); stream.AppendLine("\tPixelColor = prev;"); stream.AppendLine("}"); stream.AppendLine(); return(stream.ToString()); }
public Model(EndianBinaryReader reader, Arguments args) { ModelStats = new BMDInfo(); int j3d2Magic = reader.ReadInt32(); int modelMagic = reader.ReadInt32(); if (j3d2Magic != 0x4A334432 && j3d2Magic != 0x3244334A) { throw new Exception("Model was not a BMD or BDL! (J3D2 magic not found)"); } if ((modelMagic != 0x62646C34) && (modelMagic != 0x626D6433 && modelMagic != 0x346C6462)) { throw new Exception("Model was not a BMD or BDL! (Model type was not bmd3 or bdl4)"); } //Reversed magic found. File uses little endian byte order. if (j3d2Magic == 0x3244334A) { reader.CurrentEndian = Endian.Little; littleEndian = true; } int modelSize = reader.ReadInt32(); int sectionCount = reader.ReadInt32(); ModelStats.TotalSize = modelSize; // Skip the dummy section, SVR3 reader.Skip(16); Scenegraph = new INF1(reader, 32, ModelStats); VertexData = new VTX1(reader, (int)reader.BaseStream.Position, ModelStats); SkinningEnvelopes = new EVP1(reader, (int)reader.BaseStream.Position, ModelStats); PartialWeightData = new DRW1(reader, (int)reader.BaseStream.Position, ModelStats); Joints = new JNT1(reader, (int)reader.BaseStream.Position, ModelStats); SkinningEnvelopes.SetInverseBindMatrices(Joints.FlatSkeleton); Shapes = SHP1.Create(reader, (int)reader.BaseStream.Position, ModelStats); Shapes.SetVertexWeights(SkinningEnvelopes, PartialWeightData); Materials = new MAT3(reader, (int)reader.BaseStream.Position, ModelStats); SkipMDL3(reader); Textures = new TEX1(reader, (int)reader.BaseStream.Position, ModelStats); Materials.SetTextureNames(Textures); if (args.output_materials_path != "") { Materials.DumpMaterials(Path.GetDirectoryName(args.output_materials_path)); } else { if (args.output_path != "") { string outDir = Path.GetDirectoryName(args.output_path); string filenameNoExt = Path.GetFileNameWithoutExtension(args.input_path); Materials.DumpMaterials(Path.Combine(outDir, filenameNoExt + "_materials.json")); } else { string inDir = Path.GetDirectoryName(args.input_path); string filenameNoExt = Path.GetFileNameWithoutExtension(args.input_path); Materials.DumpMaterials(Path.Combine(inDir, filenameNoExt + "_materials.json")); } } foreach (Geometry.Shape shape in Shapes.Shapes) { packetCount += shape.Packets.Count; } vertexCount = VertexData.Attributes.Positions.Count; }
public Model(Scene scene, Arguments args, List <SuperBMDLib.Materials.Material> mat_presets = null, string additionalTexPath = null) { ModelStats = new BMDInfo(); if (args.ensure_one_material_per_mesh) { EnsureOneMaterialPerMesh(scene); } Console.WriteLine(); if (args.sort_meshes) { SortMeshesByObjectNames(scene); Console.WriteLine(); } // For FBX mesh names are empty, instead we need to check the nodes and rename // the meshes after the node names. foreach (Assimp.Node node in scene.RootNode.Children) { foreach (int meshindex in node.MeshIndices) { Assimp.Mesh mesh = scene.Meshes[meshindex]; if (mesh.Name == String.Empty) { mesh.Name = node.Name; } } } Console.WriteLine(); Console.Write("Searching for the Skeleton Root"); Assimp.Node root = null; for (int i = 0; i < scene.RootNode.ChildCount; i++) { if (scene.RootNode.Children[i].Name.ToLowerInvariant() == "skeleton_root") { if (scene.RootNode.Children[i].ChildCount == 0) { throw new System.Exception("skeleton_root has no children! If you are making a rigged model, make sure skeleton_root contains the root of your skeleton."); } root = scene.RootNode.Children[i].Children[0]; break; } Console.Write("."); } Console.Write(root == null ? "✓ No Skeleton found" : "✓ Skeleton Found"); Console.WriteLine(); foreach (Mesh mesh in scene.Meshes) { if (mesh.HasBones && root == null) { throw new System.Exception("Model uses bones but the skeleton root has not been found! Make sure your skeleton is inside a dummy object called 'skeleton_root'."); } } if (args.rotate_model) { Console.WriteLine(); Console.Write("Rotating the model"); int i = 0; Matrix4x4 rotate = Matrix4x4.FromRotationX((float)(-(1 / 2.0) * Math.PI)); Matrix4x4 rotateinv = rotate; rotateinv.Inverse(); foreach (Mesh mesh in scene.Meshes) { if (root != null) { foreach (Assimp.Bone bone in mesh.Bones) { bone.OffsetMatrix = rotateinv * bone.OffsetMatrix; Console.Write("|"); } } for (i = 0; i < mesh.VertexCount; i++) { Vector3D vertex = mesh.Vertices[i]; vertex.Set(vertex.X, vertex.Z, -vertex.Y); mesh.Vertices[i] = vertex; } for (i = 0; i < mesh.Normals.Count; i++) { Vector3D norm = mesh.Normals[i]; norm.Set(norm.X, norm.Z, -norm.Y); mesh.Normals[i] = norm; } Console.Write("."); } Console.Write("✓"); Console.WriteLine(); } foreach (Mesh mesh in scene.Meshes) { if (mesh.HasNormals) { for (int i = 0; i < mesh.Normals.Count; i++) { Vector3D normal = mesh.Normals[i]; normal.X = (float)Math.Round(normal.X, 4); normal.Y = (float)Math.Round(normal.Y, 4); normal.Z = (float)Math.Round(normal.Z, 4); mesh.Normals[i] = normal; } } } Console.WriteLine(); Console.WriteLine("Generating the Vertex Data ->"); VertexData = new VTX1(scene, args.forceFloat); Console.WriteLine(); Console.Write("Generating the Bone Data"); Joints = new JNT1(scene, VertexData); Console.WriteLine(); Console.WriteLine("Generating the Texture Data -> "); Textures = new TEX1(scene, args); Console.WriteLine(); Console.Write("Generating the Envelope Data"); SkinningEnvelopes = new EVP1(); SkinningEnvelopes.SetInverseBindMatrices(scene, Joints.FlatSkeleton); Console.WriteLine(); Console.Write("Generating the Weight Data"); PartialWeightData = new DRW1(scene, Joints.BoneNameIndices); Console.WriteLine(); Console.WriteLine(); Console.WriteLine("Generating the Mesh Data ->"); Shapes = SHP1.Create(scene, Joints.BoneNameIndices, VertexData.Attributes, SkinningEnvelopes, PartialWeightData, args.tristrip_mode, args.degenerateTriangles); Console.WriteLine(); Console.WriteLine("Generating the Material Data ->"); Materials = new MAT3(scene, Textures, Shapes, args, mat_presets); Console.WriteLine(); Console.WriteLine("Loading the Textures ->"); if (additionalTexPath == null) { Materials.LoadAdditionalTextures(Textures, Path.GetDirectoryName(args.input_path), args.readMipmaps); } else { Materials.LoadAdditionalTextures(Textures, additionalTexPath, args.readMipmaps); } Materials.MapTextureNamesToIndices(Textures); if (args.output_bdl) { Console.WriteLine(); Console.WriteLine("Compiling the MDL3 ->"); MatDisplayList = new MDL3(Materials.m_Materials, Textures.Textures); } if (args.littleEndian) { littleEndian = true; } Console.WriteLine(); Console.Write("Generating the Joints"); Scenegraph = new INF1(scene, Joints); foreach (Geometry.Shape shape in Shapes.Shapes) { packetCount += shape.Packets.Count; } vertexCount = VertexData.Attributes.Positions.Count; }
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()); }