public static List <WadMesh> ImportFromExternalModel(string fileName, IOGeometrySettings settings, bool mergeIntoOne) { IOModel tmpModel = null; var meshList = new List <WadMesh>(); bool calculateNormals = false; // Import the model try { var importer = BaseGeometryImporter.CreateForFile(fileName, settings, absoluteTexturePath => { return(new WadTexture(ImageC.FromFile(absoluteTexturePath))); }); tmpModel = importer.ImportFromFile(fileName); calculateNormals = importer is MetasequoiaImporter; } catch (Exception ex) { logger.Error(ex, "Geometry import failed!"); return(null); } // Create a new mesh (all meshes from model will be joined) WadMesh mesh = null; var lastBaseVertex = 0; for (int i = 0; i < tmpModel.Meshes.Count; i++) { var tmpMesh = tmpModel.Meshes[i]; if (mesh == null || !mergeIntoOne) { mesh = new WadMesh(); mesh.Name = string.IsNullOrEmpty(tmpMesh.Name) ? "ImportedMesh" + i : tmpMesh.Name; if (mergeIntoOne) { lastBaseVertex = 0; // Reset if we're doing multi-mesh import } } mesh.VerticesPositions.AddRange(tmpMesh.Positions); // Copy normals as well, if they are consistent if (tmpMesh.Normals.Count == tmpMesh.Positions.Count) { mesh.VerticesNormals.AddRange(tmpMesh.Normals); } // FIXME: Why do we keep white intensity shades for wad2 meshes internally, and not vertex colors? if (tmpMesh.Colors.Count == tmpMesh.Positions.Count) { mesh.VerticesShades.AddRange(tmpMesh.Colors.Select(v => (short)(8191.0f - (v.To3().GetLuma() * 8191.0f)))); } foreach (var tmpSubmesh in tmpMesh.Submeshes) { foreach (var tmpPoly in tmpSubmesh.Value.Polygons) { if (tmpPoly.Shape == IOPolygonShape.Quad) { var poly = new WadPolygon { Shape = WadPolygonShape.Quad }; poly.Index0 = tmpPoly.Indices[0] + lastBaseVertex; poly.Index1 = tmpPoly.Indices[1] + lastBaseVertex; poly.Index2 = tmpPoly.Indices[2] + lastBaseVertex; poly.Index3 = tmpPoly.Indices[3] + lastBaseVertex; var area = new TextureArea(); area.TexCoord0 = tmpMesh.UV[tmpPoly.Indices[0]]; area.TexCoord1 = tmpMesh.UV[tmpPoly.Indices[1]]; area.TexCoord2 = tmpMesh.UV[tmpPoly.Indices[2]]; area.TexCoord3 = tmpMesh.UV[tmpPoly.Indices[3]]; area.Texture = tmpSubmesh.Value.Material.Texture; area.DoubleSided = tmpSubmesh.Value.Material.DoubleSided; poly.Texture = area; poly.ShineStrength = (byte)Math.Round(tmpSubmesh.Value.Material.Shininess / 16.0f, MidpointRounding.ToEven); mesh.Polys.Add(poly); } else { var poly = new WadPolygon { Shape = WadPolygonShape.Triangle }; poly.Index0 = tmpPoly.Indices[0] + lastBaseVertex; poly.Index1 = tmpPoly.Indices[1] + lastBaseVertex; poly.Index2 = tmpPoly.Indices[2] + lastBaseVertex; var area = new TextureArea(); area.TexCoord0 = tmpMesh.UV[tmpPoly.Indices[0]]; area.TexCoord1 = tmpMesh.UV[tmpPoly.Indices[1]]; area.TexCoord2 = tmpMesh.UV[tmpPoly.Indices[2]]; area.TexCoord3 = area.TexCoord2; area.Texture = tmpSubmesh.Value.Material.Texture; area.DoubleSided = tmpSubmesh.Value.Material.DoubleSided; poly.Texture = area; poly.ShineStrength = (byte)Math.Round(tmpSubmesh.Value.Material.Shininess / 16.0f, MidpointRounding.ToEven); mesh.Polys.Add(poly); } } } if (!mergeIntoOne || i == tmpModel.Meshes.Count - 1) { mesh.BoundingBox = mesh.CalculateBoundingBox(); mesh.BoundingSphere = mesh.CalculateBoundingSphere(); if (mesh.VerticesNormals.Count == 0 || calculateNormals) { mesh.CalculateNormals(); // MQO files rarely have normals } if (mesh.VerticesPositions.Count != mesh.VerticesShades.Count) { mesh.VerticesShades.Clear(); // Reset vertex shades in case they got desynced from vertex count } lastBaseVertex = 0; meshList.Add(mesh); } else { lastBaseVertex = mesh.VerticesPositions.Count; } } return(meshList); }
public static WadAnimation ImportAnimationFromModel(WadToolClass tool, IWin32Window owner, int nodeCount, string fileName) { IOModel tmpModel = null; // Import the model try { var settings = new IOGeometrySettings() { ProcessAnimations = true, ProcessGeometry = false }; using (var form = new GeometryIOSettingsDialog(settings)) { form.AddPreset(IOSettingsPresets.AnimationSettingsPresets); string resultingExtension = Path.GetExtension(fileName).ToLowerInvariant(); if (resultingExtension.Equals(".fbx")) { form.SelectPreset("3dsmax Filmbox (FBX)"); } else if (resultingExtension.Equals(".dae")) { form.SelectPreset("3dsmax COLLADA"); } if (form.ShowDialog(owner) != DialogResult.OK) { return(null); } var importer = BaseGeometryImporter.CreateForFile(fileName, settings, null); tmpModel = importer.ImportFromFile(fileName); // We don't support animation importing from custom-written mqo importer yet... if (importer is MetasequoiaImporter) { tool.SendMessage("Metasequoia importer isn't currently supported.", PopupType.Error); return(null); } // If no animations, return null if (tmpModel.Animations.Count == 0) { tool.SendMessage("Selected file has no supported animations!", PopupType.Error); return(null); } } } catch (Exception ex) { tool.SendMessage("Unknown error while importing animation. \n" + ex?.Message, PopupType.Error); logger.Warn(ex, "'ImportAnimationFromModel' failed."); return(null); } IOAnimation animToImport; if (tmpModel.Animations.Count > 1) { using (var dialog = new AnimationImportDialog(tmpModel.Animations.Select(o => o.Name).ToList())) { dialog.ShowDialog(owner); if (dialog.DialogResult == DialogResult.Cancel) { return(null); } else { animToImport = tmpModel.Animations[dialog.AnimationToImport]; } } } else { animToImport = tmpModel.Animations[0]; } // Integrity check, for cases when something totally went wrong with assimp if (animToImport == null) { tool.SendMessage("Animation importer encountered serious error. No animation imported.", PopupType.Error); return(null); } // Integrity check, is there any valid frames? if (animToImport.Frames.Count <= 0) { tool.SendMessage("Selected animation has no frames!", PopupType.Error); return(null); } // Integrity check, number of bones = number of nodes? if (animToImport.NumNodes != nodeCount) { tool.SendMessage("Selected animation has different number of bones!", PopupType.Error); return(null); } WadAnimation animation = new WadAnimation(); animation.Name = animToImport.Name; foreach (var frame in animToImport.Frames) { var keyFrame = new WadKeyFrame(); keyFrame.Offset = frame.Offset; frame.Angles.ForEach(angle => keyFrame.Angles.Add(new WadKeyFrameRotation() { Rotations = angle })); animation.KeyFrames.Add(keyFrame); } animation.EndFrame = (ushort)(animToImport.Frames.Count - 1); return(animation); }
public void Update(LevelSettings settings, Dictionary <string, Texture> absolutePathTextureLookup, ImportedGeometryInfo info) { Info = info; LoadException = null; DirectXModel = null; Textures.Clear(); try { string importedGeometryPath = settings.MakeAbsolute(info.Path); string importedGeometryDirectory = Path.GetDirectoryName(importedGeometryPath); // Invoke the TombLib geometry import code var settingsIO = new IOGeometrySettings { Scale = info.Scale, SwapXY = info.SwapXY, SwapXZ = info.SwapXZ, SwapYZ = info.SwapYZ, FlipX = info.FlipX, FlipY = info.FlipY, FlipZ = info.FlipZ, FlipUV_V = info.FlipUV_V, InvertFaces = info.InvertFaces, UseVertexColor = true }; BaseGeometryImporter importer = BaseGeometryImporter.CreateForFile(importedGeometryPath, settingsIO, absoluteTexturePath => { return(GetOrAddTexture(absolutePathTextureLookup, importedGeometryDirectory, absoluteTexturePath)); }); var tmpModel = importer.ImportFromFile(importedGeometryPath); SynchronizationContext.Current.Post(unused => // Synchronize DirectX, we can't 'send' because that may deadlock with the level settings reloader { if (Device == null) { return; } // Create a new static model DirectXModel = new Model(Device, info.Scale); DirectXModel.BoundingBox = tmpModel.BoundingBox; // Create materials foreach (var tmpMaterial in tmpModel.Materials) { var material = new Material(tmpMaterial.Name); material.Texture = tmpMaterial.Texture; material.AdditiveBlending = tmpMaterial.AdditiveBlending; material.DoubleSided = tmpMaterial.DoubleSided; DirectXModel.Materials.Add(material); } // Loop for each mesh loaded in scene foreach (var mesh in tmpModel.Meshes) { var modelMesh = new ImportedGeometryMesh(Device, mesh.Name); modelMesh.HasVertexColors = (mesh.Colors.Count != 0); var currentIndex = 0; var currPoly = 0; foreach (var tmpSubmesh in mesh.Submeshes) { var material = DirectXModel.Materials[tmpModel.Materials.IndexOf(tmpSubmesh.Value.Material)]; var submesh = new Submesh(material); foreach (var tmpPoly in tmpSubmesh.Value.Polygons) { if (tmpPoly.Shape == IOPolygonShape.Quad) { var vertexList = new List <ImportedGeometryVertex>(); for (var i = 0; i < 4; i++) { var vertex = new ImportedGeometryVertex(); vertex.Position = mesh.Positions[tmpPoly.Indices[i]]; vertex.Color = tmpPoly.Indices[i] < mesh.Colors.Count ? mesh.Colors[tmpPoly.Indices[i]].To3() : Vector3.One; vertex.UV = tmpPoly.Indices[i] < mesh.UV.Count ? mesh.UV[tmpPoly.Indices[i]] : Vector2.Zero; vertex.Normal = tmpPoly.Indices[i] < mesh.Normals.Count ? mesh.Normals[tmpPoly.Indices[i]] : Vector3.Zero; vertexList.Add(vertex); } // HACK: Triangulate and disjoint quad faces for imported geometry, because otherwise another hack which joints // disjointed vertices together will fail in Rooms.cs submesh.Indices.Add(currentIndex); submesh.Indices.Add(currentIndex + 1); submesh.Indices.Add(currentIndex + 2); submesh.Indices.Add(currentIndex + 3); submesh.Indices.Add(currentIndex + 4); submesh.Indices.Add(currentIndex + 5); modelMesh.Vertices.Add(vertexList[0]); modelMesh.Vertices.Add(vertexList[1]); modelMesh.Vertices.Add(vertexList[2]); modelMesh.Vertices.Add(vertexList[0]); modelMesh.Vertices.Add(vertexList[2]); modelMesh.Vertices.Add(vertexList[3]); currentIndex += 6; } else { for (var i = 0; i < 3; i++) { var vertex = new ImportedGeometryVertex(); vertex.Position = mesh.Positions[tmpPoly.Indices[i]]; vertex.Color = tmpPoly.Indices[i] < mesh.Colors.Count ? mesh.Colors[tmpPoly.Indices[i]].To3() : Vector3.One; vertex.UV = tmpPoly.Indices[i] < mesh.UV.Count ? mesh.UV[tmpPoly.Indices[i]] : Vector2.Zero; vertex.Normal = tmpPoly.Indices[i] < mesh.Normals.Count ? mesh.Normals[tmpPoly.Indices[i]] : Vector3.Zero; modelMesh.Vertices.Add(vertex); submesh.Indices.Add(currentIndex); currentIndex++; } } currPoly++; } modelMesh.Submeshes.Add(material, submesh); } DirectXModel.Meshes.Add(modelMesh); } DirectXModel.UpdateBuffers(); }, null); } catch (OperationCanceledException) { throw; } catch (Exception exc) { LoadException = exc; DirectXModel = null; logger.Warn(exc, "Unable to load model \"" + info.Name + "\" from \"" + info.Path + "\" because an exception occurred during loading."); } }