public void BuildFromScratch(Collada form) { _isImport = true; _influences.Clean(); _influences.Sort(); CleanTextures(); _linker = ModelLinker.Prepare(this); int size = ModelEncoder.CalcSize(form, _linker); FileMap uncompMap = FileMap.FromTempFile(size); ModelEncoder.Build(form, _linker, (MDL0Header*)uncompMap.Address, size, true); _replSrc.Close(); _replUncompSrc.Close(); _replSrc = _replUncompSrc = new DataSource(uncompMap.Address, size); _replSrc.Map = _replUncompSrc.Map = uncompMap; IsDirty = false; _reopen = true; _isImport = false; }
//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 static int CalcSize(Collada form, ModelLinker linker) { MDL0Node model = linker.Model; model._needsNrmMtxArray = model._needsTexMtxArray = false; model._numFacepoints = model._numFaces = 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) if (w.Bone != null && w.Weight != 0 && w.Bone._nodeIndex < linker.NodeCache.Length && w.Bone._nodeIndex >= 0 && linker.NodeCache[w.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._numFaces += poly._numFaces; model._numFacepoints += poly._numFacepoints; if (poly.OpaMaterialNode != null) opaLen += 8; if (poly.XluMaterialNode != null) 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>(); 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(); 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; if (model._objList.Count > 0) { model._needsNrmMtxArray = true; foreach (MDL0ObjectNode n in model._objList) if (n.HasTexMtx) model._needsTexMtxArray = true; } } 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) + (linker.Version > 9 ? model._userEntries.GetSize() : 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 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 *props = new MDL0Props(linker.Version, linker.Model._numFacepoints, linker.Model._numFaces, linker.Model._numNodes, linker.Model._scalingRule, linker.Model._texMtxMode, linker.Model._needsNrmMtxArray, linker.Model._needsTexMtxArray, linker.Model._enableExtents, linker.Model._envMtxMode, linker.Model.BoxMin, linker.Model.BoxMax); }