Ejemplo n.º 1
0
        /// <summary>
        /// Converts a buffer model to the GC format
        /// </summary>
        /// <param name="model">The model to converter</param>
        /// <param name="optimize">Whether to optimize the attaches</param>
        /// <param name="ignoreWeights">If conversion should still happen, despite weights existing</param>
        /// <param name="forceUpdate">Whether to convert, regardless of whether the attaches are already GC</param>
        public static void ConvertModelToGC(NJObject model, bool optimize = true, bool ignoreWeights = false, bool forceUpdate = false)
        {
            if (model.Parent != null)
            {
                throw new FormatException($"Model {model.Name} is not hierarchy root!");
            }

            if (model.AttachFormat == AttachFormat.GC && !forceUpdate)
            {
                return;
            }

            if (model.HasWeight && !ignoreWeights)
            {
                throw new FormatException("Model is weighted, cannot convert to basic format!");
            }

            AttachHelper.ProcessWeightlessModel(model, (cacheAtc, ogAtc) =>
            {
                // getting the vertex information
                Vector3[] positions = new Vector3[cacheAtc.vertices.Length];
                Vector3[] normals   = new Vector3[positions.Length];

                for (int i = 0; i < positions.Length; i++)
                {
                    var vtx      = cacheAtc.vertices[i];
                    positions[i] = vtx.Position;
                    normals[i]   = vtx.Normal;
                }

                // getting the corner information
                int cornerCount = 0;
                for (int i = 0; i < cacheAtc.corners.Length; i++)
                {
                    cornerCount += cacheAtc.corners[i].Length;
                }

                Vector2[] texcoords = new Vector2[cornerCount];
                Color[] colors      = new Color[cornerCount];
                Corner[][] corners  = new Corner[cacheAtc.corners.Length][];

                ushort cornerIndex = 0;
                for (int i = 0; i < corners.Length; i++)
                {
                    BufferCorner[] bufferCorners = cacheAtc.corners[i];
                    Corner[] meshCorners         = new Corner[bufferCorners.Length];
                    for (int j = 0; j < bufferCorners.Length; j++)
                    {
                        BufferCorner bcorner = bufferCorners[j];

                        texcoords[cornerIndex] = bcorner.Texcoord;
                        colors[cornerIndex]    = bcorner.Color;

                        meshCorners[j] = new Corner()
                        {
                            PositionIndex = bcorner.VertexIndex,
                            NormalIndex   = bcorner.VertexIndex,
                            UV0Index      = cornerIndex,
                            Color0Index   = cornerIndex
                        };

                        cornerIndex++;
                    }
                    corners[i] = meshCorners;
                }

                bool hasUVs = texcoords.Any(x => x != default);
                // if it has no normals, always use colors (even if they are all white)
                bool hasColors = colors.Any(x => x != Color.White) || !normals.Any(x => x != Vector3.UnitY);

                // Puttin together the vertex sets
                VertexSet[] vertexData = new VertexSet[2 + (hasUVs ? 1 : 0)];

                IndexAttributeParameter iaParam = new() { IndexAttributes = IndexAttributes.HasPosition };
                if (positions.Length > 256)
                {
                    iaParam.IndexAttributes |= IndexAttributes.Position16BitIndex;
                }
                vertexData[0] = new VertexSet(positions, false);

                if (hasColors)
                {
                    iaParam.IndexAttributes |= IndexAttributes.HasColor;
                    if (colors.Length > 256)
                    {
                        iaParam.IndexAttributes |= IndexAttributes.Color16BitIndex;
                    }

                    vertexData[1] = new VertexSet(colors);
                }
                else
                {
                    iaParam.IndexAttributes |= IndexAttributes.HasNormal;
                    if (normals.Length > 256)
                    {
                        iaParam.IndexAttributes |= IndexAttributes.Normal16BitIndex;
                    }

                    vertexData[1] = new VertexSet(normals, true);
                }

                if (hasUVs)
                {
                    iaParam.IndexAttributes |= IndexAttributes.HasUV;
                    if (texcoords.Length > 256)
                    {
                        iaParam.IndexAttributes |= IndexAttributes.UV16BitIndex;
                    }
                    vertexData[2] = new VertexSet(texcoords);
                }

                // stitching polygons together
                BufferMaterial currentMaterial = null;
                Mesh ProcessBufferMesh(int index)
                {
                    // generating parameter info
                    List <IParameter> parameters = new();

                    BufferMaterial cacheMaterial = cacheAtc.materials[index];
                    if (currentMaterial == null)
                    {
                        parameters.Add(new VtxAttrFmtParameter(VertexAttribute.Position));
                        parameters.Add(new VtxAttrFmtParameter(hasColors ? VertexAttribute.Color0 : VertexAttribute.Normal));
                        if (hasUVs)
                        {
                            parameters.Add(new VtxAttrFmtParameter(VertexAttribute.Tex0));
                        }
                        parameters.Add(iaParam);

                        if (cacheMaterial == null)
                        {
                            currentMaterial = new BufferMaterial()
                            {
                                MaterialAttributes = MaterialAttributes.noSpecular
                            };
                        }
                        else
                        {
                            currentMaterial = cacheMaterial;
                        }

                        parameters.Add(new LightingParameter()
                        {
                            LightingAttributes = LightingParameter.DefaultLighting.LightingAttributes,
                            ShadowStencil      = currentMaterial.ShadowStencil
                        });

                        parameters.Add(new BlendAlphaParameter()
                        {
                            SourceAlpha = currentMaterial.SourceBlendMode,
                            DestAlpha   = currentMaterial.DestinationBlendmode
                        });

                        parameters.Add(new AmbientColorParameter()
                        {
                            AmbientColor = currentMaterial.Ambient
                        });

                        TextureParameter texParam = new();
                        texParam.TextureID        = (ushort)currentMaterial.TextureIndex;

                        if (!currentMaterial.ClampU)
                        {
                            texParam.Tiling |= GCTileMode.RepeatU;
                        }

                        if (!currentMaterial.ClampV)
                        {
                            texParam.Tiling |= GCTileMode.RepeatV;
                        }

                        if (currentMaterial.MirrorU)
                        {
                            texParam.Tiling |= GCTileMode.MirrorU;
                        }

                        if (currentMaterial.MirrorV)
                        {
                            texParam.Tiling |= GCTileMode.MirrorV;
                        }

                        parameters.Add(texParam);

                        parameters.Add(Unknown9Parameter.DefaultValues);
                        parameters.Add(new TexCoordGenParameter()
                        {
                            TexCoordID = currentMaterial.TexCoordID,
                            TexGenType = currentMaterial.TexGenType,
                            TexGenSrc  = currentMaterial.TexGenSrc,
                            MatrixID   = currentMaterial.MatrixID
                        });
                    }
                    else
                    {
                        if (currentMaterial.ShadowStencil != cacheMaterial.ShadowStencil)
                        {
                            parameters.Add(new LightingParameter()
                            {
                                ShadowStencil = cacheMaterial.ShadowStencil
                            });
                        }

                        if (currentMaterial.SourceBlendMode != cacheMaterial.SourceBlendMode ||
                            currentMaterial.DestinationBlendmode != cacheMaterial.DestinationBlendmode)
                        {
                            parameters.Add(new BlendAlphaParameter()
                            {
                                SourceAlpha = cacheMaterial.SourceBlendMode,
                                DestAlpha   = cacheMaterial.DestinationBlendmode
                            });
                        }

                        if (currentMaterial.Ambient != cacheMaterial.Ambient)
                        {
                            parameters.Add(new AmbientColorParameter()
                            {
                                AmbientColor = cacheMaterial.Ambient
                            });
                        }

                        if (currentMaterial.TextureIndex != cacheMaterial.TextureIndex ||
                            currentMaterial.MirrorU != cacheMaterial.MirrorU ||
                            currentMaterial.MirrorV != cacheMaterial.MirrorV ||
                            currentMaterial.ClampU != cacheMaterial.ClampU ||
                            currentMaterial.ClampV != cacheMaterial.ClampV)
                        {
                            TextureParameter texParam = new();
                            texParam.TextureID        = (ushort)cacheMaterial.TextureIndex;

                            if (!cacheMaterial.ClampU)
                            {
                                texParam.Tiling |= GCTileMode.RepeatU;
                            }

                            if (!cacheMaterial.ClampV)
                            {
                                texParam.Tiling |= GCTileMode.RepeatV;
                            }

                            if (cacheMaterial.MirrorU)
                            {
                                texParam.Tiling |= GCTileMode.MirrorU;
                            }

                            if (cacheMaterial.MirrorV)
                            {
                                texParam.Tiling |= GCTileMode.MirrorV;
                            }

                            parameters.Add(texParam);
                        }

                        if (currentMaterial.TexCoordID != cacheMaterial.TexCoordID ||
                            currentMaterial.TexGenType != cacheMaterial.TexGenType ||
                            currentMaterial.TexGenSrc != cacheMaterial.TexGenSrc ||
                            currentMaterial.MatrixID != cacheMaterial.MatrixID)
                        {
                            parameters.Add(new TexCoordGenParameter()
                            {
                                TexCoordID = cacheMaterial.TexCoordID,
                                TexGenType = cacheMaterial.TexGenType,
                                TexGenSrc  = cacheMaterial.HasAttribute(MaterialAttributes.normalMapping) ? TexGenSrc.Normal : cacheMaterial.TexGenSrc,
                                MatrixID   = cacheMaterial.MatrixID
                            });
                        }

                        currentMaterial = cacheMaterial;
                    }

                    // note: a single triangle polygon can only carry 0xFFFF corners, so about 22k tris
                    Corner[] triangleCorners = corners[index];

                    List <Poly> polygons = new();
                    if (triangleCorners.Length > 0xFFFF)
                    {
                        int remainingLength = triangleCorners.Length;
                        int offset          = 0;
                        while (remainingLength > 0)
                        {
                            Corner[] finalCorners = new Corner[Math.Max(0xFFFF, remainingLength)];
                            Array.Copy(triangleCorners, offset, finalCorners, 0, finalCorners.Length);
                            offset          += finalCorners.Length;
                            remainingLength -= finalCorners.Length;

                            Poly triangle = new(PolyType.Triangles, finalCorners);
                            polygons.Add(triangle);
                        }
Ejemplo n.º 2
0
        /// <summary>
        /// Converts the buffer data of a model to BASIC attaches
        /// </summary>
        /// <param name="model">The tip of the model hierarchy to convert</param>
        /// <param name="optimize">Whether to optimize the data</param>
        /// <param name="ignoreWeights">Convert regardless of weight information being lost</param>
        /// <param name="forceUpdate">Still convert, even if the attaches are Basic already</param>
        public static void ConvertModelToBasic(NJObject model, bool optimize = true, bool ignoreWeights = false, bool forceUpdate = false)
        {
            if (model.Parent != null)
            {
                throw new FormatException($"Model {model.Name} is not hierarchy root!");
            }

            if (model.AttachFormat == AttachFormat.BASIC && !forceUpdate)
            {
                return;
            }

            if (model.HasWeight && !ignoreWeights)
            {
                throw new FormatException("Model is weighted, cannot convert to basic format!");
            }

            AttachHelper.ProcessWeightlessModel(model, (cacheAtc, ogAtc) =>
            {
                // getting the vertex information
                Vector3[] positions = new Vector3[cacheAtc.vertices.Length];
                Vector3[] normals   = new Vector3[positions.Length];
                bool hasNormals     = false;

                for (int i = 0; i < positions.Length; i++)
                {
                    var vtx      = cacheAtc.vertices[i];
                    positions[i] = vtx.Position;
                    normals[i]   = vtx.Normal;
                    if (vtx.Normal != Vector3.UnitY)
                    {
                        hasNormals = true;
                    }
                }

                if (!hasNormals)
                {
                    normals = null;
                }

                // putting together polygons
                Mesh[] meshes        = new Mesh[cacheAtc.corners.Length];
                Material[] materials = new Material[cacheAtc.corners.Length];

                for (int i = 0; i < cacheAtc.corners.Length; i++)
                {
                    // creating the material
                    Material mat        = new();
                    BufferMaterial bmat = cacheAtc.materials[i];
                    if (bmat != null)
                    {
                        mat.DiffuseColor     = bmat.Diffuse;
                        mat.SpecularColor    = bmat.Specular;
                        mat.Exponent         = bmat.SpecularExponent;
                        mat.TextureID        = bmat.TextureIndex;
                        mat.FilterMode       = bmat.TextureFiltering;
                        mat.MipmapDAdjust    = bmat.MipmapDistanceAdjust;
                        mat.SuperSample      = bmat.AnisotropicFiltering;
                        mat.ClampU           = bmat.ClampU;
                        mat.ClampV           = bmat.ClampV;
                        mat.MirrorU          = bmat.MirrorU;
                        mat.MirrorV          = bmat.MirrorV;
                        mat.UseAlpha         = bmat.UseAlpha;
                        mat.SourceAlpha      = bmat.SourceBlendMode;
                        mat.DestinationAlpha = bmat.DestinationBlendmode;
                        mat.DoubleSided      = !bmat.Culling;

                        mat.IgnoreLighting = bmat.HasAttribute(MaterialAttributes.noDiffuse);
                        mat.IgnoreSpecular = bmat.HasAttribute(MaterialAttributes.noSpecular);
                        mat.UseTexture     = bmat.HasAttribute(MaterialAttributes.useTexture);
                        mat.EnvironmentMap = bmat.HasAttribute(MaterialAttributes.normalMapping);
                    }
                    materials[i] = mat;

                    // creating the polygons

                    BufferCorner[] bCorners = cacheAtc.corners[i];
                    IPoly[] triangles       = new IPoly[bCorners.Length / 3];
                    Vector2[] texcoords     = new Vector2[bCorners.Length];
                    Color[] colors          = new Color[bCorners.Length];

                    Triangle current = new();
                    for (int j = 0; j < bCorners.Length; j++)
                    {
                        BufferCorner corner = bCorners[j];

                        int vIndex = j % 3;
                        current.Indices[vIndex] = corner.VertexIndex;
                        if (vIndex == 2)
                        {
                            triangles[(j - 2) / 3] = current;
                            current = new Triangle();
                        }

                        texcoords[j] = corner.Texcoord;
                        colors[j]    = corner.Color;
                    }

                    bool hasTexcoords = texcoords.Any(x => x != default);
                    bool hasColors    = colors.Any(x => x != Color.White);

                    Mesh basicmesh = new (BASICPolyType.Triangles, triangles, false, hasColors, hasTexcoords, (ushort)i);
                    if (hasColors)
                    {
                        basicmesh.Colors = colors;
                    }
                    if (hasTexcoords)
                    {
                        basicmesh.Texcoords = texcoords;
                    }

                    meshes[i] = basicmesh;
                }