예제 #1
0
        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]);
        }
예제 #2
0
        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);
        }
예제 #3
0
        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;
        }
예제 #4
0
        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");
        }
예제 #5
0
        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;
        }
예제 #6
0
        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;
        }
예제 #7
0
 private void LoadMAT3FromStream(EndianBinaryReader reader, long tagStart)
 {
     m_mat3Section = new MAT3();
     m_mat3Section.LoadMAT3FromStream(reader, tagStart);
 }
예제 #8
0
 internal static void MAT3identity(ref MAT3 a)
 {
     MAT3identity_Internal(ref a);
 }
예제 #9
0
 private static extern void MAT3identity_Internal(
     ref MAT3 a);
예제 #10
0
파일: Model.cs 프로젝트: kai13xd/SuperBMD
        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;
        }
예제 #11
0
        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();
        }
예제 #12
0
        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());
        }
예제 #13
0
        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;
        }
예제 #14
0
        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;
        }
예제 #15
0
        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());
        }