public override void Read(string filename) { bchHeader header = new bchHeader(); FileData f = new FileData(filename); f.Endian = System.IO.Endianness.Little; f.skip(4); header.backwardCompatibility = f.readByte(); header.forwardCompatibility = f.readByte(); header.version = f.readUShort(); header.mainHeaderOffset = f.readInt(); header.stringTableOffset = f.readInt(); header.gpuCommandsOffset = f.readInt(); header.dataOffset = f.readInt(); if (header.backwardCompatibility > 0x20) { header.dataExtendedOffset = f.readInt(); } header.relocationTableOffset = f.readInt(); header.mainHeaderLength = f.readInt(); header.stringTableLength = f.readInt(); header.gpuCommandsLength = f.readInt(); header.dataLength = f.readInt(); if (header.backwardCompatibility > 0x20) { header.dataExtendedLength = f.readInt(); } header.relocationTableLength = f.readInt(); header.uninitializedDataSectionLength = f.readInt(); header.uninitializedDescriptionSectionLength = f.readInt(); if (header.backwardCompatibility > 7) { header.flags = f.readUShort(); header.addressCount = f.readUShort(); } // Relocation table for (int i = 0; i < header.relocationTableLength; i += 4) { f.seek(header.relocationTableOffset + i); int val = f.readInt(); int off = val & 0x1FFFFFF; byte flag = (byte)(val >> 25); switch (flag) { case 0: f.seek((off * 4) + header.mainHeaderOffset); f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.mainHeaderOffset); break; case 1: f.seek(off + header.mainHeaderOffset); f.writeInt((off) + header.mainHeaderOffset, f.readInt() + header.stringTableOffset); break; case 2: f.seek((off * 4) + header.mainHeaderOffset); f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.gpuCommandsOffset); break; case 0xc: f.seek((off * 4) + header.mainHeaderOffset); f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.dataOffset); break; } f.seek((off * 4) + header.gpuCommandsOffset); if (header.backwardCompatibility < 6) { switch (flag) { case 0x23: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break; //Texture case 0x25: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break; //Vertex //case 0x26: f.writeInt((off * 4) + header.gpuCommandsOffset, ((f.readInt() + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode case 0x27: f.writeInt((off * 4) + header.gpuCommandsOffset, (f.readInt() + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode } } else if (header.backwardCompatibility < 8) { switch (flag) { case 0x24: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break; //Texture case 0x26: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break; //Vertex //case 0x27: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode case 0x28: f.writeInt((off * 4) + header.gpuCommandsOffset, (f.readInt() + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode } } else if (header.backwardCompatibility < 0x21) { switch (flag) { case 0x25: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break; //Texture case 0x27: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break; //Vertex //case 0x28: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode case 0x29: f.writeInt((off * 4) + header.gpuCommandsOffset, (f.readInt() + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode } } else { switch (flag) { case 0x25: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break; //Texture case 0x26: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break; //Vertex relative to Data Offset //case 0x27: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Offset case 0x28: f.writeInt((off * 4) + header.gpuCommandsOffset, (f.readInt() + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode relative to Data Offset case 0x2b: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataExtendedOffset); break; //Vertex relative to Data Extended Offset //case 0x2c: writer.Write(((peek(input) + header.dataExtendedOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Extended Offset case 0x2d: f.writeInt((off * 4) + header.gpuCommandsOffset, (f.readInt() + header.dataExtendedOffset) & 0x7fffffff); break; //Index 8 bits mode relative to Data Extended Offset } } } // Content Header f.seek(header.mainHeaderOffset); bchContentHeader content = new bchContentHeader(); { content.modelsPointerTableOffset = f.readInt(); content.modelsPointerTableEntries = f.readInt(); content.modelsNameOffset = f.readInt(); content.materialsPointerTableOffset = f.readInt(); content.materialsPointerTableEntries = f.readInt(); content.materialsNameOffset = f.readInt(); content.shadersPointerTableOffset = f.readInt(); content.shadersPointerTableEntries = f.readInt(); content.shadersNameOffset = f.readInt(); content.texturesPointerTableOffset = f.readInt(); content.texturesPointerTableEntries = f.readInt(); content.texturesNameOffset = f.readInt(); content.materialsLUTPointerTableOffset = f.readInt(); content.materialsLUTPointerTableEntries = f.readInt(); content.materialsLUTNameOffset = f.readInt(); content.lightsPointerTableOffset = f.readInt(); content.lightsPointerTableEntries = f.readInt(); content.lightsNameOffset = f.readInt(); content.camerasPointerTableOffset = f.readInt(); content.camerasPointerTableEntries = f.readInt(); content.camerasNameOffset = f.readInt(); content.fogsPointerTableOffset = f.readInt(); content.fogsPointerTableEntries = f.readInt(); content.fogsNameOffset = f.readInt(); content.skeletalAnimationsPointerTableOffset = f.readInt(); content.skeletalAnimationsPointerTableEntries = f.readInt(); content.skeletalAnimationsNameOffset = f.readInt(); content.materialAnimationsPointerTableOffset = f.readInt(); content.materialAnimationsPointerTableEntries = f.readInt(); content.materialAnimationsNameOffset = f.readInt(); content.visibilityAnimationsPointerTableOffset = f.readInt(); content.visibilityAnimationsPointerTableEntries = f.readInt(); content.visibilityAnimationsNameOffset = f.readInt(); content.lightAnimationsPointerTableOffset = f.readInt(); content.lightAnimationsPointerTableEntries = f.readInt(); content.lightAnimationsNameOffset = f.readInt(); content.cameraAnimationsPointerTableOffset = f.readInt(); content.cameraAnimationsPointerTableEntries = f.readInt(); content.cameraAnimationsNameOffset = f.readInt(); content.fogAnimationsPointerTableOffset = f.readInt(); content.fogAnimationsPointerTableEntries = f.readInt(); content.fogAnimationsNameOffset = f.readInt(); content.scenePointerTableOffset = f.readInt(); content.scenePointerTableEntries = f.readInt(); content.sceneNameOffset = f.readInt(); } //Skeletal animation for (int index1 = 0; index1 < content.skeletalAnimationsPointerTableEntries; index1++) { f.seek(content.skeletalAnimationsPointerTableOffset + (index1 * 4)); int dataOffset = f.readInt(); f.seek(dataOffset); string skeletalAnimationName = f.readString(f.readInt(), -1); int animationFlags = f.readInt(); //int skeletalAnimationloopMode = f.readByte(); //pas ça du tout float skeletalAnimationframeSize = f.readFloat(); int boneTableOffset = f.readInt(); int boneTableEntries = f.readInt(); int metaDataPointerOffset = f.readInt(); //Debug.WriteLine("Animation Name: " + skeletalAnimationName); //Debug.WriteLine("BonetableOffset: " + boneTableOffset.ToString("X")); //Debug.WriteLine("BonetableEntry: " + boneTableEntries.ToString("X")); for (int i = 0; i < boneTableEntries; i++) { f.seek(boneTableOffset + (i * 4)); int offset = f.readInt(); OSkeletalAnimationBone bone = new OSkeletalAnimationBone(); f.seek(offset); bone.name = f.readString(f.readInt(), -1); Console.WriteLine("Bone Name: " + bone.name); int animationTypeFlags = f.readInt(); int flags = f.readInt(); OSegmentType segmentType = (OSegmentType)((animationTypeFlags >> 16) & 0xf); switch (segmentType) { case OSegmentType.transform: f.seek(offset + 0x18); int notExistMask = 0x80000; int constantMask = 0x200; for (int j = 0; j < 2; j++) { for (int axis = 0; axis < 3; axis++) { bool notExist = (flags & notExistMask) > 0; bool constant = (flags & constantMask) > 0; OAnimationKeyFrameGroup frame = new OAnimationKeyFrameGroup(); frame.exists = !notExist; if (frame.exists) { if (constant) { frame.interpolation = OInterpolationMode.linear; frame.keyFrames.Add(new OAnimationKeyFrame(f.readFloat(), 0)); } else { int frameOffset = f.readInt(); int position = f.pos(); f.seek(frameOffset); //getAnimationKeyFrame(input, frame); f.seek(position); } } else { f.seek(f.pos() + 0x04); } if (j == 0) { switch (axis) { case 0: bone.rotationX = frame; break; case 1: bone.rotationY = frame; break; case 2: bone.rotationZ = frame; break; } } else { switch (axis) { case 0: bone.translationX = frame; break; case 1: bone.translationY = frame; break; case 2: bone.translationZ = frame; break; } } notExistMask <<= 1; constantMask <<= 1; } constantMask <<= 1; } break; case OSegmentType.transformQuaternion: bone.isFrameFormat = true; int scaleOffset = f.readInt(); int rotationOffset = f.readInt(); int translationOffset = f.readInt(); if ((flags & 0x20) == 0) { bone.scale.exists = true; f.seek(scaleOffset); if ((flags & 4) > 0) { bone.scale.vector.Add(new Vector4( f.readFloat(), f.readFloat(), f.readFloat(), 0)); } else { bone.scale.startFrame = f.readFloat(); bone.scale.endFrame = f.readFloat(); int scaleFlags = f.readInt(); int scaleDataOffset = f.readInt(); int scaleEntries = f.readInt(); f.seek(scaleDataOffset); for (int j = 0; j < scaleEntries; j++) { bone.scale.vector.Add(new Vector4( f.readFloat(), f.readFloat(), f.readFloat(), 0)); } } } if ((flags & 0x10) == 0) { bone.rotationQuaternion.exists = true; f.seek(rotationOffset); if ((flags & 2) > 0) { bone.rotationQuaternion.vector.Add(new Vector4( f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat())); } else { bone.rotationQuaternion.startFrame = f.readFloat(); bone.rotationQuaternion.endFrame = f.readFloat(); int rotationFlags = f.readInt(); int rotationDataOffset = f.readInt(); int rotationEntries = f.readInt(); f.seek(rotationDataOffset); for (int j = 0; j < rotationEntries; j++) { bone.rotationQuaternion.vector.Add(new Vector4( f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat())); } } } if ((flags & 8) == 0) { bone.translation.exists = true; f.seek(translationOffset); if ((flags & 1) > 0) { bone.translation.vector.Add(new Vector4( f.readFloat(), f.readFloat(), f.readFloat(), 0)); } else { bone.translation.startFrame = f.readFloat(); bone.translation.endFrame = f.readFloat(); int translationFlags = f.readInt(); int translationDataOffset = f.readInt(); int translationEntries = f.readInt(); f.seek(translationDataOffset); for (int j = 0; j < translationEntries; j++) { bone.translation.vector.Add(new Vector4( f.readFloat(), f.readFloat(), f.readFloat(), 0)); } } } break; case OSegmentType.transformMatrix: bone.isFullBakedFormat = true; f.readInt(); f.readInt(); int matrixOffset = f.readInt(); int entries = f.readInt(); f.seek(matrixOffset); for (int j = 0; j < entries; j++) { /*OMatrix transform = new OMatrix(); * transform.M11 = f.readFloat(); * transform.M21 = f.readFloat(); * transform.M31 = f.readFloat(); * transform.M41 = f.readFloat(); * * transform.M12 = f.readFloat(); * transform.M22 = f.readFloat(); * transform.M32 = f.readFloat(); * transform.M42 = f.readFloat(); * * transform.M13 = f.readFloat(); * transform.M23 = f.readFloat(); * transform.M33 = f.readFloat(); * transform.M43 = f.readFloat(); * * bone.transform.Add(transform);*/ } break; default: throw new Exception(string.Format("BCH: Unknow Segment Type {0} on Skeletal Animation bone {1}! STOP!", segmentType, bone.name)); } //skeletalAnimation.bone.Add(bone); } } //Shaders (unused for now, until someone wants to add them) for (int index = 0; index < content.shadersPointerTableEntries; index++) { f.seek(content.shadersPointerTableOffset + (index * 4)); int dataOffset = f.readInt(); f.seek(dataOffset); int shaderDataOffset = f.readInt(); int shaderDataLength = f.readInt(); } // Textures // WIP Section for (int index = 0; index < content.texturesPointerTableEntries; index++) { f.seek(content.texturesPointerTableOffset + (index * 4)); int dOffset = f.readInt(); f.seek(dOffset); int textureCommandsOffset = f.readInt(); int textureCommandsWordCount = f.readInt(); f.seek(f.pos() + 0x14); String textureName = f.readString(f.readInt(), -1); f.seek(textureCommandsOffset); BCH_Texture tex = new BCH_Texture(); textures.Add(textureName, tex); tex.Height = f.readUShort(); tex.Width = f.readUShort(); f.skip(12); int doffset = f.readInt(); f.skip(4); tex.type = f.readInt(); tex.data = f.getSection(doffset, f.size() - doffset); tex.texture = _3DS.DecodeImage(tex.data, tex.Width, tex.Height, (_3DS.Tex_Formats)tex.type); //Texture texture = new Texture2D(tex.texture); //tex.display = texture.Id; } // Model data for (int modelIndex = 0; modelIndex < content.modelsPointerTableEntries; modelIndex++) { f.seek(content.modelsPointerTableOffset + (modelIndex * 4)); int objectsHeaderOffset = f.readInt(); // Objects f.seek(objectsHeaderOffset); BCH_Model model = new BCH_Model(); models.Add(model); model.flags = f.readByte(); model.skeletonScaleType = f.readByte(); model.silhouetteMaterialEntries = f.readUShort(); model.worldTransform = new Matrix4(f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat() , f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat() , f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat() , 0, 0, 0, 1); int materialsTableOffset = f.readInt(); int materialsTableEntries = f.readInt(); int materialsNameOffset = f.readInt(); int verticesTableOffset = f.readInt(); int verticesTableEntries = f.readInt(); f.skip(0x28); int skeletonOffset = f.readInt(); int skeletonEntries = f.readInt(); int skeletonNameOffset = f.readInt(); int objectsNodeVisibilityOffset = f.readInt(); int objectsNodeCount = f.readInt(); String name = f.readString(f.readInt(), -1); int objectsNodeNameEntries = f.readInt(); int objectsNodeNameOffset = f.readInt(); f.readInt(); //0x0 int metaDataPointerOffset = f.readInt(); f.seek(objectsNodeVisibilityOffset); int nodeVisibility = f.readInt(); string[] objectName = new string[objectsNodeNameEntries]; f.seek(objectsNodeNameOffset); int rootReferenceBit = f.readInt(); //Radix tree int rootLeftNode = f.readUShort(); int rootRightNode = f.readUShort(); int rootNameOffset = f.readInt(); for (int i = 0; i < objectsNodeNameEntries; i++) { int referenceBit = f.readInt(); short leftNode = f.readShort(); short rightNode = f.readShort(); objectName[i] = f.readString(f.readInt(), -1); } // Materials // NOTE: MATERIALS AND OBJECT SECTIONS ARE REALLY MESSY ATM String[] materialNames = new String[materialsTableEntries]; for (int index = 0; index < materialsTableEntries; index++) { f.seek(materialsTableOffset + (index * 0x2c)); int materialParametersOffset = f.readInt(); f.readInt(); f.readInt(); f.readInt(); int textureCommandsOffset = f.readInt(); int textureCommandsWordCount = f.readInt(); int materialMapperOffset = f.readInt(); materialNames[index] = f.readString(f.readInt(), -1); } // Object Descriptions... // Assumes MBN is already loaded for now f.seek(verticesTableOffset); List <objDes> objDescriptors = new List <objDes>(); if (mbn == null) { mbn = new Smash_Forge.MBN(); for (int index = 0; index < verticesTableEntries; index++) { mbn.mesh.Add(new MBN.Mesh()); } mbn.PreRender(); } for (int index = 0; index < mbn.mesh.Count; index++) { int i = f.readUShort(); if (index > mbn.mesh.Count) { break; } if (i > materialNames.Length) { break; } mbn.mesh[index].texId = textures[materialNames[i]].display; Console.WriteLine("Tex index" + mbn.mesh[index].texId); f.skip(2); // flags int nameId = f.readUShort(); mbn.mesh[index].Text = objectName[nameId]; // node visibility TODO: finish... mbn.mesh[index].Checked = ((nodeVisibility & (1 << nameId)) > 0); mbn.mesh[index].renderPriority = f.readUShort(); objDes des = new objDes(); objDescriptors.Add(des); des.vshAttBufferCommandOffset = f.readInt(); des.vshAttBufferCommandCount = f.readInt(); des.faceOffset = f.readInt(); des.faceCount = f.readInt(); des.vshAttBufferCommandOffsetEx = f.readInt(); des.vshAttBufferCommandCountEx = f.readInt(); f.skip(12); // center vector f.skip(4); // flagsOffset f.skip(4); // 0? f.readInt(); //bbOffsets[i] } //Skeleton f.seek(skeletonOffset); for (int index = 0; index < skeletonEntries; index++) { Bone bone = new Bone(model.skeleton); int boneFlags = f.readInt(); bone.parentIndex = f.readShort(); short boneSpace = f.readShort(); bone.scale = new float[3]; bone.rotation = new float[3]; bone.position = new float[3]; bone.scale[0] = f.readFloat(); bone.scale[1] = f.readFloat(); bone.scale[2] = f.readFloat(); bone.rotation[0] = f.readFloat(); bone.rotation[1] = f.readFloat(); bone.rotation[2] = f.readFloat(); bone.position[0] = f.readFloat(); bone.position[1] = f.readFloat(); bone.position[2] = f.readFloat(); // bone matrix... not really needed to be stored per say f.skip(4 * 4 * 3); bone.Text = f.readString(f.readInt(), -1); f.skip(4); // Meta data bones.bones.Add(bone); model.skeleton.bones.Add(bone); } model.skeleton.reset(); model.skeleton.update(); } }
/// <summary> /// Loads a BCH file. /// Note that BCH must start at offset 0x0 (don't try using it for BCHs inside containers). /// </summary> /// <param name="data">Memory Stream of the BCH file. The Stream will not be usable after</param> /// <returns></returns> public static RenderBase.OModelGroup load(MemoryStream data) { BinaryReader input = new BinaryReader(data); BinaryWriter writer = new BinaryWriter(data); RenderBase.OModelGroup models = new RenderBase.OModelGroup(); //Primary header bchHeader header = new bchHeader(); header.magic = IOUtils.readString(input, 0); data.Seek(4, SeekOrigin.Current); header.backwardCompatibility = input.ReadByte(); header.forwardCompatibility = input.ReadByte(); header.version = input.ReadUInt16(); header.mainHeaderOffset = input.ReadUInt32(); header.stringTableOffset = input.ReadUInt32(); header.gpuCommandsOffset = input.ReadUInt32(); header.dataOffset = input.ReadUInt32(); if (header.backwardCompatibility > 0x20) header.dataExtendedOffset = input.ReadUInt32(); header.relocationTableOffset = input.ReadUInt32(); header.mainHeaderLength = input.ReadUInt32(); header.stringTableLength = input.ReadUInt32(); header.gpuCommandsLength = input.ReadUInt32(); header.dataLength = input.ReadUInt32(); if (header.backwardCompatibility > 0x20) header.dataExtendedLength = input.ReadUInt32(); header.relocationTableLength = input.ReadUInt32(); header.uninitializedDataSectionLength = input.ReadUInt32(); header.uninitializedDescriptionSectionLength = input.ReadUInt32(); if (header.backwardCompatibility > 7) { header.flags = input.ReadUInt16(); header.addressCount = input.ReadUInt16(); } //Transform relative offsets to absolute offsets, also add extra bits if necessary. //The game does this on RAM after the BCH is loaded, so offsets to data is absolute and points to VRAM. for (uint o = header.relocationTableOffset; o < header.relocationTableOffset + header.relocationTableLength; o += 4) { data.Seek(o, SeekOrigin.Begin); uint value = input.ReadUInt32(); uint offset = value & 0x1ffffff; byte flags = (byte)(value >> 25); switch (flags) { case 0: data.Seek((offset * 4) + header.mainHeaderOffset, SeekOrigin.Begin); writer.Write(peek(input) + header.mainHeaderOffset); break; case 1: data.Seek(offset + header.mainHeaderOffset, SeekOrigin.Begin); writer.Write(peek(input) + header.stringTableOffset); break; case 2: data.Seek((offset * 4) + header.mainHeaderOffset, SeekOrigin.Begin); writer.Write(peek(input) + header.gpuCommandsOffset); break; case 7: case 0xc: data.Seek((offset * 4) + header.mainHeaderOffset, SeekOrigin.Begin); writer.Write(peek(input) + header.dataOffset); break; } //The moron that designed the format used different flags on different versions, instead of keeping compatibility. data.Seek((offset * 4) + header.gpuCommandsOffset, SeekOrigin.Begin); if (header.backwardCompatibility < 6) { switch (flags) { case 0x23: writer.Write(peek(input) + header.dataOffset); break; //Texture case 0x25: writer.Write(peek(input) + header.dataOffset); break; //Vertex case 0x26: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode case 0x27: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode } } else if (header.backwardCompatibility < 8) { switch (flags) { case 0x24: writer.Write(peek(input) + header.dataOffset); break; //Texture case 0x26: writer.Write(peek(input) + header.dataOffset); break; //Vertex case 0x27: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode case 0x28: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode } } else if (header.backwardCompatibility < 0x21) { switch (flags) { case 0x25: writer.Write(peek(input) + header.dataOffset); break; //Texture case 0x27: writer.Write(peek(input) + header.dataOffset); break; //Vertex case 0x28: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode case 0x29: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode } } else { switch (flags) { case 0x25: writer.Write(peek(input) + header.dataOffset); break; //Texture case 0x26: writer.Write(peek(input) + header.dataOffset); break; //Vertex relative to Data Offset case 0x27: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Offset case 0x28: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode relative to Data Offset case 0x2b: writer.Write(peek(input) + header.dataExtendedOffset); break; //Vertex relative to Data Extended Offset case 0x2c: writer.Write(((peek(input) + header.dataExtendedOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Extended Offset case 0x2d: writer.Write((peek(input) + header.dataExtendedOffset) & 0x7fffffff); break; //Index 8 bits mode relative to Data Extended Offset } } } //Content header data.Seek(header.mainHeaderOffset, SeekOrigin.Begin); bchContentHeader contentHeader = new bchContentHeader { modelsPointerTableOffset = input.ReadUInt32(), modelsPointerTableEntries = input.ReadUInt32(), modelsNameOffset = input.ReadUInt32(), materialsPointerTableOffset = input.ReadUInt32(), materialsPointerTableEntries = input.ReadUInt32(), materialsNameOffset = input.ReadUInt32(), shadersPointerTableOffset = input.ReadUInt32(), shadersPointerTableEntries = input.ReadUInt32(), shadersNameOffset = input.ReadUInt32(), texturesPointerTableOffset = input.ReadUInt32(), texturesPointerTableEntries = input.ReadUInt32(), texturesNameOffset = input.ReadUInt32(), materialsLUTPointerTableOffset = input.ReadUInt32(), materialsLUTPointerTableEntries = input.ReadUInt32(), materialsLUTNameOffset = input.ReadUInt32(), lightsPointerTableOffset = input.ReadUInt32(), lightsPointerTableEntries = input.ReadUInt32(), lightsNameOffset = input.ReadUInt32(), camerasPointerTableOffset = input.ReadUInt32(), camerasPointerTableEntries = input.ReadUInt32(), camerasNameOffset = input.ReadUInt32(), fogsPointerTableOffset = input.ReadUInt32(), fogsPointerTableEntries = input.ReadUInt32(), fogsNameOffset = input.ReadUInt32(), skeletalAnimationsPointerTableOffset = input.ReadUInt32(), skeletalAnimationsPointerTableEntries = input.ReadUInt32(), skeletalAnimationsNameOffset = input.ReadUInt32(), materialAnimationsPointerTableOffset = input.ReadUInt32(), materialAnimationsPointerTableEntries = input.ReadUInt32(), materialAnimationsNameOffset = input.ReadUInt32(), visibilityAnimationsPointerTableOffset = input.ReadUInt32(), visibilityAnimationsPointerTableEntries = input.ReadUInt32(), visibilityAnimationsNameOffset = input.ReadUInt32(), lightAnimationsPointerTableOffset = input.ReadUInt32(), lightAnimationsPointerTableEntries = input.ReadUInt32(), lightAnimationsNameOffset = input.ReadUInt32(), cameraAnimationsPointerTableOffset = input.ReadUInt32(), cameraAnimationsPointerTableEntries = input.ReadUInt32(), cameraAnimationsNameOffset = input.ReadUInt32(), fogAnimationsPointerTableOffset = input.ReadUInt32(), fogAnimationsPointerTableEntries = input.ReadUInt32(), fogAnimationsNameOffset = input.ReadUInt32(), scenePointerTableOffset = input.ReadUInt32(), scenePointerTableEntries = input.ReadUInt32(), sceneNameOffset = input.ReadUInt32() }; //Note: NameOffset are PATRICIA trees //Shaders for (int index = 0; index < contentHeader.shadersPointerTableEntries; index++) { data.Seek(contentHeader.shadersPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); uint shaderDataOffset = input.ReadUInt32(); uint shaderDataLength = input.ReadUInt32(); } //Textures for (int index = 0; index < contentHeader.texturesPointerTableEntries; index++) { data.Seek(contentHeader.texturesPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); uint textureCommandsOffset = input.ReadUInt32(); uint textureCommandsWordCount = input.ReadUInt32(); data.Seek(0x14, SeekOrigin.Current); string textureName = readString(input); data.Seek(textureCommandsOffset, SeekOrigin.Begin); PICACommandReader textureCommands = new PICACommandReader(data, textureCommandsWordCount); //Note: It have textures for the 3 texture units. //The other texture units are used with textureCoordinate1 and 2. data.Seek(textureCommands.getTexUnit0Address(), SeekOrigin.Begin); Size textureSize = textureCommands.getTexUnit0Size(); byte[] buffer = new byte[textureSize.Width * textureSize.Height * 4]; input.Read(buffer, 0, buffer.Length); Bitmap texture = TextureCodec.decode( buffer, textureSize.Width, textureSize.Height, textureCommands.getTexUnit0Format()); models.texture.Add(new RenderBase.OTexture(texture, textureName)); } //LookUp Tables for (int index = 0; index < contentHeader.materialsLUTPointerTableEntries; index++) { data.Seek(contentHeader.materialsLUTPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); input.ReadUInt32(); uint samplersCount = input.ReadUInt32(); string name = readString(input); RenderBase.OLookUpTable table = new RenderBase.OLookUpTable(); table.name = name; for (int i = 0; i < samplersCount; i++) { RenderBase.OLookUpTableSampler sampler = new RenderBase.OLookUpTableSampler(); input.ReadUInt32(); uint tableOffset = input.ReadUInt32(); uint tableWordCount = input.ReadUInt32(); sampler.name = readString(input); long dataPosition = data.Position; data.Seek(tableOffset, SeekOrigin.Begin); PICACommandReader lutCommands = new PICACommandReader(data, tableWordCount); sampler.table = lutCommands.getFSHLookUpTable(); table.sampler.Add(sampler); data.Seek(dataPosition, SeekOrigin.Begin); } models.lookUpTable.Add(table); } //Lights for (int index = 0; index < contentHeader.lightsPointerTableEntries; index++) { data.Seek(contentHeader.lightsPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); RenderBase.OLight light = new RenderBase.OLight(); light.name = readString(input); light.transformScale = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); light.transformRotate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); light.transformTranslate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); uint lightFlags = input.ReadUInt32(); switch (lightFlags & 0xf) { case 1: light.lightUse = RenderBase.OLightUse.hemiSphere; break; case 2: light.lightUse = RenderBase.OLightUse.ambient; break; case 5: case 6: case 7: light.lightUse = RenderBase.OLightUse.vertex; switch (lightFlags & 0xf) { case 5: light.lightType = RenderBase.OLightType.directional; break; case 6: light.lightType = RenderBase.OLightType.point; break; case 7: light.lightType = RenderBase.OLightType.spot; break; } break; case 9: case 0xa: case 0xb: light.lightUse = RenderBase.OLightUse.fragment; switch (lightFlags & 0xf) { case 9: light.lightType = RenderBase.OLightType.directional; break; case 0xa: light.lightType = RenderBase.OLightType.point; break; case 0xb: light.lightType = RenderBase.OLightType.spot; break; } break; default: Debug.WriteLine(string.Format("BCH: Warning - Unknow Light Flags {0}", lightFlags.ToString("X8"))); break; } light.isLightEnabled = (lightFlags & 0x100) > 0; light.isTwoSideDiffuse = (lightFlags & 0x10000) > 0; light.isDistanceAttenuationEnabled = (lightFlags & 0x20000) > 0; light.angleSampler.input = (RenderBase.OFragmentSamplerInput)((lightFlags >> 24) & 0xf); light.angleSampler.scale = (RenderBase.OFragmentSamplerScale)((lightFlags >> 28) & 0xf); input.ReadUInt32(); switch (light.lightUse) { case RenderBase.OLightUse.hemiSphere: light.groundColor = MeshUtils.getColorFloat(input); light.skyColor = MeshUtils.getColorFloat(input); light.direction = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); light.lerpFactor = input.ReadSingle(); break; case RenderBase.OLightUse.ambient: light.ambient = MeshUtils.getColor(input); break; case RenderBase.OLightUse.vertex: light.ambient = MeshUtils.getColorFloat(input); light.diffuse = MeshUtils.getColorFloat(input); light.direction = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); light.distanceAttenuationConstant = input.ReadSingle(); light.distanceAttenuationLinear = input.ReadSingle(); light.distanceAttenuationQuadratic = input.ReadSingle(); light.spotExponent = input.ReadSingle(); light.spotCutoffAngle = input.ReadSingle(); break; case RenderBase.OLightUse.fragment: light.ambient = MeshUtils.getColor(input); light.diffuse = MeshUtils.getColor(input); light.specular0 = MeshUtils.getColor(input); light.specular1 = MeshUtils.getColor(input); light.direction = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); input.ReadUInt32(); input.ReadUInt32(); light.attenuationStart = input.ReadSingle(); light.attenuationEnd = input.ReadSingle(); light.distanceSampler.tableName = readString(input); light.distanceSampler.samplerName = readString(input); light.angleSampler.tableName = readString(input); light.angleSampler.samplerName = readString(input); break; } models.light.Add(light); } //Cameras for (int index = 0; index < contentHeader.camerasPointerTableEntries; index++) { data.Seek(contentHeader.camerasPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); RenderBase.OCamera camera = new RenderBase.OCamera(); camera.name = readString(input); camera.transformScale = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); camera.transformRotate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); camera.transformTranslate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); uint cameraFlags = input.ReadUInt32(); camera.isInheritingTargetRotate = (cameraFlags & 0x10000) > 0; camera.isInheritingTargetTranslate = (cameraFlags & 0x20000) > 0; camera.isInheritingUpRotate = (cameraFlags & 0x40000) > 0; camera.view = (RenderBase.OCameraView)(cameraFlags & 0xf); camera.projection = (RenderBase.OCameraProjection)((cameraFlags >> 8) & 0xf); input.ReadSingle(); uint viewOffset = input.ReadUInt32(); uint projectionOffset = input.ReadUInt32(); data.Seek(viewOffset, SeekOrigin.Begin); camera.target = new RenderBase.OVector3(); camera.rotation = new RenderBase.OVector3(); camera.upVector = new RenderBase.OVector3(); RenderBase.OVector3 target = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); switch (camera.view) { case RenderBase.OCameraView.aimTarget: camera.target = target; camera.twist = input.ReadSingle(); break; case RenderBase.OCameraView.lookAtTarget: camera.target = target; camera.upVector = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); break; case RenderBase.OCameraView.rotate: camera.rotation = target; break; } data.Seek(projectionOffset, SeekOrigin.Begin); camera.zNear = input.ReadSingle(); camera.zFar = input.ReadSingle(); camera.aspectRatio = input.ReadSingle(); switch (camera.projection) { case RenderBase.OCameraProjection.perspective: camera.fieldOfViewY = input.ReadSingle(); break; case RenderBase.OCameraProjection.orthogonal: camera.height = input.ReadSingle(); break; } models.camera.Add(camera); } //Fogs for (int index = 0; index < contentHeader.fogsPointerTableEntries; index++) { data.Seek(contentHeader.fogsPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); RenderBase.OFog fog = new RenderBase.OFog(); fog.name = readString(input); fog.transformScale = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); fog.transformRotate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); fog.transformTranslate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); uint fogFlags = input.ReadUInt32(); fog.fogUpdater = (RenderBase.OFogUpdater)(fogFlags & 0xf); fog.isZFlip = (fogFlags & 0x100) > 0; fog.isAttenuateDistance = (fogFlags & 0x200) > 0; fog.fogColor = MeshUtils.getColor(input); fog.minFogDepth = input.ReadSingle(); fog.maxFogDepth = input.ReadSingle(); fog.fogDensity = input.ReadSingle(); models.fog.Add(fog); } //Skeletal Animations for (int index = 0; index < contentHeader.skeletalAnimationsPointerTableEntries; index++) { data.Seek(contentHeader.skeletalAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); RenderBase.OSkeletalAnimation skeletalAnimation = new RenderBase.OSkeletalAnimation(); skeletalAnimation.name = readString(input); uint animationFlags = input.ReadUInt32(); skeletalAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1); skeletalAnimation.frameSize = input.ReadSingle(); uint boneTableOffset = input.ReadUInt32(); uint boneTableEntries = input.ReadUInt32(); uint metaDataPointerOffset = input.ReadUInt32(); if (metaDataPointerOffset != 0) { data.Seek(metaDataPointerOffset, SeekOrigin.Begin); skeletalAnimation.userData = getMetaData(input); } for (int i = 0; i < boneTableEntries; i++) { data.Seek(boneTableOffset + (i * 4), SeekOrigin.Begin); uint offset = input.ReadUInt32(); RenderBase.OSkeletalAnimationBone bone = new RenderBase.OSkeletalAnimationBone(); data.Seek(offset, SeekOrigin.Begin); bone.name = readString(input); uint animationTypeFlags = input.ReadUInt32(); uint flags = input.ReadUInt32(); input.ReadUInt32(); RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf); switch (segmentType) { case RenderBase.OSegmentType.transform: data.Seek(offset + 0x18, SeekOrigin.Begin); uint notExistMask = 0x80000; uint constantMask = 0x200; for (int j = 0; j < 2; j++) { for (int axis = 0; axis < 3; axis++) { bool notExist = (flags & notExistMask) > 0; bool constant = (flags & constantMask) > 0; RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup(); frame.exists = !notExist; if (frame.exists) { if (constant) { frame.interpolation = RenderBase.OInterpolationMode.linear; frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0)); } else { uint frameOffset = input.ReadUInt32(); long position = data.Position; data.Seek(frameOffset, SeekOrigin.Begin); getAnimationKeyFrame(input, frame); data.Seek(position, SeekOrigin.Begin); } } else data.Seek(4, SeekOrigin.Current); if (j == 0) { switch (axis) { case 0: bone.rotationX = frame; break; case 1: bone.rotationY = frame; break; case 2: bone.rotationZ = frame; break; } } else { switch (axis) { case 0: bone.translationX = frame; break; case 1: bone.translationY = frame; break; case 2: bone.translationZ = frame; break; } } notExistMask <<= 1; constantMask <<= 1; } constantMask <<= 1; } break; case RenderBase.OSegmentType.transformQuaternion: bone.isFrameFormat = true; long originalPos = data.Position; uint rotationOffset = input.ReadUInt32(); uint translationOffset = input.ReadUInt32(); if ((flags & 0x10) == 0) { bone.rotationQuaternion.exists = true; data.Seek(rotationOffset, SeekOrigin.Begin); if ((flags & 2) > 0) { bone.rotationQuaternion.vector.Add(new RenderBase.OVector4( input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle())); } else { bone.rotationQuaternion.startFrame = input.ReadSingle(); bone.rotationQuaternion.endFrame = input.ReadSingle(); uint rotationFlags = input.ReadUInt32(); uint rotationDataOffset = input.ReadUInt32(); uint rotationEntries = input.ReadUInt32(); data.Seek(rotationDataOffset, SeekOrigin.Begin); for (int j = 0; j < rotationEntries; j++) { bone.rotationQuaternion.vector.Add(new RenderBase.OVector4( input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle())); } } } if ((flags & 8) == 0) { bone.translation.exists = true; data.Seek(translationOffset, SeekOrigin.Begin); if ((flags & 1) > 0) { bone.translation.vector.Add(new RenderBase.OVector4( input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), 0)); } else { bone.translation.startFrame = input.ReadSingle(); bone.translation.endFrame = input.ReadSingle(); uint translationFlags = input.ReadUInt32(); uint translationDataOffset = input.ReadUInt32(); uint translationEntries = input.ReadUInt32(); data.Seek(translationDataOffset, SeekOrigin.Begin); for (int j = 0; j < translationEntries; j++) { bone.translation.vector.Add(new RenderBase.OVector4( input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), 0)); } } } break; case RenderBase.OSegmentType.transformMatrix: bone.isFullBakedFormat = true; input.ReadUInt32(); uint matrixOffset = input.ReadUInt32(); uint entries = input.ReadUInt32(); data.Seek(matrixOffset, SeekOrigin.Begin); for (int j = 0; j < entries; j++) { RenderBase.OMatrix transform = new RenderBase.OMatrix(); transform.M11 = input.ReadSingle(); transform.M21 = input.ReadSingle(); transform.M31 = input.ReadSingle(); transform.M41 = input.ReadSingle(); transform.M12 = input.ReadSingle(); transform.M22 = input.ReadSingle(); transform.M32 = input.ReadSingle(); transform.M42 = input.ReadSingle(); transform.M13 = input.ReadSingle(); transform.M23 = input.ReadSingle(); transform.M33 = input.ReadSingle(); transform.M43 = input.ReadSingle(); bone.transform.Add(transform); } break; default: throw new Exception(string.Format("BCH: Unknow Segment Type {0} on Skeletal Animation bone {1}! STOP!", segmentType, bone.name)); } skeletalAnimation.bone.Add(bone); } models.skeletalAnimation.list.Add(skeletalAnimation); } //Material Animations for (int index = 0; index < contentHeader.materialAnimationsPointerTableEntries; index++) { data.Seek(contentHeader.materialAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); RenderBase.OMaterialAnimation materialAnimation = new RenderBase.OMaterialAnimation(); materialAnimation.name = readString(input); uint animationFlags = input.ReadUInt32(); materialAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1); materialAnimation.frameSize = input.ReadSingle(); uint dataTableOffset = input.ReadUInt32(); uint dataTableEntries = input.ReadUInt32(); input.ReadUInt32(); uint textureNameTableOffset = input.ReadUInt32(); uint textureNameTableEntries = input.ReadUInt32(); data.Seek(textureNameTableOffset, SeekOrigin.Begin); for (int i = 0; i < textureNameTableEntries; i++) { string name = readString(input); materialAnimation.textureName.Add(name); } for (int i = 0; i < dataTableEntries; i++) { data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin); uint offset = input.ReadUInt32(); RenderBase.OMaterialAnimationData animationData = new RenderBase.OMaterialAnimationData(); data.Seek(offset, SeekOrigin.Begin); animationData.name = readString(input); uint animationTypeFlags = input.ReadUInt32(); uint flags = input.ReadUInt32(); animationData.type = (RenderBase.OMaterialAnimationType)(animationTypeFlags & 0xff); RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf); int segmentCount = 0; switch (segmentType) { case RenderBase.OSegmentType.rgbaColor: segmentCount = 4; break; case RenderBase.OSegmentType.vector2: segmentCount = 2; break; case RenderBase.OSegmentType.single: segmentCount = 1; break; case RenderBase.OSegmentType.integer: segmentCount = 1; break; } for (int j = 0; j < segmentCount; j++) { RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup(); data.Seek(offset + 0xc + (j * 4), SeekOrigin.Begin); frame.exists = (flags & (0x100 << j)) == 0; bool constant = (flags & (1 << j)) > 0; if (frame.exists) { if (constant) { frame.interpolation = RenderBase.OInterpolationMode.linear; frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0)); } else { uint frameOffset = input.ReadUInt32(); data.Seek(frameOffset, SeekOrigin.Begin); getAnimationKeyFrame(input, frame); } } animationData.frameList.Add(frame); } materialAnimation.data.Add(animationData); } models.materialAnimation.list.Add(materialAnimation); } //Visibility Animations for (int index = 0; index < contentHeader.visibilityAnimationsPointerTableEntries; index++) { data.Seek(contentHeader.visibilityAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); RenderBase.OVisibilityAnimation visibilityAnimation = new RenderBase.OVisibilityAnimation(); visibilityAnimation.name = readString(input); uint animationFlags = input.ReadUInt32(); visibilityAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1); visibilityAnimation.frameSize = input.ReadSingle(); uint dataTableOffset = input.ReadUInt32(); uint dataTableEntries = input.ReadUInt32(); input.ReadUInt32(); input.ReadUInt32(); for (int i = 0; i < dataTableEntries; i++) { data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin); uint offset = input.ReadUInt32(); RenderBase.OVisibilityAnimationData animationData = new RenderBase.OVisibilityAnimationData(); data.Seek(offset, SeekOrigin.Begin); animationData.name = readString(input); uint animationTypeFlags = input.ReadUInt32(); uint flags = input.ReadUInt32(); RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf); if (segmentType == RenderBase.OSegmentType.boolean) { RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup(); if (segmentType == RenderBase.OSegmentType.boolean) frame = getAnimationKeyFrameBool(input); animationData.visibilityList = frame; } visibilityAnimation.data.Add(animationData); } models.visibilityAnimation.list.Add(visibilityAnimation); } //Light Animations for (int index = 0; index < contentHeader.lightAnimationsPointerTableEntries; index++) { data.Seek(contentHeader.lightAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); RenderBase.OLightAnimation lightAnimation = new RenderBase.OLightAnimation(); lightAnimation.name = readString(input); uint animationFlags = input.ReadUInt32(); lightAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1); lightAnimation.frameSize = input.ReadSingle(); uint dataTableOffset = input.ReadUInt32(); uint dataTableEntries = input.ReadUInt32(); input.ReadUInt32(); uint typeFlags = input.ReadUInt32(); lightAnimation.lightType = (RenderBase.OLightType)((typeFlags & 3) - 1); lightAnimation.lightUse = (RenderBase.OLightUse)((typeFlags >> 2) & 3); for (int i = 0; i < dataTableEntries; i++) { data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin); uint offset = input.ReadUInt32(); RenderBase.OLightAnimationData animationData = new RenderBase.OLightAnimationData(); data.Seek(offset, SeekOrigin.Begin); animationData.name = readString(input); uint animationTypeFlags = input.ReadUInt32(); uint flags = input.ReadUInt32(); animationData.type = (RenderBase.OLightAnimationType)(animationTypeFlags & 0xff); RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf); int segmentCount = 0; switch (segmentType) { case RenderBase.OSegmentType.transform: segmentCount = 9; break; case RenderBase.OSegmentType.rgbaColor: segmentCount = 4; break; case RenderBase.OSegmentType.vector3: segmentCount = 3; break; case RenderBase.OSegmentType.single: segmentCount = 1; break; case RenderBase.OSegmentType.boolean: segmentCount = 1; break; } uint constantMask = 0x40; for (int j = 0; j < segmentCount; j++) { RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup(); data.Seek(offset + 0xc + (j * 4), SeekOrigin.Begin); frame.exists = ((flags >> (segmentType == RenderBase.OSegmentType.transform ? 16 : 8)) & (1 << j)) == 0; if (frame.exists) { if (segmentType == RenderBase.OSegmentType.boolean) { frame = getAnimationKeyFrameBool(input); } else { bool constant; if (segmentType == RenderBase.OSegmentType.transform) { constant = (flags & constantMask) > 0; if (j == 5) constantMask <<= 2; else constantMask <<= 1; } else constant = (flags & (1 << j)) > 0; if (constant) { frame.interpolation = RenderBase.OInterpolationMode.linear; frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0.0f)); } else { uint frameOffset = input.ReadUInt32(); data.Seek(frameOffset, SeekOrigin.Begin); getAnimationKeyFrame(input, frame); } } } animationData.frameList.Add(frame); } lightAnimation.data.Add(animationData); } models.lightAnimation.list.Add(lightAnimation); } //Camera Animations for (int index = 0; index < contentHeader.cameraAnimationsPointerTableEntries; index++) { data.Seek(contentHeader.cameraAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); RenderBase.OCameraAnimation cameraAnimation = new RenderBase.OCameraAnimation(); cameraAnimation.name = readString(input); uint animationFlags = input.ReadUInt32(); cameraAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1); cameraAnimation.frameSize = input.ReadSingle(); uint dataTableOffset = input.ReadUInt32(); uint dataTableEntries = input.ReadUInt32(); input.ReadUInt32(); uint modeFlags = input.ReadUInt32(); cameraAnimation.viewMode = (RenderBase.OCameraView)(modeFlags & 0xf); cameraAnimation.projectionMode = (RenderBase.OCameraProjection)((modeFlags >> 8) & 0xf); for (int i = 0; i < dataTableEntries; i++) { data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin); uint offset = input.ReadUInt32(); RenderBase.OCameraAnimationData animationData = new RenderBase.OCameraAnimationData(); data.Seek(offset, SeekOrigin.Begin); animationData.name = readString(input); uint animationTypeFlags = input.ReadUInt32(); uint flags = input.ReadUInt32(); animationData.type = (RenderBase.OCameraAnimationType)(animationTypeFlags & 0xff); RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf); int segmentCount = 0; switch (segmentType) { case RenderBase.OSegmentType.transform: segmentCount = 9; break; case RenderBase.OSegmentType.vector3: segmentCount = 3; break; case RenderBase.OSegmentType.single: segmentCount = 1; break; } uint constantMask = 0x40; for (int j = 0; j < segmentCount; j++) { RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup(); data.Seek(offset + 0xc + (j * 4), SeekOrigin.Begin); frame.exists = ((flags >> (segmentType == RenderBase.OSegmentType.transform ? 16 : 8)) & (1 << j)) == 0; bool constant; if (segmentType == RenderBase.OSegmentType.transform) { constant = (flags & constantMask) > 0; if (j == 5) constantMask <<= 2; else constantMask <<= 1; } else constant = (flags & (1 << j)) > 0; if (frame.exists) { if (constant) { frame.interpolation = RenderBase.OInterpolationMode.linear; frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0.0f)); } else { uint frameOffset = input.ReadUInt32(); data.Seek(frameOffset, SeekOrigin.Begin); getAnimationKeyFrame(input, frame); } } animationData.frameList.Add(frame); } cameraAnimation.data.Add(animationData); } models.cameraAnimation.list.Add(cameraAnimation); } //Fog Animations for (int index = 0; index < contentHeader.fogAnimationsPointerTableEntries; index++) { data.Seek(contentHeader.fogAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); RenderBase.OFogAnimation fogAnimation = new RenderBase.OFogAnimation(); fogAnimation.name = readString(input); uint animationFlags = input.ReadUInt32(); fogAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1); fogAnimation.frameSize = input.ReadSingle(); uint dataTableOffset = input.ReadUInt32(); uint dataTableEntries = input.ReadUInt32(); input.ReadUInt32(); input.ReadUInt32(); for (int i = 0; i < dataTableEntries; i++) { data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin); uint offset = input.ReadUInt32(); RenderBase.OFogAnimationData animationData = new RenderBase.OFogAnimationData(); data.Seek(offset, SeekOrigin.Begin); animationData.name = readString(input); uint animationTypeFlags = input.ReadUInt32(); uint flags = input.ReadUInt32(); RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf); int segmentCount = segmentType == RenderBase.OSegmentType.rgbaColor ? 4 : 0; for (int j = 0; j < segmentCount; j++) { RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup(); data.Seek(offset + 0xc + (j * 4), SeekOrigin.Begin); frame.exists = ((flags >> 8) & (1 << j)) == 0; if (frame.exists) { bool constant = (flags & (1 << j)) > 0; if (constant) { frame.interpolation = RenderBase.OInterpolationMode.linear; frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0.0f)); } else { uint frameOffset = input.ReadUInt32(); data.Seek(frameOffset, SeekOrigin.Begin); getAnimationKeyFrame(input, frame); } } animationData.colorList.Add(frame); } fogAnimation.data.Add(animationData); } models.fogAnimation.list.Add(fogAnimation); } //Scene Environment for (int index = 0; index < contentHeader.scenePointerTableEntries; index++) { data.Seek(contentHeader.scenePointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); RenderBase.OScene scene = new RenderBase.OScene(); scene.name = readString(input); uint cameraReferenceOffset = input.ReadUInt32(); uint cameraReferenceEntries = input.ReadUInt32(); uint lightReferenceOffset = input.ReadUInt32(); uint lightReferenceEntries = input.ReadUInt32(); uint fogReferenceOffset = input.ReadUInt32(); uint fogReferenceEntries = input.ReadUInt32(); data.Seek(cameraReferenceOffset, SeekOrigin.Begin); for (int i = 0; i < cameraReferenceEntries; i++) { RenderBase.OSceneReference reference = new RenderBase.OSceneReference(); reference.slotIndex = input.ReadUInt32(); reference.name = readString(input); scene.cameras.Add(reference); } data.Seek(lightReferenceOffset, SeekOrigin.Begin); for (int i = 0; i < lightReferenceEntries; i++) { RenderBase.OSceneReference reference = new RenderBase.OSceneReference(); reference.slotIndex = input.ReadUInt32(); reference.name = readString(input); scene.lights.Add(reference); } data.Seek(fogReferenceOffset, SeekOrigin.Begin); for (int i = 0; i < fogReferenceEntries; i++) { RenderBase.OSceneReference reference = new RenderBase.OSceneReference(); reference.slotIndex = input.ReadUInt32(); reference.name = readString(input); scene.fogs.Add(reference); } } //Models for (int modelIndex = 0; modelIndex < contentHeader.modelsPointerTableEntries; modelIndex++) { RenderBase.OModel model = new RenderBase.OModel(); data.Seek(contentHeader.modelsPointerTableOffset + (modelIndex * 4), SeekOrigin.Begin); uint objectsHeaderOffset = input.ReadUInt32(); //Objects header data.Seek(objectsHeaderOffset, SeekOrigin.Begin); bchModelHeader modelHeader; modelHeader.flags = input.ReadByte(); modelHeader.skeletonScalingType = input.ReadByte(); modelHeader.silhouetteMaterialEntries = input.ReadUInt16(); modelHeader.worldTransform = new RenderBase.OMatrix(); modelHeader.worldTransform.M11 = input.ReadSingle(); modelHeader.worldTransform.M21 = input.ReadSingle(); modelHeader.worldTransform.M31 = input.ReadSingle(); modelHeader.worldTransform.M41 = input.ReadSingle(); modelHeader.worldTransform.M12 = input.ReadSingle(); modelHeader.worldTransform.M22 = input.ReadSingle(); modelHeader.worldTransform.M32 = input.ReadSingle(); modelHeader.worldTransform.M42 = input.ReadSingle(); modelHeader.worldTransform.M13 = input.ReadSingle(); modelHeader.worldTransform.M23 = input.ReadSingle(); modelHeader.worldTransform.M33 = input.ReadSingle(); modelHeader.worldTransform.M43 = input.ReadSingle(); modelHeader.materialsTableOffset = input.ReadUInt32(); modelHeader.materialsTableEntries = input.ReadUInt32(); modelHeader.materialsNameOffset = input.ReadUInt32(); modelHeader.verticesTableOffset = input.ReadUInt32(); modelHeader.verticesTableEntries = input.ReadUInt32(); data.Seek(header.backwardCompatibility > 6 ? 0x28 : 0x20, SeekOrigin.Current); modelHeader.skeletonOffset = input.ReadUInt32(); modelHeader.skeletonEntries = input.ReadUInt32(); modelHeader.skeletonNameOffset = input.ReadUInt32(); modelHeader.objectsNodeVisibilityOffset = input.ReadUInt32(); modelHeader.objectsNodeCount = input.ReadUInt32(); modelHeader.modelName = readString(input); modelHeader.objectsNodeNameEntries = input.ReadUInt32(); modelHeader.objectsNodeNameOffset = input.ReadUInt32(); input.ReadUInt32(); //0x0 modelHeader.metaDataPointerOffset = input.ReadUInt32(); model.transform = modelHeader.worldTransform; model.name = modelHeader.modelName; string[] objectName = new string[modelHeader.objectsNodeNameEntries]; data.Seek(modelHeader.objectsNodeNameOffset, SeekOrigin.Begin); int rootReferenceBit = input.ReadInt32(); //Radix tree uint rootLeftNode = input.ReadUInt16(); uint rootRightNode = input.ReadUInt16(); uint rootNameOffset = input.ReadUInt32(); for (int i = 0; i < modelHeader.objectsNodeNameEntries; i++) { int referenceBit = input.ReadInt32(); ushort leftNode = input.ReadUInt16(); ushort rightNode = input.ReadUInt16(); objectName[i] = readString(input); } //Materials for (int index = 0; index < modelHeader.materialsTableEntries; index++) { //Nota: As versões mais antigas tinham o Coordinator já no header do material. //As versões mais recentes tem uma seção reservada só pra ele, por isso possuem tamanho do header menor. if (header.backwardCompatibility < 0x21) data.Seek(modelHeader.materialsTableOffset + (index * 0x58), SeekOrigin.Begin); else data.Seek(modelHeader.materialsTableOffset + (index * 0x2c), SeekOrigin.Begin); RenderBase.OMaterial material = new RenderBase.OMaterial(); uint materialParametersOffset = input.ReadUInt32(); input.ReadUInt32(); //TODO input.ReadUInt32(); input.ReadUInt32(); uint textureCommandsOffset = input.ReadUInt32(); uint textureCommandsWordCount = input.ReadUInt32(); uint materialMapperOffset = 0; if (header.backwardCompatibility < 0x21) { materialMapperOffset = (uint)data.Position; data.Seek(0x30, SeekOrigin.Current); } else materialMapperOffset = input.ReadUInt32(); material.name0 = readString(input); material.name1 = readString(input); material.name2 = readString(input); material.name = readString(input); //Parameters //Same pointer of Materials section. Why? if (materialParametersOffset != 0) { data.Seek(materialParametersOffset, SeekOrigin.Begin); uint hash = input.ReadUInt32(); ushort materialFlags = input.ReadUInt16(); material.isFragmentLightEnabled = (materialFlags & 1) > 0; material.isVertexLightEnabled = (materialFlags & 2) > 0; material.isHemiSphereLightEnabled = (materialFlags & 4) > 0; material.isHemiSphereOcclusionEnabled = (materialFlags & 8) > 0; material.isFogEnabled = (materialFlags & 0x10) > 0; material.rasterization.isPolygonOffsetEnabled = (materialFlags & 0x20) > 0; ushort fragmentFlags = input.ReadUInt16(); material.fragmentShader.bump.isBumpRenormalize = (fragmentFlags & 1) > 0; material.fragmentShader.lighting.isClampHighLight = (fragmentFlags & 2) > 0; material.fragmentShader.lighting.isDistribution0Enabled = (fragmentFlags & 4) > 0; material.fragmentShader.lighting.isDistribution1Enabled = (fragmentFlags & 8) > 0; material.fragmentShader.lighting.isGeometryFactor0Enabled = (fragmentFlags & 0x10) > 0; material.fragmentShader.lighting.isGeometryFactor1Enabled = (fragmentFlags & 0x20) > 0; material.fragmentShader.lighting.isReflectionEnabled = (fragmentFlags & 0x40) > 0; RenderBase.OBlendMode blendMode = (RenderBase.OBlendMode)((fragmentFlags >> 10) & 3); input.ReadUInt32(); for (int i = 0; i < 3; i++) { RenderBase.OTextureCoordinator coordinator; uint projectionAndCamera = input.ReadUInt32(); coordinator.projection = (RenderBase.OTextureProjection)((projectionAndCamera >> 16) & 0xff); coordinator.referenceCamera = projectionAndCamera >> 24; coordinator.scaleU = input.ReadSingle(); coordinator.scaleV = input.ReadSingle(); coordinator.rotate = input.ReadSingle(); coordinator.translateU = input.ReadSingle(); coordinator.translateV = input.ReadSingle(); material.textureCoordinator[i] = coordinator; } material.lightSetIndex = input.ReadUInt16(); material.fogIndex = input.ReadUInt16(); material.materialColor.emission = MeshUtils.getColor(input); material.materialColor.ambient = MeshUtils.getColor(input); material.materialColor.diffuse = MeshUtils.getColor(input); material.materialColor.specular0 = MeshUtils.getColor(input); material.materialColor.specular1 = MeshUtils.getColor(input); material.materialColor.constant0 = MeshUtils.getColor(input); material.materialColor.constant1 = MeshUtils.getColor(input); material.materialColor.constant2 = MeshUtils.getColor(input); material.materialColor.constant3 = MeshUtils.getColor(input); material.materialColor.constant4 = MeshUtils.getColor(input); material.materialColor.constant5 = MeshUtils.getColor(input); material.fragmentOperation.blend.blendColor = MeshUtils.getColor(input); material.materialColor.colorScale = input.ReadSingle(); input.ReadUInt32(); //TODO: Figure out input.ReadUInt32(); input.ReadUInt32(); input.ReadUInt32(); input.ReadUInt32(); input.ReadUInt32(); uint fragmentData = input.ReadUInt32(); material.fragmentShader.bump.texture = (RenderBase.OBumpTexture)(fragmentData >> 24); material.fragmentShader.bump.mode = (RenderBase.OBumpMode)((fragmentData >> 16) & 0xff); material.fragmentShader.lighting.fresnelConfig = (RenderBase.OFresnelConfig)((fragmentData >> 8) & 0xff); material.fragmentShader.layerConfig = fragmentData & 0xff; //Some Fragment Lighting related commands... This seems a bit out of place. long position = data.Position; PICACommandReader fshLightingCommands = new PICACommandReader(data, 6, true); PICACommand.fragmentSamplerAbsolute sAbs = fshLightingCommands.getReflectanceSamplerAbsolute(); material.fragmentShader.lighting.reflectanceRSampler.isAbsolute = sAbs.r; material.fragmentShader.lighting.reflectanceGSampler.isAbsolute = sAbs.g; material.fragmentShader.lighting.reflectanceBSampler.isAbsolute = sAbs.b; material.fragmentShader.lighting.distribution0Sampler.isAbsolute = sAbs.d0; material.fragmentShader.lighting.distribution1Sampler.isAbsolute = sAbs.d1; material.fragmentShader.lighting.fresnelSampler.isAbsolute = sAbs.fresnel; PICACommand.fragmentSamplerInput sInput = fshLightingCommands.getReflectanceSamplerInput(); material.fragmentShader.lighting.reflectanceRSampler.input = sInput.r; material.fragmentShader.lighting.reflectanceGSampler.input = sInput.g; material.fragmentShader.lighting.reflectanceBSampler.input = sInput.b; material.fragmentShader.lighting.distribution0Sampler.input = sInput.d0; material.fragmentShader.lighting.distribution1Sampler.input = sInput.d1; material.fragmentShader.lighting.fresnelSampler.input = sInput.fresnel; PICACommand.fragmentSamplerScale sScale = fshLightingCommands.getReflectanceSamplerScale(); material.fragmentShader.lighting.reflectanceRSampler.scale = sScale.r; material.fragmentShader.lighting.reflectanceGSampler.scale = sScale.g; material.fragmentShader.lighting.reflectanceBSampler.scale = sScale.b; material.fragmentShader.lighting.distribution0Sampler.scale = sScale.d0; material.fragmentShader.lighting.distribution1Sampler.scale = sScale.d1; material.fragmentShader.lighting.fresnelSampler.scale = sScale.fresnel; data.Seek(position + (4 * 6), SeekOrigin.Begin); //Just to be sure ;) RenderBase.OConstantColor[] constantList = new RenderBase.OConstantColor[6]; uint constantColor = input.ReadUInt32(); for (int i = 0; i < 6; i++) constantList[i] = (RenderBase.OConstantColor)((constantColor >> (i * 4)) & 0xf); material.rasterization.polygonOffsetUnit = input.ReadSingle(); uint fshCommandsOffset = input.ReadUInt32(); uint fshCommandsWordCount = input.ReadUInt32(); input.ReadUInt32(); material.fragmentShader.lighting.distribution0Sampler.tableName = readString(input); material.fragmentShader.lighting.distribution1Sampler.tableName = readString(input); material.fragmentShader.lighting.fresnelSampler.tableName = readString(input); material.fragmentShader.lighting.reflectanceRSampler.tableName = readString(input); material.fragmentShader.lighting.reflectanceGSampler.tableName = readString(input); material.fragmentShader.lighting.reflectanceBSampler.tableName = readString(input); material.fragmentShader.lighting.distribution0Sampler.samplerName = readString(input); material.fragmentShader.lighting.distribution1Sampler.samplerName = readString(input); material.fragmentShader.lighting.fresnelSampler.samplerName = readString(input); material.fragmentShader.lighting.reflectanceRSampler.samplerName = readString(input); material.fragmentShader.lighting.reflectanceGSampler.samplerName = readString(input); material.fragmentShader.lighting.reflectanceBSampler.samplerName = readString(input); material.shaderReference = new RenderBase.OReference(readString(input)); material.modelReference = new RenderBase.OReference(readString(input)); //User Data if (header.backwardCompatibility > 6) { uint metaDataPointerOffset = input.ReadUInt32(); if (metaDataPointerOffset != 0) { data.Seek(metaDataPointerOffset, SeekOrigin.Begin); material.userData = getMetaData(input); } } //Mapper data.Seek(materialMapperOffset, SeekOrigin.Begin); for (int i = 0; i < 3; i++) { RenderBase.OTextureMapper mapper; uint wrapAndMagFilter = input.ReadUInt32(); uint levelOfDetailAndMinFilter = input.ReadUInt32(); mapper.wrapU = (RenderBase.OTextureWrap)((wrapAndMagFilter >> 8) & 0xff); mapper.wrapV = (RenderBase.OTextureWrap)((wrapAndMagFilter >> 16) & 0xff); mapper.magFilter = (RenderBase.OTextureMagFilter)(wrapAndMagFilter >> 24); mapper.minFilter = (RenderBase.OTextureMinFilter)(levelOfDetailAndMinFilter & 0xff); mapper.minLOD = (levelOfDetailAndMinFilter >> 8) & 0xff; //max 232 mapper.LODBias = input.ReadSingle(); mapper.borderColor = MeshUtils.getColor(input); material.textureMapper[i] = mapper; } //Fragment Shader commands data.Seek(fshCommandsOffset, SeekOrigin.Begin); PICACommandReader fshCommands = new PICACommandReader(data, fshCommandsWordCount); for (byte stage = 0; stage < 6; stage++) material.fragmentShader.textureCombiner[stage] = fshCommands.getTevStage(stage); material.fragmentShader.bufferColor = fshCommands.getFragmentBufferColor(); material.fragmentOperation.blend = fshCommands.getBlendOperation(); material.fragmentOperation.blend.logicalOperation = fshCommands.getColorLogicOperation(); material.fragmentShader.alphaTest = fshCommands.getAlphaTest(); material.fragmentOperation.stencil = fshCommands.getStencilTest(); material.fragmentOperation.depth = fshCommands.getDepthTest(); material.rasterization.cullMode = fshCommands.getCullMode(); material.fragmentOperation.blend.mode = blendMode; } model.material.Add(material); } //Skeleton data.Seek(modelHeader.skeletonOffset, SeekOrigin.Begin); for (int index = 0; index < modelHeader.skeletonEntries; index++) { RenderBase.OBone bone = new RenderBase.OBone(); uint boneFlags = input.ReadUInt32(); bone.billboardMode = (RenderBase.OBillboardMode)((boneFlags >> 16) & 0xf); bone.isSegmentScaleCompensate = (boneFlags & 0x00400000) > 0; bone.parentId = input.ReadInt16(); ushort boneSpacer = input.ReadUInt16(); bone.scale = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); bone.rotation = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); bone.translation = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); bone.absoluteScale = new RenderBase.OVector3(bone.scale); RenderBase.OMatrix boneMatrix = new RenderBase.OMatrix(); boneMatrix.M11 = input.ReadSingle(); boneMatrix.M21 = input.ReadSingle(); boneMatrix.M31 = input.ReadSingle(); boneMatrix.M41 = input.ReadSingle(); boneMatrix.M12 = input.ReadSingle(); boneMatrix.M22 = input.ReadSingle(); boneMatrix.M32 = input.ReadSingle(); boneMatrix.M42 = input.ReadSingle(); boneMatrix.M13 = input.ReadSingle(); boneMatrix.M23 = input.ReadSingle(); boneMatrix.M33 = input.ReadSingle(); boneMatrix.M43 = input.ReadSingle(); bone.name = readString(input); uint metaDataPointerOffset = input.ReadUInt32(); if (metaDataPointerOffset != 0) { long position = data.Position; data.Seek(metaDataPointerOffset, SeekOrigin.Begin); bone.userData = getMetaData(input); data.Seek(position, SeekOrigin.Begin); } model.skeleton.Add(bone); } List<RenderBase.OMatrix> skeletonTransform = new List<RenderBase.OMatrix>(); for (int index = 0; index < modelHeader.skeletonEntries; index++) { RenderBase.OMatrix transform = new RenderBase.OMatrix(); transformSkeleton(model.skeleton, index, ref transform); skeletonTransform.Add(transform); } data.Seek(modelHeader.objectsNodeVisibilityOffset, SeekOrigin.Begin); uint nodeVisibility = input.ReadUInt32(); //Vertices header data.Seek(modelHeader.verticesTableOffset, SeekOrigin.Begin); List<bchObjectEntry> objects = new List<bchObjectEntry>(); for (int index = 0; index < modelHeader.verticesTableEntries; index++) { bchObjectEntry objectEntry = new bchObjectEntry(); objectEntry.materialId = input.ReadUInt16(); ushort flags = input.ReadUInt16(); if (header.backwardCompatibility != 8) objectEntry.isSilhouette = (flags & 1) > 0; objectEntry.nodeId = input.ReadUInt16(); objectEntry.renderPriority = input.ReadUInt16(); objectEntry.vshAttributesBufferCommandsOffset = input.ReadUInt32(); //Buffer 0 objectEntry.vshAttributesBufferCommandsWordCount = input.ReadUInt32(); objectEntry.facesHeaderOffset = input.ReadUInt32(); objectEntry.facesHeaderEntries = input.ReadUInt32(); objectEntry.vshExtraAttributesBufferCommandsOffset = input.ReadUInt32(); //Buffers 1-11 objectEntry.vshExtraAttributesBufferCommandsWordCount = input.ReadUInt32(); objectEntry.centerVector = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); objectEntry.flagsOffset = input.ReadUInt32(); input.ReadUInt32(); //ex: 0x0 fixo objectEntry.boundingBoxOffset = input.ReadUInt32(); objects.Add(objectEntry); } for (int objIndex = 0; objIndex < objects.Count; objIndex++) { if (objects[objIndex].isSilhouette) continue; //TODO: Figure out for what "Silhouette" is used. RenderBase.OMesh obj = new RenderBase.OMesh(); obj.materialId = objects[objIndex].materialId; obj.renderPriority = objects[objIndex].renderPriority; if (objects[objIndex].nodeId < objectName.Length) obj.name = objectName[objects[objIndex].nodeId]; else obj.name = "mesh" + objIndex.ToString(); obj.isVisible = (nodeVisibility & (1 << objects[objIndex].nodeId)) > 0; //Vertices data.Seek(objects[objIndex].vshAttributesBufferCommandsOffset, SeekOrigin.Begin); PICACommandReader vshCommands = new PICACommandReader(data, objects[objIndex].vshAttributesBufferCommandsWordCount); Stack<float> vshAttributesUniformReg6 = vshCommands.getVSHFloatUniformData(6); Stack<float> vshAttributesUniformReg7 = vshCommands.getVSHFloatUniformData(7); RenderBase.OVector4 positionOffset = new RenderBase.OVector4( vshAttributesUniformReg6.Pop(), vshAttributesUniformReg6.Pop(), vshAttributesUniformReg6.Pop(), vshAttributesUniformReg6.Pop()); float texture0Scale = vshAttributesUniformReg7.Pop(); float texture1Scale = vshAttributesUniformReg7.Pop(); float texture2Scale = vshAttributesUniformReg7.Pop(); float boneWeightScale = vshAttributesUniformReg7.Pop(); float positionScale = vshAttributesUniformReg7.Pop(); float normalScale = vshAttributesUniformReg7.Pop(); float tangentScale = vshAttributesUniformReg7.Pop(); float colorScale = vshAttributesUniformReg7.Pop(); //Faces uint facesCount = objects[objIndex].facesHeaderEntries; bool hasFaces = facesCount > 0; uint facesTableOffset = 0; if (!hasFaces) { data.Seek(modelHeader.verticesTableOffset + modelHeader.verticesTableEntries * 0x38, SeekOrigin.Begin); data.Seek(objIndex * 0x1c + 0x10, SeekOrigin.Current); facesTableOffset = input.ReadUInt32(); facesCount = input.ReadUInt32(); } for (uint f = 0; f < facesCount; f++) { RenderBase.OSkinningMode skinningMode = RenderBase.OSkinningMode.none; List<ushort> nodeList = new List<ushort>(); uint idxBufferOffset; PICACommand.indexBufferFormat idxBufferFormat; uint idxBufferTotalVertices; if (hasFaces) { uint baseOffset = objects[objIndex].facesHeaderOffset + f * 0x34; data.Seek(baseOffset, SeekOrigin.Begin); skinningMode = (RenderBase.OSkinningMode)input.ReadUInt16(); ushort nodeIdEntries = input.ReadUInt16(); for (int n = 0; n < nodeIdEntries; n++) nodeList.Add(input.ReadUInt16()); data.Seek(baseOffset + 0x2c, SeekOrigin.Begin); uint faceHeaderOffset = input.ReadUInt32(); uint faceHeaderWordCount = input.ReadUInt32(); data.Seek(faceHeaderOffset, SeekOrigin.Begin); PICACommandReader idxCommands = new PICACommandReader(data, faceHeaderWordCount); idxBufferOffset = idxCommands.getIndexBufferAddress(); idxBufferFormat = idxCommands.getIndexBufferFormat(); idxBufferTotalVertices = idxCommands.getIndexBufferTotalVertices(); } else { data.Seek(facesTableOffset + f * 8, SeekOrigin.Begin); idxBufferOffset = input.ReadUInt32(); idxBufferFormat = PICACommand.indexBufferFormat.unsignedShort; idxBufferTotalVertices = input.ReadUInt32(); } //Carregamento de dados relacionados ao Vertex Shader uint vshAttributesBufferOffset = vshCommands.getVSHAttributesBufferAddress(0); uint vshAttributesBufferStride = vshCommands.getVSHAttributesBufferStride(0); uint vshTotalAttributes = vshCommands.getVSHTotalAttributes(0); PICACommand.vshAttribute[] vshMainAttributesBufferPermutation = vshCommands.getVSHAttributesBufferPermutation(); uint[] vshAttributesBufferPermutation = vshCommands.getVSHAttributesBufferPermutation(0); PICACommand.attributeFormat[] vshAttributesBufferFormat = vshCommands.getVSHAttributesBufferFormat(); for (int attribute = 0; attribute < vshTotalAttributes; attribute++) { switch (vshMainAttributesBufferPermutation[vshAttributesBufferPermutation[attribute]]) { case PICACommand.vshAttribute.normal: obj.hasNormal = true; break; case PICACommand.vshAttribute.tangent: obj.hasTangent = true; break; case PICACommand.vshAttribute.color: obj.hasColor = true; break; case PICACommand.vshAttribute.textureCoordinate0: obj.texUVCount = Math.Max(obj.texUVCount, 1); break; case PICACommand.vshAttribute.textureCoordinate1: obj.texUVCount = Math.Max(obj.texUVCount, 2); break; case PICACommand.vshAttribute.textureCoordinate2: obj.texUVCount = Math.Max(obj.texUVCount, 3); break; } } if (nodeList.Count > 0) { obj.hasNode = true; obj.hasWeight = true; } data.Seek(idxBufferOffset, SeekOrigin.Begin); for (int faceIndex = 0; faceIndex < idxBufferTotalVertices; faceIndex++) { ushort index = 0; switch (idxBufferFormat) { case PICACommand.indexBufferFormat.unsignedShort: index = input.ReadUInt16(); break; case PICACommand.indexBufferFormat.unsignedByte: index = input.ReadByte(); break; } long dataPosition = data.Position; long vertexOffset = vshAttributesBufferOffset + (index * vshAttributesBufferStride); data.Seek(vertexOffset, SeekOrigin.Begin); RenderBase.OVertex vertex = new RenderBase.OVertex(); vertex.diffuseColor = 0xffffffff; for (int attribute = 0; attribute < vshTotalAttributes; attribute++) { //gdkchan self note: The Attribute type flags are used for something else on Bone Weight (and bone index?) PICACommand.vshAttribute att = vshMainAttributesBufferPermutation[vshAttributesBufferPermutation[attribute]]; PICACommand.attributeFormat format = vshAttributesBufferFormat[vshAttributesBufferPermutation[attribute]]; if (att == PICACommand.vshAttribute.boneWeight) format.type = PICACommand.attributeFormatType.unsignedByte; RenderBase.OVector4 vector = getVector(input, format); switch (att) { case PICACommand.vshAttribute.position: float x = (vector.x * positionScale) + positionOffset.x; float y = (vector.y * positionScale) + positionOffset.y; float z = (vector.z * positionScale) + positionOffset.z; vertex.position = new RenderBase.OVector3(x, y, z); break; case PICACommand.vshAttribute.normal: vertex.normal = new RenderBase.OVector3(vector.x * normalScale, vector.y * normalScale, vector.z * normalScale); break; case PICACommand.vshAttribute.tangent: vertex.tangent = new RenderBase.OVector3(vector.x * tangentScale, vector.y * tangentScale, vector.z * tangentScale); break; case PICACommand.vshAttribute.color: uint r = MeshUtils.saturate((vector.x * colorScale) * 0xff); uint g = MeshUtils.saturate((vector.y * colorScale) * 0xff); uint b = MeshUtils.saturate((vector.z * colorScale) * 0xff); uint a = MeshUtils.saturate((vector.w * colorScale) * 0xff); vertex.diffuseColor = b | (g << 8) | (r << 16) | (a << 24); break; case PICACommand.vshAttribute.textureCoordinate0: vertex.texture0 = new RenderBase.OVector2(vector.x * texture0Scale, vector.y * texture0Scale); break; case PICACommand.vshAttribute.textureCoordinate1: vertex.texture1 = new RenderBase.OVector2(vector.x * texture1Scale, vector.y * texture1Scale); break; case PICACommand.vshAttribute.textureCoordinate2: vertex.texture2 = new RenderBase.OVector2(vector.x * texture2Scale, vector.y * texture2Scale); break; case PICACommand.vshAttribute.boneIndex: vertex.node.Add(nodeList[(int)vector.x]); if (skinningMode == RenderBase.OSkinningMode.smoothSkinning) { if (format.attributeLength > 0) vertex.node.Add(nodeList[(int)vector.y]); if (format.attributeLength > 1) vertex.node.Add(nodeList[(int)vector.z]); if (format.attributeLength > 2) vertex.node.Add(nodeList[(int)vector.w]); } break; case PICACommand.vshAttribute.boneWeight: vertex.weight.Add(vector.x * boneWeightScale); if (skinningMode == RenderBase.OSkinningMode.smoothSkinning) { if (format.attributeLength > 0) vertex.weight.Add(vector.y * boneWeightScale); if (format.attributeLength > 1) vertex.weight.Add(vector.z * boneWeightScale); if (format.attributeLength > 2) vertex.weight.Add(vector.w * boneWeightScale); } break; } } //If the node list have 4 or less bones, then there is no need to store the indices per vertex //Instead, the entire list is used, since it supports up to 4 bones. if (vertex.node.Count == 0 && nodeList.Count <= 4) { for (int n = 0; n < nodeList.Count; n++) vertex.node.Add(nodeList[n]); if (vertex.weight.Count == 0) vertex.weight.Add(1); } if (skinningMode != RenderBase.OSkinningMode.smoothSkinning && vertex.node.Count > 0) { //Note: Rigid skinning can have only one bone per vertex //Note2: Vertex with Rigid skinning seems to be always have meshes centered, so is necessary to make them follow the skeleton if (vertex.weight.Count == 0) vertex.weight.Add(1); vertex.position = RenderBase.OVector3.transform(vertex.position, skeletonTransform[vertex.node[0]]); } MeshUtils.calculateBounds(model, vertex); obj.vertices.Add(vertex); data.Seek(dataPosition, SeekOrigin.Begin); } } //Bounding box if (objects[objIndex].boundingBoxOffset != 0) { data.Seek(objects[objIndex].boundingBoxOffset, SeekOrigin.Begin); uint bBoxDataOffset = input.ReadUInt32(); uint bBoxEntries = input.ReadUInt32(); uint bBoxNameOffset = input.ReadUInt32(); for (int index = 0; index < bBoxEntries; index++) { data.Seek(bBoxDataOffset + (index * 0xc), SeekOrigin.Begin); RenderBase.OOrientedBoundingBox bBox = new RenderBase.OOrientedBoundingBox(); bBox.name = readString(input); uint flags = input.ReadUInt32(); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); bBox.centerPosition = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); bBox.orientationMatrix.M11 = input.ReadSingle(); bBox.orientationMatrix.M21 = input.ReadSingle(); bBox.orientationMatrix.M31 = input.ReadSingle(); bBox.orientationMatrix.M12 = input.ReadSingle(); bBox.orientationMatrix.M22 = input.ReadSingle(); bBox.orientationMatrix.M32 = input.ReadSingle(); bBox.orientationMatrix.M13 = input.ReadSingle(); bBox.orientationMatrix.M23 = input.ReadSingle(); bBox.orientationMatrix.M33 = input.ReadSingle(); bBox.size = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); obj.boundingBox.Add(bBox); } } model.mesh.Add(obj); } if (modelHeader.metaDataPointerOffset != 0) { data.Seek(modelHeader.metaDataPointerOffset, SeekOrigin.Begin); model.userData = getMetaData(input); } for (int index = 0; index < modelHeader.skeletonEntries; index++) { scaleSkeleton(model.skeleton, index, index); } models.model.Add(model); } data.Close(); return models; }
public override void Read(string filename) { bchHeader header = new bchHeader(); FileData f = new FileData(filename); f.Endian = System.IO.Endianness.Little; f.skip(4); header.backwardCompatibility = f.readByte(); header.forwardCompatibility = f.readByte(); header.version = f.readShort(); header.mainHeaderOffset = f.readInt(); header.stringTableOffset = f.readInt(); header.gpuCommandsOffset = f.readInt(); header.dataOffset = f.readInt(); if (header.backwardCompatibility > 0x20) { header.dataExtendedOffset = f.readInt(); } header.relocationTableOffset = f.readInt(); header.mainHeaderLength = f.readInt(); header.stringTableLength = f.readInt(); header.gpuCommandsLength = f.readInt(); header.dataLength = f.readInt(); if (header.backwardCompatibility > 0x20) { header.dataExtendedLength = f.readInt(); } header.relocationTableLength = f.readInt(); header.uninitializedDataSectionLength = f.readInt(); header.uninitializedDescriptionSectionLength = f.readInt(); if (header.backwardCompatibility > 7) { header.flags = f.readShort(); header.addressCount = f.readShort(); } // Relocation table for (int i = 0; i < header.relocationTableLength; i += 4) { f.seek(header.relocationTableOffset + i); int val = f.readInt(); int off = val & 0x1FFFFFF; byte flag = (byte)(val >> 25); switch (flag) { case 0: f.seek((off * 4) + header.mainHeaderOffset); f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.mainHeaderOffset); break; case 1: f.seek(off + header.mainHeaderOffset); f.writeInt((off) + header.mainHeaderOffset, f.readInt() + header.stringTableOffset); break; case 2: f.seek((off * 4) + header.mainHeaderOffset); f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.gpuCommandsOffset); break; case 0xc: f.seek((off * 4) + header.mainHeaderOffset); f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.dataOffset); break; } f.seek((off * 4) + header.gpuCommandsOffset); if (header.backwardCompatibility < 6) { switch (flag) { case 0x23: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break; //Texture case 0x25: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break; //Vertex //case 0x26: f.writeInt((off * 4) + header.gpuCommandsOffset, ((f.readInt() + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode case 0x27: f.writeInt((off * 4) + header.gpuCommandsOffset, (f.readInt() + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode } } else if (header.backwardCompatibility < 8) { switch (flag) { case 0x24: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break; //Texture case 0x26: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break; //Vertex //case 0x27: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode case 0x28: f.writeInt((off * 4) + header.gpuCommandsOffset, (f.readInt() + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode } } else if (header.backwardCompatibility < 0x21) { switch (flag) { case 0x25: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break; //Texture case 0x27: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break; //Vertex //case 0x28: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode case 0x29: f.writeInt((off * 4) + header.gpuCommandsOffset, (f.readInt() + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode } } else { switch (flag) { case 0x25: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break; //Texture case 0x26: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break; //Vertex relative to Data Offset //case 0x27: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Offset case 0x28: f.writeInt((off * 4) + header.gpuCommandsOffset, (f.readInt() + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode relative to Data Offset case 0x2b: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataExtendedOffset); break; //Vertex relative to Data Extended Offset //case 0x2c: writer.Write(((peek(input) + header.dataExtendedOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Extended Offset case 0x2d: f.writeInt((off * 4) + header.gpuCommandsOffset, (f.readInt() + header.dataExtendedOffset) & 0x7fffffff); break; //Index 8 bits mode relative to Data Extended Offset } } } // Content Header f.seek(header.mainHeaderOffset); bchContentHeader content = new bchContentHeader(); { content.modelsPointerTableOffset = f.readInt(); content.modelsPointerTableEntries = f.readInt(); content.modelsNameOffset = f.readInt(); content.materialsPointerTableOffset = f.readInt(); content.materialsPointerTableEntries = f.readInt(); content.materialsNameOffset = f.readInt(); content.shadersPointerTableOffset = f.readInt(); content.shadersPointerTableEntries = f.readInt(); content.shadersNameOffset = f.readInt(); content.texturesPointerTableOffset = f.readInt(); content.texturesPointerTableEntries = f.readInt(); content.texturesNameOffset = f.readInt(); content.materialsLUTPointerTableOffset = f.readInt(); content.materialsLUTPointerTableEntries = f.readInt(); content.materialsLUTNameOffset = f.readInt(); content.lightsPointerTableOffset = f.readInt(); content.lightsPointerTableEntries = f.readInt(); content.lightsNameOffset = f.readInt(); content.camerasPointerTableOffset = f.readInt(); content.camerasPointerTableEntries = f.readInt(); content.camerasNameOffset = f.readInt(); content.fogsPointerTableOffset = f.readInt(); content.fogsPointerTableEntries = f.readInt(); content.fogsNameOffset = f.readInt(); content.skeletalAnimationsPointerTableOffset = f.readInt(); content.skeletalAnimationsPointerTableEntries = f.readInt(); content.skeletalAnimationsNameOffset = f.readInt(); content.materialAnimationsPointerTableOffset = f.readInt(); content.materialAnimationsPointerTableEntries = f.readInt(); content.materialAnimationsNameOffset = f.readInt(); content.visibilityAnimationsPointerTableOffset = f.readInt(); content.visibilityAnimationsPointerTableEntries = f.readInt(); content.visibilityAnimationsNameOffset = f.readInt(); content.lightAnimationsPointerTableOffset = f.readInt(); content.lightAnimationsPointerTableEntries = f.readInt(); content.lightAnimationsNameOffset = f.readInt(); content.cameraAnimationsPointerTableOffset = f.readInt(); content.cameraAnimationsPointerTableEntries = f.readInt(); content.cameraAnimationsNameOffset = f.readInt(); content.fogAnimationsPointerTableOffset = f.readInt(); content.fogAnimationsPointerTableEntries = f.readInt(); content.fogAnimationsNameOffset = f.readInt(); content.scenePointerTableOffset = f.readInt(); content.scenePointerTableEntries = f.readInt(); content.sceneNameOffset = f.readInt(); } //Shaders (unused for now, until someone wants to add them) for (int index = 0; index < content.shadersPointerTableEntries; index++) { f.seek(content.shadersPointerTableOffset + (index * 4)); int dataOffset = f.readInt(); f.seek(dataOffset); int shaderDataOffset = f.readInt(); int shaderDataLength = f.readInt(); } // Textures // WIP Section for (int index = 0; index < content.texturesPointerTableEntries; index++) { f.seek(content.texturesPointerTableOffset + (index * 4)); int dOffset = f.readInt(); f.seek(dOffset); int textureCommandsOffset = f.readInt(); int textureCommandsWordCount = f.readInt(); f.seek(f.pos() + 0x14); String textureName = f.readString(f.readInt(), -1); //Debug.WriteLine("gpuCommandOffset: " + header.gpuCommandsOffset.ToString("X")); f.seek(textureCommandsOffset); //Debug.WriteLine("textureCommandOffset: " + textureCommandsOffset.ToString("X")); BCH_Texture tex = new BCH_Texture(); textures.Add(textureName, tex); tex.height = f.readShort(); tex.width = f.readShort(); f.skip(12); int doffset = f.readInt(); //Debug.WriteLine("doffset: " + doffset.ToString("X")); f.skip(4); tex.type = f.readInt(); tex.data = f.getSection(doffset, f.size() - doffset); if (tex.type == 12) { tex.display = NUT.loadImage(Pixel.decodeETC(tex.data, tex.width, tex.height)); } } // Model data for (int modelIndex = 0; modelIndex < content.modelsPointerTableEntries; modelIndex++) { f.seek(content.modelsPointerTableOffset + (modelIndex * 4)); int objectsHeaderOffset = f.readInt(); // Objects f.seek(objectsHeaderOffset); BCH_Model model = new BCH_Model(); models.Add(model); model.flags = f.readByte(); model.skeletonScaleType = f.readByte(); model.silhouetteMaterialEntries = f.readShort(); model.worldTransform = new Matrix4(f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat() , f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat() , f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat() , 0, 0, 0, 1); model.materialsTableOffset = f.readInt(); model.materialsTableEntries = f.readInt(); model.materialsNameOffset = f.readInt(); model.verticesTableOffset = f.readInt(); //Debug.WriteLine("Mesh Count: " + f.pos().ToString("X")); model.verticesTableEntries = f.readInt(); f.skip(0x28); model.skeletonOffset = f.readInt(); model.skeletonEntries = f.readInt(); model.skeletonNameOffset = f.readInt(); model.objectsNodeVisibilityOffset = f.readInt(); model.objectsNodeCount = f.readInt(); model.name = f.readString(f.readInt(), -1); model.objectsNodeNameEntries = f.readInt(); model.objectsNodeNameOffset = f.readInt(); f.readInt(); //0x0 model.metaDataPointerOffset = f.readInt(); f.seek(model.objectsNodeVisibilityOffset); int nodeVisibility = f.readInt(); string[] objectName = new string[model.objectsNodeNameEntries]; f.seek(model.objectsNodeNameOffset); int rootReferenceBit = f.readInt(); //Radix tree int rootLeftNode = f.readShort(); int rootRightNode = f.readShort(); int rootNameOffset = f.readInt() + header.mainHeaderOffset; for (int i = 0; i < model.objectsNodeNameEntries; i++) { int referenceBit = f.readInt(); short leftNode = (short)f.readShort(); short rightNode = (short)f.readShort(); objectName[i] = f.readString(f.readInt(), -1); //Debug.WriteLine(objectName[i]); } // Materials // NOTE: MATERIALS AND OBJECT SECTIONS ARE REALLY MESSY ATM String[] materialNames = new String[model.materialsTableEntries]; for (int index = 0; index < model.materialsTableEntries; index++) { f.seek(model.materialsTableOffset + (index * 0x2c)); int materialParametersOffset = f.readInt(); f.readInt(); f.readInt(); f.readInt(); int textureCommandsOffset = f.readInt(); int textureCommandsWordCount = f.readInt(); int materialMapperOffset = f.readInt(); materialNames[index] = f.readString(f.readInt(), -1); } // Object Descriptions... // Assumes MBN is already loaded for now f.seek(model.verticesTableOffset); List <objDes> objDescriptors = new List <objDes>(); Debug.WriteLine(model.name); if (mbn == null) { mbn = new Smash_Forge.MBN(); for (int index = 0; index < model.verticesTableEntries; index++) { mbn.mesh.Add(new MBN.Mesh()); } mbn.PreRender(); } for (int index = 0; index < mbn.mesh.Count; index++) { int i = f.readShort(); if (index > mbn.mesh.Count) { break; } if (i > materialNames.Length) { break; } mbn.mesh[index].texId = textures[materialNames[i]].display; Console.WriteLine("Tex index" + mbn.mesh[index].texId); f.skip(2); // flags int nameId = f.readShort(); mbn.mesh[index].name = objectName[nameId]; // node visibility TODO: finish... //mbn.mesh[index].isVisible = ((nodeVisibility & (1 << nameId)) > 0); mbn.mesh[index].renderPriority = f.readShort(); objDes des = new objDes(); objDescriptors.Add(des); des.vshAttBufferCommandOffset = f.readInt(); des.vshAttBufferCommandCount = f.readInt(); des.faceOffset = f.readInt(); des.faceCount = f.readInt(); des.vshAttBufferCommandOffsetEx = f.readInt(); des.vshAttBufferCommandCountEx = f.readInt(); f.skip(12); // center vector f.skip(4); // flagsOffset f.skip(4); // 0? f.readInt(); //bbOffsets[i] = + mainheaderOffset //Debug.WriteLine(des.vshAttBufferCommandOffset.ToString("X")); } //Skeleton f.seek(model.skeletonOffset); for (int index = 0; index < model.skeletonEntries; index++) { Bone bone = new Smash_Forge.Bone(model.skeleton); int boneFlags = f.readInt(); bone.parentIndex = (short)f.readShort(); short boneSpace = (short)f.readShort(); bone.scale = new float[3]; bone.rotation = new float[3]; bone.position = new float[3]; bone.scale[0] = f.readFloat(); bone.scale[1] = f.readFloat(); bone.scale[2] = f.readFloat(); bone.rotation[0] = f.readFloat(); bone.rotation[1] = f.readFloat(); bone.rotation[2] = f.readFloat(); bone.position[0] = f.readFloat(); bone.position[1] = f.readFloat(); bone.position[2] = f.readFloat(); // bone matrix... not really needed to be stored per say f.skip(4 * 4 * 3); bone.Text = f.readString(f.readInt(), -1); f.skip(4); // Meta data model.skeleton.bones.Add(bone); } model.skeleton.reset(); } }
public static AnimationGroupNode Read(string filename) { bchHeader header = new bchHeader(); FileData f = new FileData(filename); f.Endian = System.IO.Endianness.Little; f.skip(4); header.backwardCompatibility = f.readByte(); header.forwardCompatibility = f.readByte(); header.version = f.readUShort(); header.mainHeaderOffset = f.readInt(); header.stringTableOffset = f.readInt(); header.gpuCommandsOffset = f.readInt(); header.dataOffset = f.readInt(); if (header.backwardCompatibility > 0x20) { header.dataExtendedOffset = f.readInt(); } header.relocationTableOffset = f.readInt(); header.mainHeaderLength = f.readInt(); header.stringTableLength = f.readInt(); header.gpuCommandsLength = f.readInt(); header.dataLength = f.readInt(); if (header.backwardCompatibility > 0x20) { header.dataExtendedLength = f.readInt(); } header.relocationTableLength = f.readInt(); header.uninitializedDataSectionLength = f.readInt(); header.uninitializedDescriptionSectionLength = f.readInt(); if (header.backwardCompatibility > 7) { header.flags = f.readUShort(); header.addressCount = f.readUShort(); } // Relocation table for (int i = 0; i < header.relocationTableLength; i += 4) { f.seek(header.relocationTableOffset + i); int val = f.readInt(); int off = val & 0x1FFFFFF; byte flag = (byte)(val >> 25); switch (flag) { case 0: f.seek((off * 4) + header.mainHeaderOffset); f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.mainHeaderOffset); break; case 1: f.seek(off + header.mainHeaderOffset); f.writeInt((off) + header.mainHeaderOffset, f.readInt() + header.stringTableOffset); break; case 2: f.seek((off * 4) + header.mainHeaderOffset); f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.gpuCommandsOffset); break; case 0xc: f.seek((off * 4) + header.mainHeaderOffset); f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.dataOffset); break; } } // Content Header f.seek(header.mainHeaderOffset); bchContentHeader content = new bchContentHeader(); { content.modelsPointerTableOffset = f.readInt(); content.modelsPointerTableEntries = f.readInt(); content.modelsNameOffset = f.readInt(); content.materialsPointerTableOffset = f.readInt(); content.materialsPointerTableEntries = f.readInt(); content.materialsNameOffset = f.readInt(); content.shadersPointerTableOffset = f.readInt(); content.shadersPointerTableEntries = f.readInt(); content.shadersNameOffset = f.readInt(); content.texturesPointerTableOffset = f.readInt(); content.texturesPointerTableEntries = f.readInt(); content.texturesNameOffset = f.readInt(); content.materialsLUTPointerTableOffset = f.readInt(); content.materialsLUTPointerTableEntries = f.readInt(); content.materialsLUTNameOffset = f.readInt(); content.lightsPointerTableOffset = f.readInt(); content.lightsPointerTableEntries = f.readInt(); content.lightsNameOffset = f.readInt(); content.camerasPointerTableOffset = f.readInt(); content.camerasPointerTableEntries = f.readInt(); content.camerasNameOffset = f.readInt(); content.fogsPointerTableOffset = f.readInt(); content.fogsPointerTableEntries = f.readInt(); content.fogsNameOffset = f.readInt(); content.skeletalAnimationsPointerTableOffset = f.readInt(); content.skeletalAnimationsPointerTableEntries = f.readInt(); content.skeletalAnimationsNameOffset = f.readInt(); content.materialAnimationsPointerTableOffset = f.readInt(); content.materialAnimationsPointerTableEntries = f.readInt(); content.materialAnimationsNameOffset = f.readInt(); content.visibilityAnimationsPointerTableOffset = f.readInt(); content.visibilityAnimationsPointerTableEntries = f.readInt(); content.visibilityAnimationsNameOffset = f.readInt(); content.lightAnimationsPointerTableOffset = f.readInt(); content.lightAnimationsPointerTableEntries = f.readInt(); content.lightAnimationsNameOffset = f.readInt(); content.cameraAnimationsPointerTableOffset = f.readInt(); content.cameraAnimationsPointerTableEntries = f.readInt(); content.cameraAnimationsNameOffset = f.readInt(); content.fogAnimationsPointerTableOffset = f.readInt(); content.fogAnimationsPointerTableEntries = f.readInt(); content.fogAnimationsNameOffset = f.readInt(); content.scenePointerTableOffset = f.readInt(); content.scenePointerTableEntries = f.readInt(); content.sceneNameOffset = f.readInt(); } //Skeletal animation AnimationGroupNode ThisAnimation = new AnimationGroupNode() { Text = filename }; for (int index1 = 0; index1 < content.skeletalAnimationsPointerTableEntries; index1++)// { f.seek(content.skeletalAnimationsPointerTableOffset + (index1 * 4)); int dataOffset = f.readInt(); f.seek(dataOffset); string skeletalAnimationName = f.readString(f.readInt(), -1); int animationFlags = f.readInt(); //int skeletalAnimationloopMode = f.readByte(); //pas ça du tout float skeletalAnimationframeSize = f.readFloat(); int boneTableOffset = f.readInt(); int boneTableEntries = f.readInt(); int metaDataPointerOffset = f.readInt(); //Runtime.Animations.Add(skeletalAnimationName, a); //MainForm.animNode.Nodes.Add(skeletalAnimationName); //Debug.WriteLine("Animation Name: " + skeletalAnimationName); Animation a = new Animation(skeletalAnimationName); ThisAnimation.Nodes.Add(a); for (int i = 0; i < boneTableEntries; i++) { f.seek(boneTableOffset + (i * 4)); int offset = f.readInt(); Animation.KeyNode bone = new Animation.KeyNode(""); a.Bones.Add(bone); f.seek(offset); bone.Text = f.readString(f.readInt(), -1); //Console.WriteLine("Bone Name: " + bone.name); int animationTypeFlags = f.readInt(); uint flags = (uint)f.readInt(); OSegmentType segmentType = (OSegmentType)((animationTypeFlags >> 16) & 0xf); //Debug.WriteLine(bone.Text + " " + flags.ToString("x")); switch (segmentType) { case OSegmentType.transform: f.seek(offset + 0xC); //Console.WriteLine(f.pos().ToString("x") + " " + flags.ToString("x")); uint notExistMask = 0x10000; uint constantMask = 0x40; for (int j = 0; j < 3; j++) { for (int axis = 0; axis < 3; axis++) { bool notExist = (flags & notExistMask) > 0; bool constant = (flags & constantMask) > 0; //Console.WriteLine(notExist + " " + constant); Animation.KeyGroup group = new Animation.KeyGroup(); //frame.exists = !notExist; if (!notExist) { if (constant) { Animation.KeyFrame frame = new Animation.KeyFrame(); frame.InterType = Animation.InterpolationType.LINEAR; frame.Value = f.readFloat(); frame.Frame = 0; group.Keys.Add(frame); } else { int frameOffset = f.readInt(); int position = f.pos(); f.seek(frameOffset); float c = 0; //Debug.WriteLine(j + " " + axis + " " + bone.Text); getAnimationKeyFrame(f, group, out c); if (c > a.FrameCount) { a.FrameCount = (int)c; } f.seek(position); } } else { f.seek(f.pos() + 0x04); } bone.RotType = Animation.RotationType.EULER; if (j == 0) { switch (axis) { case 0: bone.XSCA = group; break; case 1: bone.YSCA = group; break; case 2: bone.ZSCA = group; break; } } else if (j == 1) { switch (axis) { case 0: bone.XROT = group; break; case 1: bone.YROT = group; break; case 2: bone.ZROT = group; break; } } else if (j == 2) { switch (axis) { case 0: bone.XPOS = group; break; case 1: bone.YPOS = group; break; case 2: bone.ZPOS = group; break; } } notExistMask <<= 1; constantMask <<= 1; } if (j == 1) { constantMask <<= 1; } } break; /*case OSegmentType.transformQuaternion: * bone.isFrameFormat = true; * * int scaleOffset = f.readInt(); * int rotationOffset = f.readInt(); * int translationOffset = f.readInt(); * * if ((flags & 0x20) == 0) * { * bone.scale.exists = true; * f.seek(scaleOffset); * * if ((flags & 4) > 0) * { * bone.scale.vector.Add(new Vector4( * f.readFloat(), * f.readFloat(), * f.readFloat(), * 0)); * } * else * { * bone.scale.startFrame = f.readFloat(); * bone.scale.endFrame = f.readFloat(); * * int scaleFlags = f.readInt(); * int scaleDataOffset = f.readInt(); * int scaleEntries = f.readInt(); * * f.seek(scaleDataOffset); * for (int j = 0; j < scaleEntries; j++) * { * bone.scale.vector.Add(new Vector4( * f.readFloat(), * f.readFloat(), * f.readFloat(), * 0)); * } * } * } * * if ((flags & 0x10) == 0) * { * bone.rotationQuaternion.exists = true; * f.seek(rotationOffset); * * if ((flags & 2) > 0) * { * bone.rotationQuaternion.vector.Add(new Vector4( * f.readFloat(), * f.readFloat(), * f.readFloat(), * f.readFloat())); * } * else * { * bone.rotationQuaternion.startFrame = f.readFloat(); * bone.rotationQuaternion.endFrame = f.readFloat(); * * int rotationFlags = f.readInt(); * int rotationDataOffset = f.readInt(); * int rotationEntries = f.readInt(); * * f.seek(rotationDataOffset); * for (int j = 0; j < rotationEntries; j++) * { * bone.rotationQuaternion.vector.Add(new Vector4( * f.readFloat(), * f.readFloat(), * f.readFloat(), * f.readFloat())); * } * } * } * * if ((flags & 8) == 0) * { * bone.translation.exists = true; * f.seek(translationOffset); * * if ((flags & 1) > 0) * { * bone.translation.vector.Add(new Vector4( * f.readFloat(), * f.readFloat(), * f.readFloat(), * 0)); * } * else * { * bone.translation.startFrame = f.readFloat(); * bone.translation.endFrame = f.readFloat(); * * int translationFlags = f.readInt(); * int translationDataOffset = f.readInt(); * int translationEntries = f.readInt(); * * f.seek(translationDataOffset); * for (int j = 0; j < translationEntries; j++) * { * bone.translation.vector.Add(new Vector4( * f.readFloat(), * f.readFloat(), * f.readFloat(), * 0)); * } * } * } * * break; * case OSegmentType.transformMatrix: * bone.isFullBakedFormat = true; * * f.readInt(); * f.readInt(); * int matrixOffset = f.readInt(); * int entries = f.readInt(); * * f.seek(matrixOffset); * for (int j = 0; j < entries; j++) * { * OMatrix transform = new OMatrix(); * transform.M11 = f.readFloat(); * transform.M21 = f.readFloat(); * transform.M31 = f.readFloat(); * transform.M41 = f.readFloat(); * * transform.M12 = f.readFloat(); * transform.M22 = f.readFloat(); * transform.M32 = f.readFloat(); * transform.M42 = f.readFloat(); * * transform.M13 = f.readFloat(); * transform.M23 = f.readFloat(); * transform.M33 = f.readFloat(); * transform.M43 = f.readFloat(); * * bone.transform.Add(transform); * } * * break;*/ default: throw new Exception(string.Format("BCH: Unknow Segment Type {0} on Skeletal Animation bone {1}! STOP!", segmentType, bone.Text)); } //skeletalAnimation.bone.Add(bone); } } //return a; return(ThisAnimation); }
public override void Read(string filename) { bchHeader header = new bchHeader(); FileData f = new FileData(filename); f.Endian = System.IO.Endianness.Little; f.skip(4); header.backwardCompatibility = f.readByte(); header.forwardCompatibility = f.readByte(); header.version = f.readShort(); header.mainHeaderOffset = f.readInt(); header.stringTableOffset = f.readInt(); header.gpuCommandsOffset = f.readInt(); header.dataOffset = f.readInt(); if (header.backwardCompatibility > 0x20) { header.dataExtendedOffset = f.readInt(); } header.relocationTableOffset = f.readInt(); header.mainHeaderLength = f.readInt(); header.stringTableLength = f.readInt(); header.gpuCommandsLength = f.readInt(); header.dataLength = f.readInt(); if (header.backwardCompatibility > 0x20) { header.dataExtendedLength = f.readInt(); } header.relocationTableLength = f.readInt(); header.uninitializedDataSectionLength = f.readInt(); header.uninitializedDescriptionSectionLength = f.readInt(); if (header.backwardCompatibility > 7) { header.flags = f.readShort(); header.addressCount = f.readShort(); } // TODO: Finished Relocation table stuff for (int i = 0; i < header.relocationTableLength; i += 4) { f.seek(header.relocationTableOffset + i); int val = f.readInt(); int off = val & 0x1FFFFFF; byte flag = (byte)(val >> 25); switch (flag) { case 0: f.seek((off * 4) + header.mainHeaderOffset); f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.mainHeaderOffset); break; case 1: f.seek(off + header.mainHeaderOffset); f.writeInt((off) + header.mainHeaderOffset, f.readInt() + header.stringTableOffset); break; case 2: f.seek((off * 4) + header.mainHeaderOffset); f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.gpuCommandsOffset); break; case 0xc: f.seek((off * 4) + header.mainHeaderOffset); f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.dataOffset); break; } /*f.seek((off * 4) + header.gpuCommandsOffset); * if (header.backwardCompatibility < 6) * { * switch (flag) * { * case 0x23: f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.dataOffset); break; //Texture * case 0x25: f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.dataOffset); break; //Vertex * // Trying to understand the mess first * case 0x26: f.writeInt((off * 4) + header.mainHeaderOffset, Convert.ToInt32(((f.readInt() + header.dataOffset) & 0x7fffffff) | 0x80000000)); break; //Index 16 bits mode * //case 0x27: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode * } * } * else if (header.backwardCompatibility < 8) * { * switch (flag) * { * case 0x24: writer.Write(peek(input) + header.dataOffset); break; //Texture * case 0x26: writer.Write(peek(input) + header.dataOffset); break; //Vertex * case 0x27: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode * case 0x28: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode * } * } * else if (header.backwardCompatibility < 0x21) * { * switch (flag) * { * case 0x25: writer.Write(peek(input) + header.dataOffset); break; //Texture * case 0x27: writer.Write(peek(input) + header.dataOffset); break; //Vertex * case 0x28: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode * case 0x29: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode * } * } * else * { * switch (flag) * { * case 0x25: writer.Write(peek(input) + header.dataOffset); break; //Texture * case 0x26: writer.Write(peek(input) + header.dataOffset); break; //Vertex relative to Data Offset * case 0x27: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Offset * case 0x28: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode relative to Data Offset * case 0x2b: writer.Write(peek(input) + header.dataExtendedOffset); break; //Vertex relative to Data Extended Offset * case 0x2c: writer.Write(((peek(input) + header.dataExtendedOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Extended Offset * case 0x2d: writer.Write((peek(input) + header.dataExtendedOffset) & 0x7fffffff); break; //Index 8 bits mode relative to Data Extended Offset * } * } */ } // Content Header f.seek(header.mainHeaderOffset); bchContentHeader content = new bchContentHeader(); { content.modelsPointerTableOffset = f.readInt(); content.modelsPointerTableEntries = f.readInt(); content.modelsNameOffset = f.readInt(); content.materialsPointerTableOffset = f.readInt(); content.materialsPointerTableEntries = f.readInt(); content.materialsNameOffset = f.readInt(); content.shadersPointerTableOffset = f.readInt(); content.shadersPointerTableEntries = f.readInt(); content.shadersNameOffset = f.readInt(); content.texturesPointerTableOffset = f.readInt(); content.texturesPointerTableEntries = f.readInt(); content.texturesNameOffset = f.readInt(); content.materialsLUTPointerTableOffset = f.readInt(); content.materialsLUTPointerTableEntries = f.readInt(); content.materialsLUTNameOffset = f.readInt(); content.lightsPointerTableOffset = f.readInt(); content.lightsPointerTableEntries = f.readInt(); content.lightsNameOffset = f.readInt(); content.camerasPointerTableOffset = f.readInt(); content.camerasPointerTableEntries = f.readInt(); content.camerasNameOffset = f.readInt(); content.fogsPointerTableOffset = f.readInt(); content.fogsPointerTableEntries = f.readInt(); content.fogsNameOffset = f.readInt(); content.skeletalAnimationsPointerTableOffset = f.readInt(); content.skeletalAnimationsPointerTableEntries = f.readInt(); content.skeletalAnimationsNameOffset = f.readInt(); content.materialAnimationsPointerTableOffset = f.readInt(); content.materialAnimationsPointerTableEntries = f.readInt(); content.materialAnimationsNameOffset = f.readInt(); content.visibilityAnimationsPointerTableOffset = f.readInt(); content.visibilityAnimationsPointerTableEntries = f.readInt(); content.visibilityAnimationsNameOffset = f.readInt(); content.lightAnimationsPointerTableOffset = f.readInt(); content.lightAnimationsPointerTableEntries = f.readInt(); content.lightAnimationsNameOffset = f.readInt(); content.cameraAnimationsPointerTableOffset = f.readInt(); content.cameraAnimationsPointerTableEntries = f.readInt(); content.cameraAnimationsNameOffset = f.readInt(); content.fogAnimationsPointerTableOffset = f.readInt(); content.fogAnimationsPointerTableEntries = f.readInt(); content.fogAnimationsNameOffset = f.readInt(); content.scenePointerTableOffset = f.readInt(); content.scenePointerTableEntries = f.readInt(); content.sceneNameOffset = f.readInt(); } //Shaders (unused for now, until someone wants to add them) for (int index = 0; index < content.shadersPointerTableEntries; index++) { f.seek(content.shadersPointerTableOffset + (index * 4)); int dataOffset = f.readInt(); f.seek(dataOffset); int shaderDataOffset = f.readInt(); int shaderDataLength = f.readInt(); } // Textures // WIP Section for (int index = 0; index < content.texturesPointerTableEntries; index++) { f.seek(content.texturesPointerTableOffset + (index * 4)); int dOffset = f.readInt(); f.seek(dOffset); Debug.WriteLine("dOffset: " + dOffset.ToString("X")); int textureCommandsOffset = f.readInt(); int textureCommandsWordCount = f.readInt(); f.seek(f.pos() + 0x14); String textureName = f.readString(f.readInt(), -1); //Debug.WriteLine("gpuCommandOffset: " + header.gpuCommandsOffset.ToString("X")); f.seek(textureCommandsOffset); //Debug.WriteLine("textureCommandOffset: " + textureCommandsOffset.ToString("X")); BCH_Texture tex = new BCH_Texture(); textures.Add(textureName, tex); tex.height = f.readShort(); tex.width = f.readShort(); f.skip(12); int doffset = f.readInt(); //Debug.WriteLine("doffset: " + doffset.ToString("X")); f.skip(4); tex.type = f.readInt(); tex.data = f.getSection(doffset, f.size() - doffset); if (tex.type == 12) { tex.display = NUT.loadImage(Pixel.decodeETC(tex.data, tex.width, tex.height)); } } // Model data for (int modelIndex = 0; modelIndex < content.modelsPointerTableEntries; modelIndex++) { f.seek(content.modelsPointerTableOffset + (modelIndex * 4)); int objectsHeaderOffset = f.readInt(); // Objects f.seek(objectsHeaderOffset); BCH_Model model = new BCH_Model(); models.Add(model); model.flags = f.readByte(); model.skeletonScaleType = f.readByte(); model.silhouetteMaterialEntries = f.readShort(); model.worldTransform = new Matrix4(f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat() , f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat() , f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat() , 0, 0, 0, 1); model.materialsTableOffset = f.readInt(); model.materialsTableEntries = f.readInt(); model.materialsNameOffset = f.readInt(); model.verticesTableOffset = f.readInt(); //Debug.WriteLine("Mesh Count: " + f.pos().ToString("X")); model.verticesTableEntries = f.readInt(); f.skip(0x28); model.skeletonOffset = f.readInt(); model.skeletonEntries = f.readInt(); model.skeletonNameOffset = f.readInt(); model.objectsNodeVisibilityOffset = f.readInt(); model.objectsNodeCount = f.readInt(); model.name = f.readString(f.readInt(), -1); model.objectsNodeNameEntries = f.readInt(); model.objectsNodeNameOffset = f.readInt(); f.readInt(); //0x0 model.metaDataPointerOffset = f.readInt(); f.seek(model.objectsNodeVisibilityOffset); int nodeVisibility = f.readInt(); string[] objectName = new string[model.objectsNodeNameEntries]; f.seek(model.objectsNodeNameOffset); int rootReferenceBit = f.readInt(); //Radix tree int rootLeftNode = f.readShort(); int rootRightNode = f.readShort(); int rootNameOffset = f.readInt() + header.mainHeaderOffset; for (int i = 0; i < model.objectsNodeNameEntries; i++) { int referenceBit = f.readInt(); short leftNode = (short)f.readShort(); short rightNode = (short)f.readShort(); objectName[i] = f.readString(f.readInt(), -1); //Debug.WriteLine(objectName[i]); } // Materials // NOTE: MATERIALS AND OBJECT SECTIONS ARE REALLY MESSY ATM String[] materialNames = new String[model.materialsTableEntries]; for (int index = 0; index < model.materialsTableEntries; index++) { f.seek(model.materialsTableOffset + (index * 0x2c)); int materialParametersOffset = f.readInt(); f.readInt(); f.readInt(); f.readInt(); int textureCommandsOffset = f.readInt(); int textureCommandsWordCount = f.readInt(); int materialMapperOffset = f.readInt(); materialNames[index] = f.readString(f.readInt(), -1); } // Object Descriptions... // Assumes MBN is already loaded for now f.seek(model.verticesTableOffset); List <objDes> objDescriptors = new List <objDes>(); Debug.WriteLine(model.name); if (mbn == null) { mbn = new Smash_Forge.MBN(); for (int index = 0; index < model.verticesTableEntries; index++) { mbn.mesh.Add(new MBN.Mesh()); } mbn.PreRender(); } for (int index = 0; index < mbn.mesh.Count; index++) { int i = f.readShort(); if (index > mbn.mesh.Count) { break; } if (i > materialNames.Length) { break; } mbn.mesh[index].texId = textures[materialNames[i]].display; Console.WriteLine("Tex index" + mbn.mesh[index].texId); f.skip(2); // flags int nameId = f.readShort(); mbn.mesh[index].name = objectName[nameId]; // node visibility TODO: finish... //mbn.mesh[index].isVisible = ((nodeVisibility & (1 << nameId)) > 0); mbn.mesh[index].renderPriority = f.readShort(); objDes des = new objDes(); objDescriptors.Add(des); des.vshAttBufferCommandOffset = f.readInt(); des.vshAttBufferCommandCount = f.readInt(); des.faceOffset = f.readInt(); des.faceCount = f.readInt(); des.vshAttBufferCommandOffsetEx = f.readInt(); des.vshAttBufferCommandCountEx = f.readInt(); f.skip(12); // center vector f.skip(4); // flagsOffset f.skip(4); // 0? f.readInt(); //bbOffsets[i] = + mainheaderOffset //Debug.WriteLine(des.vshAttBufferCommandOffset.ToString("X")); } //Skeleton f.seek(model.skeletonOffset); for (int index = 0; index < model.skeletonEntries; index++) { Bone bone = new Smash_Forge.Bone(model.skeleton); int boneFlags = f.readInt(); bone.parentIndex = (short)f.readShort(); short boneSpace = (short)f.readShort(); bone.scale = new float[3]; bone.rotation = new float[3]; bone.position = new float[3]; bone.scale[0] = f.readFloat(); bone.scale[1] = f.readFloat(); bone.scale[2] = f.readFloat(); bone.rotation[0] = f.readFloat(); bone.rotation[1] = f.readFloat(); bone.rotation[2] = f.readFloat(); bone.position[0] = f.readFloat(); bone.position[1] = f.readFloat(); bone.position[2] = f.readFloat(); // bone matrix... not really needed to be stored per say f.skip(4 * 4 * 3); bone.boneName = f.readString(f.readInt(), -1).ToCharArray(); f.skip(4); // Meta data model.skeleton.bones.Add(bone); } model.skeleton.reset(); /* //Skeletal Animations * * SkelAnimation anim = new SkelAnimation(); * * for (int index = 0; index < content.skeletalAnimationsPointerTableEntries; index++) * { * f.seek(content.skeletalAnimationsPointerTableOffset + (index * 4)); * int dataOffset = f.readInt(); * f.seek(dataOffset); * * RenderBase.OSkeletalAnimation skeletalAnimation = new RenderBase.OSkeletalAnimation(); * * skeletalAnimation.name = f.readString(f.readInt(), -1); * int animationFlags = f.readInt(); * skeletalAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1); * skeletalAnimation.frameSize = f.readFloat(); * int boneTableOffset = f.readInt(); * int boneTableEntries = f.readInt(); * int metaDataPointerOffset = f.readInt(); * * // /*if (metaDataPointerOffset != 0) * // { * // data.Seek(metaDataPointerOffset, SeekOrigin.Begin); * // skeletalAnimation.userData = getMetaData(input); * // } * * for (int i = 0; i < boneTableEntries; i++) * { * f.seek(boneTableOffset + (i * 4)); * int offset = f.readInt(); * * RenderBase.OSkeletalAnimationBone bone = new RenderBase.OSkeletalAnimationBone(); * * f.seek(offset); * bone.name = f.readString(f.readInt(), -1); * int animationTypeFlags = f.readInt(); * * int flags = f.readInt(); * * RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf); * switch (segmentType) * { * case RenderBase.OSegmentType.transform: * f.seek(offset + 0x18); * * uint notExistMask = 0x80000; * uint constantMask = 0x200; * * for (int j = 0; j < 2; j++) * { * for (int axis = 0; axis < 3; axis++) * { * bool notExist = (flags & notExistMask) > 0; * bool constant = (flags & constantMask) > 0; * * RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup(); * frame.exists = !notExist; * if (frame.exists) * { * if (constant) * { * frame.interpolation = RenderBase.OInterpolationMode.linear; * frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0)); * } * else * { * int frameOffset = f.readInt(); * long position = data.Position; * f.seek(frameOffset); * getAnimationKeyFrame(input, frame); * f.seek(position); * } * } * else * data.Seek(4, SeekOrigin.Current); * * if (j == 0) * { * switch (axis) * { * case 0: bone.rotationX = frame; break; * case 1: bone.rotationY = frame; break; * case 2: bone.rotationZ = frame; break; * } * } * else * { * switch (axis) * { * case 0: bone.translationX = frame; break; * case 1: bone.translationY = frame; break; * case 2: bone.translationZ = frame; break; * } * } * * notExistMask <<= 1; * constantMask <<= 1; * } * * constantMask <<= 1; * } * * break; * case RenderBase.OSegmentType.transformQuaternion: * bone.isFrameFormat = true; * * uint scaleOffset = input.ReadUInt32(); * uint rotationOffset = input.ReadUInt32(); * uint translationOffset = input.ReadUInt32(); * * if ((flags & 0x20) == 0) * { * bone.scale.exists = true; * data.Seek(scaleOffset, SeekOrigin.Begin); * * if ((flags & 4) > 0) * { * bone.scale.vector.Add(new RenderBase.OVector4( * input.ReadSingle(), * input.ReadSingle(), * input.ReadSingle(), * 0)); * } * else * { * bone.scale.startFrame = input.ReadSingle(); * bone.scale.endFrame = input.ReadSingle(); * * uint scaleFlags = input.ReadUInt32(); * uint scaleDataOffset = input.ReadUInt32(); * uint scaleEntries = input.ReadUInt32(); * * data.Seek(scaleDataOffset, SeekOrigin.Begin); * for (int j = 0; j < scaleEntries; j++) * { * bone.scale.vector.Add(new RenderBase.OVector4( * input.ReadSingle(), * input.ReadSingle(), * input.ReadSingle(), * 0)); * } * } * } * * if ((flags & 0x10) == 0) * { * bone.rotationQuaternion.exists = true; * data.Seek(rotationOffset, SeekOrigin.Begin); * * if ((flags & 2) > 0) * { * bone.rotationQuaternion.vector.Add(new RenderBase.OVector4( * input.ReadSingle(), * input.ReadSingle(), * input.ReadSingle(), * input.ReadSingle())); * } * else * { * bone.rotationQuaternion.startFrame = input.ReadSingle(); * bone.rotationQuaternion.endFrame = input.ReadSingle(); * * uint rotationFlags = input.ReadUInt32(); * uint rotationDataOffset = input.ReadUInt32(); * uint rotationEntries = input.ReadUInt32(); * * data.Seek(rotationDataOffset, SeekOrigin.Begin); * for (int j = 0; j < rotationEntries; j++) * { * bone.rotationQuaternion.vector.Add(new RenderBase.OVector4( * input.ReadSingle(), * input.ReadSingle(), * input.ReadSingle(), * input.ReadSingle())); * } * } * } * * if ((flags & 8) == 0) * { * bone.translation.exists = true; * data.Seek(translationOffset, SeekOrigin.Begin); * * if ((flags & 1) > 0) * { * bone.translation.vector.Add(new RenderBase.OVector4( * input.ReadSingle(), * input.ReadSingle(), * input.ReadSingle(), * 0)); * } * else * { * bone.translation.startFrame = input.ReadSingle(); * bone.translation.endFrame = input.ReadSingle(); * * uint translationFlags = input.ReadUInt32(); * uint translationDataOffset = input.ReadUInt32(); * uint translationEntries = input.ReadUInt32(); * * data.Seek(translationDataOffset, SeekOrigin.Begin); * for (int j = 0; j < translationEntries; j++) * { * bone.translation.vector.Add(new RenderBase.OVector4( * input.ReadSingle(), * input.ReadSingle(), * input.ReadSingle(), * 0)); * } * } * } * * break; * case RenderBase.OSegmentType.transformMatrix: * bone.isFullBakedFormat = true; * * input.ReadUInt32(); * input.ReadUInt32(); * uint matrixOffset = input.ReadUInt32(); * uint entries = input.ReadUInt32(); * * data.Seek(matrixOffset, SeekOrigin.Begin); * for (int j = 0; j < entries; j++) * { * RenderBase.OMatrix transform = new RenderBase.OMatrix(); * transform.M11 = input.ReadSingle(); * transform.M21 = input.ReadSingle(); * transform.M31 = input.ReadSingle(); * transform.M41 = input.ReadSingle(); * * transform.M12 = input.ReadSingle(); * transform.M22 = input.ReadSingle(); * transform.M32 = input.ReadSingle(); * transform.M42 = input.ReadSingle(); * * transform.M13 = input.ReadSingle(); * transform.M23 = input.ReadSingle(); * transform.M33 = input.ReadSingle(); * transform.M43 = input.ReadSingle(); * * bone.transform.Add(transform); * } * * break; * default: throw new Exception(string.Format("BCH: Unknow Segment Type {0} on Skeletal Animation bone {1}! STOP!", segmentType, bone.name)); * } * * skeletalAnimation.bone.Add(bone); * } * * models.skeletalAnimation.list.Add(skeletalAnimation); * } * * //Material Animations * for (int index = 0; index < contentHeader.materialAnimationsPointerTableEntries; index++) * { * data.Seek(contentHeader.materialAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin); * uint dataOffset = input.ReadUInt32(); * data.Seek(dataOffset, SeekOrigin.Begin); * * RenderBase.OMaterialAnimation materialAnimation = new RenderBase.OMaterialAnimation(); * * materialAnimation.name = readString(input); * uint animationFlags = input.ReadUInt32(); * materialAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1); * materialAnimation.frameSize = input.ReadSingle(); * uint dataTableOffset = input.ReadUInt32(); * uint dataTableEntries = input.ReadUInt32(); * input.ReadUInt32(); * uint textureNameTableOffset = input.ReadUInt32(); * uint textureNameTableEntries = input.ReadUInt32(); * * data.Seek(textureNameTableOffset, SeekOrigin.Begin); * for (int i = 0; i < textureNameTableEntries; i++) * { * string name = readString(input); * materialAnimation.textureName.Add(name); * } * * for (int i = 0; i < dataTableEntries; i++) * { * data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin); * uint offset = input.ReadUInt32(); * * RenderBase.OMaterialAnimationData animationData = new RenderBase.OMaterialAnimationData(); * * data.Seek(offset, SeekOrigin.Begin); * animationData.name = readString(input); * uint animationTypeFlags = input.ReadUInt32(); * uint flags = input.ReadUInt32(); * * animationData.type = (RenderBase.OMaterialAnimationType)(animationTypeFlags & 0xff); * RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf); * * int segmentCount = 0; * switch (segmentType) * { * case RenderBase.OSegmentType.rgbaColor: segmentCount = 4; break; * case RenderBase.OSegmentType.vector2: segmentCount = 2; break; * case RenderBase.OSegmentType.single: segmentCount = 1; break; * case RenderBase.OSegmentType.integer: segmentCount = 1; break; * } * * for (int j = 0; j < segmentCount; j++) * { * RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup(); * * data.Seek(offset + 0xc + (j * 4), SeekOrigin.Begin); * * frame.exists = (flags & (0x100 << j)) == 0; * bool constant = (flags & (1 << j)) > 0; * * if (frame.exists) * { * if (constant) * { * frame.interpolation = RenderBase.OInterpolationMode.linear; * frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0)); * } * else * { * uint frameOffset = input.ReadUInt32(); * data.Seek(frameOffset, SeekOrigin.Begin); * getAnimationKeyFrame(input, frame); * } * } * * animationData.frameList.Add(frame); * } * * materialAnimation.data.Add(animationData); * } * * models.materialAnimation.list.Add(materialAnimation); * } * */ } }