//This assumes influence list has already been cleaned public static void AssignNodeIndices(ModelLinker linker) { MDL0Node model = linker.Model; int index = 0; int count = model._influences.Count + linker.BoneCache.Length; linker._nodeCount = count; linker.Model._numNodes = count; linker.NodeCache = new IMatrixNode[count]; //Add referenced primaries foreach (MDL0BoneNode bone in linker.BoneCache) { if (bone.ReferenceCount > 0 || bone._infPolys.Count > 0) linker.NodeCache[bone._nodeIndex = index++] = bone; else bone._nodeIndex = -1; bone._weightCount = 0; } //Add weight groups foreach (Influence i in model._influences._influences) { linker.NodeCache[i._index = index++] = i; foreach (BoneWeight b in i._weights) if (b.Bone != null) b.Bone._weightCount++; } //Add remaining bones foreach (MDL0BoneNode bone in linker.BoneCache) if (bone._nodeIndex == -1) linker.NodeCache[bone._nodeIndex = index++] = bone; }
public void ShouldBindModelToTableModel() { ModelTest model = new ModelTest() { id = 1, name = "test"}; TableModel tModel = new ModelLinker<ModelTest>(model).LinkModel(); Assert.AreEqual("TAB_MODEL", tModel.GetTableName()); Assert.AreEqual(1, tModel.GetColumnValue("MODEL_ID")); Assert.AreEqual("test", tModel.GetColumnValue("name")); }
protected override int OnCalculateSize(bool force) { //Clean and sort influence list _influences.Clean(); _influences.Sort(); //Clean and sort texture list _textures.Clean(); _textures.Sort(); _linker = ModelLinker.Prepare(this); return(ModelEncoder.CalcSize(_linker)); }
protected override void OnPopulate() { InitGroups(); _linker = new ModelLinker(Header); _assets = new AssetStorage(_linker); try { //Set def flags _hasMix = _hasOpa = _hasTree = _hasXlu = false; if (_linker.Defs != null) { foreach (ResourcePair p in *_linker.Defs) { if (p.Name == "NodeTree") { _hasTree = true; } else if (p.Name == "NodeMix") { _hasMix = true; } else if (p.Name == "DrawOpa") { _hasOpa = true; } else if (p.Name == "DrawXlu") { _hasXlu = true; } } } _boneGroup.Parse(this); _matGroup.Parse(this); _shadGroup.Parse(this); _polyGroup.Parse(this); //Texture group doesn't need parsing //It's only used as a name reference/link and will be re-created on build. //Eliminate influences with no references? } finally { //Clean up! _assets.Dispose(); _assets = null; _linker = null; CleanGroups(); } }
protected override bool OnInitialize() { MDL0Polygon *header = Header; int nodeId = header->_nodeId; ModelLinker linker = Model._linker; //Attach single bind. Doesn't have to be bone node. if (nodeId >= 0) { Influence = linker.NodeCache[nodeId]; } if (header->_defFlags != 0x80) { Console.WriteLine("OMG!"); } if (header->_defSize != 0xE0) { Console.WriteLine("OMG!"); } if (header->_dataLen1 != header->_dataLen2) { Console.WriteLine("DataLen deviation!"); } if (header->_unk3 != 0) { Console.WriteLine("OMG!"); } if (header != null) { //Conditional name assignment if ((_name == null) && (header->_stringOffset != 0)) { _name = header->ResourceString; } //Create primitive manager if (_parent != null) { _manager = new PrimitiveManager(header, Model._assets, linker.NodeCache); } } return(false); }
public AssetStorage(ModelLinker linker) { int index; //Vertices if (linker.Vertices != null) { Assets[0] = new UnsafeBuffer[linker.Vertices->_numEntries]; index = 0; foreach (ResourcePair p in *linker.Vertices) Assets[0][index++] = VertexCodec.Decode((MDL0VertexData*)p.Data); } //Normals if (linker.Normals != null) { Assets[1] = new UnsafeBuffer[linker.Normals->_numEntries]; index = 0; foreach (ResourcePair p in *linker.Normals) Assets[1][index++] = VertexCodec.Decode((MDL0NormalData*)p.Data); } //Colors if (linker.Colors != null) { Assets[2] = new UnsafeBuffer[linker.Colors->_numEntries]; index = 0; foreach (ResourcePair p in *linker.Colors) Assets[2][index++] = ColorCodec.Decode((MDL0ColorData*)p.Data); } //UVs if (linker.UVs != null) { Assets[3] = new UnsafeBuffer[linker.UVs->_numEntries]; index = 0; foreach (ResourcePair p in *linker.UVs) Assets[3][index++] = VertexCodec.Decode((MDL0UVData*)p.Data); } }
//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(ModelLinker linker) { return CalcSize(null, linker); }
public override int OnCalculateSize(bool force) { //Clean and sort influence list _influences.Clean(); //_influences.Sort(); //Clean texture list CleanTextures(); _linker = ModelLinker.Prepare(this); return ModelEncoder.CalcSize(_linker); }
internal void Parse(MDL0Node model) { Influence inf; ModelLinker linker = model._linker; int typeIndex = (int)_type; fixed(ResourceGroup **gList = &linker.Defs) if (gList[typeIndex] != null) ExtractGroup(gList[typeIndex], ModelLinker.TypeBank[typeIndex]); else return; //Nothing to read //Special handling for bones and objects if (_type == MDLResourceType.Bones) { //Bones have been parsed from raw data as a flat list. //Bones re-assign parents in their Initialize block, so parents are true. //Parents must be assigned now as bones will be moved in memory when assigned as children. //Cache flat list linker.BoneCache = _children.Select(x => x as MDL0BoneNode).ToArray(); //Reset children so we can rebuild _children.Clear(); //Assign children using each bones' parent offset in case NodeTree is corrupted. //Bone parents are assigned when they are initialized in a flat array. foreach (MDL0BoneNode b in linker.BoneCache) { MDL0Bone *header = b.Header; //Assign true parent using parent header offset int offset = header->_parentOffset; if (offset != 0) { //Get address of parent header MDL0Bone *pHeader = (MDL0Bone *)((byte *)header + offset); //Search bone list for matching header foreach (MDL0BoneNode b2 in linker.BoneCache) { if (pHeader == b2.Header) { b._parent = b2; break; } } } if (b._boneFlags.HasFlag(BoneFlags.HasBillboardParent)) { b._bbRefNode = model._linker.BoneCache[header->_bbIndex] as MDL0BoneNode; } } //Make sure the node cache is the correct size int highest = 0; //Add bones to their parent's child lists and find highest node id foreach (MDL0BoneNode b in linker.BoneCache) { b._parent._children.Add(b); if (b._nodeIndex >= linker.NodeCache.Length && b._nodeIndex > highest) { highest = b._nodeIndex; } } if (highest >= linker.NodeCache.Length) { linker.NodeCache = new IMatrixNode[highest + 1]; } //Populate node cache MDL0BoneNode bone = null; int index; int count = linker.BoneCache.Length; for (int i = 0; i < count; i++) { linker.NodeCache[(bone = linker.BoneCache[i] as MDL0BoneNode)._nodeIndex] = bone; } int nullCount = 0; bool nodeTreeError = false; //Now that bones and primary influences have been cached, we can create weighted influences. foreach (ResourcePair p in *linker.Defs) { if (p.Name == "NodeTree") { //Double check bone tree using the NodeTree definition. //If the NodeTree is corrupt, the user will be informed that it needs to be rebuilt. byte *pData = (byte *)p.Data; bool fixCS0159 = false; List <MDL0BoneNode> bones = linker.BoneCache.ToList(); STop: if (*pData == 2) { bone = linker.BoneCache[*(bushort *)(pData + 1)] as MDL0BoneNode; index = *(bushort *)(pData + 3); //Parent bone node index if (bone.Header->_parentOffset == 0) { if (!_children.Contains(bone)) { nodeTreeError = true; continue; } else { bones.Remove(bone); } } else { MDL0BoneNode parent = linker.NodeCache[index] as MDL0BoneNode; if (parent == null || bone._parent != parent || !parent._children.Contains(bone)) { nodeTreeError = true; continue; } else { bones.Remove(bone); } } pData += 5; fixCS0159 = true; } if (fixCS0159) { fixCS0159 = false; goto STop; } if (bones.Count > 0) { nodeTreeError = true; } } else if (p.Name == "NodeMix") { //Use node mix to create weight groups byte *pData = (byte *)p.Data; bool fixCS0159 = false; TTop: switch (*pData) { //Type 3 is for weighted influences case 3: //Get index/count fields index = *(bushort *)(pData + 1); count = pData[3]; //Get data pointer (offset of 4) MDL0NodeType3Entry *nEntry = (MDL0NodeType3Entry *)(pData + 4); //Create influence with specified count inf = new Influence(); //Iterate through weights, adding each to the influence //Here, we are referring back to the NodeCache to grab the bone. //Note that the weights do not reference other influences, only bones. There is a good reason for this. MDL0BoneNode b = null; List <int> nullIndices = new List <int>(); for (int i = 0; i < count; i++, nEntry++) { if (nEntry->_id < linker.NodeCache.Length && (b = (linker.NodeCache[nEntry->_id] as MDL0BoneNode)) != null) { inf.AddWeight(new BoneWeight(b, nEntry->_value)); } else { nullIndices.Add(i); } } bool noWeights = false; if ((nullCount = nullIndices.Count) > 0) { List <BoneWeight> newWeights = new List <BoneWeight>(); for (int i = 0; i < inf.Weights.Count; i++) { if (!nullIndices.Contains(i)) { newWeights.Add(inf.Weights[i]); } } if (newWeights.Count == 0) { noWeights = true; } else { inf.SetWeights(newWeights); } } //Add influence to model object, while adding it to the cache. //Don't add user references here, they will be added during each object's initialization if (!noWeights) { ((Influence)(linker.NodeCache[index] = model._influences.FindOrCreate(inf)))._index = index; } //Move data pointer to next entry pData = (byte *)nEntry; fixCS0159 = true; break; //Type 5 is for primary influences case 5: pData += 5; fixCS0159 = true; break; } if (fixCS0159) { fixCS0159 = false; goto TTop; } } } if (nullCount > 0) { model._errors.Add("There were " + nullCount + " null weights in NodeMix."); } if (nodeTreeError) { model._errors.Add("The NodeTree definition did not match the bone tree."); } } else if (_type == MDLResourceType.Objects) { //Attach materials to polygons. //This assumes that materials have already been parsed. List <ResourceNode> matList = ((MDL0Node)_parent)._matList; MDL0ObjectNode obj; MDL0MaterialNode mat; //Find DrawOpa or DrawXlu entry in Definition list foreach (ResourcePair p in *linker.Defs) { if ((p.Name == "DrawOpa") || (p.Name == "DrawXlu")) { bool isXLU = p.Name == "DrawXlu"; ushort objectIndex = 0; byte * pData = (byte *)p.Data; while (*pData++ == 4) { //Get object with index objectIndex = *(bushort *)(pData + 2); if (objectIndex >= _children.Count || objectIndex < 0) { model._errors.Add("Object index was greater than the actual object count."); objectIndex = 0; } obj = _children[objectIndex] as MDL0ObjectNode; //Get material with index mat = matList[*(bushort *)pData] as MDL0MaterialNode; //Get bone with index int boneIndex = *(bushort *)(pData + 4); MDL0BoneNode visBone = null; if (linker.BoneCache != null && boneIndex >= 0 && boneIndex < linker.BoneCache.Length) { visBone = linker.BoneCache[boneIndex] as MDL0BoneNode; } obj._drawCalls.Add(new DrawCall(obj) { _drawOrder = pData[6], _isXLU = isXLU, MaterialNode = mat, VisibilityBoneNode = visBone, }); //Increment pointer pData += 7; } } } foreach (MDL0ObjectNode m in _children) { int max = 0; foreach (DrawCall c in m._drawCalls) { max = Maths.Max(max, c.MaterialNode.Children.Count); if (c.MaterialNode.MetalMaterial != null) { max = Maths.Max(max, c.MaterialNode.MetalMaterial.Children.Count); } } bool hasUnused = false; if (m._manager != null) { for (int i = max; i < 8; i++) { if (m._manager.HasTextureMatrix[i]) { m._manager.HasTextureMatrix[i] = false; m._forceRebuild = true; hasUnused = true; } } } if (hasUnused) { ((MDL0Node)Parent)._errors.Add("Object " + m.Index + " has unused texture matrices."); } //This error doesn't seem to always be true for factory models... //if (m.HasTexMtx && m.HasNonFloatVertices) //{ // ((MDL0Node)Parent)._errors.Add("Object " + m.Index + " has texture matrices and non-float vertices, meaning it will explode in-game."); // m.SignalPropertyChange(); //} } } }
private static void WriteNodeTable(ModelLinker linker) { bint* ptr = (bint*)((byte*)linker.Header + linker._headerLen); int len = linker._nodeCount; int i = 0; //Set length *ptr++ = len; //Write indices while (i < len) { IMatrixNode n = linker.NodeCache[i++]; if (n.IsPrimaryNode) *ptr++ = ((MDL0BoneNode)n)._entryIndex; else *ptr++ = -1; } }
internal void Parse(MDL0Node model) { Influence inf; ModelLinker linker = model._linker; switch (_type) { case MDLResourceType.Bones: //Break if there are no bones defined if (linker.Bones == null) { break; } //Parse bones from raw data (flat list). //Bones re-assign parents in their Initialize block, so parents are true. ExtractGroup(linker.Bones, typeof(MDL0BoneNode)); //Cache flat list linker.BoneCache = _children.ToArray(); //Reset children so we can rebuild _children.Clear(); //Populate node cache MDL0BoneNode bone = null; int index; int count = linker.BoneCache.Length; for (int i = 0; i < count; i++) { //bone = linker.BoneCache[i] as MDL0BoneNode; linker.NodeCache[(bone = linker.BoneCache[i] as MDL0BoneNode)._nodeIndex] = bone; } //Now that bones and primary influences have been cached, we can create weighted influences. foreach (ResourcePair p in *linker.Defs) { if (p.Name == "NodeTree") { //Use node tree to rebuild bone heirarchy byte *pData = (byte *)p.Data; Top: if (*pData == 2) { bone = linker.BoneCache[*(bushort *)(pData + 1)] as MDL0BoneNode; index = *(bushort *)(pData + 3); if (bone.Header->_parentOffset == 0) { _children.Add(bone); } else { (bone._parent = linker.NodeCache[index] as ResourceNode)._children.Add(bone); } pData += 5; goto Top; } } else if (p.Name == "NodeMix") { //Use node mix to create weight groups byte *pData = (byte *)p.Data; Top: switch (*pData) { //Type 3 is for weighted influences case 3: //Get index/count fields index = *(bushort *)(pData + 1); count = pData[3]; //Get data pointer (offset of 4) MDL0NodeType3Entry *nEntry = (MDL0NodeType3Entry *)(pData + 4); //Create influence with specified count inf = new Influence(count); //Iterate through weights, adding each to the influence //Here, we are referring back to the NodeCache to grab the bone. //Note that the weights do not reference other influences, only bones. There is a good reason for this. for (int i = 0; i < count; i++, nEntry++) { inf._weights[i] = new BoneWeight(linker.NodeCache[nEntry->_id] as MDL0BoneNode, nEntry->_value); } //Add influence to model object, while adding it to the cache. linker.NodeCache[index] = model._influences.AddOrCreate(inf); //Move data pointer to next entry pData = (byte *)nEntry; goto Top; //Type 5 is for primary influences case 5: pData += 5; goto Top; } } } break; case MDLResourceType.Materials: if (linker.Materials != null) { ExtractGroup(linker.Materials, typeof(MDL0MaterialNode)); } break; case MDLResourceType.Shaders: if (linker.Shaders != null) { ExtractGroup(linker.Shaders, typeof(MDL0ShaderNode)); } break; case MDLResourceType.Polygons: //Break if no polygons defined if (linker.Polygons == null) { break; } //Extract ExtractGroup(linker.Polygons, typeof(MDL0PolygonNode)); //Attach materials to polygons. //This assumes that materials have already been parsed. List <ResourceNode> matList = ((MDL0Node)_parent)._matList; MDL0PolygonNode poly; MDL0MaterialNode mat; //Find DrawOpa or DrawXlu entry in Definition list foreach (ResourcePair p in *linker.Defs) { if ((p.Name == "DrawOpa") || (p.Name == "DrawXlu")) { byte *pData = (byte *)p.Data; while (*pData++ == 4) { //Get polygon from index poly = _children[*(bushort *)(pData + 2)] as MDL0PolygonNode; //Get material from index mat = matList[*(bushort *)pData] as MDL0MaterialNode; //Assign material to polygon poly._material = mat; //Add polygon to material reference list mat._polygons.Add(poly); //Increment pointer pData += 7; } } } break; } }
internal void Parse(MDL0Node model) { Influence inf; ModelLinker linker = model._linker; switch (_type) { case MDLResourceType.Definitions: if (linker.Defs != null) { ExtractGroup(linker.Defs, typeof(MDL0DefNode)); } break; case MDLResourceType.Bones: //Break if there are no bones defined if (linker.Bones == null) { break; } //Parse bones from raw data (flat list). //Bones re-assign parents in their Initialize block, so parents are true. //Parents must be assigned now as bones will be moved in memory when assigned as children. ExtractGroup(linker.Bones, typeof(MDL0BoneNode)); //Cache flat list linker.BoneCache = _children.ToArray(); //Make sure the node cache is the correct size int highest = 0; foreach (MDL0BoneNode b in linker.BoneCache) { if (b._nodeIndex >= linker.NodeCache.Length && b._nodeIndex > highest) { highest = b._nodeIndex; } } if (highest >= linker.NodeCache.Length) { linker.NodeCache = new IMatrixNode[highest + 1]; } //Reset children so we can rebuild _children.Clear(); //Assign children using each bones' parent offset in case NodeTree is corrupted foreach (MDL0BoneNode b in linker.BoneCache) { b._parent._children.Add(b); } //Populate node cache MDL0BoneNode bone = null; int index; int count = linker.BoneCache.Length; for (int i = 0; i < count; i++) { linker.NodeCache[(bone = linker.BoneCache[i] as MDL0BoneNode)._nodeIndex] = bone; } int nullCount = 0; bool nodeTreeError = false; //Now that bones and primary influences have been cached, we can create weighted influences. foreach (ResourcePair p in *linker.Defs) { if (p.Name == "NodeTree") { //Double check bone tree using the NodeTree definition. //If the NodeTree is corrupt, the user will be informed that it needs to be rebuilt. byte *pData = (byte *)p.Data; Top: if (*pData == 2) { bone = linker.BoneCache[*(bushort *)(pData + 1)] as MDL0BoneNode; index = *(bushort *)(pData + 3); if (bone.Header->_parentOffset == 0) { if (!_children.Contains(bone)) { nodeTreeError = true; continue; } } else { ResourceNode n = linker.NodeCache[index] as ResourceNode; if (n == null || bone._parent != n || !n._children.Contains(bone)) { nodeTreeError = true; continue; } } pData += 5; goto Top; } } else if (p.Name == "NodeMix") { //Use node mix to create weight groups byte *pData = (byte *)p.Data; Top: switch (*pData) { //Type 3 is for weighted influences case 3: //Get index/count fields index = *(bushort *)(pData + 1); count = pData[3]; //Get data pointer (offset of 4) MDL0NodeType3Entry *nEntry = (MDL0NodeType3Entry *)(pData + 4); //Create influence with specified count inf = new Influence(); //Iterate through weights, adding each to the influence //Here, we are referring back to the NodeCache to grab the bone. //Note that the weights do not reference other influences, only bones. There is a good reason for this. MDL0BoneNode b = null; List <int> nullIndices = new List <int>(); for (int i = 0; i < count; i++, nEntry++) { if (nEntry->_id < linker.NodeCache.Length && (b = (linker.NodeCache[nEntry->_id] as MDL0BoneNode)) != null) { inf._weights.Add(new BoneWeight(b, nEntry->_value)); } else { nullIndices.Add(i); nullCount++; } } bool d = false; if (nullIndices.Count > 0) { List <BoneWeight> newWeights = new List <BoneWeight>(); for (int i = 0; i < inf._weights.Count; i++) { if (!nullIndices.Contains(i)) { newWeights.Add(inf._weights[i]); } } if (newWeights.Count == 0) { d = true; } else { inf._weights = newWeights; } } //Add influence to model object, while adding it to the cache. if (!d) { ((Influence)(linker.NodeCache[index] = model._influences.FindOrCreate(inf, true)))._index = index; } //Move data pointer to next entry pData = (byte *)nEntry; goto Top; //Type 5 is for primary influences case 5: pData += 5; goto Top; } } } if (nullCount > 0) { model._errors.Add("There were " + nullCount + " null weights in NodeMix."); SignalPropertyChange(); } if (nodeTreeError) { model._errors.Add("The NodeTree definition did not match the bone tree."); SignalPropertyChange(); } break; case MDLResourceType.Materials: if (linker.Materials != null) { ExtractGroup(linker.Materials, typeof(MDL0MaterialNode)); } break; case MDLResourceType.Shaders: if (linker.Shaders != null) { ExtractGroup(linker.Shaders, typeof(MDL0ShaderNode)); } break; case MDLResourceType.Vertices: if (linker.Vertices != null) { ExtractGroup(linker.Vertices, typeof(MDL0VertexNode)); } break; case MDLResourceType.Normals: if (linker.Normals != null) { ExtractGroup(linker.Normals, typeof(MDL0NormalNode)); } break; case MDLResourceType.UVs: if (linker.UVs != null) { ExtractGroup(linker.UVs, typeof(MDL0UVNode)); } break; case MDLResourceType.FurLayerCoords: if (linker.FurLayerCoords != null) { ExtractGroup(linker.FurLayerCoords, typeof(MDL0FurPosNode)); } break; case MDLResourceType.FurVectors: if (linker.FurVectors != null) { ExtractGroup(linker.FurVectors, typeof(MDL0FurVecNode)); } break; case MDLResourceType.Objects: //Break if no polygons defined if (linker.Polygons == null) { break; } //Extract ExtractGroup(linker.Polygons, typeof(MDL0ObjectNode)); //Attach materials to polygons. //This assumes that materials have already been parsed. List <ResourceNode> matList = ((MDL0Node)_parent)._matList; MDL0ObjectNode poly; MDL0MaterialNode mat; //Find DrawOpa or DrawXlu entry in Definition list foreach (ResourcePair p in *linker.Defs) { if ((p.Name == "DrawOpa") || (p.Name == "DrawXlu")) { bool opa = p.Name == "DrawOpa"; ushort dIndex = 0; byte * pData = (byte *)p.Data; while (*pData++ == 4) { //Get polygon from index dIndex = *(bushort *)(pData + 2); if (dIndex >= _children.Count || dIndex < 0) { model._errors.Add("Object index was greater than the actual object count."); SignalPropertyChange(); dIndex = 0; } poly = _children[dIndex] as MDL0ObjectNode; poly._drawIndex = pData[6]; //Get material from index mat = matList[*(bushort *)pData] as MDL0MaterialNode; //Get bone from index and assign int boneIndex = *(bushort *)(pData + 4); if (linker.BoneCache != null && boneIndex >= 0 && boneIndex < linker.BoneCache.Length) { poly.BoneNode = linker.BoneCache[boneIndex] as MDL0BoneNode; } //Assign material to polygon if (opa) { poly.OpaMaterialNode = mat; } else { poly.XluMaterialNode = mat; } //Increment pointer pData += 7; } } } foreach (MDL0ObjectNode m in _children) { int max = Maths.Max( m.OpaMaterialNode != null ? m.OpaMaterialNode.Children.Count : 0, m.XluMaterialNode != null ? m.XluMaterialNode.Children.Count : 0, m.OpaMaterialNode != null && m.OpaMaterialNode.MetalMaterial != null ? m.OpaMaterialNode.MetalMaterial.Children.Count : 0, m.XluMaterialNode != null && m.XluMaterialNode.MetalMaterial != null ? m.XluMaterialNode.MetalMaterial.Children.Count : 0); bool hasUnused = false; for (int i = max; i < 8; i++) { if (m.HasTextureMatrix[i]) { m.HasTextureMatrix[i] = false; m._rebuild = true; hasUnused = true; } } if (hasUnused) { ((MDL0Node)Parent)._errors.Add("Object " + m.Index + " has unused texture matrices."); m.SignalPropertyChange(); } if (m.HasTexMtx && m.HasNonFloatVertices) { ((MDL0Node)Parent)._errors.Add("Object " + m.Index + " has texture matrices and non-float vertices, meaning it will explode in-game."); m.SignalPropertyChange(); } } break; case MDLResourceType.Colors: if (linker.Colors != null) { ExtractGroup(linker.Colors, typeof(MDL0ColorNode)); } break; case MDLResourceType.Textures: if (linker.Textures != null) { ExtractGroup(linker.Textures, typeof(MDL0TextureNode)); } break; case MDLResourceType.Palettes: if (linker.Palettes != null) { ExtractGroup(linker.Palettes, typeof(MDL0TextureNode)); } break; } }
protected internal override void OnRebuild(VoidPtr address, int length, bool force) { ModelEncoder.Build(_linker, (MDL0Header *)address, length, force); _linker = null; //ModelEncoder.Build(this, (MDL0Header*)address, length, force); }
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); }
private static void WriteDefs(ModelLinker linker, ref byte* pGroup, ref byte* pData) { MDL0Node mdl = linker.Model; //This should never happen! if (!mdl._hasMix && !mdl._hasOpa && !mdl._hasTree && !mdl._hasXlu) return; ResourceNode[] polyList = null; if (mdl._objList != null) { polyList = new ResourceNode[mdl._objList.Count]; Array.Copy(mdl._objList.ToArray(), polyList, mdl._objList.Count); } MDL0ObjectNode poly; int entryCount = 0; byte* floor = pData; int dataLen; ResourceGroup* group = linker.Defs = (ResourceGroup*)pGroup; ResourceEntry* entry = &group->_first + 1; //NodeTree if (mdl._hasTree) { //Write group entry entry[entryCount++]._dataOffset = (int)(pData - pGroup); int bCount = linker.BoneCache.Length; for (int i = 0; i < bCount; i++) { MDL0BoneNode bone = linker.BoneCache[i] as MDL0BoneNode; *pData = 2; //Entry tag *(bushort*)(pData + 1) = (ushort)bone._entryIndex; *(bushort*)(pData + 3) = (ushort)(bone._parent is MDL0BoneNode ? ((MDL0BoneNode)bone._parent)._nodeIndex : 0); pData += 5; //Advance } *pData++ = 1; //Terminate } //NodeMix //Only weight references go here. //First list bones used by weight groups, in bone order //Then list weight groups that use bones. Ordered by entry count. if (mdl._hasMix) { //Write group entry entry[entryCount++]._dataOffset = (int)(pData - pGroup); //Add bones first (using flat bone list) foreach (MDL0BoneNode b in linker.BoneCache) if (b._weightCount > 0) { *pData = 5; //Tag *(bushort*)(pData + 1) = (ushort)b._nodeIndex; *(bushort*)(pData + 3) = (ushort)b._entryIndex; pData += 5; //Advance } //Add weight groups (using sorted influence list) foreach (Influence i in mdl._influences._influences) { *pData = 3; //Tag *(bushort*)&pData[1] = (ushort)i._index; int g = 0; 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) g++; pData[3] = (byte)g; pData += 4; //Advance foreach (BoneWeight w in i._weights) { if (w.Bone == null || w.Weight == 0 || w.Bone._nodeIndex >= linker.NodeCache.Length || w.Bone._nodeIndex < 0) continue; *(bushort*)pData = (ushort)w.Bone._nodeIndex; *(bfloat*)(pData + 2) = w.Weight; pData += 6; //Advance } } *pData++ = 1; //Terminate } //DrawOpa if (mdl._hasOpa && polyList != null) { Array.Sort(polyList, MDL0ObjectNode.DrawCompareOpa); //Write group entry entry[entryCount++]._dataOffset = (int)(pData - pGroup); for (int i = 0; i < polyList.Length; i++) { poly = polyList[i] as MDL0ObjectNode; if (poly.OpaMaterialNode != null) { *pData = 4; //Tag *(bushort*)(pData + 1) = (ushort)poly.OpaMaterialNode._entryIndex; *(bushort*)(pData + 3) = (ushort)poly._entryIndex; *(bushort*)(pData + 5) = (ushort)(poly.BoneNode != null ? poly.BoneNode.BoneIndex : 0); pData[7] = poly.DrawPriority; pData += 8; //Advance } } *pData++ = 1; //Terminate } //DrawXlu if (mdl._hasXlu && polyList != null) { Array.Sort(polyList, MDL0ObjectNode.DrawCompareXlu); //Write group entry entry[entryCount++]._dataOffset = (int)(pData - pGroup); for (int i = 0; i < polyList.Length; i++) { poly = polyList[i] as MDL0ObjectNode; if (poly.XluMaterialNode != null) { *pData = 4; //Tag *(bushort*)(pData + 1) = (ushort)poly.XluMaterialNode._entryIndex; *(bushort*)(pData + 3) = (ushort)poly._entryIndex; *(bushort*)(pData + 5) = (ushort)(poly.BoneNode != null ? poly.BoneNode.BoneIndex : 0); pData[7] = poly.DrawPriority; pData += 8; //Advance } } *pData++ = 1; //Terminate } //Align data dataLen = (int)(pData - floor); while ((dataLen++ & 3) != 0) *pData++ = 0; //Set header *group = new ResourceGroup(entryCount); //Advance group poiner pGroup += group->_totalSize; }
internal static unsafe void Build(ModelLinker linker, MDL0Header* header, int length, bool force) { Build(null, linker, header, length, force); }
//Materials must already be written. Do this last! private static void WriteTextures(ModelLinker linker, ref byte* pGroup) { MDL0GroupNode texGrp = linker.Groups[(int)MDLResourceType.Textures]; MDL0GroupNode palGrp = linker.Groups[(int)MDLResourceType.Palettes]; if (texGrp == null) return; ResourceGroup* pTexGroup = null; ResourceEntry* pTexEntry = null; if (linker._texCount > 0) { linker.Textures = pTexGroup = (ResourceGroup*)pGroup; *pTexGroup = new ResourceGroup(linker._texCount); pTexEntry = &pTexGroup->_first + 1; pGroup += pTexGroup->_totalSize; } ResourceGroup* pDecGroup = null; ResourceEntry* pDecEntry = null; if (linker._palCount > 0) { linker.Palettes = pDecGroup = (ResourceGroup*)pGroup; *pDecGroup = new ResourceGroup(linker._palCount); pDecEntry = &pDecGroup->_first + 1; pGroup += pDecGroup->_totalSize; } bint* pData = (bint*)pGroup; int offset; //Textures List<ResourceNode> list = texGrp.Children; list.Sort(); //Alphabetical order if (pTexGroup != null) foreach (MDL0TextureNode t in list) if (t._references.Count > 0) { offset = (int)pData; (pTexEntry++)->_dataOffset = offset - (int)pTexGroup; *pData++ = t._references.Count; foreach (MDL0MaterialRefNode mat in t._references) { *pData++ = (int)mat.Material.WorkingUncompressed.Address - offset; *pData++ = (int)mat.WorkingUncompressed.Address - offset; } } //Palettes if (palGrp == null) return; list = palGrp.Children; list.Sort(); //Alphabetical order if (pDecGroup != null) foreach (MDL0TextureNode t in list) if (t._references.Count > 0) { offset = (int)pData; (pDecEntry++)->_dataOffset = offset - (int)pDecGroup; *pData++ = t._references.Count; foreach (MDL0MaterialRefNode mat in t._references) { *pData++ = (int)mat.Material.WorkingUncompressed.Address - offset; *pData++ = (int)mat.WorkingUncompressed.Address - offset; } } }
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); }
internal void Parse(MDL0Node model) { Influence inf; ModelLinker linker = model._linker; switch (_type) { case MDLResourceType.Definitions: if (linker.Defs != null) { ExtractGroup(linker.Defs, typeof(MDL0DefNode)); } break; case MDLResourceType.Bones: //Break if there are no bones defined if (linker.Bones == null) { break; } //Parse bones from raw data (flat list). //Bones re-assign parents in their Initialize block, so parents are true. ExtractGroup(linker.Bones, typeof(MDL0BoneNode)); //Cache flat list linker.BoneCache = _children.ToArray(); //Make sure the node cache is the correct size int highest = 0; foreach (MDL0BoneNode b in _children) { if (b._nodeIndex >= linker.NodeCache.Length && b._nodeIndex > highest) { highest = b._nodeIndex; } } if (highest >= linker.NodeCache.Length) { linker.NodeCache = new IMatrixNode[highest + 1]; } //Reset children so we can rebuild _children.Clear(); //Populate node cache MDL0BoneNode bone = null; int index; int count = linker.BoneCache.Length; for (int i = 0; i < count; i++) { linker.NodeCache[(bone = linker.BoneCache[i] as MDL0BoneNode)._nodeIndex] = bone; } //Now that bones and primary influences have been cached, we can create weighted influences. foreach (ResourcePair p in *linker.Defs) { if (p.Name == "NodeTree") { //Use node tree to rebuild bone heirarchy byte *pData = (byte *)p.Data; Top: if (*pData == 2) { bone = linker.BoneCache[*(bushort *)(pData + 1)] as MDL0BoneNode; index = *(bushort *)(pData + 3); if (bone.Header->_parentOffset == 0) { _children.Add(bone); } else { (bone._parent = linker.NodeCache[index] as ResourceNode)._children.Add(bone); } pData += 5; goto Top; } } else if (p.Name == "NodeMix") { //Use node mix to create weight groups byte *pData = (byte *)p.Data; Top: switch (*pData) { //Type 3 is for weighted influences case 3: //Get index/count fields index = *(bushort *)(pData + 1); count = pData[3]; //Get data pointer (offset of 4) MDL0NodeType3Entry *nEntry = (MDL0NodeType3Entry *)(pData + 4); //Create influence with specified count inf = new Influence(count); //Iterate through weights, adding each to the influence //Here, we are referring back to the NodeCache to grab the bone. //Note that the weights do not reference other influences, only bones. There is a good reason for this. for (int i = 0; i < count; i++, nEntry++) { if ((linker.NodeCache[nEntry->_id] as MDL0BoneNode) == null) { Console.WriteLine("Null bone entry!"); } else { inf._weights[i] = new BoneWeight(linker.NodeCache[nEntry->_id] as MDL0BoneNode, nEntry->_value); } } //Add influence to model object, while adding it to the cache. linker.NodeCache[index] = model._influences.AddOrCreate(inf); //Move data pointer to next entry pData = (byte *)nEntry; goto Top; //Type 5 is for primary influences case 5: pData += 5; goto Top; } } } int z = 0; foreach (IMatrixNode m in linker.NodeCache) { if (!m.IsPrimaryNode) { ((Influence)m)._permanentID = z; break; } z++; } break; case MDLResourceType.Materials: if (linker.Materials != null) { ExtractGroup(linker.Materials, typeof(MDL0MaterialNode)); } break; case MDLResourceType.Shaders: if (linker.Shaders != null) { ExtractGroup(linker.Shaders, typeof(MDL0ShaderNode)); } break; case MDLResourceType.Vertices: if (linker.Vertices != null) { ExtractGroup(linker.Vertices, typeof(MDL0VertexNode)); } break; case MDLResourceType.Normals: if (linker.Normals != null) { ExtractGroup(linker.Normals, typeof(MDL0NormalNode)); } break; case MDLResourceType.UVs: if (linker.UVs != null) { ExtractGroup(linker.UVs, typeof(MDL0UVNode)); } break; case MDLResourceType.Objects: //Break if no polygons defined if (linker.Polygons == null) { break; } //Extract ExtractGroup(linker.Polygons, typeof(MDL0PolygonNode)); //Attach materials to polygons. //This assumes that materials have already been parsed. List <ResourceNode> matList = ((MDL0Node)_parent)._matList; MDL0PolygonNode poly; MDL0MaterialNode mat; //Find DrawOpa or DrawXlu entry in Definition list foreach (ResourcePair p in *linker.Defs) { if ((p.Name == "DrawOpa") || (p.Name == "DrawXlu")) { ushort dIndex = 0; byte * pData = (byte *)p.Data; while (*pData++ == 4) { //Get polygon from index dIndex = *(bushort *)(pData + 2); if (dIndex >= _children.Count || dIndex < 0) { ((MDL0Node)Parent)._errors.Add("Object index was greater than the actual object count."); SignalPropertyChange(); dIndex = 0; } poly = _children[dIndex] as MDL0PolygonNode; //Get material from index mat = matList[*(bushort *)pData] as MDL0MaterialNode; //Get bone from index and assign int boneIndex = *(bushort *)(pData + 4); if (linker.BoneCache != null && boneIndex >= 0 && boneIndex < linker.BoneCache.Length) { poly.BoneNode = linker.BoneCache[boneIndex] as MDL0BoneNode; } //Assign material to polygon and add polygon to material reference list (poly._material = mat)._polygons.Add(poly); //Increment pointer pData += 7; } } } break; case MDLResourceType.Colors: if (linker.Colors != null) { ExtractGroup(linker.Colors, typeof(MDL0ColorNode)); } break; case MDLResourceType.Textures: if (linker.Textures != null) { ExtractGroup(linker.Textures, typeof(MDL0TextureNode)); } break; case MDLResourceType.Palettes: if (linker.Palettes != null) { ExtractGroup(linker.Palettes, typeof(MDL0TextureNode)); } break; } }
private static void SetBox(ModelLinker linker) { Vector3 min, max; linker.Model.ApplyCHR(null, 0); linker.Model.GetBox(out min, out max); linker.Model._min = min; linker.Model._max = max; if (linker.Model._objList != null) { linker.Model._numFacepoints = 0; linker.Model._numFaces = 0; foreach (MDL0ObjectNode n in linker.Model._objList) { linker.Model._numFacepoints += n._numFacepoints; linker.Model._numFaces += n._numFaces; } } }
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; }
private static void SetFormatLists(ModelLinker linker) { if (linker.Model._objList != null) for (int i = 0; i < linker.Model._objList.Count; i++) { MDL0ObjectNode poly = (MDL0ObjectNode)linker.Model._objList[i]; poly._fmtList = poly._manager.SetFormatList(poly, linker); } }
public override void OnPopulate() { InitGroups(); _linker = new ModelLinker(Header); _assets = new AssetStorage(_linker); try { //Set def flags _hasMix = _hasOpa = _hasTree = _hasXlu = false; if (_linker.Defs != null) foreach (ResourcePair p in *_linker.Defs) if (p.Name == "NodeTree") _hasTree = true; else if (p.Name == "NodeMix") _hasMix = true; else if (p.Name == "DrawOpa") _hasOpa = true; else if (p.Name == "DrawXlu") _hasXlu = true; //These cause some complications if not parsed... _texGroup.Parse(this); _pltGroup.Parse(this); _defGroup.Parse(this); _boneGroup.Parse(this); _matGroup.Parse(this); _shadGroup.Parse(this); _vertGroup.Parse(this); _normGroup.Parse(this); _uvGroup.Parse(this); _colorGroup.Parse(this); if (Version >= 10) { _furVecGroup.Parse(this); _furPosGroup.Parse(this); } _objGroup.Parse(this); //Parse objects last! _texList.Sort(); _pltList.Sort(); } finally //Clean up! { //We'll use the linker to access the bone cache //_linker = null; //Don't dispose assets, in case an object is replaced //_assets.Dispose(); //_assets = null; CleanGroups(); //Check for model errors if (_errors.Count > 0) { string message = _errors.Count + (_errors.Count > 1 ? " errors have" : " error has") + " been found in the model " + _name + ".\n" + (_errors.Count > 1 ? "These errors" : "This error") + " will be fixed when you save:"; foreach (string s in _errors) message += "\n - " + s; MessageBox.Show(message); } } }
public VertexAttributeFormat[] SetFormatList(MDL0ObjectNode polygon, ModelLinker linker) { List<VertexAttributeFormat> list = new List<VertexAttributeFormat>(); VertexCodec vert = null; ColorCodec col = null; for (int i = 0; i < 12; i++) { if (polygon._manager._faceData[i] != null) switch (i) { case 0: //Positions if (linker._vertices != null && linker._vertices.Count != 0 && polygon._elementIndices[0] != -1) if ((vert = linker._vertices[polygon._elementIndices[0]]) != null) list.Add(new VertexAttributeFormat( GXAttribute.Position, (GXCompType)vert._type, (GXCompCnt)(vert._hasZ ? 1 : 0), (byte)vert._scale)); break; case 1: //Normals vert = null; if (linker._normals != null && linker._normals.Count != 0 && polygon._elementIndices[1] != -1) if ((vert = linker._normals[polygon._elementIndices[1]]) != null) list.Add(new VertexAttributeFormat( GXAttribute.Normal, (GXCompType)vert._type, GXCompCnt.NrmXYZ, (byte)vert._scale)); break; case 2: //Color 1 case 3: //Color 2 col = null; if (linker._colors != null && linker._colors.Count != 0 && polygon._elementIndices[i] != -1 && (col = linker._colors[polygon._elementIndices[i]]) != null) list.Add(new VertexAttributeFormat( (GXAttribute)((int)GXAttribute.Color0 + (i - 2)), (GXCompType)col._outType, (GXCompCnt)(col._hasAlpha ? 1 : 0), 0)); break; case 4: //Tex 1 case 5: //Tex 2 case 6: //Tex 3 case 7: //Tex 4 case 8: //Tex 5 case 9: //Tex 6 case 10: //Tex 7 case 11: //Tex 8 vert = null; if (linker._uvs != null && linker._uvs.Count != 0 && polygon._elementIndices[i] != -1) if ((vert = linker._uvs[polygon._elementIndices[i]]) != null) list.Add(new VertexAttributeFormat( (GXAttribute)((int)GXAttribute.Tex0 + (i - 4)), (GXCompType)vert._type, GXCompCnt.TexST, (byte)vert._scale)); break; } } return list.ToArray(); }