public void Load(H3DModel model, BCH bch) { Renderable = new STSkeleton(); bch.DrawableContainer.Drawables.Add(Renderable); BcresParent = bch; ModelParent = model; Text = "Skeleton"; Checked = true; foreach (var bone in model.Skeleton) { Renderable.bones.Add(new H3DBoneWrapper(Renderable, bone, bch)); } Nodes.Clear(); foreach (var bone in Renderable.bones) { if (bone.Parent == null) { Nodes.Add(bone); } } Renderable.update(); Renderable.reset(); }
public void Load(Skeleton skeleton, BCRES bcres) { Renderable = new STSkeleton(); BcresParent = bcres; Skeleton = skeleton; Text = "Skeleton"; Checked = true; foreach (var bone in skeleton.Bones.Values) { var boneWrapper = new CRESBoneWrapper(); boneWrapper.skeletonParent = Renderable; boneWrapper.Load(bone, bcres); Renderable.bones.Add(boneWrapper); } Nodes.Clear(); foreach (var bone in Renderable.bones) { if (bone.Parent == null) { Nodes.Add(bone); } } Renderable.update(); Renderable.reset(); }
public G1MS(FileReader reader, bool isParent = true) { uint pos = (uint)reader.Position - 12; uint boneOffset = reader.ReadUInt32() + pos; ushort conditionNumber = reader.ReadUInt16(); if (isParent && conditionNumber == 0) { } ushort unknown = reader.ReadUInt16(); ushort numBones = reader.ReadUInt16(); ushort numBoneIndices = reader.ReadUInt16(); BoneIndices = reader.ReadInt16s((int)numBoneIndices); BoneIndexList = new ushort[numBones]; for (ushort i = 0; i < BoneIndices?.Length; i++) { if (BoneIndices[i] != -1) { BoneIndexList[BoneIndices[i]] = i; } } reader.SeekBegin(boneOffset); for (int i = 0; i < numBones; i++) { var scale = reader.ReadVec3(); int parentIndex = reader.ReadInt32(); var quat = reader.ReadQuaternion(true); var position = reader.ReadVec3(); reader.ReadSingle(); // if ((parentIndex & 0x80000000) > 0 && parentIndex != -1) // parentIndex = parentIndex & 0x7FFFFFFF; STBone genericBone = new STBone(GenericSkeleton); genericBone.Text = $"Bone {BoneIndexList[i]}"; genericBone.Rotation = quat; genericBone.Scale = scale; genericBone.Position = position; genericBone.parentIndex = parentIndex; GenericSkeleton.bones.Add(genericBone); } GenericSkeleton.reset(); GenericSkeleton.update(); }
public void Read(FileReader reader, LM2_ARCADE_Model root) { Skeleton = new STSkeleton(); DrawableRenderer = new GenericModelRenderer(); root.DrawableContainer.Drawables.Add(Skeleton); root.DrawableContainer.Drawables.Add(DrawableRenderer); reader.ReadSignature(4, "VM61"); ushort Type = reader.ReadUInt16(); ushort Unknown = reader.ReadUInt16(); if (Type == 0) { } else if ((Type == 1)) { throw new Exception("Animation files not supported yet!"); } else { throw new Exception("Unknown type found! " + Type); } Alignment = reader.ReadUInt32(); uint Padding = reader.ReadUInt32(); ulong MaterialCount = reader.ReadUInt64(); HeaderSize = reader.ReadUInt64(); ulong TextureMapsCount = reader.ReadUInt64(); ulong TextureMapsOffset = reader.ReadUInt64(); ulong BoneCount = reader.ReadUInt64(); ulong BoneOffset = reader.ReadUInt64(); ulong FirstNodeOffset = reader.ReadUInt64(); //Either an offset or the total size of section up to the node ulong LinkNodeCount = reader.ReadUInt64(); ulong LinkNodeOffset = reader.ReadUInt64(); ulong TotalNodeCount = reader.ReadUInt64(); ulong TotalNodeOffset = reader.ReadUInt64(); ulong Padding2 = reader.ReadUInt64(); root.Nodes.Add("Materials"); root.Nodes.Add("Root"); root.Nodes.Add("All Nodes"); long pos = reader.Position; if (TextureMapsOffset != 0) { reader.SeekBegin(TextureMapsOffset); for (int i = 0; i < (int)TextureMapsCount; i++) { TextureMaps.Add(reader.ReadNameOffset(false, typeof(ulong))); } } reader.SeekBegin(pos); for (int i = 0; i < (int)MaterialCount; i++) { Material mat = new Material(); mat.Read(reader); Materials.Add(mat); var genericMat = new STGenericMaterial(); genericMat.Text = mat.Name; for (int t = 0; t < mat.TextureIndices.Length; t++) { if (mat.TextureIndices[t] != -1) { Console.WriteLine("TextureIndices " + mat.TextureIndices[t]); string Texture = TextureMaps[mat.TextureIndices[t]]; var textureMap = new STGenericMatTexture(); textureMap.Name = Texture; genericMat.TextureMaps.Add(textureMap); if (Texture.EndsWith("col.dds")) { textureMap.Type = STGenericMatTexture.TextureType.Diffuse; } if (Texture.EndsWith("col.mot")) { textureMap.Type = STGenericMatTexture.TextureType.Diffuse; } } } root.Nodes[0].Nodes.Add(genericMat); } if (TotalNodeCount != 0) { for (int i = 0; i < (int)TotalNodeCount; i++) { reader.SeekBegin((int)TotalNodeOffset + (i * 16)); string NodeName = reader.ReadNameOffset(false, typeof(ulong)); var Offset = reader.ReadUInt64(); Console.WriteLine($"{NodeName}"); if (Offset != 0) { reader.SeekBegin(Offset); Node node = new Node(); node.Name = NodeName; node.Read(reader); TotalNodes.Add(node); } } } if (BoneCount != 0) { for (int i = 0; i < (int)BoneCount; i++) { reader.SeekBegin((int)BoneOffset + (i * 16)); string NodeName = reader.ReadNameOffset(false, typeof(ulong)); ulong Offset = reader.ReadUInt64(); if (Offset != 0) { reader.SeekBegin(Offset); Node node = new Node(); node.Name = NodeName; node.Read(reader); BoneList.Add(node); //This list is for mapping to meshes } } } foreach (var node in TotalNodes) { // TreeNode lowerWrapper = new TreeNode($"{node.Name}"); // root.Nodes[3].Nodes.Add(lowerWrapper); LoadChildern(TotalNodes, node, root.Nodes[2]); } if (FirstNodeOffset != 0) { reader.SeekBegin(FirstNodeOffset); ulong NodeOffset = reader.ReadUInt64(); reader.SeekBegin(NodeOffset); Node node = new Node(); node.Name = GetNodeName(TotalNodes, node); node.Read(reader); LoadBones(TotalNodes, node, root.Nodes[1]); } if (LinkNodeCount != 0) { root.Nodes.Add("Links"); for (int i = 0; i < (int)LinkNodeCount; i++) { TreeNode linkWrapper = new TreeNode($"Link {i}"); root.Nodes[3].Nodes.Add(linkWrapper); reader.SeekBegin((int)LinkNodeOffset + (i * 16)); ulong NodeOffset1 = reader.ReadUInt64(); ulong NodeOffset2 = reader.ReadUInt64(); reader.SeekBegin(NodeOffset1); Node node1 = new Node(); node1.Name = GetNodeName(TotalNodes, node1); node1.Read(reader); reader.SeekBegin(NodeOffset2); Node node2 = new Node(); node2.Name = GetNodeName(TotalNodes, node1); node2.Read(reader); // LoadChildern(TotalNodes, node1, linkWrapper); // LoadChildern(TotalNodes, node2, linkWrapper); LinkNodes.Add(Tuple.Create(node1, node2)); } } Skeleton.update(); Skeleton.reset(); }
public void NextFrame(STSkeleton skeleton, bool isChild = false, bool AdancedNextFrame = false, string bn = "") { if (Frame > FrameCount) { return; } if (Frame == 0 && !isChild) { skeleton.reset(); } foreach (object child in Children) { if (child is Animation) { ((Animation)child).SetFrame(Frame); ((Animation)child).NextFrame(skeleton, isChild, AdancedNextFrame); } } bool Updated = false; // no need to update skeleton of animations that didn't change foreach (KeyNode node in Bones) { // Get Skeleton Node STBone b = null; b = skeleton.GetBone(node.Text); if (b == null) { continue; } if (bn != "" && bn != node.Text) { continue; //If a bone name is provided, skip any bones that aren't that bone. } b.UseSegmentScaleCompensate = node.UseSegmentScaleCompensate; Updated = true; if (node.XPOS.HasAnimation()) { b.pos.X = node.XPOS.GetValue(Frame); } if (node.YPOS.HasAnimation()) { b.pos.Y = node.YPOS.GetValue(Frame); } if (node.ZPOS.HasAnimation()) { b.pos.Z = node.ZPOS.GetValue(Frame); } if (node.XSCA.HasAnimation()) { b.sca.X = node.XSCA.GetValue(Frame); } else { b.sca.X = 1; } if (node.YSCA.HasAnimation()) { b.sca.Y = node.YSCA.GetValue(Frame); } else { b.sca.Y = 1; } if (node.ZSCA.HasAnimation()) { b.sca.Z = node.ZSCA.GetValue(Frame); } else { b.sca.Z = 1; } if (node.XROT.HasAnimation() || node.YROT.HasAnimation() || node.ZROT.HasAnimation()) { if (node.RotType == RotationType.QUATERNION) { KeyFrame[] x = node.XROT.GetFrame(Frame); KeyFrame[] y = node.YROT.GetFrame(Frame); KeyFrame[] z = node.ZROT.GetFrame(Frame); KeyFrame[] w = node.WROT.GetFrame(Frame); Quaternion q1 = new Quaternion(x[0].Value, y[0].Value, z[0].Value, w[0].Value); Quaternion q2 = new Quaternion(x[1].Value, y[1].Value, z[1].Value, w[1].Value); if (x[0].Frame == Frame) { b.rot = q1; } else if (x[1].Frame == Frame) { b.rot = q2; } else { b.rot = Quaternion.Slerp(q1, q2, (Frame - x[0].Frame) / (x[1].Frame - x[0].Frame)); } } else if (node.RotType == RotationType.EULER) { float x = node.XROT.HasAnimation() ? node.XROT.GetValue(Frame) : b.EulerRotation.X; float y = node.YROT.HasAnimation() ? node.YROT.GetValue(Frame) : b.EulerRotation.Y; float z = node.ZROT.HasAnimation() ? node.ZROT.GetValue(Frame) : b.EulerRotation.Z; b.rot = EulerToQuat(z, y, x); } } } if (AdancedNextFrame) { Frame++; if (Frame > FrameCount) { Frame = 0; } } if (!isChild && Updated) { skeleton.update(); } }
public void Load(System.IO.Stream stream) { Renderer = new Strikers_Renderer(); DrawableContainer.Name = FileName; DrawableContainer.Drawables.Add(Renderer); IsGamecube = Utils.GetExtension(FileName) == ".glg"; Text = FileName; using (var reader = new FileReader(stream)) { reader.SetByteOrder(true); reader.ReadUInt32(); //magic reader.ReadUInt32(); //fileSize //Create a list of all the major sections //The sections are not in order, so we must order them while parsing Dictionary <SectionMagic, SectionHeader> SectionLookup = new Dictionary <SectionMagic, SectionHeader>(); while (!reader.EndOfStream) { uint magic = reader.ReadUInt32(); uint sectionSize = reader.ReadUInt32(); long pos = reader.Position; Console.WriteLine($"Magic {(SectionMagic)magic} sectionSize {sectionSize}"); if (!SectionLookup.ContainsKey((SectionMagic)magic)) { SectionLookup.Add((SectionMagic)magic, new SectionHeader(sectionSize, pos)); } //This section will skip sub sections so don't do that if (magic != 0x8001B000) { reader.SeekBegin(pos + sectionSize); } if (!IsGamecube) { reader.Align(4); } } var HashList = NLG_Common.HashNames; //Now read our model properly //First get our model info Matrix4 TransformMatrix = Matrix4.Identity; uint totalNumMeshes = 0; uint modelSize = 12; if (IsGamecube) { modelSize = 16; } if (SectionLookup.ContainsKey(SectionMagic.ModelData)) { var modelHeader = SectionLookup[SectionMagic.ModelData]; for (int m = 0; m < modelHeader.Size / modelSize; m++) { reader.SeekBegin(modelHeader.Position + (m * modelSize)); if (IsGamecube) { totalNumMeshes += reader.ReadUInt32(); reader.ReadUInt32(); } else { reader.ReadUInt32(); totalNumMeshes += reader.ReadUInt32(); } } } uint meshIndex = 0; if (SectionLookup.ContainsKey(SectionMagic.ModelData)) { var modelHeader = SectionLookup[SectionMagic.ModelData]; reader.SeekBegin(modelHeader.Position); uint numModels = modelHeader.Size / modelSize; uint hashId = 0; for (int m = 0; m < numModels; m++) { Model model = new Model(); Nodes.Add(model); Models.Add(model); uint numMeshes = 0; reader.SeekBegin(modelHeader.Position + (m * modelSize)); if (IsGamecube) { numMeshes = reader.ReadUInt32(); hashId = reader.ReadUInt32(); } else { hashId = reader.ReadUInt32(); numMeshes = reader.ReadUInt32(); } model.Text = hashId.ToString("X"); if (HashList.ContainsKey(hashId)) { model.Text = HashList[hashId]; } if (SectionLookup.ContainsKey(SectionMagic.MeshData)) { var meshHeader = SectionLookup[SectionMagic.MeshData]; reader.SeekBegin(meshHeader.Position); for (int i = 0; i < numMeshes; i++) { if (IsGamecube) { uint meshSize = meshHeader.Size / totalNumMeshes; reader.SeekBegin(meshHeader.Position + ((meshIndex + i) * meshSize)); } else { reader.SeekBegin(meshHeader.Position + ((meshIndex + i) * 48)); } var mesh = new MeshData(reader, IsGamecube); model.Meshes.Add(mesh); } meshIndex += numMeshes; } } if (SectionLookup.ContainsKey(SectionMagic.MatrixData)) { var matSection = SectionLookup[SectionMagic.MatrixData]; reader.SeekBegin(matSection.Position); TransformMatrix = reader.ReadMatrix4(true); } List <VertexAttribute> Pointers = new List <VertexAttribute>(); if (SectionLookup.ContainsKey(SectionMagic.VertexAttributePointerData)) { var pointerSection = SectionLookup[SectionMagic.VertexAttributePointerData]; reader.SeekBegin(pointerSection.Position); int attributeSize = 8; if (IsGamecube) { attributeSize = 6; } for (int i = 0; i < pointerSection.Size / attributeSize; i++) { VertexAttribute pointer = new VertexAttribute(); if (IsGamecube) { pointer.Offset = reader.ReadUInt32(); pointer.Type = reader.ReadByte(); pointer.Stride = reader.ReadByte(); } else { pointer.Offset = reader.ReadUInt32(); pointer.Type = reader.ReadByte(); pointer.Stride = reader.ReadByte(); reader.ReadUInt16(); } Pointers.Add(pointer); } } int pointerIndex = 0; foreach (var model in Models) { for (int i = 0; i < model.Meshes.Count; i++) { RenderableMeshWrapper mesh = new RenderableMeshWrapper(); model.Nodes.Add(mesh); Renderer.Meshes.Add(mesh); mesh.Text = model.Meshes[i].HashID.ToString("X"); if (HashList.ContainsKey(model.Meshes[i].HashID)) { mesh.Text = HashList[model.Meshes[i].HashID]; } string material = model.Meshes[i].MaterialHashID.ToString("X"); if (HashList.ContainsKey(model.Meshes[i].MaterialHashID)) { material = HashList[model.Meshes[i].MaterialHashID]; } mesh.Nodes.Add(material); var faceSecton = SectionLookup[SectionMagic.IndexData]; var vertexSecton = SectionLookup[SectionMagic.VertexData]; STGenericPolygonGroup polyGroup = new STGenericPolygonGroup(); mesh.PolygonGroups.Add(polyGroup); reader.SeekBegin(faceSecton.Position + model.Meshes[i].IndexStartOffset); List <int> faces = new List <int>(); for (int f = 0; f < model.Meshes[i].IndexCount; f++) { if (model.Meshes[i].IndexFormat == 0) { polyGroup.faces.Add(reader.ReadUInt16()); } else { polyGroup.faces.Add(reader.ReadByte()); } } if (model.Meshes[i].FaceType == MeshData.PolygonType.TriangleStrips) { polyGroup.PrimativeType = STPrimitiveType.TrangleStrips; } else { polyGroup.PrimativeType = STPrimitiveType.Triangles; } if (IsGamecube) { uint size = Pointers[pointerIndex + 1].Offset - Pointers[pointerIndex].Offset; model.Meshes[i].VertexCount = (ushort)(size / Pointers[pointerIndex].Stride); } Console.WriteLine($"mesh {mesh.Text} {model.Meshes[i].VertexCount}"); for (int v = 0; v < model.Meshes[i].VertexCount; v++) { Vertex vert = new Vertex(); mesh.vertices.Add(vert); for (int a = 0; a < model.Meshes[i].NumAttributePointers; a++) { var pointer = Pointers[pointerIndex + a]; reader.SeekBegin(vertexSecton.Position + pointer.Offset + (pointer.Stride * v)); if (pointer.Type == 0) { if (pointer.Stride == 6) { vert.pos = new Vector3(reader.ReadInt16() / 1024f, reader.ReadInt16() / 1024f, reader.ReadInt16() / 1024f); } else if (pointer.Stride == 12) { vert.pos = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); } // vert.pos = Vector3.TransformPosition(vert.pos, TransformMatrix); } if (pointer.Type == 1) { if (pointer.Stride == 3) { vert.nrm = Read_8_8_8_Snorm(reader).Normalized(); } else if (pointer.Stride == 12) { vert.nrm = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); } } if (pointer.Type == 3) { vert.uv0 = new Vector2(reader.ReadUInt16() / 1024f, reader.ReadUInt16() / 1024f); } if (pointer.Type == 0x67) { vert.pos = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); } if (pointer.Type == 0xFE) { vert.nrm = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); } if (pointer.Type == 0x26) { vert.uv0 = new Vector2(reader.ReadUInt16() / 1024f, reader.ReadUInt16() / 1024f); } if (pointer.Type == 0xCC) { vert.uv0 = new Vector2(reader.ReadUInt16() / 1024f, reader.ReadUInt16() / 1024f); } if (pointer.Type == 0x17) { vert.uv1 = new Vector2(reader.ReadUInt16() / 1024f, reader.ReadUInt16() / 1024f); } if (pointer.Type == 0xD4) { vert.boneIds = new List <int>() { reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte() }; } if (pointer.Type == 0xB0) { vert.boneWeights = new List <float>() { reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() }; } } } mesh.TransformPosition(new Vector3(0), new Vector3(-90, 0, 0), new Vector3(1)); pointerIndex += model.Meshes[i].NumAttributePointers; } for (int i = 0; i < model.Meshes.Count; i++) { if (IsGamecube) { var renderedMesh = (RenderableMeshWrapper)Renderer.Meshes[i]; renderedMesh.Material = new STGenericMaterial(); string diffuseName = model.Meshes[i].TexturHashID.ToString("X"); if (HashList.ContainsKey(model.Meshes[i].TexturHashID)) { diffuseName = HashList[model.Meshes[i].TexturHashID]; } var texUnit = 1; renderedMesh.Material.TextureMaps.Add(new STGenericMatTexture() { textureUnit = texUnit++, Type = STGenericMatTexture.TextureType.Diffuse, Name = diffuseName, }); } if (SectionLookup.ContainsKey(SectionMagic.MaterialData)) { var materialSecton = SectionLookup[SectionMagic.MaterialData]; reader.SeekBegin(materialSecton.Position + model.Meshes[i].MaterialOffset); var renderedMesh = (RenderableMeshWrapper)Renderer.Meshes[i]; renderedMesh.Material = new STGenericMaterial(); switch (model.Meshes[i].MaterailPreset) { case MaterailPresets.EnvDiffuseDamage: { uint diffuseMapHashID = reader.ReadUInt32(); uint diffuseMapParam = reader.ReadUInt32(); string diffuseName = diffuseMapHashID.ToString("X"); if (HashList.ContainsKey(diffuseMapHashID)) { diffuseName = HashList[diffuseMapHashID]; } var texUnit = 1; renderedMesh.Material.TextureMaps.Add(new STGenericMatTexture() { textureUnit = texUnit++, Type = STGenericMatTexture.TextureType.Diffuse, Name = diffuseName, }); } break; default: { uint diffuseMapHashID = reader.ReadUInt32(); string diffuseName = diffuseMapHashID.ToString("X"); if (HashList.ContainsKey(diffuseMapHashID)) { diffuseName = HashList[diffuseMapHashID]; } var texUnit = 1; renderedMesh.Material.TextureMaps.Add(new STGenericMatTexture() { textureUnit = texUnit++, Type = STGenericMatTexture.TextureType.Diffuse, Name = diffuseName, }); } break; } } } } List <BoneListEntry> BoneLists = new List <BoneListEntry>(); List <uint> boneHashOrder = new List <uint>(); TreeNode parentBoneList = new TreeNode("Bone List"); Nodes.Add(parentBoneList); if (SectionLookup.ContainsKey(SectionMagic.SkeletonData)) { var skeletonSection = SectionLookup[SectionMagic.SkeletonData]; reader.SeekBegin(skeletonSection.Position); //Read all sub sections while (reader.Position < skeletonSection.Position + skeletonSection.Size) { uint magic = reader.ReadUInt32(); uint sectionSize = reader.ReadUInt32(); long pos = reader.Position; BoneListEntry entry = new BoneListEntry(); BoneLists.Add(entry); //Bone hashes appear for each mesh if it uses rigging //Meshes index these lists for rigging if ((SectionMagic)magic == SectionMagic.BoneHashes) { TreeNode boneListNode = new TreeNode("Mesh Bone List"); parentBoneList.Nodes.Add(boneListNode); uint numHashes = sectionSize / 4; for (int i = 0; i < numHashes; i++) { entry.Hashes.Add(reader.ReadUInt32()); if (IsGamecube) { reader.ReadUInt32(); } string hashName = entry.Hashes[i].ToString("X"); if (HashList.ContainsKey(entry.Hashes[i])) { hashName = HashList[entry.Hashes[i]]; } boneListNode.Nodes.Add(hashName); } } if ((SectionMagic)magic == SectionMagic.BoneData) { uint numBones = sectionSize / 68; for (int i = 0; i < numBones; i++) { reader.SeekBegin(pos + i * 68); BoneEntry bone = new BoneEntry(); bone.HashID = reader.ReadUInt32(); reader.ReadUInt32(); //unk reader.ReadUInt32(); //unk reader.ReadUInt32(); //unk reader.ReadSingle(); //0 bone.Scale = new Vector3( reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); reader.ReadSingle(); //0 bone.Rotate = new Vector3( reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); reader.ReadSingle(); //0 bone.Translate = new Vector3( reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); reader.ReadSingle(); //1 // bone.Translate = Vector3.TransformPosition(bone.Translate, TransformMatrix); entry.Bones.Add(bone); } } reader.SeekBegin(pos + sectionSize); } } List <BoneEntry> BoneListSorted = new List <BoneEntry>(); foreach (var hash in boneHashOrder) { foreach (var boneList in BoneLists) { foreach (var bone in boneList.Bones) { if (bone.HashID == hash) { BoneListSorted.Add(bone); } } } } STSkeleton skeleton = new STSkeleton(); DrawableContainer.Drawables.Add(skeleton); foreach (var boneList in BoneLists) { foreach (var bone in boneList.Bones) { STBone stBone = new STBone(skeleton); skeleton.bones.Add(stBone); stBone.Text = bone.HashID.ToString("X"); if (HashList.ContainsKey(bone.HashID)) { stBone.Text = HashList[bone.HashID]; } stBone.Position = bone.Translate; stBone.EulerRotation = bone.Rotate; stBone.Scale = new Vector3(0.2f, 0.2f, 0.2f); stBone.RotationType = STBone.BoneRotationType.Euler; } } skeleton.reset(); skeleton.update(); TreeNode skeletonNode = new TreeNode("Skeleton"); Nodes.Add(skeletonNode); foreach (var bone in skeleton.bones) { if (bone.Parent == null) { skeletonNode.Nodes.Add(bone); } } } } }
public void LoadFile(Model model, GFBMDL file, GFBMDL_Render Renderer) { Model = model; ParentFile = file; Renderer.Meshes.Clear(); for (int m = 0; m < Model.Materials?.Count; m++) { GenericMaterials.Add(new GFLXMaterialData(this, Model.Materials[m])); } List <int> SkinningIndices = new List <int>(); for (int b = 0; b < Model.Bones?.Count; b++) { var bone = Model.Bones[b]; Skeleton.bones.Add(new GFLXBone(this, bone)); if (bone.RigidCheck == null) { SkinningIndices.Add(b); } } Skeleton.reset(); Skeleton.update(); for (int g = 0; g < Model.Groups?.Count; g++) { var group = Model.Groups[g]; var mesh = Model.Meshes[g]; OpenTK.Matrix4 transform = OpenTK.Matrix4.Identity; GFLXMesh genericMesh = new GFLXMesh(this, group, mesh); genericMesh.Checked = true; genericMesh.ImageKey = "model"; genericMesh.SelectedImageKey = "model"; int boneIndex = (int)group.BoneIndex; if (boneIndex < Skeleton.bones.Count && boneIndex > 0) { genericMesh.BoneIndex = boneIndex; transform = Skeleton.bones[boneIndex].Transform; genericMesh.Text = Skeleton.bones[boneIndex].Text; } // if (group.MeshID < Skeleton.bones.Count && group.MeshID > 0) // genericMesh.Text = Skeleton.bones[(int)group.MeshID].Text; Renderer.Meshes.Add(genericMesh); GenericMeshes.Add(genericMesh); //Load the vertex data genericMesh.Transform = transform; genericMesh.vertices = GFLXMeshBufferHelper.LoadVertexData(mesh, transform, SkinningIndices); genericMesh.FlipUvsVertical(); //Load faces for (int p = 0; p < mesh.Polygons?.Count; p++) { var poly = mesh.Polygons[p]; var polygonGroup = new STGenericPolygonGroup(); polygonGroup.MaterialIndex = (int)poly.MaterialIndex; genericMesh.PolygonGroups.Add(polygonGroup); if (GenericMaterials.Count > poly.MaterialIndex) { polygonGroup.Material = GenericMaterials[(int)poly.MaterialIndex]; } for (int f = 0; f < poly.Faces?.Count; f++) { polygonGroup.faces.Add((int)poly.Faces[f]); } } } }
public void Load(System.IO.Stream stream) { CanSave = true; Text = FileName; //Set renderer //Load it to a drawables list Renderer = new MDL_Renderer(); Skeleton = new STSkeleton(); DrawableContainer.Name = FileName; DrawableContainer.Drawables.Add(Renderer); DrawableContainer.Drawables.Add(Skeleton); using (var reader = new FileReader(stream)) { reader.SetByteOrder(true); string[] JointNames = new string[0]; Joint[] Joints = new Joint[0]; Envelopes = new Envelope[0]; while (reader.EndOfStream == false) { long chunkStart = reader.Position; int opcode = reader.ReadInt32(); int lengthOfStruct = reader.ReadInt32(); // basic error checking if ((chunkStart & 0x1F) != 0) { throw new Exception($"Chunk start ({chunkStart}) not on boundary!"); } switch ((ChunkNames)opcode) { case ChunkNames.VertexPosition: Vertices = ReadVector3Array(reader); break; case ChunkNames.VertexNormal: VertexNormals = ReadVector3Array(reader); break; case ChunkNames.VertexColor: Colors = ReadVertexColors(reader); break; case ChunkNames.Mesh: ReadMeshChunk(reader); break; case ChunkNames.Envelope: Envelopes = ParseArray <Envelope>(reader); break; case ChunkNames.JointName: JointNames = ReadStrings(reader); break; case ChunkNames.Joint: Joints = ParseArray <Joint>(reader); break; default: reader.Seek(lengthOfStruct, System.IO.SeekOrigin.Current); break; } } for (int i = 0; i < Joints.Length; i++) { STBone bone = new STBone(Skeleton); bone.parentIndex = Joints[i].ParentIndex; bone.Position = Joints[i].Position; bone.EulerRotation = Joints[i].Rotation; bone.Scale = Joints[i].Scale; Skeleton.bones.Add(bone); } Skeleton.reset(); Skeleton.update(); foreach (var mesh in Renderer.Meshes) { for (int v = 0; v < mesh.vertices.Count; v++) { var vertex = mesh.vertices[v]; if (vertex.boneIds.Count == 1) { var transform = Skeleton.bones[vertex.boneIds[0]].Transform; vertex.pos = Vector3.TransformPosition(vertex.pos, transform); } } } } }
public void Load(System.IO.Stream stream) { CanSave = false; Renderer = new CMB_Renderer(); DrawableContainer.Drawables.Add(Renderer); Skeleton = new STSkeleton(); //These models/skeletons come out massive so scale them with an overridden scale Skeleton.PreviewScale = Renderer.PreviewScale; Skeleton.BonePointScale = 40; Renderer.Skeleton = Skeleton; DrawableContainer.Drawables.Add(Skeleton); cmb.ReadCMB(stream); Text = cmb.Header.Name; DrawableContainer.Name = Text; //Load textures if (cmb.TexturesChunk != null) { texFolder = new TextureFolder("Texture"); TreeNode meshFolder = new TreeNode("Meshes"); TreeNode materialFolder = new TreeNode("Materials"); TreeNode skeletonFolder = new TreeNode("Skeleton"); bool HasTextures = cmb.TexturesChunk.Textures != null && cmb.TexturesChunk.Textures.Count != 0; bool HasMeshes = cmb.MeshesChunk.SHP.SEPDs != null && cmb.MeshesChunk.SHP.SEPDs.Count != 0; bool HasSkeleton = cmb.SkeletonChunk != null && cmb.SkeletonChunk.Bones.Count != 0; bool HasMaterials = cmb.MaterialsChunk != null && cmb.MaterialsChunk.Materials.Count != 0; if (HasSkeleton) { var bonesOrdered = cmb.SkeletonChunk.Bones.OrderBy(x => x.ID).ToList(); foreach (var bone in bonesOrdered) { STBone genericBone = new STBone(Skeleton); genericBone.parentIndex = bone.ParentID; genericBone.Checked = true; genericBone.Text = $"Bone {bone.ID}"; genericBone.RotationType = STBone.BoneRotationType.Euler; genericBone.Position = new OpenTK.Vector3( bone.Translation.X, bone.Translation.Y, bone.Translation.Z ); genericBone.EulerRotation = new OpenTK.Vector3( bone.Rotation.X, bone.Rotation.Y, bone.Rotation.Z ); genericBone.Scale = new OpenTK.Vector3( bone.Scale.X, bone.Scale.Y, bone.Scale.Z ); Skeleton.bones.Add(genericBone); } foreach (var bone in Skeleton.bones) { if (bone.Parent == null) { skeletonFolder.Nodes.Add(bone); } } Skeleton.reset(); Skeleton.update(); } if (HasTextures) { int texIndex = 0; foreach (var tex in cmb.TexturesChunk.Textures) { var texWrapper = new CTXB.TextureWrapper(new CTXB.Texture()); texWrapper.Text = $"Texture {texIndex++}"; texWrapper.ImageKey = "texture"; texWrapper.SelectedImageKey = texWrapper.ImageKey; if (tex.Name != string.Empty) { texWrapper.Text = tex.Name; } texWrapper.Width = tex.Width; texWrapper.Height = tex.Height; CTXB.Texture.TextureFormat Format = (CTXB.Texture.TextureFormat)((tex.DataType << 16) | tex.ImageFormat); texWrapper.Format = CTR_3DS.ConvertPICAToGenericFormat(CTXB.Texture.FormatList[Format]); texWrapper.ImageData = tex.ImageData; texFolder.Nodes.Add(texWrapper); Renderer.TextureList.Add(texWrapper); } } if (HasMaterials) { int materialIndex = 0; foreach (var mat in cmb.MaterialsChunk.Materials) { H3DMaterial H3D = ToH3DMaterial(mat); CMBMaterialWrapper material = new CMBMaterialWrapper(mat, this, H3D); material.Text = $"Material {materialIndex++}"; materialFolder.Nodes.Add(material); Materials.Add(material); bool HasDiffuse = false; foreach (var tex in mat.TextureMappers) { if (tex.TextureID != -1) { CMBTextureMapWrapper matTexture = new CMBTextureMapWrapper(tex, this); matTexture.TextureIndex = tex.TextureID; material.TextureMaps.Add(matTexture); if (tex.TextureID < Renderer.TextureList.Count && tex.TextureID >= 0) { matTexture.Name = Renderer.TextureList[tex.TextureID].Text; material.Nodes.Add(matTexture.Name); } if (!HasDiffuse && matTexture.Name != "bg_syadowmap") //Quick hack till i do texture env stuff { matTexture.Type = STGenericMatTexture.TextureType.Diffuse; HasDiffuse = true; } } } } } if (HasMeshes) { int MeshIndex = 0; foreach (var mesh in cmb.MeshesChunk.MSHS.Meshes) { STGenericMaterial mat = Materials[mesh.MaterialIndex]; CmbMeshWrapper genericMesh = new CmbMeshWrapper(mat); genericMesh.Text = $"Mesh_{MeshIndex++}_ID_{mesh.VisIndex}"; genericMesh.MaterialIndex = mesh.MaterialIndex; var shape = cmb.MeshesChunk.SHP.SEPDs[mesh.SEPDIndex]; genericMesh.Mesh = shape; List <ushort> SkinnedBoneTable = new List <ushort>(); foreach (var prim in shape.PRMS) { if (prim.BoneIndices != null) { SkinnedBoneTable.AddRange(prim.BoneIndices); } } //Now load the vertex and face data foreach (var prm in shape.PRMS) { if (shape.HasPosition) { int VertexCount = prm.VertexCount; for (int v = 0; v < VertexCount; v++) { Vertex vert = new Vertex(); vert.pos = new OpenTK.Vector3( prm.Vertices.Position[v].X, prm.Vertices.Position[v].Y, prm.Vertices.Position[v].Z); if (shape.HasNormal) { vert.nrm = new OpenTK.Vector3( prm.Vertices.Normal[v].X, prm.Vertices.Normal[v].Y, prm.Vertices.Normal[v].Z).Normalized(); } if (shape.HasColor) { vert.col = new OpenTK.Vector4( prm.Vertices.Color[v].X, prm.Vertices.Color[v].Y, prm.Vertices.Color[v].Z, prm.Vertices.Color[v].W).Normalized(); } if (shape.HasUV0) { vert.uv0 = new OpenTK.Vector2(prm.Vertices.UV0[v].X, -prm.Vertices.UV0[v].Y + 1); } if (shape.HasUV1) { vert.uv1 = new OpenTK.Vector2(prm.Vertices.UV1[v].X, -prm.Vertices.UV1[v].Y + 1); } if (shape.HasUV2) { vert.uv2 = new OpenTK.Vector2(prm.Vertices.UV2[v].X, -prm.Vertices.UV2[v].Y + 1); } if (prm.SkinningMode == SkinningMode.Smooth) { //Indices if (shape.HasIndices) { if (shape.BoneDimensionCount >= 1) { vert.boneIds.Add((int)prm.Vertices.BoneIndices[v].X); } if (shape.BoneDimensionCount >= 2) { vert.boneIds.Add((int)prm.Vertices.BoneIndices[v].Y); } if (shape.BoneDimensionCount >= 3) { vert.boneIds.Add((int)prm.Vertices.BoneIndices[v].Z); } if (shape.BoneDimensionCount >= 4) { vert.boneIds.Add((int)prm.Vertices.BoneIndices[v].W); } } //Weights if (shape.HasWeights) { if (shape.BoneDimensionCount >= 1) { vert.boneWeights.Add(prm.Vertices.BoneWeights[v].X); } if (shape.BoneDimensionCount >= 2) { vert.boneWeights.Add(prm.Vertices.BoneWeights[v].Y); } if (shape.BoneDimensionCount >= 3) { vert.boneWeights.Add(prm.Vertices.BoneWeights[v].Z); } if (shape.BoneDimensionCount >= 4) { vert.boneWeights.Add(prm.Vertices.BoneWeights[v].W); } } } if (prm.SkinningMode == SkinningMode.Rigid) { int boneId = (int)prm.Vertices.BoneIndices[v].X; vert.boneIds.Add(boneId); vert.boneWeights.Add(1); vert.pos = OpenTK.Vector3.TransformPosition(vert.pos, Skeleton.bones[boneId].Transform); if (shape.HasNormal) { vert.nrm = OpenTK.Vector3.TransformNormal(vert.nrm, Skeleton.bones[boneId].Transform); } } if (prm.SkinningMode == SkinningMode.Mixed) { int boneId = prm.BoneIndices[0]; vert.boneIds.Add(boneId); vert.boneWeights.Add(1); vert.pos = OpenTK.Vector3.TransformPosition(vert.pos, Skeleton.bones[boneId].Transform); if (shape.HasNormal) { vert.nrm = OpenTK.Vector3.TransformNormal(vert.nrm, Skeleton.bones[boneId].Transform); } } genericMesh.vertices.Add(vert); } } STGenericPolygonGroup group = new STGenericPolygonGroup(); genericMesh.PolygonGroups.Add(group); for (int i = 0; i < prm.FaceIndices.Count; i++) { group.faces.Add((int)prm.FaceIndices[i].X); group.faces.Add((int)prm.FaceIndices[i].Y); group.faces.Add((int)prm.FaceIndices[i].Z); } } Renderer.Meshes.Add(genericMesh); meshFolder.Nodes.Add(genericMesh); } } if (meshFolder.Nodes.Count > 0) { Nodes.Add(meshFolder); } if (skeletonFolder.Nodes.Count > 0) { Nodes.Add(skeletonFolder); } if (materialFolder.Nodes.Count > 0) { Nodes.Add(materialFolder); } if (texFolder.Nodes.Count > 0) { Nodes.Add(texFolder); } } }
public void Load(System.IO.Stream stream) { DrawableContainer.Name = FileName; Renderer = new PunchOutWii_Renderer(); DrawableContainer.Drawables.Add(Renderer); Text = FileName; HeaderFile = new DictionaryFile(); HeaderFile.Read(new FileReader(stream), FilePath); var HashList = NLG_Common.HashNames; string DataFile = $"{FilePath.Replace(".dict", ".data")}"; if (System.IO.File.Exists(DataFile)) { using (var reader = new FileReader(DataFile, true)) { reader.SetByteOrder(true); TreeNode blocks = new TreeNode("Blocks"); TreeNode chunks = new TreeNode("Chunks"); TreeNode modelFolder = new TreeNode("Models"); foreach (var blockInfo in HeaderFile.Blocks) { ChunkViewer chunkNode = new ChunkViewer("block"); if (blockInfo.Size > 0) { blocks.Nodes.Add(chunkNode); } chunkNode.FileData = new SubStream(reader.BaseStream, blockInfo.Offset, blockInfo.Size); } List <PO_Texture> currentTextures = new List <PO_Texture>(); List <ModelFileData> modelData = new List <ModelFileData>(); ModelFileData currentModel = null; STTextureFolder textureFolder = new STTextureFolder("Textures"); Nodes.Add(blocks); Nodes.Add(chunks); Nodes.Add(textureFolder); Nodes.Add(modelFolder); foreach (var chunk in HeaderFile.DataChunks) { if (chunk.BlockIndex == -1) { continue; } ChunkViewer chunkNode = new ChunkViewer(chunk.Type.ToString("") + " " + chunk.Type.ToString("X")); chunks.Nodes.Add(chunkNode); var blockInfo = HeaderFile.Blocks[chunk.BlockIndex]; if (blockInfo.Offset + chunk.Offset + chunk.Size > reader.BaseStream.Length) { continue; } chunkNode.FileData = new SubStream(reader.BaseStream, blockInfo.Offset + chunk.Offset, chunk.Size); uint chunkPos = blockInfo.Offset + chunk.Offset; reader.SeekBegin(chunkPos); switch (chunk.Type) { case SectionMagic.MaterialData: currentModel = new ModelFileData(); currentModel.MaterialOffset = chunkPos; modelData.Add(currentModel); break; case SectionMagic.TextureHeaders: uint numTextures = chunk.Size / 96; for (int i = 0; i < numTextures; i++) { var tex = new PO_Texture(); tex.ImageKey = "texture"; tex.SelectedImageKey = "texture"; tex.Read(reader); tex.Text = tex.HashID.ToString("X"); if (HashList.ContainsKey(tex.HashID)) { tex.Text = HashList[tex.HashID]; } currentTextures.Add(tex); Renderer.TextureList.Add(tex.Text, tex); textureFolder.Nodes.Add(tex); } break; case SectionMagic.TextureData: for (int i = 0; i < currentTextures.Count; i++) { reader.SeekBegin(chunkPos + currentTextures[i].DataOffset); currentTextures[i].ImageData = reader.ReadBytes((int)currentTextures[i].ImageSize); } break; case SectionMagic.IndexData: currentModel.indexBufferOffset = chunkPos; break; case SectionMagic.VertexData: currentModel.vertexBufferOffset = chunkPos; break; case SectionMagic.MeshData: uint numMeshes = chunk.Size / 52; for (int i = 0; i < numMeshes; i++) { reader.SeekBegin(chunkPos + (i * 52)); PO_Mesh mesh = new PO_Mesh(reader); currentModel.meshes.Add(mesh); } break; case SectionMagic.VertexAttributePointerData: uint numAttributes = chunk.Size / 8; for (int i = 0; i < numAttributes; i++) { PO_VertexAttribute att = new PO_VertexAttribute(); att.Offset = reader.ReadUInt32(); att.Type = reader.ReadByte(); att.Stride = reader.ReadByte(); reader.ReadUInt16(); currentModel.attributes.Add(att); } break; case SectionMagic.ModelData: uint numModels = chunk.Size / 12; Console.WriteLine($"numModels {numModels}"); for (int i = 0; i < numModels; i++) { PO_Model mdl = new PO_Model(); mdl.ParentDictionary = this; mdl.HashID = reader.ReadUInt32(); mdl.NumMeshes = reader.ReadUInt32(); reader.ReadUInt32(); //0 currentModel.models.Add(mdl); } break; case SectionMagic.BoneData: STSkeleton Skeleton = new STSkeleton(); DrawableContainer.Drawables.Add(Skeleton); uint numBones = chunk.Size / 68; for (int i = 0; i < numBones; i++) { reader.SeekBegin(chunkPos + (i * 68)); uint HashID = reader.ReadUInt32(); reader.ReadUInt32(); //unk reader.ReadUInt32(); //unk reader.ReadUInt32(); //unk reader.ReadSingle(); //0 STBone bone = new STBone(Skeleton); var Scale = new OpenTK.Vector3( reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); reader.ReadSingle(); //0 bone.EulerRotation = new OpenTK.Vector3( reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); reader.ReadSingle(); //0 bone.Position = new OpenTK.Vector3( reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); reader.ReadSingle(); //1 bone.Text = HashID.ToString("X"); if (NLG_Common.HashNames.ContainsKey(HashID)) { bone.Text = NLG_Common.HashNames[HashID]; } else { Console.WriteLine($"bone hash {HashID}"); } bone.Scale = new Vector3(0.2f, 0.2f, 0.2f); bone.RotationType = STBone.BoneRotationType.Euler; Skeleton.bones.Add(bone); } Skeleton.reset(); Skeleton.update(); break; } } foreach (var modelFile in modelData) { int pointerIndex = 0; foreach (var model in modelFile.models) { model.Text = model.HashID.ToString("X"); if (HashList.ContainsKey(model.HashID)) { model.Text = HashList[model.HashID]; } modelFolder.Nodes.Add(model); for (int i = 0; i < model.NumMeshes; i++) { var mesh = modelFile.meshes[i]; RenderableMeshWrapper genericMesh = new RenderableMeshWrapper(); model.Nodes.Add(genericMesh); model.RenderedMeshes.Add(genericMesh); Renderer.Meshes.Add(genericMesh); genericMesh.Text = mesh.HashID.ToString("X"); if (HashList.ContainsKey(mesh.HashID)) { genericMesh.Text = HashList[mesh.HashID]; } string material = mesh.MaterialHashID.ToString("X"); if (HashList.ContainsKey(mesh.MaterialHashID)) { material = HashList[mesh.MaterialHashID]; } genericMesh.Nodes.Add(material); genericMesh.Material = new STGenericMaterial(); reader.SeekBegin(modelFile.MaterialOffset + mesh.MaterialOffset); switch (mesh.MaterailPreset) { case MaterailPresets.EnvDiffuseDamage: { uint diffuseMapHashID = reader.ReadUInt32(); uint diffuseMapParam = reader.ReadUInt32(); uint envSpecMapHashID = reader.ReadUInt32(); uint envSpecMapParam = reader.ReadUInt32(); uint specMapHashID = reader.ReadUInt32(); uint specMapParam = reader.ReadUInt32(); uint megaStrikeMapHashID = reader.ReadUInt32(); uint megaStrikeMapParam = reader.ReadUInt32(); uint dirtMapHashID = reader.ReadUInt32(); uint dirtMapParam = reader.ReadUInt32(); uint iceMapHashID = reader.ReadUInt32(); uint iceMapParam = reader.ReadUInt32(); string diffuseName = diffuseMapHashID.ToString("X"); if (HashList.ContainsKey(diffuseMapHashID)) { diffuseName = HashList[diffuseMapHashID]; } var texUnit = 1; genericMesh.Material.TextureMaps.Add(new STGenericMatTexture() { textureUnit = texUnit++, Type = STGenericMatTexture.TextureType.Diffuse, Name = diffuseName, }); } break; default: { uint diffuseMapHashID = reader.ReadUInt32(); string diffuseName = diffuseMapHashID.ToString("X"); if (HashList.ContainsKey(diffuseMapHashID)) { diffuseName = HashList[diffuseMapHashID]; } Console.WriteLine($"diffuseName {diffuseName}"); var texUnit = 1; genericMesh.Material.TextureMaps.Add(new STGenericMatTexture() { textureUnit = texUnit++, Type = STGenericMatTexture.TextureType.Diffuse, Name = diffuseName, }); } break; } Console.WriteLine($"mesh {i}"); STGenericPolygonGroup polyGroup = new STGenericPolygonGroup(); genericMesh.PolygonGroups.Add(polyGroup); reader.SeekBegin(modelFile.indexBufferOffset + mesh.IndexStartOffset); List <int> faces = new List <int>(); for (int f = 0; f < mesh.IndexCount; f++) { if (mesh.IndexFormat == 0) { polyGroup.faces.Add(reader.ReadUInt16()); } else { polyGroup.faces.Add(reader.ReadByte()); } } if (mesh.FaceType == PO_Mesh.PolygonType.TriangleStrips) { polyGroup.PrimativeType = STPrimitiveType.TrangleStrips; } else { polyGroup.PrimativeType = STPrimitiveType.Triangles; } for (int a = 0; a < mesh.NumAttributePointers; a++) { Console.WriteLine($"pointer {genericMesh.Text} { modelFile.vertexBufferOffset + modelFile.attributes[pointerIndex + a].Offset}"); } for (int v = 0; v < mesh.VertexCount; v++) { Vertex vert = new Vertex(); genericMesh.vertices.Add(vert); int attributeIndex = 0; for (int a = 0; a < mesh.NumAttributePointers; a++) { var pointer = modelFile.attributes[pointerIndex + a]; reader.SeekBegin(modelFile.vertexBufferOffset + pointer.Offset + (pointer.Stride * v)); if (attributeIndex == 0) { if (pointer.Stride == 12) { vert.pos = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); } } if (attributeIndex == 1) { if (pointer.Stride == 12) { vert.nrm = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); } } if (attributeIndex == 2) { if (pointer.Stride == 4) { vert.uv0 = new Vector2(reader.ReadUInt16() / 1024f, reader.ReadUInt16() / 1024f); } } /* if (pointer.Type == 0xD4) * { * vert.boneIds = new List<int>() { reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte() }; * } * if (pointer.Type == 0xB0) * { * vert.boneWeights = new List<float>() { reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() }; * }*/ attributeIndex++; } } genericMesh.TransformPosition(new Vector3(0), new Vector3(-90, 0, 0), new Vector3(1)); pointerIndex += mesh.NumAttributePointers; } } } } } }