Example #1
0
        /// <summary>
        /// Load a gc attach from a file
        /// </summary>
        /// <param name="source">Byte source from a file</param>
        /// <param name="address">Address at which the attach is located</param>
        /// <param name="imageBase">Address image base</param>
        /// <param name="labels">Labels for the data to use</param>
        /// <returns></returns>
        public static GCAttach Read(byte[] source, uint address, uint imageBase, Dictionary <uint, string> labels)
        {
            string name;

            if (labels.ContainsKey(address))
            {
                name = labels[address];
            }
            else
            {
                name = "attach_" + address.ToString("X8");
            }

            // The struct is 36/0x24 bytes long

            uint vertexAddress = source.ToUInt32(address) - imageBase;
            //uint gap = source.ToUInt32(address + 4);
            uint opaqueAddress      = source.ToUInt32(address + 8) - imageBase;
            uint transparentAddress = source.ToUInt32(address + 12) - imageBase;

            int opaqueCount      = source.ToInt16(address + 16);
            int transparentCount = source.ToInt16(address + 18);

            address += 20;
            Bounds bounds = Bounds.Read(source, ref address);

            // reading vertex data
            List <VertexSet> vertexData = new();
            VertexSet        vertexSet  = VertexSet.Read(source, vertexAddress, imageBase);

            while (vertexSet.Attribute != VertexAttribute.Null)
            {
                vertexData.Add(vertexSet);
                vertexAddress += 16;
                vertexSet      = VertexSet.Read(source, vertexAddress, imageBase);
            }

            IndexAttributes indexAttribs = IndexAttributes.HasPosition;

            List <Mesh> opaqueMeshes = new();

            for (int i = 0; i < opaqueCount; i++)
            {
                Mesh mesh = Mesh.Read(source, opaqueAddress, imageBase, ref indexAttribs);
                opaqueMeshes.Add(mesh);
                opaqueAddress += 16;
            }

            indexAttribs = IndexAttributes.HasPosition;

            List <Mesh> transparentMeshes = new();

            for (int i = 0; i < transparentCount; i++)
            {
                Mesh mesh = Mesh.Read(source, transparentAddress, imageBase, ref indexAttribs);
                transparentMeshes.Add(mesh);
                transparentAddress += 16;
            }

            return(new GCAttach(vertexData.ToArray(), opaqueMeshes.ToArray(), transparentMeshes.ToArray())
            {
                Name = name,
                MeshBounds = bounds
            });
        }
Example #2
0
        public override uint Write(EndianWriter writer, uint imageBase, bool DX, Dictionary <string, uint> labels)
        {
            VertexSet[] sets         = VertexData.Values.ToArray();
            uint[]      vtxAddresses = new uint[VertexData.Count];
            for (int i = 0; i < sets.Length; i++)
            {
                VertexSet vtxSet = sets[i];
                vtxAddresses[i] = writer.Position + imageBase;
                IOType outputType = vtxSet.DataType.ToStructType();

                switch (vtxSet.Attribute)
                {
                case VertexAttribute.Position:
                case VertexAttribute.Normal:
                    foreach (Vector3 vec in vtxSet.Vector3Data)
                    {
                        vec.Write(writer, outputType);
                    }
                    break;

                case VertexAttribute.Color0:
                case VertexAttribute.Color1:
                    foreach (Color col in vtxSet.ColorData)
                    {
                        col.Write(writer, outputType);
                    }
                    break;

                case VertexAttribute.Tex0:
                case VertexAttribute.Tex1:
                case VertexAttribute.Tex2:
                case VertexAttribute.Tex3:
                case VertexAttribute.Tex4:
                case VertexAttribute.Tex5:
                case VertexAttribute.Tex6:
                case VertexAttribute.Tex7:
                    foreach (Vector2 uv in vtxSet.UVData)
                    {
                        (uv * 256).Write(writer, outputType);
                    }
                    break;

                case VertexAttribute.PositionMatrixId:
                case VertexAttribute.Null:
                default:
                    throw new FormatException($"Vertex set had an invalid or unavailable type: {vtxSet.Attribute}");
                }
            }

            uint vtxAddr = writer.Position + imageBase;

            // writing vertex attributes
            for (int i = 0; i < sets.Length; i++)
            {
                VertexSet vtxSet = sets[i];

                writer.Write(new byte[] { (byte)vtxSet.Attribute, (byte)vtxSet.StructSize });
                writer.WriteUInt16((ushort)vtxSet.DataLength);

                uint structure = (uint)vtxSet.StructType;
                structure |= (uint)((byte)vtxSet.DataType << 4);
                writer.WriteUInt32(structure);

                writer.WriteUInt32(vtxAddresses[i]);
                writer.WriteUInt32((uint)(vtxSet.DataLength * vtxSet.StructSize));
            }

            // empty vtx attribute
            byte[] nullVtx = new byte[16];
            nullVtx[0] = 0xFF;
            writer.Write(nullVtx);

            // writing geometry data
            uint[] WriteMeshData(Mesh[] meshes)
            {
                uint[]          result       = new uint[meshes.Length * 4];
                IndexAttributes indexAttribs = IndexAttributes.HasPosition;

                for (int i = 0, ri = 0; i < meshes.Length; i++, ri += 4)
                {
                    Mesh m = meshes[i];

                    IndexAttributes?t = m.IndexAttributes;
                    if (t.HasValue)
                    {
                        indexAttribs = t.Value;
                    }

                    // writing parameters
                    result[ri]     = writer.Position + imageBase;
                    result[ri + 1] = (uint)m.Parameters.Length;
                    foreach (IParameter p in m.Parameters)
                    {
                        p.Write(writer);
                    }

                    // writing polygons
                    uint addr = writer.Position;
                    foreach (Poly p in m.Polys)
                    {
                        p.Write(writer, indexAttribs);
                    }
                    result[ri + 2] = addr + imageBase;
                    result[ri + 3] = writer.Position - addr;
                }
                return(result);
            }

            uint[] opaqueMeshStructs      = WriteMeshData(OpaqueMeshes);
            uint[] transparentMeshStructs = WriteMeshData(TransparentMeshes);

            // writing geometry properties
            uint opaqueAddress = writer.Position + imageBase;

            foreach (uint i in opaqueMeshStructs)
            {
                writer.Write(i);
            }

            uint transparentAddress = writer.Position + imageBase;

            foreach (uint i in transparentMeshStructs)
            {
                writer.Write(i);
            }

            uint address = writer.Position + imageBase;

            labels.AddLabel(Name, address);

            writer.WriteUInt32(vtxAddr);
            writer.WriteUInt32(0);
            writer.WriteUInt32(opaqueAddress);
            writer.WriteUInt32(transparentAddress);
            writer.WriteUInt16((ushort)OpaqueMeshes.Length);
            writer.WriteUInt16((ushort)TransparentMeshes.Length);
            MeshBounds.Write(writer);
            return(address);
        }
Example #3
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);
                        }