Beispiel #1
0
        //Write assets will only be used for model imports.
        private static void WriteAssets(Collada form, ModelLinker linker, ref byte *pData)
        {
            int      index;
            MDL0Node model = linker.Model;

            if (linker._vertices != null && linker._vertices.Count != 0)
            {
                model.LinkGroup(new MDL0GroupNode(MDLResourceType.Vertices));
                model._vertGroup._parent = model;

                index = 0;
                foreach (VertexCodec c in linker._vertices)
                {
                    MDL0VertexNode node = new MDL0VertexNode();

                    node._name = model.Name + "_" + model._objList[index]._name;
                    if (((MDL0ObjectNode)model._objList[index])._drawCalls[0].MaterialNode != null)
                    {
                        node._name += "_" + ((MDL0ObjectNode)model._objList[index])._drawCalls[0].MaterialNode._name;
                    }

                    if (form != null)
                    {
                        form.Say("Writing Vertices - " + node.Name);
                    }

                    MDL0VertexData *header = (MDL0VertexData *)pData;
                    header->_dataLen     = c._dataLen.Align(0x20) + 0x40;
                    header->_dataOffset  = 0x40;
                    header->_index       = index++;
                    header->_isXYZ       = c._hasZ ? 1 : 0;
                    header->_type        = (int)c._type;
                    header->_divisor     = (byte)c._scale;
                    header->_entryStride = (byte)c._dstStride;
                    header->_numVertices = (ushort)c._dstCount;
                    header->_eMin        = c._min;
                    header->_eMax        = c._max;
                    header->_pad1        = header->_pad2 = 0;

                    c.Write(pData + 0x40);

                    node._replSrc = node._replUncompSrc = new DataSource(header, header->_dataLen);
                    model._vertGroup.AddChild(node, false);

                    pData += header->_dataLen;
                }
            }

            if (linker._normals != null && linker._normals.Count != 0)
            {
                model.LinkGroup(new MDL0GroupNode(MDLResourceType.Normals));
                model._normGroup._parent = model;

                index = 0;
                foreach (VertexCodec c in linker._normals)
                {
                    MDL0NormalNode node = new MDL0NormalNode();

                    node._name = model.Name + "_" + model._objList[index]._name;
                    if (((MDL0ObjectNode)model._objList[index])._drawCalls[0].MaterialNode != null)
                    {
                        node._name += "_" + ((MDL0ObjectNode)model._objList[index])._drawCalls[0].MaterialNode._name;
                    }

                    if (form != null)
                    {
                        form.Say("Writing Normals - " + node.Name);
                    }

                    MDL0NormalData *header = (MDL0NormalData *)pData;
                    header->_dataLen     = c._dataLen.Align(0x20) + 0x20;
                    header->_dataOffset  = 0x20;
                    header->_index       = index++;
                    header->_isNBT       = 0;
                    header->_type        = (int)c._type;
                    header->_divisor     = (byte)c._scale;
                    header->_entryStride = (byte)c._dstStride;
                    header->_numVertices = (ushort)c._dstCount;

                    c.Write(pData + 0x20);

                    node._replSrc = node._replUncompSrc = new DataSource(header, header->_dataLen);
                    model._normGroup.AddChild(node, false);

                    pData += header->_dataLen;
                }
            }

            if (linker._colors != null && linker._colors.Count != 0)
            {
                model.LinkGroup(new MDL0GroupNode(MDLResourceType.Colors));
                model._colorGroup._parent = model;

                index = 0;
                foreach (ColorCodec c in linker._colors)
                {
                    MDL0ColorNode node = new MDL0ColorNode();

                    node._name = model.Name + "_" + model._objList[index]._name;
                    if (((MDL0ObjectNode)model._objList[index])._drawCalls[0].MaterialNode != null)
                    {
                        node._name += "_" + ((MDL0ObjectNode)model._objList[index])._drawCalls[0].MaterialNode._name;
                    }

                    if (form != null)
                    {
                        form.Say("Writing Colors - " + node.Name);
                    }

                    MDL0ColorData *header = (MDL0ColorData *)pData;
                    header->_dataLen     = c._dataLen.Align(0x20) + 0x20;
                    header->_dataOffset  = 0x20;
                    header->_index       = index++;
                    header->_isRGBA      = c._hasAlpha ? 1 : 0;
                    header->_format      = (int)c._outType;
                    header->_entryStride = (byte)c._dstStride;
                    header->_pad         = 0;
                    header->_numEntries  = (ushort)c._dstCount;

                    c.Write(pData + 0x20);

                    node._replSrc = node._replUncompSrc = new DataSource(header, header->_dataLen);
                    model._colorGroup.AddChild(node, false);

                    pData += header->_dataLen;
                }
            }

            if (linker._uvs != null && linker._uvs.Count != 0)
            {
                model.LinkGroup(new MDL0GroupNode(MDLResourceType.UVs));
                model._uvGroup._parent = model;

                index = 0;
                foreach (VertexCodec c in linker._uvs)
                {
                    MDL0UVNode node = new MDL0UVNode()
                    {
                        _name = "#" + index
                    };

                    if (form != null)
                    {
                        form.Say("Writing UVs - " + node.Name);
                    }

                    MDL0UVData *header = (MDL0UVData *)pData;
                    header->_dataLen     = c._dataLen.Align(0x20) + 0x40;
                    header->_dataOffset  = 0x40;
                    header->_index       = index++;
                    header->_format      = (int)c._type;
                    header->_divisor     = (byte)c._scale;
                    header->_isST        = 1;
                    header->_entryStride = (byte)c._dstStride;
                    header->_numEntries  = (ushort)c._dstCount;
                    header->_min         = (Vector2)c._min;
                    header->_max         = (Vector2)c._max;
                    header->_pad1        = header->_pad2 = header->_pad3 = header->_pad4 = 0;

                    c.Write(pData + 0x40);

                    node._replSrc = node._replUncompSrc = new DataSource(header, header->_dataLen);
                    model._uvGroup.AddChild(node, false);

                    pData += header->_dataLen;
                }
            }

            //Clean groups
            if (model._vertList != null && model._vertList.Count > 0)
            {
                model._children.Add(model._vertGroup);
                linker.Groups[(int)(MDLResourceType)Enum.Parse(typeof(MDLResourceType), model._vertGroup.Name)] = model._vertGroup;
            }
            else
            {
                model.UnlinkGroup(model._vertGroup);
            }

            if (model._normList != null && model._normList.Count > 0)
            {
                model._children.Add(model._normGroup);
                linker.Groups[(int)(MDLResourceType)Enum.Parse(typeof(MDLResourceType), model._normGroup.Name)] = model._normGroup;
            }
            else
            {
                model.UnlinkGroup(model._normGroup);
            }

            if (model._uvList != null && model._uvList.Count > 0)
            {
                model._children.Add(model._uvGroup);
                linker.Groups[(int)(MDLResourceType)Enum.Parse(typeof(MDLResourceType), model._uvGroup.Name)] = model._uvGroup;
            }
            else
            {
                model.UnlinkGroup(model._uvGroup);
            }

            if (model._colorList != null && model._colorList.Count > 0)
            {
                model._children.Add(model._colorGroup);
                linker.Groups[(int)(MDLResourceType)Enum.Parse(typeof(MDLResourceType), model._colorGroup.Name)] = model._colorGroup;
            }
            else
            {
                model.UnlinkGroup(model._colorGroup);
            }

            //Link sets
            if (model._objList != null)
            {
                foreach (MDL0ObjectNode poly in model._objList)
                {
                    if (poly._elementIndices[0] != -1 && model._vertList != null && model._vertList.Count > poly._elementIndices[0])
                    {
                        poly._vertexNode = (MDL0VertexNode)model._vertGroup._children[poly._elementIndices[0]];
                    }
                    if (poly._elementIndices[1] != -1 && model._normList != null && model._normList.Count > poly._elementIndices[1])
                    {
                        poly._normalNode = (MDL0NormalNode)model._normGroup._children[poly._elementIndices[1]];
                    }
                    for (int i = 2; i < 4; i++)
                    {
                        if (poly._elementIndices[i] != -1 && model._colorList != null && model._colorList.Count > poly._elementIndices[i])
                        {
                            poly._colorSet[i - 2] = (MDL0ColorNode)model._colorGroup._children[poly._elementIndices[i]];
                        }
                    }
                    for (int i = 4; i < 12; i++)
                    {
                        if (poly._elementIndices[i] != -1 && model._uvList != null && model._uvList.Count > poly._elementIndices[i])
                        {
                            poly._uvSet[i - 4] = (MDL0UVNode)model._uvGroup._children[poly._elementIndices[i]];
                        }
                    }
                }
            }
        }
Beispiel #2
0
        public static int CalcSize(Collada form, ModelLinker linker)
        {
            MDL0Node model = linker.Model;

            model._needsNrmMtxArray = model._needsTexMtxArray = false;
            model._numFacepoints    = model._numTriangles = 0;

            int headerLen,
                groupLen = 0,
                tableLen = 0,
                texLen   = 0,
                boneLen  = 0,
                dataLen  = 0,
                defLen   = 0,
                assetLen = 0,
                treeLen  = 0,
                mixLen   = 0,
                opaLen   = 0,
                xluLen   = 0;

            int aInd, aLen;

            //Get header length
            switch (linker.Version)
            {
            case 0x08:
            case 0x09: headerLen = 0x80; break;

            case 0x0A: headerLen = 0x88; break;

            case 0x0B: headerLen = 0x8C; break;

            default: headerLen = 0x80;
                //Unsupported version. Change to 9 as default.
                linker.Version = 9; break;
            }

            //Assign node indices
            AssignNodeIndices(linker);

            //Get table length
            tableLen = (linker._nodeCount + 1) << 2;

            //Get group/data length
            List <MDLResourceType> iList = ModelLinker.IndexBank[linker.Version];

            foreach (MDLResourceType resType in iList)
            {
                IEnumerable entryList = null;
                int         entries   = 0;

                switch (resType)
                {
                case MDLResourceType.Definitions:

                    //NodeTree
                    treeLen = linker.BoneCache.Length * 5;

                    //NodeMix
                    foreach (Influence i in model._influences._influences)
                    {
                        mixLen += 4;
                        foreach (BoneWeight w in i.Weights)
                        {
                            MDL0BoneNode bone = w.Bone as MDL0BoneNode;
                            if (bone != null && w.Weight != 0 && bone._nodeIndex < linker.NodeCache.Length && bone._nodeIndex >= 0 && linker.NodeCache[bone._nodeIndex] is MDL0BoneNode)
                            {
                                mixLen += 6;
                            }
                        }
                    }
                    foreach (MDL0BoneNode b in linker.BoneCache)
                    {
                        if (b._weightCount > 0)
                        {
                            mixLen += 5;
                        }
                    }

                    //DrawOpa and DrawXlu
                    //Get assigned materials and categorize
                    if (model._objList != null)
                    {
                        for (int i = 0; i < model._objList.Count; i++)
                        {
                            //Entries are ordered by material, not by polygon.
                            //Using the material's attached polygon list is untrustable if the definitions were corrupt on parse.
                            MDL0ObjectNode poly = model._objList[i] as MDL0ObjectNode;

                            model._numTriangles  += poly._numFaces;
                            model._numFacepoints += poly._numFacepoints;

                            foreach (DrawCall c in poly._drawCalls)
                            {
                                if (c.DrawPass == DrawCall.DrawPassType.Opaque)
                                {
                                    opaLen += 8;
                                }
                                else
                                {
                                    xluLen += 8;
                                }
                            }
                        }
                    }

                    //Add terminate byte and set model def flags
                    if (model._hasTree = (treeLen > 0))
                    {
                        treeLen++; entries++;
                    }
                    if (model._hasMix = (mixLen > 0))
                    {
                        mixLen++; entries++;
                    }
                    if (model._hasOpa = (opaLen > 0))
                    {
                        opaLen++; entries++;
                    }
                    if (model._hasXlu = (xluLen > 0))
                    {
                        xluLen++; entries++;
                    }

                    //Align data
                    defLen += (treeLen + mixLen + opaLen + xluLen).Align(4);

                    break;

                case MDLResourceType.Vertices:
                    if (model._vertList != null)
                    {
                        entryList = model._vertList;
                        break;
                    }
                    else
                    {
                        aInd = 0;     //Set the ID
                        aLen = 1;     //Offset count
                    }

EvalAssets:

                    List <ResourceNode> polyList = model._objList;
                    if (polyList == null)
                    {
                        break;
                    }

                    string str = "";

                    bool direct = linker._forceDirectAssets[aInd];

                    //Create asset lists
                    IList aList;
                    switch (aInd)     //Switch by the set ID
                    {
                    case 0: aList = linker._vertices = new List <VertexCodec>(polyList.Count); str = "Vertices "; break;

                    case 1: aList = linker._normals = new List <VertexCodec>(polyList.Count); str = "Normals "; break;

                    case 2: aList = linker._colors = new List <ColorCodec>(polyList.Count); str = "Colors "; break;

                    default: aList = linker._uvs = new List <VertexCodec>(polyList.Count); str = "UVs "; break;
                    }

                    aLen += aInd;
                    for (int i = 0; i < polyList.Count; i++)
                    {
                        MDL0ObjectNode obj = polyList[i] as MDL0ObjectNode;
                        for (int x = aInd; x < aLen; x++)
                        {
                            if (obj._manager._faceData[x] != null)
                            {
                                //Remap color nodes
                                if ((x == 2 || x == 3))
                                {
                                    if (Collada._importOptions._rmpClrs)
                                    {
                                        obj._elementIndices[x] = -1;
                                        foreach (MDL0ObjectNode thatObj in polyList.OrderBy(c => - ((MDL0ObjectNode)c)._manager.GetColors(x - 2, false).Length))
                                        {
                                            //Only compare up to the current object
                                            if (thatObj == obj)
                                            {
                                                break;
                                            }

                                            var  thatArr = thatObj._manager.GetColors(x - 2, false);
                                            var  thisArr = obj._manager.GetColors(x - 2, false);
                                            bool equals  = true;
                                            if (thisArr.Length == thatArr.Length)
                                            {
                                                for (int n = 0; n < thisArr.Length; n++)
                                                {
                                                    if (thisArr[n] != thatArr[n])
                                                    {
                                                        equals = false;
                                                        break;
                                                    }
                                                }
                                            }
                                            else
                                            {
                                                foreach (RGBAPixel px in thisArr)
                                                {
                                                    if (Array.IndexOf(thatArr, px) < 0)
                                                    {
                                                        equals = false;
                                                        break;
                                                    }
                                                }
                                            }

                                            if (equals)
                                            {
                                                //Found a match
                                                obj._elementIndices[x]         = thatObj._elementIndices[x];
                                                obj._manager._newClrObj[x - 2] = thatObj.Index;
                                                break;
                                            }
                                        }
                                        if (obj._elementIndices[x] != -1)
                                        {
                                            continue;
                                        }
                                    }
                                    else
                                    {
                                        obj._manager._newClrObj[x - 2] = i;
                                    }
                                }

                                obj._elementIndices[x] = (short)aList.Count;

                                if (form != null)
                                {
                                    form.Say("Encoding " + str + (x - aInd) + " for Object " + i + ": " + obj.Name);
                                }

                                VertexCodec vert;
                                switch (aInd)
                                {
                                case 0:
                                    vert = new VertexCodec(obj._manager.GetVertices(false), false, Collada._importOptions._fltVerts);
                                    aList.Add(vert);
                                    if (!direct)
                                    {
                                        assetLen += vert._dataLen.Align(0x20) + 0x40;
                                    }
                                    break;

                                case 1:
                                    vert = new VertexCodec(obj._manager.GetNormals(false), false, Collada._importOptions._fltNrms);
                                    aList.Add(vert);
                                    if (!direct)
                                    {
                                        assetLen += vert._dataLen.Align(0x20) + 0x20;
                                    }
                                    break;

                                case 2:
                                    ColorCodec col = new ColorCodec(obj._manager.GetColors(x - 2, false));
                                    aList.Add(col);
                                    if (!direct)
                                    {
                                        assetLen += col._dataLen.Align(0x20) + 0x20;
                                    }
                                    break;

                                default:
                                    vert = new VertexCodec(obj._manager.GetUVs(x - 4, false), Collada._importOptions._fltUVs);
                                    aList.Add(vert);
                                    if (!direct)
                                    {
                                        assetLen += vert._dataLen.Align(0x20) + 0x40;
                                    }
                                    break;
                                }
                            }
                            else
                            {
                                obj._elementIndices[x] = -1;
                            }
                        }
                    }
                    if (!direct)
                    {
                        entries = aList.Count;
                    }
                    break;

                case MDLResourceType.Normals:
                    if (model._normList != null)
                    {
                        entryList = model._normList;
                    }
                    else
                    {
                        aInd = 1;     //Set the ID
                        aLen = 1;     //Offset count
                        goto EvalAssets;
                    }
                    break;

                case MDLResourceType.Colors:
                    if (model._colorList != null)
                    {
                        entryList = model._colorList;
                    }
                    else
                    {
                        if (Collada._importOptions._useOneNode)
                        {
                            HashSet <RGBAPixel> pixels = new HashSet <RGBAPixel>();
                            if (model._objList != null)
                            {
                                foreach (MDL0ObjectNode obj in model._objList)
                                {
                                    for (int i = 0; i < 2; i++)
                                    {
                                        var arr = obj._manager.GetColors(i, false);
                                        if (arr.Length > 0)
                                        {
                                            obj._elementIndices[i + 2] = 0;
                                            foreach (RGBAPixel p in arr)
                                            {
                                                pixels.Add(p);
                                            }
                                        }
                                        else
                                        {
                                            obj._elementIndices[i + 2] = -1;
                                        }
                                    }
                                }
                            }
                            var le = pixels.ToList();
                            le.Sort();

                            if (le.Count == 0)
                            {
                                break;
                            }

                            Collada._importOptions._singleColorNodeEntries = le.ToArray();

                            ColorCodec col = new ColorCodec(Collada._importOptions._singleColorNodeEntries);
                            linker._colors = new List <ColorCodec>()
                            {
                                col
                            };
                            assetLen += col._dataLen.Align(0x20) + 0x20;
                            entries   = 1;
                        }
                        else
                        {
                            aInd = 2;     //Set the ID
                            aLen = 2;     //Offset count
                            goto EvalAssets;
                        }
                    }
                    break;

                case MDLResourceType.UVs:
                    if (model._uvList != null)
                    {
                        entryList = model._uvList;
                    }
                    else
                    {
                        aInd = 4;     //Set the ID
                        aLen = 8;     //Offset count
                        goto EvalAssets;
                    }
                    break;

                case MDLResourceType.Bones:
                    int index = 0;
                    foreach (MDL0BoneNode b in linker.BoneCache)
                    {
                        if (form != null)
                        {
                            form.Say("Calculating the size of the Bones - " + b.Name);
                        }

                        b._entryIndex = index++;
                        boneLen      += b.CalculateSize(true);
                    }
                    entries = linker.BoneCache.Length;
                    break;

                case MDLResourceType.Materials:
                    if (model._matList != null)
                    {
                        entries = model._matList.Count;
                    }
                    break;

                case MDLResourceType.Objects:
                    if (model._objList != null)
                    {
                        entryList = model._objList;
                        foreach (MDL0ObjectNode n in model._objList)
                        {
                            if (n.NormalNode != null || n._manager._faceData[1] != null)
                            {
                                model._needsNrmMtxArray = true;
                            }
                            if (n.HasTexMtx)
                            {
                                model._needsTexMtxArray = true;
                            }
                        }
                    }
                    break;

                case MDLResourceType.Shaders:
                    if (model._matList != null && (entryList = model.GetUsedShaders()) != null)
                    {
                        entries = model._matList.Count;
                    }
                    break;

                case MDLResourceType.Textures:
                    if (model._texList != null)
                    {
                        List <MDL0TextureNode> texNodes = new List <MDL0TextureNode>();
                        foreach (MDL0TextureNode tex in model._texList)
                        {
                            texNodes.Add(tex);
                            texLen += (tex._references.Count * 8) + 4;
                        }
                        entries = (linker._texList = texNodes).Count;
                    }
                    break;

                case MDLResourceType.Palettes:
                    if (model._pltList != null)
                    {
                        List <MDL0TextureNode> pltNodes = new List <MDL0TextureNode>();
                        foreach (MDL0TextureNode plt in model._pltList)
                        {
                            pltNodes.Add(plt);
                            texLen += (plt._references.Count * 8) + 4;
                        }
                        entries = (linker._pltList = pltNodes).Count;
                    }
                    break;
                }

                if (entryList != null)
                {
                    int index = 0;
                    foreach (MDL0EntryNode e in entryList)
                    {
                        if (form != null)
                        {
                            if (resType == MDLResourceType.Objects)
                            {
                                form.Say("Encoding the " + resType.ToString() + " - " + e.Name);
                            }
                            else
                            {
                                form.Say("Calculating the size of the " + resType.ToString() + " - " + e.Name);
                            }
                        }

                        e._entryIndex = index++;
                        dataLen      += e.CalculateSize(true);
                    }
                    if (entries == 0)
                    {
                        entries = index;
                    }
                }

                if (entries > 0)
                {
                    groupLen += (entries * 0x10) + 0x18;
                }
            }

            //Align the materials perfectly using the data length
            int temp = 0;

            if (model._matList != null && iList.IndexOf(MDLResourceType.Materials) != -1)
            {
                int index             = 0;
                MDL0MaterialNode prev = null;
                foreach (MDL0MaterialNode e in model._matList)
                {
                    if (form != null)
                    {
                        form.Say("Calculating the size of the Materials - " + e.Name);
                    }

                    if (index != 0)
                    {
                        e._mdlOffset = (prev = ((MDL0MaterialNode)model._matList[index - 1]))._mdlOffset + prev._calcSize;
                    }
                    else if ((temp = (e._mdlOffset = headerLen + tableLen + groupLen + texLen + defLen + boneLen).Align(0x10)) != e._mdlOffset)
                    {
                        e._dataAlign = temp - e._mdlOffset;
                    }

                    e._entryIndex = index++;
                    dataLen      += e.CalculateSize(true);
                }
            }

            if (model._isImport && model._objList != null)
            {
                foreach (MDL0ObjectNode obj1 in model._objList)
                {
                    if (obj1 == null || obj1._drawCalls == null || obj1._drawCalls.Count == 0)
                    {
                        continue;
                    }

                    MDL0MaterialNode p = obj1._drawCalls[0].MaterialNode;
                    if (p == null)
                    {
                        continue;
                    }

                    //Set materials to use register color if option set
                    if (!Collada._importOptions._useReg &&
                        linker._colors != null &&
                        linker._colors.Count > 0)
                    {
                        p.C1AlphaMaterialSource = GXColorSrc.Vertex;
                        p.C1ColorMaterialSource = GXColorSrc.Vertex;
                    }
                    else
                    {
                        p.C1MaterialColor       = Collada._importOptions._dfltClr;
                        p.C1ColorMaterialSource = GXColorSrc.Register;
                        p.C1AlphaMaterialSource = GXColorSrc.Register;
                    }
                }
            }

            return
                ((linker._headerLen = headerLen) +
                 (linker._tableLen = tableLen) +
                 (linker._groupLen = groupLen) +
                 (linker._texLen = texLen) +
                 (linker._defLen = defLen) +
                 (linker._boneLen = boneLen) +
                 (linker._assetLen = assetLen) +
                 (linker._dataLen = dataLen) +
                 (linker.Version > 9 ? model._userEntries.GetSize() : 0));
        }
Beispiel #3
0
        internal static unsafe void Build(Collada form, ModelLinker linker, MDL0Header *header, int length, bool force)
        {
            byte *groupAddr = (byte *)header + linker._headerLen + linker._tableLen;
            byte *dataAddr  = groupAddr + linker._groupLen + linker._texLen; //Definitions start here
            byte *assetAddr = dataAddr + linker._defLen + linker._boneLen + linker._dataLen;

            linker.Header = header;

            if (form != null)
            {
                form.Say("Writing header...");
            }

            //Create new model header
            *header = new MDL0Header(length, linker.Version);

            if (form != null)
            {
                form.Say("Writing node table...");
            }

            //Write node table, assign node ids
            WriteNodeTable(linker);

            if (form != null)
            {
                form.Say("Writing definitions...");
            }

            //Write def table
            WriteDefs(linker, ref groupAddr, ref dataAddr);

            //Set format list for each polygon's UVAT groups
            SetFormatLists(linker);

            //Write assets first, but only if the model is an import
            if (linker.Model._isImport)
            {
                WriteAssets(form, linker, ref assetAddr);
            }

            //Write groups
            linker.Write(form, ref groupAddr, ref dataAddr, force);

            //Write user entries
            if (linker.Model._userEntries.Count > 0 && linker.Version > 9)
            {
                header->UserDataOffset = (int)dataAddr - (int)header;
                linker.Model._userEntries.Write(header->UserData);
            }
            else
            {
                header->UserDataOffset = 0;
            }

            //Write textures
            WriteTextures(linker, ref groupAddr);

            //Set box min and box max
            if (linker.Model._isImport)
            {
                SetBox(linker);
            }

            //Store group offsets
            linker.Finish();

            //Set new properties
            *header->Properties = new MDL0Props(linker.Version, linker.Model._numFacepoints, linker.Model._numTriangles, linker.Model._numNodes, linker.Model._scalingRule, linker.Model._texMtxMode, linker.Model._needsNrmMtxArray, linker.Model._needsTexMtxArray, linker.Model._enableExtents, linker.Model._envMtxMode, linker.Model._extents.Min, linker.Model._extents.Max);
        }
        public void Write(Collada form, ref byte *pGroup, ref byte *pData, bool force)
        {
            MDL0GroupNode  group;
            ResourceGroup *pGrp;
            ResourceEntry *pEntry;
            int            len;

            //Write data in the order it appears
            foreach (MDLResourceType resType in OrderBank)
            {
                if (((group = Groups[(int)resType]) == null) || SpecialRebuildData((int)resType))
                {
                    continue;
                }

                if (resType == MDLResourceType.Bones)
                {
                    if (form != null)
                    {
                        form.Say("Writing Bones");
                    }

                    MDL0Bone *pBone = (MDL0Bone *)pData;
                    foreach (MDL0BoneNode e in BoneCache)
                    {
                        len = e._calcSize;
                        e.Rebuild(pData, len, true);
                        pData += len;
                    }
                    //Loop through after all bones are written
                    //and set header offsets to related bones
                    foreach (MDL0BoneNode e in BoneCache)
                    {
                        e.CalculateOffsets();
                    }
                }
                else if (resType == MDLResourceType.Shaders)
                {
                    MDL0GroupNode mats = Groups[(int)MDLResourceType.Materials];
                    MDL0Material *mHeader;

                    if (form != null)
                    {
                        form.Say("Writing Shaders");
                    }

                    //Write data without headers
                    foreach (ResourceNode e in group.Children)
                    {
                        if (((MDL0ShaderNode)e)._materials.Count > 0)
                        {
                            len = e._calcSize;
                            e.Rebuild(pData, len, force);
                            pData += len;
                        }
                    }
                    //Write one header for each material, using same order.
                    if (mats != null)
                    {
                        foreach (MDL0MaterialNode mat in mats.Children)
                        {
                            mHeader = mat.Header;
                            if (mat._shader != null)
                            {
                                len = (int)mat._shader.Header;
                                mHeader->_shaderOffset = len - (int)mHeader;
                            }
                            else
                            {
                                mHeader->_shaderOffset = 0;
                            }
                        }
                    }
                }
                else if (resType == MDLResourceType.Objects || resType == MDLResourceType.Materials)
                {
                    foreach (ResourceNode r in group.Children)
                    {
                        if (form != null)
                        {
                            form.Say("Writing " + resType.ToString() + " - " + r.Name);
                        }

                        len = r._calcSize;
                        r.Rebuild(pData, len, true); //Forced to fix object node ids and align materials
                        pData += len;
                    }
                }
                else
                {
                    bool rebuild = true;

                    if (Model._isImport)
                    {
                        if (group._name == "Vertices" ||
                            group._name == "Normals" ||
                            group._name == "UVs" ||
                            group._name == "Colors")
                        {
                            rebuild = false; //The data has already been written!
                        }
                    }
                    if (rebuild)
                    {
                        foreach (ResourceNode e in group.Children)
                        {
                            //Console.WriteLine("Rebuilding the " + group.Name);

                            if (form != null)
                            {
                                form.Say("Writing the " + resType.ToString() + " - " + e.Name);
                            }

                            len = e._calcSize;
                            e.Rebuild(pData, len, true); //Forced just in case we need to convert to float.
                            pData += len;
                        }
                    }
                }
            }

            //Write relocation offsets in the order of the header
            fixed(ResourceGroup **pOut = &Defs)
            foreach (MDLResourceType resType in IndexBank[Version])
            {
                if (((group = Groups[(int)resType]) == null) || SpecialRebuildData((int)resType))
                {
                    continue;
                }

                pOut[(int)resType] = pGrp = (ResourceGroup *)pGroup;
                pEntry             = &pGrp->_first + 1;
                if (resType == MDLResourceType.Bones)
                {
                    *pGrp = new ResourceGroup(BoneCache.Length);
                    foreach (ResourceNode e in BoneCache)
                    {
                        (pEntry++)->_dataOffset = (int)((byte *)(e.WorkingUncompressed.Address) - pGroup);
                    }
                }
                else if (resType == MDLResourceType.Shaders)
                {
                    MDL0GroupNode mats = Groups[(int)MDLResourceType.Materials];

                    if (mats != null)
                    {
                        //Create a material group with the amount of entries
                        *pGrp = new ResourceGroup(mats.Children.Count);

                        foreach (MDL0MaterialNode mat in mats.Children)
                        {
                            (pEntry++)->_dataOffset = (int)mat._shader.Header - (int)pGrp;
                        }
                    }
                }
                else
                {
                    *pGrp = new ResourceGroup(group.Children.Count);
                    foreach (ResourceNode e in group.Children)
                    {
                        (pEntry++)->_dataOffset = (int)((byte *)(e.WorkingUncompressed.Address) - pGroup);
                    }
                }
                pGroup += pGrp->_totalSize;
            }
        }
        public static int CalcSize(Collada form, ModelLinker linker)
        {
            MDL0Node model = linker.Model;

            int headerLen,
                groupLen = 0,
                tableLen = 0,
                texLen   = 0,
                boneLen  = 0,
                dataLen  = 0,
                defLen   = 0,
                assetLen = 0,
                treeLen  = 0,
                mixLen   = 0,
                opaLen   = 0,
                xluLen   = 0;

            int aInd, aLen;

            //Get header length
            switch (linker.Version)
            {
            case 0x08:
            case 0x09: headerLen = 0x80; break;

            case 0x0A: headerLen = 0x88; break;

            case 0x0B: headerLen = 0x8C; break;

            default: headerLen = 0x80;
                //Unsupported version. Change to 9 as default.
                linker.Version = 9; break;
            }

            //Assign node indices
            AssignNodeIndices(linker);

            //Get table length
            tableLen = (linker._nodeCount + 1) << 2;

            //Get group/data length
            List <MDLResourceType> iList = ModelLinker.IndexBank[linker.Version];

            foreach (MDLResourceType resType in iList)
            {
                IEnumerable entryList = null;
                int         entries   = 0;

                switch (resType)
                {
                case MDLResourceType.Definitions:

                    //NodeTree
                    treeLen = linker.BoneCache.Length * 5;

                    //NodeMix
                    foreach (Influence i in model._influences._influences)
                    {
                        mixLen += 4;
                        foreach (BoneWeight w in i._weights)
                        {
                            mixLen += 6;
                        }
                    }
                    foreach (MDL0BoneNode b in linker.BoneCache)
                    {
                        if (b._weightCount > 0)
                        {
                            mixLen += 5;
                        }
                    }

                    //DrawOpa and DrawXlu
                    //Get assigned materials and categorize
                    if (model._matList != null)
                    {
                        for (int i = 0; i < model._matList.Count; i++)
                        {
                            //Entries are ordered by material, not by polygon.
                            MDL0MaterialNode mat = model._matList[i] as MDL0MaterialNode;
                            if (!mat.isMetal)
                            {
                                for (int l = 0; l < mat._polygons.Count; l++)
                                {
                                    if (!mat.XLUMaterial)
                                    {
                                        opaLen += 8;
                                    }
                                    else
                                    {
                                        xluLen += 8;
                                    }
                                }
                            }
                        }
                    }

                    //Add terminate byte and set model def flags
                    if (model._hasTree = (treeLen > 0))
                    {
                        treeLen++; entries++;
                    }
                    if (model._hasMix = (mixLen > 0))
                    {
                        mixLen++; entries++;
                    }
                    if (model._hasOpa = (opaLen > 0))
                    {
                        opaLen++; entries++;
                    }
                    if (model._hasXlu = (xluLen > 0))
                    {
                        xluLen++; entries++;
                    }

                    //Align data
                    defLen += (treeLen + mixLen + opaLen + xluLen).Align(4);

                    break;

                case MDLResourceType.Vertices:
                    if (model._vertList != null)
                    {
                        entryList = model._vertList;
                        break;
                    }
                    else
                    {
                        aInd = 0;     //Set the ID
                        aLen = 1;     //Offset count
                    }

EvalAssets:

                    List <ResourceNode> polyList = model._polyList;
                    if (polyList == null)
                    {
                        break;
                    }

                    string str = "";

                    //Create asset lists
                    IList aList;
                    switch (aInd)     //Switch by the set ID
                    {
                    case 0: aList = linker._vertices = new List <VertexCodec>(polyList.Count); str = "Vertices "; break;

                    case 1: aList = linker._normals = new List <VertexCodec>(polyList.Count); str = "Normals "; break;

                    case 2: aList = linker._colors = new List <ColorCodec>(polyList.Count); str = "Colors "; break;

                    default: aList = linker._uvs = new List <VertexCodec>(polyList.Count); str = "UVs "; break;
                    }

                    aLen += aInd;
                    for (int i = 0; i < polyList.Count; i++)
                    {
                        MDL0PolygonNode p = polyList[i] as MDL0PolygonNode;
                        for (int x = aInd; x < aLen; x++)
                        {
                            if (p._manager._faceData[x] != null)
                            {
                                if (model._importOptions._rmpClrs && model._importOptions._addClrs)
                                {
                                    if (i > 0 && x == 2 && model._noColors == true)
                                    {
                                        p._elementIndices[x] = 0; continue;
                                    }
                                    else if (i >= 0 && x == 3 && model._noColors == true)
                                    {
                                        p._elementIndices[x] = -1; break;
                                    }
                                }

                                p._elementIndices[x] = (short)aList.Count;

                                if (form != null)
                                {
                                    form.Say("Encoding " + str + (x - aInd) + " for Object " + i + ": " + p.Name);
                                }

                                switch (aInd)
                                {
                                case 0:
                                    VertexCodec vert;
                                    aList.Add(vert = new VertexCodec(p._manager.RawVertices, false, model._importOptions._fltVerts));
                                    assetLen      += vert._dataLen.Align(0x20) + 0x40;
                                    break;

                                case 1:
                                    aList.Add(vert = new VertexCodec(p._manager.RawNormals, false, model._importOptions._fltNrms));
                                    assetLen      += vert._dataLen.Align(0x20) + 0x20;
                                    break;

                                case 2:
                                    ColorCodec col;
                                    aList.Add(col = new ColorCodec(p._manager.Colors(x - 2)));
                                    assetLen     += col._dataLen.Align(0x20) + 0x20;
                                    break;

                                default:
                                    aList.Add(vert = new VertexCodec(p._manager.UVs(x - 4), model._importOptions._fltUVs));
                                    assetLen      += vert._dataLen.Align(0x20) + 0x40;
                                    break;
                                }
                            }
                            else
                            {
                                p._elementIndices[x] = -1;
                            }
                        }
                    }
                    entries = aList.Count;
                    break;

                case MDLResourceType.Normals:
                    if (model._normList != null)
                    {
                        entryList = model._normList;
                    }
                    else
                    {
                        aInd = 1;     //Set the ID
                        aLen = 1;     //Offset count
                        goto EvalAssets;
                    }
                    break;

                case MDLResourceType.Colors:
                    if (model._colorList != null)
                    {
                        entryList = model._colorList;
                    }
                    else
                    {
                        aInd = 2;     //Set the ID
                        aLen = 2;     //Offset count
                        goto EvalAssets;
                    }
                    break;

                case MDLResourceType.UVs:
                    if (model._uvList != null)
                    {
                        entryList = model._uvList;
                    }
                    else
                    {
                        aInd = 4;     //Set the ID
                        aLen = 8;     //Offset count
                        goto EvalAssets;
                    }
                    break;

                case MDLResourceType.Bones:
                    int index = 0;
                    foreach (MDL0BoneNode b in linker.BoneCache)
                    {
                        if (form != null)
                        {
                            form.Say("Calculating the size of the Bones - " + b.Name);
                        }

                        b._entryIndex = index++;
                        boneLen      += b.CalculateSize(true);
                    }
                    entries = linker.BoneCache.Length;
                    break;

                case MDLResourceType.Materials:
                    if (model._matList != null)
                    {
                        entries = model._matList.Count;
                    }
                    break;

                case MDLResourceType.Objects:
                    if (model._polyList != null)
                    {
                        entryList = model._polyList;
                    }
                    break;

                case MDLResourceType.Shaders:
                    if ((entryList = model.GetUsedShaders()) != null && model._matList != null)
                    {
                        entries = model._matList.Count;
                    }
                    break;

                case MDLResourceType.Textures:
                    if (model._texList != null)
                    {
                        foreach (MDL0TextureNode tex in model._texList)
                        {
                            texLen += (tex._references.Count * 8) + 4;
                        }

                        linker._texCount = entries = model._texList.Count;
                    }
                    break;

                case MDLResourceType.Palettes:
                    if (model._pltList != null)
                    {
                        foreach (MDL0TextureNode pal in model._pltList)
                        {
                            texLen += (pal._references.Count * 8) + 4;
                        }

                        linker._palCount = entries = model._pltList.Count;
                    }
                    break;
                }

                if (entryList != null)
                {
                    int index = 0;
                    foreach (MDL0EntryNode e in entryList)
                    {
                        if (form != null)
                        {
                            if (resType == MDLResourceType.Objects)
                            {
                                form.Say("Encoding the " + resType.ToString() + " - " + e.Name);
                            }
                            else
                            {
                                form.Say("Calculating the size of the " + resType.ToString() + " - " + e.Name);
                            }
                        }

                        e._entryIndex = index++;
                        dataLen      += e.CalculateSize(true);
                    }
                    if (entries == 0)
                    {
                        entries = index;
                    }
                }

                if (entries > 0)
                {
                    groupLen += (entries * 0x10) + 0x18;
                }
            }

            //Align the materials perfectly using the data length
            int temp = 0;

            if (model._matList != null && iList.IndexOf(MDLResourceType.Materials) != -1)
            {
                int index             = 0;
                MDL0MaterialNode prev = null;
                foreach (MDL0MaterialNode e in model._matList)
                {
                    if (form != null)
                    {
                        form.Say("Calculating the size of the Materials - " + e.Name);
                    }

                    e._entryIndex = index++;

                    if (index == 1)
                    {
                        if ((temp = (e._mdlOffset = headerLen + tableLen + groupLen + texLen + defLen + boneLen).Align(0x10)) != e._mdlOffset)
                        {
                            e._dataAlign = temp - e._mdlOffset;
                        }
                    }
                    else
                    {
                        e._mdlOffset = (prev = ((MDL0MaterialNode)model._matList[index - 1]))._mdlOffset + prev._calcSize;
                    }

                    dataLen += e.CalculateSize(true);
                }
            }

            return
                ((linker._headerLen = headerLen) +
                 (linker._tableLen = tableLen) +
                 (linker._groupLen = groupLen) +
                 (linker._texLen = texLen) +
                 (linker._defLen = defLen) +
                 (linker._boneLen = boneLen) +
                 (linker._assetLen = assetLen) +
                 (linker._dataLen = dataLen) +
                 (model._part2Entries.Count > 0 ? 0x1C + model._part2Entries.Count * 0x2C : 0));
        }
        internal static unsafe void Build(Collada form, ModelLinker linker, MDL0Header *header, int length, bool force)
        {
            byte *groupAddr = (byte *)header + linker._headerLen + linker._tableLen;
            byte *dataAddr  = groupAddr + linker._groupLen + linker._texLen; //Definitions start here
            byte *assetAddr = dataAddr + linker._defLen + linker._boneLen + linker._dataLen;

            linker.Header = header;

            if (form != null)
            {
                form.Say("Writing header...");
            }

            //Create new model header
            *header = new MDL0Header(length, linker.Version);
            MDL0Props *props = header->Properties;

            if (form != null)
            {
                form.Say("Writing node table...");
            }

            //Write node table, assign node ids
            WriteNodeTable(linker);

            if (form != null)
            {
                form.Say("Writing definitions...");
            }

            //Write def table
            WriteDefs(linker, ref groupAddr, ref dataAddr);

            //Set format list for each polygon's UVAT groups
            SetFormatLists(linker);

            //Write assets first, but only if the model is an import
            if (linker.Model._isImport)
            {
                WriteAssets(form, linker, ref assetAddr);
            }

            //Write groups
            linker.Write(form, ref groupAddr, ref dataAddr, force);

            //Write Part2 Entries
            if (linker.Model._part2Entries.Count > 0 && linker.Version != 9)
            {
                header->_part2Offset = (int)dataAddr - (int)header;
                Part2Data *part2 = header->Part2;
                if (part2 != null)
                {
                    part2->_totalLen = 0x1C + linker.Model._part2Entries.Count * 0x2C;
                    ResourceGroup *pGroup = part2->Group;
                    *pGroup = new ResourceGroup(linker.Model._part2Entries.Count);
                    ResourceEntry *pEntry = &pGroup->_first + 1;
                    byte *         pData  = (byte *)pGroup + pGroup->_totalSize;
                    foreach (string s in linker.Model._part2Entries)
                    {
                        (pEntry++)->_dataOffset = (int)pData - (int)pGroup;
                        Part2DataEntry *p = (Part2DataEntry *)pData;
                        *p = new Part2DataEntry(1);
                        pData += 0x1C;
                    }
                }
            }
            else
            {
                header->_part2Offset = 0;
            }

            //Write textures
            WriteTextures(linker, ref groupAddr);

            //Set box min and box max
            if (linker.Model._isImport)
            {
                SetBox(linker);
            }

            //Store group offsets
            linker.Finish();

            //Set new properties
            *props = new MDL0Props(linker.Version, linker.Model._numVertices, linker.Model._numFaces, linker.Model._numNodes, linker.Model._unk1, linker.Model._unk2, linker.Model._unk3, linker.Model._unk4, linker.Model._unk5, linker.Model._unk6, linker.Model.BoxMin, linker.Model.BoxMax);
        }