internal void DrawInternal(RenderSettings settings, IEnumerable <IDecorator> decorators) { _device.SetRenderState(RenderState.ZEnable, true); _device.SetRenderState(RenderState.ZWriteEnable, true); // Draw opaque objects first. foreach (ModelMesh modelMesh in Meshes.Where(m => m.Opaque)) { modelMesh.Draw(_vertexDeclaration, settings, decorators); } _device.SetRenderState(RenderState.ZWriteEnable, false); // Draw transparent objects. TODO: Sort by distance from camera and draw from back to front. _device.SetRenderState(RenderState.AlphaBlendEnable, true); _device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha); _device.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha); foreach (ModelMesh modelMesh in Meshes.Where(m => !m.Opaque)) { modelMesh.Draw(_vertexDeclaration, settings, decorators); } _device.SetRenderState(RenderState.AlphaBlendEnable, false); _device.SetRenderState(RenderState.ZWriteEnable, true); }
private void _setParents() { // Bandaid, as we really want only 1 root mesh if (MainMeshNames.Count > 1) { foreach (var mesh in Meshes) { mesh.ParentName = "__ROOT"; } Mesh root = new Mesh { Name = "__ROOT" }; MainMesh = root; Meshes.Add(root); } // Fix : 2015-04-23 // Sets the parents in each mesh by using their references. foreach (var mesh in Meshes) { // No parent, they are ignored if (String.IsNullOrEmpty(mesh.ParentName) || mesh == MainMesh) { continue; } List <Mesh> parents = Meshes.Where(p => p.Name == mesh.ParentName && mesh != p).ToList(); if (parents.Count == 0) { continue; } mesh.Parent = parents[0]; parents[0].Children.Add(mesh); } }
public Mesh[] GetMeshesByType(Mesh.MeshType mp) { return(Meshes.Where(m => m.Types.Contains(mp)).ToArray()); }
public void Save(Stream destination, ObjectDatabase objectDatabase, TextureDatabase textureDatabase, BoneDatabase boneDatabase, bool leaveOpen = false) { if (objectDatabase != null) { foreach (var mesh in Meshes) { mesh.Id = objectDatabase.GetMesh(mesh.Name)?.Id ?? mesh.Id; } } if (boneDatabase != null) { string fileName = destination is FileStream fileStream ? Path.GetFileName(fileStream.Name) : string.Empty; var skeletonEntry = boneDatabase.Skeletons.FirstOrDefault(x => fileName.StartsWith(x.Name, StringComparison.OrdinalIgnoreCase)) ?? boneDatabase.Skeletons.FirstOrDefault(x => x.Name.Equals("CMN", StringComparison.OrdinalIgnoreCase)) ?? boneDatabase.Skeletons[0]; if (skeletonEntry != null) { foreach (var skin in Meshes.Where(x => x.Skin != null).Select(x => x.Skin)) { foreach (var bone in skin.Bones) { int index = skin.ExData?.BoneNames?.FindIndex(x => x.Equals(bone.Name, StringComparison.OrdinalIgnoreCase)) ?? -1; if (index == -1) { index = skeletonEntry.BoneNames1.FindIndex(x => x.Equals(bone.Name, StringComparison.OrdinalIgnoreCase)); } else { index = 0x8000 | index; } if (index == -1) { continue; } foreach (var childBone in skin.Bones.Where(x => x.ParentId == bone.Id)) { childBone.ParentId = index; } bone.Id = index; } } } } if (textureDatabase != null && TextureSet != null) { int id = textureDatabase.Textures.Max(x => x.Id) + 1; var idDictionary = new Dictionary <int, int>(TextureSet.Textures.Count); foreach (var texture in TextureSet.Textures) { var textureEntry = !string.IsNullOrEmpty(texture.Name) ? textureDatabase.GetTexture(texture.Name) : textureDatabase.GetTexture(texture.Id); if (textureEntry == null) { textureDatabase.Textures.Add( textureEntry = new TextureEntry { Name = texture.Name, Id = id++ }); if (string.IsNullOrEmpty(textureEntry.Name)) { textureEntry.Name = $"Unnamed {textureEntry.Id}"; } } idDictionary[texture.Id] = textureEntry.Id; texture.Name = textureEntry.Name; texture.Id = textureEntry.Id; } foreach (var materialTexture in Meshes.SelectMany(x => x.Materials) .SelectMany(x => x.MaterialTextures)) { if (idDictionary.TryGetValue(materialTexture.TextureId, out id)) { materialTexture.TextureId = id; } } TextureIds.Clear(); TextureIds.AddRange(TextureSet.Textures.Select(x => x.Id)); } Save(destination, leaveOpen); }
internal void Save(EndianBinaryWriter output, GcGame game) { int headerSize = SizeOfHeader(); // In the GCMF file, the triangle meshes are classified in two layers: // The "opaque" layer and the "translucid" layer. // We store both kinds of layers in the same vector for easier manipulation // (setting the layer property on the triangle mesh itself), which the user // may order freely, but now that we're writting the GCMF again, we need to // reclassify the meshes in both layers. GcmfMesh[] layer1Meshes = Meshes.Where(mesh => mesh.Layer == GcmfMesh.MeshLayer.Layer1).ToArray(); GcmfMesh[] layer2Meshes = Meshes.Where(mesh => mesh.Layer == GcmfMesh.MeshLayer.Layer2).ToArray(); GcmfMesh[] meshesSortedByLayer = layer1Meshes.Union(layer2Meshes).ToArray(); // Write GCMF header output.Write(GcmfMagic); output.Write(SectionFlags); output.Write(BoundingSphereCenter.X); output.Write(BoundingSphereCenter.Y); output.Write(BoundingSphereCenter.Z); output.Write(BoundingSphereRadius); output.Write(Convert.ToUInt16(Materials.Count)); output.Write(Convert.ToUInt16(layer1Meshes.Length)); output.Write(Convert.ToUInt16(layer2Meshes.Length)); output.Write(Convert.ToByte(TransformMatrices.Count)); output.Write((byte)0); output.Write(headerSize); output.Write((uint)0); output.Write(TransformMatrixDefaultIdxs); output.Write((uint)0); output.Write((uint)0); output.Write((uint)0); output.Write((uint)0); for (int i = 0; i < Materials.Count; i++) { Materials[i].Save(output, i); } foreach (GcmfTransformMatrix tmtx in TransformMatrices) { tmtx.Save(output); } output.Align(0x20); if ((SectionFlags & (uint)GcmfSectionFlags.SkinModel) != 0 || (SectionFlags & (uint)GcmfSectionFlags.EffectiveModel) != 0) { int offsetPartVertexPool = SizeOf2Header() + SizeOf2MeshHeaders(); int offsetPartType8Unknown1 = offsetPartVertexPool + SizeOf2VertexPool(game); int offsetPartType8Unknown2 = offsetPartType8Unknown1 + (((SectionFlags & (uint)GcmfSectionFlags.SkinModel) != 0) ? SizeOf2Type8Unknown1() : 0); int offsetPartMeshData = offsetPartType8Unknown2 + (((SectionFlags & (uint)GcmfSectionFlags.SkinModel) != 0) ? SizeOf2Type8Unknown2() : 0); if (!GcmfVersionDetails.IsGcmfSkinModelSupported(game)) { // Those are zero on SMB1 offsetPartType8Unknown1 = 0; offsetPartType8Unknown2 = 0; } output.Write(VertexPool.Count); output.Write(offsetPartType8Unknown1); output.Write(offsetPartVertexPool); output.Write(offsetPartMeshData); output.Write(offsetPartType8Unknown2); output.Write((uint)0); output.Write((uint)0); output.Write((uint)0); // Write the mesh headers foreach (GcmfMesh mesh in meshesSortedByLayer) { mesh.SaveHeader(output, true, false); } // Write the vertex pool bool isVtx16Bit = (SectionFlags & (uint)GcmfSectionFlags._16Bit) != 0 && !GcmfVersionDetails.Is16BitEffectiveModelIgnored(game); foreach (GcmfVertex vtx in VertexPool) { vtx.SaveIndexed(output, isVtx16Bit); } if ((SectionFlags & (uint)GcmfSectionFlags.SkinModel) != 0) { foreach (GcmfType8Unknown1 unk1 in Type8Unknown1) { unk1.Save(output); } foreach (ushort unk2 in Type8Unknown2) { output.Write(unk2); } output.Align(0x20); } // Write the section data itself (the indexed triangle strips) Dictionary <GcmfVertex, int> vertexPoolIndexes = new Dictionary <GcmfVertex, int>(); for (int i = 0; i < VertexPool.Count; i++) { vertexPoolIndexes.Add(VertexPool[i], i); } foreach (GcmfMesh mesh in meshesSortedByLayer) { mesh.SaveIndexedData(output, isVtx16Bit, vertexPoolIndexes); } output.Align(0x20); } else { bool is16Bit = ((SectionFlags & (uint)GcmfSectionFlags._16Bit) != 0); foreach (GcmfMesh mesh in meshesSortedByLayer) { mesh.SaveHeader(output, false, is16Bit); mesh.SaveNonIndexedData(output, is16Bit); } } }
public void Save(Stream destination, ObjectDatabase objectDatabase, TextureDatabase textureDatabase, BoneDatabase boneDatabase, bool leaveOpen = false) { if (objectDatabase != null) { foreach (var mesh in Meshes) { mesh.ID = objectDatabase.GetMesh(mesh.Name)?.ID ?? mesh.ID; } } if (boneDatabase != null) { string fileName = (destination is FileStream fileStream) ? Path.GetFileName(fileStream.Name) : string.Empty; // Assume we are exporting in game's style var skeleton = boneDatabase.Skeletons.FirstOrDefault(x => fileName.StartsWith(x.Name, StringComparison.OrdinalIgnoreCase)); // If we couldn't find it, default to CMN skeleton if (skeleton == null) { skeleton = boneDatabase.Skeletons.FirstOrDefault(x => x.Name.Equals("CMN", StringComparison.OrdinalIgnoreCase)); } // Still?? Then default to the first skeleton (this is unlikely to happen though) if (skeleton == null) { skeleton = boneDatabase.Skeletons[0]; } // Pretty much impossible to miss if (skeleton != null) { foreach (var skin in Meshes.Where(x => x.Skin != null).Select(x => x.Skin)) { foreach (var bone in skin.Bones) { int index = skin.ExData?.BoneNames?.FindIndex(x => x.Equals(bone.Name, StringComparison.OrdinalIgnoreCase)) ?? -1; if (index == -1) { index = skeleton.BoneNames1.FindIndex(x => x.Equals(bone.Name, StringComparison.OrdinalIgnoreCase)); } else { index = 0x8000 | index; } if (index != -1) { // Before we do this, fix the child bones foreach (var childBone in skin.Bones.Where(x => x.ParentID.Equals(bone.ID))) { childBone.ParentID = index; } // Now replace the ID bone.ID = index; } else { Debug.WriteLine($"Model.Save: Bone wasn't found in bone database or ex-data: {bone.Name}"); } } } } } if (textureDatabase != null && TextureSet != null) { var newIDs = new List <int>(TextureSet.Textures.Count); int currentID = textureDatabase.Textures.Max(x => x.ID) + 1; foreach (var texture in TextureSet.Textures) { var textureEntry = !string.IsNullOrEmpty(texture.Name) ? textureDatabase.GetTexture(texture.Name) : textureDatabase.GetTexture(texture.ID); if (textureEntry == null) { textureDatabase.Textures.Add(textureEntry = new TextureEntry { ID = currentID++, Name = texture.Name ?? $"Texture{currentID}", }); } newIDs.Add(textureEntry.ID); } if (!newIDs.SequenceEqual(TextureIDs)) { TextureUtilities.ReAssignTextureIDs(this, newIDs); } } Save(destination, leaveOpen); }