private static void Write(BinaryWriter Writer, PICAAttribute Attrib, Vector4 v, int i) { switch (i) { case 0: Write(Writer, Attrib, v.X); break; case 1: Write(Writer, Attrib, v.Y); break; case 2: Write(Writer, Attrib, v.Z); break; case 3: Write(Writer, Attrib, v.W); break; } }
private static void Write(BinaryWriter Writer, PICAAttribute Attrib, float Value) { Value /= Attrib.Scale; if (Attrib.Format != PICAAttributeFormat.Float) { //Due to float lack of precision it's better to round the number, //because directly casting it will always use the lowest number that //may cause issues for values that float can't represent (like 0.1). Value = (float)Math.Round(Value); } switch (Attrib.Format) { case PICAAttributeFormat.Byte: Writer.Write((sbyte)Value); break; case PICAAttributeFormat.Ubyte: Writer.Write((byte)Value); break; case PICAAttributeFormat.Short: Writer.Write((short)Value); break; case PICAAttributeFormat.Float: Writer.Write(Value); break; } }
public GFMesh(BinaryReader Reader) : this() { GFSection MeshSection = new GFSection(Reader); long Position = Reader.BaseStream.Position; uint NameHash = Reader.ReadUInt32(); string NameStr = Reader.ReadPaddedString(0x40); Name = NameStr; Reader.ReadUInt32(); BBoxMinVector = Reader.ReadVector4(); //Is it right? seems to be 0, 0, 0, 1 BBoxMaxVector = Reader.ReadVector4(); //Is it right? seems to be 0, 0, 0, 1 uint SubMeshesCount = Reader.ReadUInt32(); BoneIndicesPerVertex = Reader.ReadInt32(); Reader.BaseStream.Seek(0x10, SeekOrigin.Current); //Padding List <uint[]> CmdList = new List <uint[]>(); uint CommandsLength; uint CommandIndex; uint CommandsCount; uint Padding; do { CommandsLength = Reader.ReadUInt32(); CommandIndex = Reader.ReadUInt32(); CommandsCount = Reader.ReadUInt32(); Padding = Reader.ReadUInt32(); uint[] Commands = new uint[CommandsLength >> 2]; for (int Index = 0; Index < Commands.Length; Index++) { Commands[Index] = Reader.ReadUInt32(); } CmdList.Add(Commands); }while (CommandIndex < CommandsCount - 1); SubMeshSize[] SMSizes = new SubMeshSize[SubMeshesCount]; //Add SubMesh with Hash, Name and Bone Indices. //The rest is added latter (because the data is split inside the file). for (int MeshIndex = 0; MeshIndex < SubMeshesCount; MeshIndex++) { GFSubMesh SM = new GFSubMesh(); uint SMNameHash = Reader.ReadUInt32(); SM.Name = Reader.ReadIntLengthString(); SM.BoneIndicesCount = Reader.ReadByte(); for (int Bone = 0; Bone < 0x1f; Bone++) { SM.BoneIndices[Bone] = Reader.ReadByte(); } SMSizes[MeshIndex] = new SubMeshSize() { VerticesCount = Reader.ReadInt32(), IndicesCount = Reader.ReadInt32(), VerticesLength = Reader.ReadInt32(), IndicesLength = Reader.ReadInt32() }; SubMeshes.Add(SM); } for (int MeshIndex = 0; MeshIndex < SubMeshesCount; MeshIndex++) { GFSubMesh SM = SubMeshes[MeshIndex]; uint[] EnableCommands = CmdList[MeshIndex * 3 + 0]; uint[] DisableCommands = CmdList[MeshIndex * 3 + 1]; uint[] IndexCommands = CmdList[MeshIndex * 3 + 2]; PICACommandReader CmdReader; CmdReader = new PICACommandReader(EnableCommands); PICAVectorFloat24[] Fixed = new PICAVectorFloat24[12]; ulong BufferFormats = 0; ulong BufferAttributes = 0; ulong BufferPermutation = 0; int AttributesCount = 0; int AttributesTotal = 0; int FixedIndex = 0; while (CmdReader.HasCommand) { PICACommand Cmd = CmdReader.GetCommand(); uint Param = Cmd.Parameters[0]; switch (Cmd.Register) { case PICARegister.GPUREG_ATTRIBBUFFERS_FORMAT_LOW: BufferFormats |= (ulong)Param << 0; break; case PICARegister.GPUREG_ATTRIBBUFFERS_FORMAT_HIGH: BufferFormats |= (ulong)Param << 32; break; case PICARegister.GPUREG_ATTRIBBUFFER0_CONFIG1: BufferAttributes |= Param; break; case PICARegister.GPUREG_ATTRIBBUFFER0_CONFIG2: BufferAttributes |= (ulong)(Param & 0xffff) << 32; SM.VertexStride = (byte)(Param >> 16); AttributesCount = (int)(Param >> 28); break; case PICARegister.GPUREG_FIXEDATTRIB_INDEX: FixedIndex = (int)Param; break; case PICARegister.GPUREG_FIXEDATTRIB_DATA0: Fixed[FixedIndex].Word0 = Param; break; case PICARegister.GPUREG_FIXEDATTRIB_DATA1: Fixed[FixedIndex].Word1 = Param; break; case PICARegister.GPUREG_FIXEDATTRIB_DATA2: Fixed[FixedIndex].Word2 = Param; break; case PICARegister.GPUREG_VSH_NUM_ATTR: AttributesTotal = (int)(Param + 1); break; case PICARegister.GPUREG_VSH_ATTRIBUTES_PERMUTATION_LOW: BufferPermutation |= (ulong)Param << 0; break; case PICARegister.GPUREG_VSH_ATTRIBUTES_PERMUTATION_HIGH: BufferPermutation |= (ulong)Param << 32; break; } } for (int Index = 0; Index < AttributesTotal; Index++) { if (((BufferFormats >> (48 + Index)) & 1) != 0) { PICAAttributeName Name = (PICAAttributeName)((BufferPermutation >> Index * 4) & 0xf); float Scale = Name == PICAAttributeName.Color || Name == PICAAttributeName.BoneWeight ? Scales[1] : 1; SM.FixedAttributes.Add(new PICAFixedAttribute() { Name = Name, Value = Fixed[Index] * Scale }); } else { int PermutationIdx = (int)((BufferAttributes >> Index * 4) & 0xf); int AttributeName = (int)((BufferPermutation >> PermutationIdx * 4) & 0xf); int AttributeFmt = (int)((BufferFormats >> PermutationIdx * 4) & 0xf); PICAAttribute Attrib = new PICAAttribute() { Name = (PICAAttributeName)AttributeName, Format = (PICAAttributeFormat)(AttributeFmt & 3), Elements = (AttributeFmt >> 2) + 1, Scale = Scales[AttributeFmt & 3] }; if (Attrib.Name == PICAAttributeName.BoneIndex) { Attrib.Scale = 1; } SM.Attributes.Add(Attrib); } } CmdReader = new PICACommandReader(IndexCommands); uint BufferAddress = 0; uint BufferCount = 0; while (CmdReader.HasCommand) { PICACommand Cmd = CmdReader.GetCommand(); uint Param = Cmd.Parameters[0]; switch (Cmd.Register) { case PICARegister.GPUREG_INDEXBUFFER_CONFIG: BufferAddress = Param; break; case PICARegister.GPUREG_NUMVERTICES: BufferCount = Param; break; case PICARegister.GPUREG_PRIMITIVE_CONFIG: SM.PrimitiveMode = (PICAPrimitiveMode)(Param >> 8); break; } } SM.RawBuffer = Reader.ReadBytes(SMSizes[MeshIndex].VerticesLength); SM.Indices = new ushort[BufferCount]; long IndexAddress = Reader.BaseStream.Position; for (int Index = 0; Index < BufferCount; Index++) { if ((BufferAddress >> 31) != 0) { SM.Indices[Index] = Reader.ReadUInt16(); } else { SM.Indices[Index] = Reader.ReadByte(); } } Reader.BaseStream.Seek(IndexAddress + SMSizes[MeshIndex].IndicesLength, SeekOrigin.Begin); } Reader.BaseStream.Seek(Position + MeshSection.Length, SeekOrigin.Begin); }
public void Write(BinaryWriter Writer) { long StartPosition = Writer.BaseStream.Position; new GFSection(MagicStr).Write(Writer); GFNV1 FNV = new GFNV1(); FNV.Hash(Name); Writer.Write(FNV.HashCode); Writer.WritePaddedString(Name, 0x40); Writer.Write(0u); Writer.Write(BBoxMinVector); Writer.Write(BBoxMaxVector); Writer.Write((uint)SubMeshes.Count); Writer.Write(BoneIndicesPerVertex); Writer.Write(0xfffffffffffffffful); Writer.Write(0xfffffffffffffffful); for (int Index = 0; Index < SubMeshes.Count; Index++) { GFSubMesh SM = SubMeshes[Index]; PICACommandWriter CmdWriter; /* Enable commands */ CmdWriter = new PICACommandWriter(); ulong BufferFormats = 0; ulong BufferAttributes = 0; ulong BufferPermutation = 0; int AttributesTotal = 0; //Normal Attributes for (int Attr = 0; Attr < SM.Attributes.Count; Attr++) { PICAAttribute Attrib = SM.Attributes[Attr]; int Shift = AttributesTotal++ *4; ulong AttribFmt; AttribFmt = (ulong)Attrib.Format; AttribFmt |= (ulong)((Attrib.Elements - 1) & 3) << 2; BufferFormats |= AttribFmt << Shift; BufferPermutation |= (ulong)Attrib.Name << Shift; BufferAttributes |= (ulong)Attr << Shift; } BufferAttributes |= (ulong)(SM.VertexStride & 0xff) << 48; BufferAttributes |= (ulong)SM.Attributes.Count << 60; //Fixed Attributes foreach (PICAFixedAttribute Attrib in SM.FixedAttributes) { BufferFormats |= 1ul << (48 + AttributesTotal); BufferPermutation |= (ulong)Attrib.Name << AttributesTotal++ *4; } BufferFormats |= (ulong)(AttributesTotal - 1) << 60; CmdWriter.SetCommand(PICARegister.GPUREG_VSH_INPUTBUFFER_CONFIG, 0xa0000000u | (uint)(AttributesTotal - 1), 0xb); CmdWriter.SetCommand(PICARegister.GPUREG_VSH_NUM_ATTR, (uint)(AttributesTotal - 1), 1); CmdWriter.SetCommand(PICARegister.GPUREG_VSH_ATTRIBUTES_PERMUTATION_LOW, (uint)(BufferPermutation >> 0)); CmdWriter.SetCommand(PICARegister.GPUREG_VSH_ATTRIBUTES_PERMUTATION_HIGH, (uint)(BufferPermutation >> 32)); CmdWriter.SetCommand(PICARegister.GPUREG_ATTRIBBUFFERS_LOC, true, 0x03000000u, //Base Address (Place holder) (uint)(BufferFormats >> 0), (uint)(BufferFormats >> 32), 0x99999999u, //Attributes Buffer Address (Place holder) (uint)(BufferAttributes >> 0), (uint)(BufferAttributes >> 32)); for (int Attr = 0; Attr < SM.FixedAttributes.Count; Attr++) { PICAFixedAttribute Attrib = SM.FixedAttributes[Attr]; float Scale = Attrib.Name == PICAAttributeName.Color || Attrib.Name == PICAAttributeName.BoneWeight ? GFMesh.Scales[1] : 1; PICAFixedAttribute ScaledAttribute = new PICAFixedAttribute() { Name = Attrib.Name, Value = new PICAVectorFloat24(Attrib.Value.X, Attrib.Value.Y, Attrib.Value.Z, Attrib.Value.W) / Scale }; CmdWriter.SetCommand(PICARegister.GPUREG_FIXEDATTRIB_INDEX, true, (uint)(SM.Attributes.Count + Attr), ScaledAttribute.Value.Word0, ScaledAttribute.Value.Word1, ScaledAttribute.Value.Word2); } CmdWriter.WriteEnd(); WriteCmdsBuff(Writer, CmdWriter.GetBuffer(), Index * 3); /* Disable commands */ CmdWriter = new PICACommandWriter(); //Assuming that the Position isn't used as Fixed Attribute since this doesn't make sense. CmdWriter.SetCommand(PICARegister.GPUREG_ATTRIBBUFFER0_OFFSET, true, 0, 0, 0); for (int Attr = 1; Attr < 12; Attr++) { CmdWriter.SetCommand(PICARegister.GPUREG_ATTRIBBUFFER0_CONFIG2 + Attr * 3, 0); if (SM.FixedAttributes?.Any(x => (int)x.Name == Attr) ?? false) { CmdWriter.SetCommand(PICARegister.GPUREG_FIXEDATTRIB_INDEX, true, (uint)Attr, 0, 0, 0); } } CmdWriter.WriteEnd(); WriteCmdsBuff(Writer, CmdWriter.GetBuffer(), Index * 3 + 1); /* Index commands */ CmdWriter = new PICACommandWriter(); uint IdxFmtAddr = SM.IsIdx8Bits ? 0x01999999u : 0x81999999u; CmdWriter.SetCommand(PICARegister.GPUREG_RESTART_PRIMITIVE, true); CmdWriter.SetCommand(PICARegister.GPUREG_INDEXBUFFER_CONFIG, IdxFmtAddr); CmdWriter.SetCommand(PICARegister.GPUREG_NUMVERTICES, (uint)SM.Indices.Length); CmdWriter.SetCommand(PICARegister.GPUREG_START_DRAW_FUNC0, false, 1); CmdWriter.SetCommand(PICARegister.GPUREG_DRAWELEMENTS, true); CmdWriter.SetCommand(PICARegister.GPUREG_START_DRAW_FUNC0, true, 1); CmdWriter.SetCommand(PICARegister.GPUREG_VTX_FUNC, true); CmdWriter.SetCommand(PICARegister.GPUREG_PRIMITIVE_CONFIG, (uint)SM.PrimitiveMode << 8, 8); CmdWriter.SetCommand(PICARegister.GPUREG_PRIMITIVE_CONFIG, (uint)SM.PrimitiveMode << 8, 8); CmdWriter.WriteEnd(); WriteCmdsBuff(Writer, CmdWriter.GetBuffer(), Index * 3 + 2); } foreach (GFSubMesh SM in SubMeshes) { FNV = new GFNV1(); FNV.Hash(SM.Name); Writer.Write(FNV.HashCode); Writer.Write(GetPaddedLen4(SM.Name.Length)); Writer.WritePaddedString(SM.Name, GetPaddedLen4(SM.Name.Length)); Writer.Write(SM.BoneIndicesCount); byte[] BoneIndices = new byte[0x1f]; SM.BoneIndices.CopyTo(BoneIndices, 0); Writer.Write(BoneIndices); int VerticesCount = 0; if (SM.VertexStride != 0) { VerticesCount = SM.RawBuffer.Length / SM.VertexStride; } int VtxBuffLen = SM.RawBuffer.Length; int IdxBuffLen = SM.Indices.Length * (SM.IsIdx8Bits ? 1 : 2); Writer.Write(VerticesCount); Writer.Write(SM.Indices.Length); Writer.Write(GetPaddedLen16(VtxBuffLen)); Writer.Write(GetPaddedLen16(IdxBuffLen)); } foreach (GFSubMesh SM in SubMeshes) { long Position = Writer.BaseStream.Position; Writer.Write(SM.RawBuffer); int VertexBufferPaddingLength = GetPaddedLen16(SM.RawBuffer.Length) - SM.RawBuffer.Length; Writer.Write(new byte[VertexBufferPaddingLength]); /*while (((Writer.BaseStream.Position - Position) & 3) != 0) * { * Writer.Write((byte)0); * }*/ Position = Writer.BaseStream.Position; foreach (ushort Idx in SM.Indices) { if (SM.IsIdx8Bits) { Writer.Write((byte)Idx); } else { Writer.Write(Idx); } } /*while (((Writer.BaseStream.Position - Position) & 0xf) != 0) * { * Writer.Write((byte)0); * }*/ int IndexBufferLength = SM.Indices.Length * (SM.IsIdx8Bits ? 1 : 2); int IndexBufferPaddingLength = GetPaddedLen16(IndexBufferLength) - IndexBufferLength; Writer.Write(new byte[IndexBufferPaddingLength]); } Writer.Align(0x10, 0); Writer.Write(0ul); Writer.Write(0ul); long EndPosition = Writer.BaseStream.Position; Writer.BaseStream.Seek(StartPosition + 8, SeekOrigin.Begin); Writer.Write((uint)(EndPosition - StartPosition - 0x10)); Writer.BaseStream.Seek(EndPosition, SeekOrigin.Begin); }
public H3D ToH3D(string TextureSearchPath = null) { H3D Output = new H3D(); H3DModel Model = new H3DModel(); Model.Name = "Model"; ushort MaterialIndex = 0; if (Skeleton.Count > 0) { Model.Flags = H3DModelFlags.HasSkeleton; } Model.BoneScaling = H3DBoneScaling.Maya; Model.MeshNodesVisibility.Add(true); foreach (SMDMesh Mesh in Meshes) { Vector3 MinVector = new Vector3(); Vector3 MaxVector = new Vector3(); Dictionary <PICAVertex, int> Vertices = new Dictionary <PICAVertex, int>(); List <H3DSubMesh> SubMeshes = new List <H3DSubMesh>(); Queue <PICAVertex> VerticesQueue = new Queue <PICAVertex>(); foreach (PICAVertex Vertex in Mesh.Vertices) { VerticesQueue.Enqueue(Vertex); } while (VerticesQueue.Count > 2) { List <ushort> Indices = new List <ushort>(); List <ushort> BoneIndices = new List <ushort>(); int TriCount = VerticesQueue.Count / 3; while (TriCount-- > 0) { PICAVertex[] Triangle = new PICAVertex[3]; Triangle[0] = VerticesQueue.Dequeue(); Triangle[1] = VerticesQueue.Dequeue(); Triangle[2] = VerticesQueue.Dequeue(); List <ushort> TempIndices = new List <ushort>(); for (int Tri = 0; Tri < 3; Tri++) { PICAVertex Vertex = Triangle[Tri]; for (int i = 0; i < 4; i++) { ushort Index = (ushort)Vertex.Indices[i]; if (!(BoneIndices.Contains(Index) || TempIndices.Contains(Index))) { TempIndices.Add(Index); } } } if (BoneIndices.Count + TempIndices.Count > 20) { VerticesQueue.Enqueue(Triangle[0]); VerticesQueue.Enqueue(Triangle[1]); VerticesQueue.Enqueue(Triangle[2]); continue; } for (int Tri = 0; Tri < 3; Tri++) { PICAVertex Vertex = Triangle[Tri]; for (int Index = 0; Index < 4; Index++) { int BoneIndex = BoneIndices.IndexOf((ushort)Vertex.Indices[Index]); if (BoneIndex == -1) { BoneIndex = BoneIndices.Count; BoneIndices.Add((ushort)Vertex.Indices[Index]); } Vertex.Indices[Index] = BoneIndex; } if (Vertices.ContainsKey(Vertex)) { Indices.Add((ushort)Vertices[Vertex]); } else { Indices.Add((ushort)Vertices.Count); if (Vertex.Position.X < MinVector.X) { MinVector.X = Vertex.Position.X; } if (Vertex.Position.Y < MinVector.Y) { MinVector.Y = Vertex.Position.Y; } if (Vertex.Position.Z < MinVector.Z) { MinVector.Z = Vertex.Position.Z; } if (Vertex.Position.X > MaxVector.X) { MaxVector.X = Vertex.Position.X; } if (Vertex.Position.Y > MaxVector.Y) { MaxVector.Y = Vertex.Position.Y; } if (Vertex.Position.Z > MaxVector.Z) { MaxVector.Z = Vertex.Position.Z; } Vertices.Add(Vertex, Vertices.Count); } } } SubMeshes.Add(new H3DSubMesh() { Skinning = H3DSubMeshSkinning.Smooth, BoneIndicesCount = (ushort)BoneIndices.Count, BoneIndices = BoneIndices.ToArray(), Indices = Indices.ToArray() }); } List <PICAAttribute> Attributes = PICAAttribute.GetAttributes( PICAAttributeName.Position, PICAAttributeName.Normal, PICAAttributeName.Color, PICAAttributeName.TexCoord0, PICAAttributeName.BoneIndex, PICAAttributeName.BoneWeight); //Mesh H3DMesh M = new H3DMesh(Vertices.Keys, Attributes, SubMeshes) { Skinning = H3DMeshSkinning.Smooth, MeshCenter = (MinVector + MaxVector) * 0.5f, MaterialIndex = MaterialIndex }; //Material string TexName = Path.GetFileNameWithoutExtension(Mesh.MaterialName); string MatName = $"Mat{MaterialIndex++.ToString("D5")}_{TexName}"; H3DMaterial Material = H3DMaterial.GetSimpleMaterial(Model.Name, MatName, TexName); Model.Materials.Add(Material); if (TextureSearchPath != null && !Output.Textures.Contains(TexName)) { string TextureFile = Path.Combine(TextureSearchPath, Mesh.MaterialName); if (File.Exists(TextureFile)) { Output.Textures.Add(new H3DTexture(TextureFile)); } } M.UpdateBoolUniforms(Material); Model.AddMesh(M); } //Build Skeleton foreach (SMDBone Bone in Skeleton) { SMDNode Node = Nodes[Bone.NodeIndex]; Model.Skeleton.Add(new H3DBone() { Name = Node.Name, ParentIndex = (short)Node.ParentIndex, Translation = Bone.Translation, Rotation = Bone.Rotation, Scale = Vector3.One }); } //Calculate Absolute Inverse Transforms for all bones foreach (H3DBone Bone in Model.Skeleton) { Bone.CalculateTransform(Model.Skeleton); } Output.Models.Add(Model); Output.CopyMaterials(); return(Output); }
public MTAttributesGroup(BinaryReader Reader, uint StringsTblAddr) { byte AttrId = Reader.ReadByte(); //? ushort AttrCount = Reader.ReadUInt16(); //Bits 4-15 = Total attributes count byte Stride = Reader.ReadByte(); //In Word Count (32-bits) uint Padding = Reader.ReadUInt32(); //Not sure but seems to be always 0 so padding I guess AttrCount >>= 4; Stride *= 4; int RealOffset = 0; Attributes = new List <PICAAttribute>(); for (int j = 0; j < AttrCount; j++) { string Name = MTShaderEffects.GetName(Reader, StringsTblAddr); uint Format = Reader.ReadUInt32(); uint AttrIndex = (Format >> 0) & 0x3f; //Used when attribute is repeated usually uint AttrFormat = (Format >> 6) & 0x1f; //See AttributeFormat enumerator uint AttrElems = (Format >> 11) & 0x3; //2 = 2D, 3 = 3D, 4 = 4D, ... uint AttrOffset = (Format >> 24) & 0xff; //In Word Count (32-bits) AttrOffset *= 4; PICAAttribute Attr = new PICAAttribute(); switch (Name.ToLower()) { case "position": Attr.Name = PICAAttributeName.Position; break; case "normal": Attr.Name = PICAAttributeName.Normal; break; case "tangent": Attr.Name = PICAAttributeName.Tangent; break; case "color": Attr.Name = PICAAttributeName.Color; break; case "joint": Attr.Name = PICAAttributeName.BoneIndex; break; case "weight": Attr.Name = PICAAttributeName.BoneWeight; break; case "texcoord": if (AttrIndex < 3) { Attr.Name = (PICAAttributeName)((uint)PICAAttributeName.TexCoord0 + AttrIndex); } else { Attr.Name = PICAAttributeName.UserAttribute0; } break; default: Attr.Name = PICAAttributeName.UserAttribute0; break; } bool Norm = false; int Size = 1; switch ((AttributeFormat)AttrFormat) { case AttributeFormat.F32: Attr.Format = PICAAttributeFormat.Float; Norm = false; Size = 4; break; case AttributeFormat.S16: Attr.Format = PICAAttributeFormat.Short; Norm = false; Size = 2; break; case AttributeFormat.S16N: Attr.Format = PICAAttributeFormat.Short; Norm = true; Size = 2; break; case AttributeFormat.S8: Attr.Format = PICAAttributeFormat.Byte; Norm = false; Size = 1; break; case AttributeFormat.U8: Attr.Format = PICAAttributeFormat.Ubyte; Norm = false; Size = 1; break; case AttributeFormat.S8N: Attr.Format = PICAAttributeFormat.Byte; Norm = true; Size = 1; break; case AttributeFormat.U8N: Attr.Format = PICAAttributeFormat.Ubyte; Norm = true; Size = 1; break; case AttributeFormat.RGB: Attr.Format = PICAAttributeFormat.Ubyte; Norm = true; Size = 1; break; case AttributeFormat.RGBA: Attr.Format = PICAAttributeFormat.Ubyte; Norm = true; Size = 1; break; } Attr.Scale = Norm ? Scales[(int)Attr.Format] : 1; Attr.Elements = (int)AttrElems; Attributes.Add(Attr); RealOffset += (int)(Size * AttrElems); if (RealOffset >= Stride) { break; } } }
public H3D ToH3D(string TextureAndMtlSearchPath = null, bool noTextures = false) { H3D Output = new H3D(); Dictionary <string, OBJMaterial> Materials = new Dictionary <string, OBJMaterial>(); if (TextureAndMtlSearchPath != null) { TextReader Reader = null; if (textureOnly) { Reader = new StreamReader(textureOnlyFile); } else { string MaterialFile = Path.Combine(TextureAndMtlSearchPath, MtlFile); if (File.Exists(MaterialFile)) { Reader = new StreamReader(MaterialFile); } } if (Reader != null) { string MaterialName = null; OBJMaterial Material = default(OBJMaterial); for (string Line; (Line = Reader.ReadLine()) != null;) { string[] Params = Line.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); if (Params.Length == 0) { continue; } switch (Params[0]) { case "newmtl": if (Params.Length > 1) { if (MaterialName != null && Material.DiffuseTexture != null) { Materials.Add(MaterialName, Material); } Material = new OBJMaterial(); MaterialName = Line.Substring(Line.IndexOf(" ")).Trim(); } break; case "map_Kd": if (Params.Length > 1) { string Name = Line.Substring(Line.IndexOf(Params[1])); string TextureFile = Path.Combine(TextureAndMtlSearchPath, Name); string TextureName = Path.GetFileName(TextureFile); if (File.Exists(TextureFile) && !noTextures) { if (Output.Textures.Contains(TextureName)) { Output.Textures.Remove(Output.Textures[Output.Textures.Find(TextureName)]); } Output.Textures.Add(new H3DTexture(TextureFile)); } Material.DiffuseTexture = TextureName; } break; case "Ka": case "Kd": case "Ks": if (Params.Length >= 4) { Vector4 Color = new Vector4( float.Parse(Params[1], CultureInfo.InvariantCulture), float.Parse(Params[2], CultureInfo.InvariantCulture), float.Parse(Params[3], CultureInfo.InvariantCulture), 1); switch (Params[0]) { case "Ka": Material.Ambient = Color; break; case "Kd": Material.Diffuse = Color; break; case "Ks": Material.Specular = Color; break; } } break; } } Reader.Dispose(); if (MaterialName != null && !textureOnly) { Materials.Add(MaterialName, Material); } } } if (!textureOnly) { H3DModel Model = new H3DModel(); string newName = Microsoft.VisualBasic.Interaction.InputBox("Enter model name: ", "Name", Model.Name); if (newName != "") { Model.Name = newName; } ushort MaterialIndex = 0; Model.Flags = 0; Model.BoneScaling = H3DBoneScaling.Standard; Model.MeshNodesVisibility.Clear(); Model.Skeleton.Clear(); Model.MeshNodesVisibility.Add(true); float Height = 0; Meshes.Sort((x, y) => string.Compare(x.Name, y.Name)); foreach (OBJMesh Mesh in Meshes) { Vector3 MinVector = new Vector3(); Vector3 MaxVector = new Vector3(); Dictionary <PICAVertex, int> Vertices = new Dictionary <PICAVertex, int>(); List <H3DSubMesh> SubMeshes = new List <H3DSubMesh>(); Queue <PICAVertex> VerticesQueue = new Queue <PICAVertex>(); foreach (PICAVertex Vertex in Mesh.Vertices) { VerticesQueue.Enqueue(Vertex); } while (VerticesQueue.Count > 2) { List <ushort> Indices = new List <ushort>(); while (VerticesQueue.Count > 0) { for (int Tri = 0; Tri < 3; Tri++) { PICAVertex Vertex = VerticesQueue.Dequeue(); if (Mesh.Name.Contains("uncolor")) { Vertex.Color = Vector4.One; } if (Vertices.ContainsKey(Vertex)) { Indices.Add((ushort)Vertices[Vertex]); } else { Indices.Add((ushort)Vertices.Count); if (Vertex.Position.X < MinVector.X) { MinVector.X = Vertex.Position.X; } if (Vertex.Position.Y < MinVector.Y) { MinVector.Y = Vertex.Position.Y; } if (Vertex.Position.Z < MinVector.Z) { MinVector.Z = Vertex.Position.Z; } if (Vertex.Position.X > MaxVector.X) { MaxVector.X = Vertex.Position.X; } if (Vertex.Position.Y > MaxVector.Y) { MaxVector.Y = Vertex.Position.Y; } if (Vertex.Position.Z > MaxVector.Z) { MaxVector.Z = Vertex.Position.Z; } Vertices.Add(Vertex, Vertices.Count); } } } H3DSubMesh SM = new H3DSubMesh(); SM.BoneIndices = new ushort[] { }; SM.Skinning = H3DSubMeshSkinning.None; SM.Indices = Indices.ToArray(); SubMeshes.Add(SM); } //Mesh List <PICAAttribute> Attributes = PICAAttribute.GetAttributes( PICAAttributeName.Position, PICAAttributeName.Normal, PICAAttributeName.TexCoord0, PICAAttributeName.Color ); H3DMesh M = new H3DMesh(Vertices.Keys, Attributes, SubMeshes) { Skinning = H3DMeshSkinning.Rigid, MeshCenter = (MinVector + MaxVector) * 0.5f, MaterialIndex = MaterialIndex, }; if (Height < MaxVector.Y) { Height = MaxVector.Y; } //Material string MatName = $"Mat{MaterialIndex++.ToString("D5")}_{Mesh.MaterialName}"; H3DMaterial Material = H3DMaterial.GetSimpleMaterial(Model.Name, Mesh.MaterialName, Materials[Mesh.MaterialName].DiffuseTexture); Material.Texture0Name = Materials[Mesh.MaterialName].DiffuseTexture; Material.MaterialParams.FaceCulling = PICAFaceCulling.BackFace; Material.MaterialParams.Flags = H3DMaterialFlags.IsFragmentLightingEnabled | H3DMaterialFlags.IsVertexLightingEnabled | H3DMaterialFlags.IsFragmentLightingPolygonOffsetDirty; Material.MaterialParams.FragmentFlags = 0; Material.MaterialParams.LightSetIndex = 0; Material.MaterialParams.FogIndex = 0; Material.MaterialParams.LogicalOperation = PICALogicalOp.Noop; Material.MaterialParams.AmbientColor = RGBA.White; Material.MaterialParams.DiffuseColor = RGBA.White; Material.MaterialParams.EmissionColor = RGBA.Black; Material.MaterialParams.Specular0Color = RGBA.Black; Material.MaterialParams.Specular1Color = RGBA.Black; Material.MaterialParams.Constant0Color = RGBA.White; Material.MaterialParams.Constant1Color = RGBA.White; Material.MaterialParams.Constant2Color = RGBA.White; Material.MaterialParams.Constant3Color = RGBA.White; Material.MaterialParams.Constant4Color = RGBA.White; Material.MaterialParams.Constant5Color = RGBA.White; Material.MaterialParams.BlendColor = RGBA.Black; Material.MaterialParams.Constant2Assignment = 2; Material.MaterialParams.ColorScale = 1; Material.MaterialParams.FresnelSelector = H3DFresnelSelector.No; Material.MaterialParams.BumpMode = H3DBumpMode.NotUsed; Material.MaterialParams.BumpTexture = 0; Material.MaterialParams.PolygonOffsetUnit = 0; Material.MaterialParams.TexEnvBufferColor = RGBA.White; Material.MaterialParams.ColorOperation.FragOpMode = PICAFragOpMode.Default; Material.MaterialParams.ColorOperation.BlendMode = PICABlendMode.Blend; Material.MaterialParams.ColorBufferRead = true; Material.MaterialParams.ColorBufferWrite = true; Material.MaterialParams.StencilBufferRead = true; Material.MaterialParams.StencilBufferWrite = true; Material.MaterialParams.DepthBufferRead = true; Material.MaterialParams.DepthBufferWrite = false; Material.MaterialParams.TexEnvStages[0].Source.Color = new PICATextureCombinerSource[] { PICATextureCombinerSource.Texture0, PICATextureCombinerSource.PrimaryColor, PICATextureCombinerSource.Texture0 }; Material.MaterialParams.TexEnvStages[0].Source.Alpha = new PICATextureCombinerSource[] { PICATextureCombinerSource.Texture0, PICATextureCombinerSource.Texture0, PICATextureCombinerSource.Texture0 }; Material.MaterialParams.TexEnvStages[0].Operand.Color = new PICATextureCombinerColorOp[] { PICATextureCombinerColorOp.Color, PICATextureCombinerColorOp.Color, PICATextureCombinerColorOp.Color }; Material.MaterialParams.TexEnvStages[0].Operand.Alpha = new PICATextureCombinerAlphaOp[] { PICATextureCombinerAlphaOp.Alpha, PICATextureCombinerAlphaOp.Alpha, PICATextureCombinerAlphaOp.Alpha }; Material.MaterialParams.TexEnvStages[0].Combiner.Color = PICATextureCombinerMode.Modulate; Material.MaterialParams.TexEnvStages[0].Combiner.Alpha = PICATextureCombinerMode.Replace; Material.MaterialParams.TexEnvStages[0].Color = RGBA.White; Material.MaterialParams.TexEnvStages[0].Scale.Color = PICATextureCombinerScale.One; Material.MaterialParams.TexEnvStages[0].Scale.Alpha = PICATextureCombinerScale.One; Material.MaterialParams.TexEnvStages[0].UpdateColorBuffer = false; Material.MaterialParams.TexEnvStages[0].UpdateAlphaBuffer = false; Material.MaterialParams.TexEnvStages[1].Source.Color = new PICATextureCombinerSource[] { PICATextureCombinerSource.Previous, PICATextureCombinerSource.FragmentPrimaryColor, PICATextureCombinerSource.Previous }; Material.MaterialParams.TexEnvStages[1].Source.Alpha = new PICATextureCombinerSource[] { PICATextureCombinerSource.Previous, PICATextureCombinerSource.Previous, PICATextureCombinerSource.Previous }; Material.MaterialParams.TexEnvStages[1].Operand.Color = new PICATextureCombinerColorOp[] { PICATextureCombinerColorOp.Color, PICATextureCombinerColorOp.Color, PICATextureCombinerColorOp.Color }; Material.MaterialParams.TexEnvStages[1].Operand.Alpha = new PICATextureCombinerAlphaOp[] { PICATextureCombinerAlphaOp.Alpha, PICATextureCombinerAlphaOp.Alpha, PICATextureCombinerAlphaOp.Alpha }; Material.MaterialParams.TexEnvStages[1].Combiner.Color = PICATextureCombinerMode.Modulate; Material.MaterialParams.TexEnvStages[1].Combiner.Alpha = PICATextureCombinerMode.Replace; Material.MaterialParams.TexEnvStages[1].Color = RGBA.Black; Material.MaterialParams.TexEnvStages[1].Scale.Color = PICATextureCombinerScale.One; Material.MaterialParams.TexEnvStages[1].Scale.Alpha = PICATextureCombinerScale.One; Material.MaterialParams.TexEnvStages[1].UpdateColorBuffer = false; Material.MaterialParams.TexEnvStages[1].UpdateAlphaBuffer = false; Material.MaterialParams.TexEnvStages[2].Source.Color = new PICATextureCombinerSource[] { PICATextureCombinerSource.Previous, PICATextureCombinerSource.Previous, PICATextureCombinerSource.Previous }; Material.MaterialParams.TexEnvStages[2].Source.Alpha = new PICATextureCombinerSource[] { PICATextureCombinerSource.Previous, PICATextureCombinerSource.PrimaryColor, PICATextureCombinerSource.Previous }; Material.MaterialParams.TexEnvStages[2].Operand.Color = new PICATextureCombinerColorOp[] { PICATextureCombinerColorOp.Color, PICATextureCombinerColorOp.Color, PICATextureCombinerColorOp.Color }; Material.MaterialParams.TexEnvStages[2].Operand.Alpha = new PICATextureCombinerAlphaOp[] { PICATextureCombinerAlphaOp.Alpha, PICATextureCombinerAlphaOp.Alpha, PICATextureCombinerAlphaOp.Alpha }; Material.MaterialParams.TexEnvStages[2].Combiner.Color = PICATextureCombinerMode.Replace; Material.MaterialParams.TexEnvStages[2].Combiner.Alpha = PICATextureCombinerMode.Modulate; Material.MaterialParams.TexEnvStages[2].Color = RGBA.White; Material.MaterialParams.TexEnvStages[2].Scale.Color = PICATextureCombinerScale.One; Material.MaterialParams.TexEnvStages[2].Scale.Alpha = PICATextureCombinerScale.One; Material.MaterialParams.TexEnvStages[2].UpdateColorBuffer = false; Material.MaterialParams.TexEnvStages[2].UpdateAlphaBuffer = false; Material.MaterialParams.TexEnvStages[3].Source.Color = new PICATextureCombinerSource[] { PICATextureCombinerSource.Previous, PICATextureCombinerSource.Previous, PICATextureCombinerSource.Previous }; Material.MaterialParams.TexEnvStages[3].Source.Alpha = new PICATextureCombinerSource[] { PICATextureCombinerSource.Previous, PICATextureCombinerSource.Constant, PICATextureCombinerSource.Previous }; Material.MaterialParams.TexEnvStages[3].Operand.Color = new PICATextureCombinerColorOp[] { PICATextureCombinerColorOp.Color, PICATextureCombinerColorOp.Color, PICATextureCombinerColorOp.Color }; Material.MaterialParams.TexEnvStages[3].Operand.Alpha = new PICATextureCombinerAlphaOp[] { PICATextureCombinerAlphaOp.Alpha, PICATextureCombinerAlphaOp.Alpha, PICATextureCombinerAlphaOp.Alpha }; Material.MaterialParams.TexEnvStages[3].Combiner.Color = PICATextureCombinerMode.Replace; Material.MaterialParams.TexEnvStages[3].Combiner.Alpha = PICATextureCombinerMode.Modulate; Material.MaterialParams.TexEnvStages[3].Color = RGBA.White; Material.MaterialParams.TexEnvStages[3].Scale.Color = PICATextureCombinerScale.One; Material.MaterialParams.TexEnvStages[3].Scale.Alpha = PICATextureCombinerScale.One; Material.MaterialParams.TexEnvStages[3].UpdateColorBuffer = false; Material.MaterialParams.TexEnvStages[3].UpdateAlphaBuffer = false; Material.MaterialParams.TexEnvStages[4].Source.Color = new PICATextureCombinerSource[] { PICATextureCombinerSource.Previous, PICATextureCombinerSource.Previous, PICATextureCombinerSource.Previous }; Material.MaterialParams.TexEnvStages[4].Source.Alpha = new PICATextureCombinerSource[] { PICATextureCombinerSource.Previous, PICATextureCombinerSource.Previous, PICATextureCombinerSource.Previous }; Material.MaterialParams.TexEnvStages[4].Operand.Color = new PICATextureCombinerColorOp[] { PICATextureCombinerColorOp.Color, PICATextureCombinerColorOp.Color, PICATextureCombinerColorOp.Color }; Material.MaterialParams.TexEnvStages[4].Operand.Alpha = new PICATextureCombinerAlphaOp[] { PICATextureCombinerAlphaOp.Alpha, PICATextureCombinerAlphaOp.Alpha, PICATextureCombinerAlphaOp.Alpha }; Material.MaterialParams.TexEnvStages[4].Combiner.Color = PICATextureCombinerMode.Replace; Material.MaterialParams.TexEnvStages[4].Combiner.Alpha = PICATextureCombinerMode.Replace; Material.MaterialParams.TexEnvStages[4].Color = RGBA.Black; Material.MaterialParams.TexEnvStages[4].Scale.Color = PICATextureCombinerScale.One; Material.MaterialParams.TexEnvStages[4].Scale.Alpha = PICATextureCombinerScale.One; Material.MaterialParams.TexEnvStages[4].UpdateColorBuffer = true; Material.MaterialParams.TexEnvStages[4].UpdateAlphaBuffer = false; Material.MaterialParams.TexEnvStages[5].Source.Color = new PICATextureCombinerSource[] { PICATextureCombinerSource.Previous, PICATextureCombinerSource.Previous, PICATextureCombinerSource.Previous }; Material.MaterialParams.TexEnvStages[5].Source.Alpha = new PICATextureCombinerSource[] { PICATextureCombinerSource.Previous, PICATextureCombinerSource.Previous, PICATextureCombinerSource.Previous }; Material.MaterialParams.TexEnvStages[5].Operand.Color = new PICATextureCombinerColorOp[] { PICATextureCombinerColorOp.Color, PICATextureCombinerColorOp.Color, PICATextureCombinerColorOp.Color }; Material.MaterialParams.TexEnvStages[5].Operand.Alpha = new PICATextureCombinerAlphaOp[] { PICATextureCombinerAlphaOp.Alpha, PICATextureCombinerAlphaOp.Alpha, PICATextureCombinerAlphaOp.Alpha }; Material.MaterialParams.TexEnvStages[5].Combiner.Color = PICATextureCombinerMode.Replace; Material.MaterialParams.TexEnvStages[5].Combiner.Alpha = PICATextureCombinerMode.Replace; Material.MaterialParams.TexEnvStages[5].Color = RGBA.Black; Material.MaterialParams.TexEnvStages[5].Scale.Color = PICATextureCombinerScale.One; Material.MaterialParams.TexEnvStages[5].Scale.Alpha = PICATextureCombinerScale.One; Material.MaterialParams.TexEnvStages[5].UpdateColorBuffer = false; Material.MaterialParams.TexEnvStages[5].UpdateAlphaBuffer = false; if (Mesh.Name.Contains("alpha")) { if (Mesh.Name.Contains("alphablend")) { M.Layer = 1; Material.MaterialParams.ColorOperation.BlendMode = PICABlendMode.Blend; Material.MaterialParams.BlendFunction.ColorDstFunc = PICABlendFunc.OneMinusSourceAlpha; Material.MaterialParams.BlendFunction.ColorEquation = PICABlendEquation.FuncAdd; Material.MaterialParams.BlendFunction.ColorSrcFunc = PICABlendFunc.SourceAlpha; Material.MaterialParams.BlendFunction.AlphaDstFunc = PICABlendFunc.OneMinusSourceAlpha; Material.MaterialParams.BlendFunction.AlphaEquation = PICABlendEquation.FuncAdd; Material.MaterialParams.BlendFunction.AlphaSrcFunc = PICABlendFunc.SourceAlpha; if (Mesh.Name.Contains("rl")) { int digit = Mesh.Name[Mesh.Name.IndexOf("rl") + 2] - '0'; if (digit >= 0 && digit <= 3) { //set renderlayer M.Priority = digit; } } } Material.MaterialParams.AlphaTest.Enabled = true; Material.MaterialParams.AlphaTest.Function = PICATestFunc.Greater; Material.MaterialParams.AlphaTest.Reference = 0; } if (Mesh.Name.Contains("unculled")) { Material.MaterialParams.FaceCulling = PICAFaceCulling.Never; } if (Material.Name.Contains("mado") || Material.Name.Contains("window")) { Material.MaterialParams.LightSetIndex = 1; } Material.TextureMappers[0].MagFilter = H3DTextureMagFilter.Linear; if (Model.Materials.Find(Material.Name) != -1) { M.MaterialIndex = (ushort)Model.Materials.Find(Material.Name); MaterialIndex--; M.UpdateBoolUniforms(Model.Materials[M.MaterialIndex]); } else { Model.Materials.Add(Material); M.UpdateBoolUniforms(Material); } Model.AddMesh(M); } /* * On Pokémon, the root bone (on the animaiton file) is used by the game to move * characters around, and all rigged bones are parented to this bone. * It's usually the Waist bone, that points upward and is half the character height. */ /*Model.Skeleton.Add(new H3DBone( * new Vector3(0, Height * 0.5f, 0), * new Vector3(0, 0, (float)(Math.PI * 0.5)), * Vector3.One, * "Waist", * -1));*/ //Model.Skeleton[0].CalculateTransform(Model.Skeleton); Output.Models.Add(Model); Output.CopyMaterials(); } return(Output); }
public static PICAVertex[] GetVertices(DICTObjShape shape, int vertexBufferIndex) { if (!(shape.VertexBuffers[vertexBufferIndex] is VertexBufferInterleaved)) { throw new InvalidOperationException($"Currently only supports VertexBufferInterleaved, but this is a {shape.VertexBuffers[vertexBufferIndex].GetType().Name}"); } var vertexBuffer = (VertexBufferInterleaved)shape.VertexBuffers[vertexBufferIndex]; if (vertexBuffer.RawBuffer.Length == 0) { return(new PICAVertex[0]); } float[] Elems = new float[4]; PICAVertex[] Output = new PICAVertex[vertexBuffer.RawBuffer.Length / vertexBuffer.VertexStride]; using (MemoryStream MS = new MemoryStream(vertexBuffer.RawBuffer)) { BinaryReader Reader = new BinaryReader(MS); for (int Index = 0; Index < Output.Length; Index++) { PICAVertex Out = new PICAVertex(); MS.Seek(Index * vertexBuffer.VertexStride, SeekOrigin.Begin); int bi = 0; int wi = 0; foreach (PICAAttribute Attrib in vertexBuffer.Attributes.Select(a => PICAAttribute.GetPICAAttribute(a))) { AlignStream(MS, Attrib.Format); for (int Elem = 0; Elem < Attrib.Elements; Elem++) { switch (Attrib.Format) { case PICAAttributeFormat.Byte: Elems[Elem] = Reader.ReadSByte(); break; case PICAAttributeFormat.Ubyte: Elems[Elem] = Reader.ReadByte(); break; case PICAAttributeFormat.Short: Elems[Elem] = Reader.ReadInt16(); break; case PICAAttributeFormat.Float: Elems[Elem] = Reader.ReadSingle(); break; } } Vector4 v = new Vector4(Elems[0], Elems[1], Elems[2], Elems[3]); v *= Attrib.Scale; if (Attrib.Name == VertexBuffer.PICAAttributeName.Position) { v.X += shape.PositionOffset.X; v.Y += shape.PositionOffset.Y; v.Z += shape.PositionOffset.Z; } switch (Attrib.Name) { case VertexBuffer.PICAAttributeName.Position: Out.Position = v; break; case VertexBuffer.PICAAttributeName.Normal: Out.Normal = v; break; case VertexBuffer.PICAAttributeName.Tangent: Out.Tangent = v; break; case VertexBuffer.PICAAttributeName.Color: Out.Color = v; break; case VertexBuffer.PICAAttributeName.TexCoord0: Out.TexCoord0 = v; break; case VertexBuffer.PICAAttributeName.TexCoord1: Out.TexCoord1 = v; break; case VertexBuffer.PICAAttributeName.TexCoord2: Out.TexCoord2 = v; break; case VertexBuffer.PICAAttributeName.BoneIndex: Out.Indices[bi++] = (int)v.X; if (Attrib.Elements == 1) { break; } Out.Indices[bi++] = (int)v.Y; if (Attrib.Elements == 2) { break; } Out.Indices[bi++] = (int)v.Z; if (Attrib.Elements == 3) { break; } Out.Indices[bi++] = (int)v.W; break; case VertexBuffer.PICAAttributeName.BoneWeight: Out.Weights[wi++] = v.X; if (Attrib.Elements == 1) { break; } Out.Weights[wi++] = v.Y; if (Attrib.Elements == 2) { break; } Out.Weights[wi++] = v.Z; if (Attrib.Elements == 3) { break; } Out.Weights[wi++] = v.W; break; } } // TODO -- missing FixedAttribute support //if (Mesh.FixedAttributes != null) //{ // bool HasFixedIndices = Mesh.FixedAttributes.Any(x => x.Name == VertexBuffer.PICAAttributeName.BoneIndex); // bool HasFixedWeights = Mesh.FixedAttributes.Any(x => x.Name == VertexBuffer.PICAAttributeName.BoneWeight); // if (HasFixedIndices || HasFixedWeights) // { // foreach (PICAFixedAttribute Attr in Mesh.FixedAttributes) // { // switch (Attr.Name) // { // case VertexBuffer.PICAAttributeName.BoneIndex: // Out.Indices[0] = (int)Attr.Value.X; // Out.Indices[1] = (int)Attr.Value.Y; // Out.Indices[2] = (int)Attr.Value.Z; // break; // case VertexBuffer.PICAAttributeName.BoneWeight: // Out.Weights[0] = Attr.Value.X; // Out.Weights[1] = Attr.Value.Y; // Out.Weights[2] = Attr.Value.Z; // break; // } // } // } //} Output[Index] = Out; } } return(Output); }
public H3D ToH3D(string TextureAndMtlSearchPath = null) { H3D Output = new H3D(); Dictionary <string, OBJMaterial> Materials = new Dictionary <string, OBJMaterial>(); if (TextureAndMtlSearchPath != null) { string MaterialFile = Path.Combine(TextureAndMtlSearchPath, MtlFile); if (File.Exists(MaterialFile)) { string MaterialName = null; OBJMaterial Material = default(OBJMaterial); TextReader Reader = new StreamReader(MaterialFile); for (string Line; (Line = Reader.ReadLine()) != null;) { string[] Params = Line.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); if (Params.Length == 0) { continue; } switch (Params[0]) { case "newmtl": if (Params.Length > 1) { if (MaterialName != null && Material.DiffuseTexture != null) { Materials.Add(MaterialName, Material); } Material = new OBJMaterial(); MaterialName = Line.Substring(Line.IndexOf(" ")).Trim(); } break; case "map_Kd": if (Params.Length > 1) { string Name = Line.Substring(Line.IndexOf(Params[1])); string TextureFile = Path.Combine(TextureAndMtlSearchPath, Name); string TextureName = Path.GetFileNameWithoutExtension(TextureFile); if (File.Exists(TextureFile) && !Output.Textures.Contains(TextureName)) { Output.Textures.Add(new H3DTexture(TextureFile)); } Material.DiffuseTexture = Path.GetFileNameWithoutExtension(TextureName); } break; case "Ka": case "Kd": case "Ks": if (Params.Length >= 4) { Vector4 Color = new Vector4( float.Parse(Params[1], CultureInfo.InvariantCulture), float.Parse(Params[2], CultureInfo.InvariantCulture), float.Parse(Params[3], CultureInfo.InvariantCulture), 1); switch (Params[0]) { case "Ka": Material.Ambient = Color; break; case "Kd": Material.Diffuse = Color; break; case "Ks": Material.Specular = Color; break; } } break; } } Reader.Dispose(); if (MaterialName != null && Material.DiffuseTexture != null) { Materials.Add(MaterialName, Material); } } } H3DModel Model = new H3DModel(); Model.Name = "Model"; ushort MaterialIndex = 0; Model.Flags = H3DModelFlags.HasSkeleton; Model.BoneScaling = H3DBoneScaling.Maya; Model.MeshNodesVisibility.Add(true); float Height = 0; foreach (OBJMesh Mesh in Meshes) { Vector3 MinVector = new Vector3(); Vector3 MaxVector = new Vector3(); Dictionary <PICAVertex, int> Vertices = new Dictionary <PICAVertex, int>(); List <H3DSubMesh> SubMeshes = new List <H3DSubMesh>(); Queue <PICAVertex> VerticesQueue = new Queue <PICAVertex>(); foreach (PICAVertex Vertex in Mesh.Vertices) { VerticesQueue.Enqueue(Vertex); } while (VerticesQueue.Count > 2) { List <ushort> Indices = new List <ushort>(); while (VerticesQueue.Count > 0) { for (int Tri = 0; Tri < 3; Tri++) { PICAVertex Vertex = VerticesQueue.Dequeue(); if (Vertices.ContainsKey(Vertex)) { Indices.Add((ushort)Vertices[Vertex]); } else { Indices.Add((ushort)Vertices.Count); if (Vertex.Position.X < MinVector.X) { MinVector.X = Vertex.Position.X; } if (Vertex.Position.Y < MinVector.Y) { MinVector.Y = Vertex.Position.Y; } if (Vertex.Position.Z < MinVector.Z) { MinVector.Z = Vertex.Position.Z; } if (Vertex.Position.X > MaxVector.X) { MaxVector.X = Vertex.Position.X; } if (Vertex.Position.Y > MaxVector.Y) { MaxVector.Y = Vertex.Position.Y; } if (Vertex.Position.Z > MaxVector.Z) { MaxVector.Z = Vertex.Position.Z; } Vertices.Add(Vertex, Vertices.Count); } } } H3DSubMesh SM = new H3DSubMesh(); SM.BoneIndices = new ushort[] { 0 }; SM.Skinning = H3DSubMeshSkinning.Smooth; SM.Indices = Indices.ToArray(); SubMeshes.Add(SM); } //Mesh List <PICAAttribute> Attributes = PICAAttribute.GetAttributes( PICAAttributeName.Position, PICAAttributeName.Normal, PICAAttributeName.TexCoord0, PICAAttributeName.Color, PICAAttributeName.BoneIndex, PICAAttributeName.BoneWeight); H3DMesh M = new H3DMesh(Vertices.Keys, Attributes, SubMeshes) { Skinning = H3DMeshSkinning.Smooth, MeshCenter = (MinVector + MaxVector) * 0.5f, MaterialIndex = MaterialIndex }; if (Height < MaxVector.Y) { Height = MaxVector.Y; } //Material string MatName = $"Mat{MaterialIndex++.ToString("D5")}_{Mesh.MaterialName}"; H3DMaterial Material = H3DMaterial.GetSimpleMaterial(Model.Name, MatName, null); if (Materials.ContainsKey(Mesh.MaterialName)) { Material.Texture0Name = Materials[Mesh.MaterialName].DiffuseTexture; } else { Material.Texture0Name = "NoTexture"; } Model.Materials.Add(Material); M.UpdateBoolUniforms(Material); Model.AddMesh(M); } /* * On Pokémon, the root bone (on the animaiton file) is used by the game to move * characters around, and all rigged bones are parented to this bone. * It's usually the Waist bone, that points upward and is half the character height. */ Model.Skeleton.Add(new H3DBone( new Vector3(0, Height * 0.5f, 0), new Vector3(0, 0, (float)(Math.PI * 0.5)), Vector3.One, "Waist", -1)); Model.Skeleton[0].CalculateTransform(Model.Skeleton); Output.Models.Add(Model); Output.CopyMaterials(); return(Output); }