/* Functions and utilities */ #region Writing /// <summary> /// Saves the SEModel to a stream, following the current specification version, using the provided data /// </summary> /// <param name="Stream">The file stream to write to</param> public void Write(Stream Stream) { // Open up a binary writer using (ExtendedBinaryWriter writeFile = new ExtendedBinaryWriter(Stream)) { // Write magic writeFile.Write(new char[] { 'S', 'E', 'M', 'o', 'd', 'e', 'l' }); // Write version writeFile.Write((short)0x1); // Write header size writeFile.Write((short)0x14); // Build data present flags { // Buffer byte DataPresentFlags = 0x0; // Check for bones if (Bones.Count > 0) { DataPresentFlags |= (byte)SEModel_DataPresenceFlags.SEMODEL_PRESENCE_BONE; } // Check for meshes if (Meshes.Count > 0) { DataPresentFlags |= (byte)SEModel_DataPresenceFlags.SEMODEL_PRESENCE_MESH; } // Check for materials if (Materials.Count > 0) { DataPresentFlags |= (byte)SEModel_DataPresenceFlags.SEMODEL_PRESENCE_MATERIALS; } // Write it writeFile.Write((byte)DataPresentFlags); } // Dynamic properties bool HasScales = false, HasLocals = true, HasGlobals = false; // Build bone data present flags { // Buffer byte DataPresentFlags = 0x0; // Check for non-default bone data foreach (var Bone in Bones) { if (Bone.Scale != Vector3.One) { HasScales = true; } if (Bone.LocalPosition != Vector3.Zero || Bone.LocalRotation != Quaternion.Identity) { HasLocals = true; } if (Bone.GlobalPosition != Vector3.Zero || Bone.GlobalRotation != Quaternion.Identity) { HasGlobals = true; } // Check to end if (HasScales && HasLocals && HasGlobals) { break; } } // Check for bone types if (HasScales) { DataPresentFlags |= (byte)SEModel_BoneDataPresenceFlags.SEMODEL_PRESENCE_SCALES; } if (HasLocals) { DataPresentFlags |= (byte)SEModel_BoneDataPresenceFlags.SEMODEL_PRESENCE_LOCAL_MATRIX; } if (HasGlobals) { DataPresentFlags |= (byte)SEModel_BoneDataPresenceFlags.SEMODEL_PRESENCE_GLOBAL_MATRIX; } // Write it writeFile.Write((byte)DataPresentFlags); } // Dynamic properties bool HasNormals = false, HasColors = false; // Build mesh data present flags { // Buffer byte DataPresentFlags = 0x0; // Implied properties bool HasUVSet = false, HasWeights = false; // Check for non-default properties foreach (var Mesh in Meshes) { foreach (var Vertex in Mesh.Verticies) { if (Vertex.UVSets.Count > 0) { HasUVSet = true; } if (Vertex.Weights.Count > 0) { HasWeights = true; } if (Vertex.VertexColor != Color.White) { HasColors = true; } if (Vertex.VertexNormal != Vector3.Zero) { HasNormals = true; } // Check to end if (HasUVSet && HasWeights && HasNormals && HasColors) { break; } } // Check to end if (HasUVSet && HasWeights && HasNormals && HasColors) { break; } } // Check for UVSets if (HasUVSet) { DataPresentFlags |= (byte)SEModel_MeshDataPresenceFlags.SEMODEL_PRESENCE_UVSET; } // Check for normals if (HasNormals) { DataPresentFlags |= (byte)SEModel_MeshDataPresenceFlags.SEMODEL_PRESENCE_NORMALS; } // Check for colors if (HasColors) { DataPresentFlags |= (byte)SEModel_MeshDataPresenceFlags.SEMODEL_PRESENCE_COLOR; } // Check for weights if (HasWeights) { DataPresentFlags |= (byte)SEModel_MeshDataPresenceFlags.SEMODEL_PRESENCE_WEIGHTS; } // Write it writeFile.Write((byte)DataPresentFlags); } // The bonecount buffer uint BoneCountBuffer = BoneCount; // The meshcount buffer uint MeshCountBuffer = MeshCount; // The matcount buffer uint MatCountBuffer = MaterialCount; // Write count of bones writeFile.Write((uint)BoneCountBuffer); // Write count of meshes writeFile.Write((uint)MeshCountBuffer); // Write count of mats writeFile.Write((uint)MatCountBuffer); // Write 3 reserved bytes writeFile.Write(new byte[3] { 0x0, 0x0, 0x0 }); // Write bone tagnames foreach (var Bone in Bones) { writeFile.WriteNullTermString(Bone.BoneName); } // Write bone data foreach (var Bone in Bones) { // Write bone flags writeFile.Write((byte)0x0); // Write parent index writeFile.Write((int)Bone.BoneParent); // Write global matrix if (HasGlobals) { writeFile.Write((float)Bone.GlobalPosition.X); writeFile.Write((float)Bone.GlobalPosition.Y); writeFile.Write((float)Bone.GlobalPosition.Z); writeFile.Write((float)Bone.GlobalRotation.X); writeFile.Write((float)Bone.GlobalRotation.Y); writeFile.Write((float)Bone.GlobalRotation.Z); writeFile.Write((float)Bone.GlobalRotation.W); } // Write local matrix if (HasLocals) { writeFile.Write((float)Bone.LocalPosition.X); writeFile.Write((float)Bone.LocalPosition.Y); writeFile.Write((float)Bone.LocalPosition.Z); writeFile.Write((float)Bone.LocalRotation.X); writeFile.Write((float)Bone.LocalRotation.Y); writeFile.Write((float)Bone.LocalRotation.Z); writeFile.Write((float)Bone.LocalRotation.W); } // Write scales if (HasScales) { writeFile.Write((float)Bone.Scale.X); writeFile.Write((float)Bone.Scale.Y); writeFile.Write((float)Bone.Scale.Z); } } // Write mesh data foreach (var Mesh in Meshes) { // Write mesh flags writeFile.Write((byte)0x0); // Buffers for counts byte MatIndiciesCount = 0, MaxSkinInfluence = 0; int VertexCount = Mesh.Verticies.Count, FaceCount = Mesh.Faces.Count; // Iterate and calculate indicies foreach (var Vertex in Mesh.Verticies) { if (Vertex.UVSets.Count > MatIndiciesCount) { MatIndiciesCount = (byte)Vertex.UVSets.Count; } if (Vertex.Weights.Count > MaxSkinInfluence) { MaxSkinInfluence = (byte)Vertex.Weights.Count; } } // Write material indicies writeFile.Write((byte)MatIndiciesCount); // Write max skin influence writeFile.Write((byte)MaxSkinInfluence); // Write vertex count writeFile.Write((int)VertexCount); // Write face count writeFile.Write((int)FaceCount); // Write positions foreach (var Vertex in Mesh.Verticies) { writeFile.Write((float)Vertex.Position.X); writeFile.Write((float)Vertex.Position.Y); writeFile.Write((float)Vertex.Position.Z); } // Write uvlayers foreach (var Vertex in Mesh.Verticies) { for (int i = 0; i < MatIndiciesCount; i++) { var Layer = (i < Vertex.UVSets.Count) ? Vertex.UVSets[i] : Vector2.Zero; // Write it writeFile.Write((float)Layer.X); writeFile.Write((float)Layer.Y); } } // Write normals if (HasNormals) { foreach (var Vertex in Mesh.Verticies) { writeFile.Write((float)Vertex.VertexNormal.X); writeFile.Write((float)Vertex.VertexNormal.Y); writeFile.Write((float)Vertex.VertexNormal.Z); } } // Write colors if (HasColors) { foreach (var Vertex in Mesh.Verticies) { writeFile.Write((byte)Vertex.VertexColor.R); writeFile.Write((byte)Vertex.VertexColor.G); writeFile.Write((byte)Vertex.VertexColor.B); writeFile.Write((byte)Vertex.VertexColor.A); } } // Write weights foreach (var Vertex in Mesh.Verticies) { for (int i = 0; i < MaxSkinInfluence; i++) { var WeightID = (i < Vertex.Weights.Count) ? Vertex.Weights[i].BoneIndex : 0; var WeightValue = (i < Vertex.Weights.Count) ? Vertex.Weights[i].BoneWeight : 0.0f; // Write ID based on count if (BoneCountBuffer <= 0xFF) { writeFile.Write((byte)WeightID); } else if (BoneCountBuffer <= 0xFFFF) { writeFile.Write((ushort)WeightID); } else { writeFile.Write((uint)WeightID); } // Write value writeFile.Write((float)WeightValue); } } // Write faces foreach (var Face in Mesh.Faces) { // Write face indicies based on total vertex count if (VertexCount <= 0xFF) { writeFile.Write((byte)Face.FaceIndex1); writeFile.Write((byte)Face.FaceIndex2); writeFile.Write((byte)Face.FaceIndex3); } else if (VertexCount <= 0xFFFF) { writeFile.Write((ushort)Face.FaceIndex1); writeFile.Write((ushort)Face.FaceIndex2); writeFile.Write((ushort)Face.FaceIndex3); } else { writeFile.Write((uint)Face.FaceIndex1); writeFile.Write((uint)Face.FaceIndex2); writeFile.Write((uint)Face.FaceIndex3); } } // Write material indicies for (int i = 0; i < MatIndiciesCount; i++) { var Index = (i < Mesh.MaterialReferenceIndicies.Count) ? Mesh.MaterialReferenceIndicies[i] : -1; // Write the index, or null for no reference writeFile.Write((int)Index); } } // Write material data foreach (var Mat in Materials) { // Write name writeFile.WriteNullTermString(Mat.Name); // Check type if (Mat.MaterialData is SEModelSimpleMaterial) { // Simple material writeFile.Write((bool)true); // Write the image references writeFile.WriteNullTermString(((SEModelSimpleMaterial)Mat.MaterialData).DiffuseMap); writeFile.WriteNullTermString(((SEModelSimpleMaterial)Mat.MaterialData).NormalMap); writeFile.WriteNullTermString(((SEModelSimpleMaterial)Mat.MaterialData).SpecularMap); } } } }