private static void WriteNormalGroup(StreamWriter writer, MDL0NormalNode norm)
 {
     writer.WriteLine();
     writer.WriteLine("#Normals");
     foreach (Vector3 v in norm.Normals)
         writer.WriteLine("vn {0} {1} {2}", v._x, v._y, v._z);
 }
        //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])._opaMaterial != null)
                        node._name += "_" + ((MDL0ObjectNode)model._objList[index])._opaMaterial._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 = (short)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])._opaMaterial != null)
                        node._name += "_" + ((MDL0ObjectNode)model._objList[index])._opaMaterial._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])._opaMaterial != null)
                        node._name += "_" + ((MDL0ObjectNode)model._objList[index])._opaMaterial._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]];
            }
        }
        public MDL0NormalNode NewNormal()
        {
            if (_modelViewerOpen)
                return null;

            MDL0Node model = ((MDL0Node)_resource);

            MDL0GroupNode g = model._normGroup;
            if (g == null)
            {
                model.AddChild(g = new MDL0GroupNode(MDLResourceType.Normals), true);
                model._normGroup = g; model._normList = g.Children;
            }

            MDL0NormalNode node = new MDL0NormalNode() { Name = "NormalSet" + ((MDL0Node)_resource)._normList.Count };
            node.Normals = new Vector3[] { new Vector3(0) };
            g.AddChild(node, true);
            node._forceRebuild = true;
            node.Rebuild(true);
            node.SignalPropertyChange();

            FindResource(node, true).EnsureVisible();

            return node;
        }
        public override bool OnInitialize()
        {
            MDL0Object* header = Header;
            _nodeId = header->_nodeId;

            SetSizeInternal(_totalLength = header->_totalLength);
            _mdl0Offset = header->_mdl0Offset;
            _stringOffset = header->_stringOffset;

            ModelLinker linker = Model._linker;

            MatrixNode = (_nodeId >= 0 && _nodeId < Model._linker.NodeCache.Length) ? Model._linker.NodeCache[_nodeId] : null;

            _vertexFormat = header->_vertexFormat;
            _vertexSpecs = header->_vertexSpecs;
            _arrayFlags = header->_arrayFlags;

            HasPosMatrix = _arrayFlags.HasPosMatrix;
            for (int i = 0; i < 8; i++)
                HasTextureMatrix[i] = _arrayFlags.GetHasTexMatrix(i);

            _numFacepoints = header->_numVertices;
            _numFaces = header->_numFaces;

            _flag = header->_flag;

            _primBufferSize = header->_primitives._bufferSize;
            _primSize = header->_primitives._size;
            _primOffset = header->_primitives._offset;

            _defBufferSize = header->_defintions._bufferSize;
            _defSize = header->_defintions._size;
            _defOffset = header->_defintions._offset;

            _entryIndex = header->_index;

            //Conditional name assignment
            if ((_name == null) && (header->_stringOffset != 0))
                if (!_replaced)
                    _name = header->ResourceString;
                else
                    _name = "polygon" + Index;

            //Link nodes
            if (header->_vertexId >= 0 && Model._vertList != null)
                foreach (MDL0VertexNode v in Model._vertList)
                    if (header->_vertexId == v.ID)
                    {
                        (_vertexNode = v)._objects.Add(this);
                        break;
                    }

            if (header->_normalId >= 0 && Model._normList != null)
                foreach (MDL0NormalNode n in Model._normList)
                    if (header->_normalId == n.ID)
                    {
                        (_normalNode = n)._objects.Add(this);
                        break;
                    }

            int id;
            for (int i = 0; i < 2; i++)
                if ((id = ((bshort*)header->_colorIds)[i]) >= 0 && Model._colorList != null)
                    foreach (MDL0ColorNode c in Model._colorList)
                        if (id == c.ID)
                        {
                            (_colorSet[i] = c)._objects.Add(this);
                            break;
                        }

            for (int i = 0; i < 8; i++)
                if ((id = ((bshort*)header->_uids)[i]) >= 0 && Model._uvList != null)
                    foreach (MDL0UVNode u in Model._uvList)
                        if (id == u.ID)
                        {
                            (_uvSet[i] = u)._objects.Add(this);
                            break;
                        }

            if (Model._version > 9)
            {
                if (header->_furVectorId >= 0)
                    foreach (MDL0FurVecNode v in Model._furVecList)
                        if (header->_furVectorId == v.ID)
                        {
                            (_furVecNode = v)._objects.Add(this);
                            break;
                        }

                if (header->_furLayerCoordId >= 0)
                    foreach (MDL0FurPosNode n in Model._furPosList)
                        if (header->_furLayerCoordId == n.ID)
                        {
                            (_furPosNode = n)._objects.Add(this);
                            break;
                        }
            }

            //Link element indices for rebuild
            _elementIndices[0] = (short)(_vertexNode != null ? _vertexNode.Index : -1);
            _elementIndices[1] = (short)(_normalNode != null ? _normalNode.Index : -1);
            for (int i = 2; i < 4; i++)
                _elementIndices[i] = (short)(_colorSet[i - 2] != null ? _colorSet[i - 2].Index : -1);
            for (int i = 4; i < 12; i++)
                _elementIndices[i] = (short)(_uvSet[i - 4] != null ? _uvSet[i - 4].Index : -1);
            _elementIndices[12] = (short)(_furVecNode != null ? _furVecNode.Index : -1);
            _elementIndices[13] = (short)(_furPosNode != null ? _furPosNode.Index : -1);

            //Create primitive manager
            if (_parent != null)
            {
                int i = 0;
                _manager = new PrimitiveManager(header, Model._assets, linker.NodeCache, this);
                if (_manager._vertices != null)
                    foreach (Vertex3 v in _manager._vertices)
                    {
                        v._index = i++;
                        v._object = this;
                    }
            }

            //Get polygon UVAT groups
            MDL0PolygonDefs* Defs = (MDL0PolygonDefs*)header->DefList;
            UVATGroups = new CPElementSpec(
                (uint)Defs->UVATA,
                (uint)Defs->UVATB,
                (uint)Defs->UVATC);

            //Read internal object node cache and read influence list
            if (Model._linker.NodeCache != null)
            {
                if (_matrixNode == null)
                {
                    _influences = new List<IMatrixNode>();
                    bushort* weights = header->WeightIndices(Model._version);
                    int count = *(bint*)weights; weights += 2;
                    for (int i = 0; i < count; i++)
                        if (*weights < Model._linker.NodeCache.Length)
                            _influences.Add(Model._linker.NodeCache[*weights++]);
                        else
                            weights++;
                }
            }

            //Check for errors
            if (header->_totalLength % 0x20 != 0)
            {
                Model._errors.Add("Object " + Index + " has an improper data length.");
                SignalPropertyChange(); _rebuild = true;
            }
            if ((int)(0x24 + header->_primitives._offset) % 0x20 != 0)
            {
                Model._errors.Add("Object " + Index + " has an improper primitives start offset.");
                SignalPropertyChange(); _rebuild = true;
            }
            if (CheckVertexFormat())
            {
                Model._errors.Add("Object " + Index + " has a facepoint descriptor that does not match its linked nodes.");
                SignalPropertyChange(); _rebuild = true;

                for (int i = 0; i < 2; i++)
                    if (_colorSet[i] != null && _manager._faceData[i + 2] == null)
                    {
                        _manager._faceData[i + 2] = new UnsafeBuffer(_manager._pointCount * 4);
                        _colorChanged[i] = true;
                    }
            }
            if (HasTexMtx && !Weighted)
            {
                Model._errors.Add("Object " + Index + " has texture matrices but is not weighted.");
                for (int i = 0; i < 8; i++)
                    HasTextureMatrix[i] = false;
                SignalPropertyChange();
                _rebuild = true;
            }

            //if (!Weighted)
            //{
            //    bool notFloat = HasANonFloatAsset;
            //    foreach (PrimitiveGroup p in _primGroups)
            //    {
            //        bool o = false;
            //        foreach (PrimitiveHeader ph in p._headers)
            //            if (ph.Type != WiiPrimitiveType.TriangleList && notFloat)
            //            {
            //                Model._errors.Add("Object " + Index + " will explode in-game due to assets that are not written as float.");
            //                SignalPropertyChange();

            //                if (_vertexNode.Format != WiiVertexComponentType.Float)
            //                    _vertexNode._forceRebuild = _vertexNode._forceFloat = true;

            //                if (_normalNode != null && _normalNode.Format != WiiVertexComponentType.Float)
            //                    _normalNode._forceRebuild = _normalNode._forceFloat = true;

            //                for (int i = 4; i < 12; i++)
            //                    if (_uvSet[i - 4] != null && _uvSet[i - 4].Format != WiiVertexComponentType.Float)
            //                        _uvSet[i - 4]._forceRebuild = _uvSet[i - 4]._forceFloat = true;

            //                o = true;
            //                break;
            //            }
            //        if (o)
            //            break;
            //    }
            //}

            return false;
        }