private bool PromptFileSave(IGeometryModel model, out string fileName, out string formatId) { var getExportFormats = Substrate.GetSharedFunction <GetExportFormats>("Reclaimer.Plugins.ModelViewerPlugin.GetExportFormats"); var exportFormats = getExportFormats() .Select(f => new { FormatId = f, Extension = ModelViewerPlugin.GetFormatExtension(f), Description = ModelViewerPlugin.GetFormatDescription(f) }).ToList(); var filter = string.Join("|", exportFormats.Select(f => $"{f.Description}|*.{f.Extension}")); var sfd = new SaveFileDialog { OverwritePrompt = true, FileName = model.Name, Filter = filter, FilterIndex = 1 + exportFormats.TakeWhile(f => f.FormatId != ArchitectSettingsPlugin.Settings.DefaultSaveFormat).Count(), AddExtension = true }; if (sfd.ShowDialog() != true) { fileName = formatId = null; return(false); } fileName = sfd.FileName; formatId = exportFormats[sfd.FilterIndex - 1].FormatId; ArchitectSettingsPlugin.Settings.DefaultSaveFormat = formatId; return(true); }
public MaskedGeometryModel(IGeometryModel source, IList <IGeometryPermutation> permutations) { this.source = source; var regions = new List <GeometryRegion>(); foreach (var reg in source.Regions) { var perms = reg.Permutations .Where(p => permutations.Contains(p)) .ToList(); if (!perms.Any()) { continue; } regions.Add(new GeometryRegion { Name = reg.Name, Permutations = perms }); } MarkerGroups = new List <IGeometryMarkerGroup>(); Regions = regions.OfType <IGeometryRegion>().ToList(); }
public ModelProperties(IGeometryModel source) { Name = source.Name; Regions = source.Regions.ToList(); MarkerGroups = source.MarkerGroups.ToList(); Nodes = source.Nodes.ToList(); Materials = source.Materials.ToList(); var standard = new List <int>(); var instances = new List <int>(); for (int i = 0; i < source.Meshes.Count; i++) { if (source.Meshes[i].IsInstancing) { instances.Add(i); } else { standard.Add(i); } } MeshCount = source.Meshes.Count; StandardMeshes = standard; InstanceMeshes = instances; }
public MaskedGeometryModel(IGeometryModel source, IEnumerable <IGeometryPermutation> filter) { this.source = source; filter = (filter as IList <IGeometryPermutation>) ?? filter.ToList(); regions = source.Regions.Select((r, i) => new MaskedRegion(i, r)).ToList(); foreach (var region in regions) { region.Permutations.RemoveAll(p => !filter.Contains(p.SourcePermutation)); } regions.RemoveAll(r => r.Permutations.Count == 0); markerGroups = new List <GeometryMarkerGroup>(); foreach (var group in source.MarkerGroups) { var markers = group.Markers .Select(m => new MaskedMarker(m, regions)) .Where(m => m.IsValid) .OfType <IGeometryMarker>() .ToList(); if (markers.Count == 0) { continue; } markerGroups.Add(new GeometryMarkerGroup { Name = group.Name, Markers = markers }); } }
private void InteractObject(IGeometryModel model) { var clicks = model.AddClick(); var selectData = clicksData.clicksData .Where(x => x.objectType == model.Type && (clicks >= x.minClicksCount && clicks <= x.maxClicksCount)); foreach (var data in selectData) { model.ChangeColor(data.color); break; } }
public static MeshTemplate FromModel(IGeometryModel model, int meshIndex) { var mesh = model.Meshes[meshIndex]; if (mesh.IsInstancing) { return(new InstancedMeshTemplate(model, mesh)); } else { return(new MeshTemplate(model, mesh)); } }
private IEnumerable <Material> GetMaterials(IGeometryModel model) { var indexes = model.Meshes.SelectMany(m => m.Submeshes) .Select(s => s.MaterialIndex).Distinct().ToArray(); var bitmapLookup = new Dictionary <int, DiffuseMaterial>(); for (short i = 0; i < model.Materials.Count; i++) { if (!indexes.Contains(i)) { yield return(null); continue; } var mat = model.Materials[i]; DiffuseMaterial material; try { var diffuse = mat.Submaterials.First(m => m.Usage == MaterialUsage.Diffuse); if (!bitmapLookup.ContainsKey(diffuse.Bitmap.Id)) { var dds = diffuse.Bitmap.ToDds(0); var brush = new ImageBrush(dds.ToBitmapSource(new DdsOutputArgs(DecompressOptions.Bgr24))) { ViewportUnits = BrushMappingMode.Absolute, TileMode = TileMode.Tile, Viewport = new Rect(0, 0, 1f / Math.Abs(diffuse.Tiling.X), 1f / Math.Abs(diffuse.Tiling.Y)) }; brush.Freeze(); material = new DiffuseMaterial(brush); material.Freeze(); bitmapLookup[diffuse.Bitmap.Id] = material; } else { material = bitmapLookup[diffuse.Bitmap.Id]; } } catch { material = ErrorMaterial; } yield return(material); } }
private IEnumerable <IGeometryPermutation> GetSelectedPermutations(IGeometryModel model) { foreach (var parent in TreeViewItems.Where(i => i.IsChecked != false)) { var region = model.Regions.ElementAtOrDefault((parent.Tag as RenderModel3D.Region)?.SourceIndex ?? -1); if (region == null) { continue; } foreach (var child in parent.Items.Where(i => i.IsChecked == true)) { var permutation = region.Permutations.ElementAtOrDefault((child.Tag as RenderModel3D.Permutation)?.SourceIndex ?? -1); if (permutation != null) { yield return(permutation); } } } }
public MultiMesh(IGeometryModel model, int meshIndex, int meshCount) { if (model == null) { throw new ArgumentNullException(nameof(model)); } if (meshIndex < 0 || meshIndex >= model.Meshes.Count) { throw new ArgumentOutOfRangeException(nameof(meshIndex)); } if (meshCount < 1 || meshIndex + meshCount > model.Meshes.Count) { throw new ArgumentOutOfRangeException(nameof(meshCount)); } this.model = model; this.meshIndex = meshIndex; this.meshCount = meshCount; }
public static void WriteModelFile(IGeometryModel model, string fileName, string formatId) { if (model == null) { throw new ArgumentNullException(nameof(model)); } if (string.IsNullOrWhiteSpace(fileName)) { throw new ArgumentNullException(nameof(fileName)); } formatId = (formatId ?? Settings.DefaultSaveFormat).ToLower(); if (!ExportFormats.Any(f => f.FormatId == formatId)) { throw new ArgumentException($"{formatId} is not a supported format.", nameof(formatId)); } var ext = "." + GetFormatExtension(formatId); if (!fileName.EndsWith(ext, StringComparison.OrdinalIgnoreCase)) { fileName += ext; } var format = ExportFormats.First(f => f.FormatId == formatId); if (format.ExportFunction != null) { format.ExportFunction(model, fileName); } else { using (var context = new Assimp.AssimpContext()) { var scene = model.CreateAssimpScene(context, formatId); context.ExportFile(scene, fileName, formatId); } } }
public static Assimp.Scene CreateAssimpScene(this IGeometryModel model, Assimp.AssimpContext context, string formatId) { var scale = ModelViewerPlugin.Settings.GeometryScale; //either Assimp or collada has issues when there is a name conflict const string bonePrefix = "~"; const string geomPrefix = "-"; const string scenPrefix = "$"; var scene = new Assimp.Scene(); scene.RootNode = new Assimp.Node($"{scenPrefix}{model.Name}"); //Assimp is Y-up in inches by default - this forces it to export as Z-up in meters scene.RootNode.Transform = (CoordinateSystem.HaloCEX * ModelViewerPlugin.Settings.AssimpScale).ToAssimp4x4(); #region Nodes var allNodes = new List <Assimp.Node>(); foreach (var node in model.Nodes) { var result = new Assimp.Node($"{bonePrefix}{node.Name}"); var q = new System.Numerics.Quaternion(node.Rotation.X, node.Rotation.Y, node.Rotation.Z, node.Rotation.W); var mat = System.Numerics.Matrix4x4.CreateFromQuaternion(q); mat.Translation = new System.Numerics.Vector3(node.Position.X * scale, node.Position.Y * scale, node.Position.Z * scale); result.Transform = mat.ToAssimp4x4(); allNodes.Add(result); } for (int i = 0; i < model.Nodes.Count; i++) { var node = model.Nodes[i]; if (node.ParentIndex >= 0) { allNodes[node.ParentIndex].Children.Add(allNodes[i]); } else { scene.RootNode.Children.Add(allNodes[i]); } } #endregion var meshLookup = new List <int>(); #region Meshes for (int i = 0; i < model.Meshes.Count; i++) { var geom = model.Meshes[i]; if (geom.Submeshes.Count == 0) { meshLookup.Add(-1); continue; } meshLookup.Add(scene.MeshCount); foreach (var sub in geom.Submeshes) { var m = new Assimp.Mesh($"mesh{i:D3}"); var indices = geom.Indicies.Skip(sub.IndexStart).Take(sub.IndexLength); var minIndex = indices.Min(); var maxIndex = indices.Max(); var vertCount = maxIndex - minIndex + 1; if (geom.IndexFormat == IndexFormat.TriangleStrip) { indices = indices.Unstrip(); } indices = indices.Select(x => x - minIndex); var vertices = geom.Vertices.Skip(minIndex).Take(vertCount); if (geom.BoundsIndex >= 0) { vertices = vertices.Select(v => (IVertex) new CompressedVertex(v, model.Bounds[geom.BoundsIndex.Value])); } int vIndex = -1; var boneLookup = new Dictionary <int, Assimp.Bone>(); foreach (var v in vertices) { vIndex++; if (v.Position.Count > 0) { m.Vertices.Add(v.Position[0].ToAssimp3D(scale)); //some Halo shaders use position W as the colour alpha - add it to a colour channel to preserve it //also assimp appears to have issues exporting obj when a colour channel exists so only do this for collada if (formatId == "collada" && v.Color.Count == 0 && !float.IsNaN(v.Position[0].W)) { m.VertexColorChannels[0].Add(new Assimp.Color4D { R = v.Position[0].W }); } } if (v.Normal.Count > 0) { m.Normals.Add(v.Normal[0].ToAssimp3D()); } if (v.TexCoords.Count > 0) { m.TextureCoordinateChannels[0].Add(v.TexCoords[0].ToAssimpUV()); } if (geom.VertexWeights == VertexWeights.None && !geom.NodeIndex.HasValue) { continue; } #region Vertex Weights var weights = new List <Tuple <int, float> >(4); if (geom.NodeIndex.HasValue) { weights.Add(Tuple.Create <int, float>(geom.NodeIndex.Value, 1)); } else if (geom.VertexWeights == VertexWeights.Skinned) { var ind = v.BlendIndices[0]; var wt = v.BlendWeight[0]; if (wt.X > 0) { weights.Add(Tuple.Create((int)ind.X, wt.X)); } if (wt.Y > 0) { weights.Add(Tuple.Create((int)ind.Y, wt.Y)); } if (wt.Z > 0) { weights.Add(Tuple.Create((int)ind.Z, wt.Z)); } if (wt.W > 0) { weights.Add(Tuple.Create((int)ind.W, wt.W)); } } foreach (var val in weights) { Assimp.Bone b; if (boneLookup.ContainsKey(val.Item1)) { b = boneLookup[val.Item1]; } else { var t = model.Nodes[val.Item1].OffsetTransform; t.M41 *= scale; t.M42 *= scale; t.M43 *= scale; b = new Assimp.Bone { Name = bonePrefix + model.Nodes[val.Item1].Name, OffsetMatrix = t.ToAssimp4x4() }; m.Bones.Add(b); boneLookup.Add(val.Item1, b); } b.VertexWeights.Add(new Assimp.VertexWeight(vIndex, val.Item2)); } #endregion } m.SetIndices(indices.ToArray(), 3); m.MaterialIndex = sub.MaterialIndex; scene.Meshes.Add(m); } } #endregion #region Regions foreach (var reg in model.Regions) { var regNode = new Assimp.Node($"{geomPrefix}{reg.Name}"); foreach (var perm in reg.Permutations) { var meshStart = meshLookup[perm.MeshIndex]; if (meshStart < 0) { continue; } var permNode = new Assimp.Node($"{geomPrefix}{perm.Name}"); if (perm.TransformScale != 1 || !perm.Transform.IsIdentity) { permNode.Transform = Assimp.Matrix4x4.FromScaling(new Assimp.Vector3D(perm.TransformScale)) * perm.Transform.ToAssimp4x4(scale); } var meshCount = Enumerable.Range(perm.MeshIndex, perm.MeshCount).Sum(i => model.Meshes[i].Submeshes.Count); permNode.MeshIndices.AddRange(Enumerable.Range(meshStart, meshCount)); regNode.Children.Add(permNode); } if (regNode.ChildCount > 0) { scene.RootNode.Children.Add(regNode); } } #endregion #region Materials foreach (var mat in model.Materials) { var m = new Assimp.Material { Name = mat?.Name ?? "unused" }; //prevent max from making every material super shiny m.ColorEmissive = m.ColorReflective = m.ColorSpecular = new Assimp.Color4D(0, 0, 0, 1); m.ColorDiffuse = m.ColorTransparent = new Assimp.Color4D(1); //max only seems to care about diffuse var dif = mat?.Submaterials.FirstOrDefault(s => s.Usage == MaterialUsage.Diffuse); if (dif != null) { var suffix = dif.Bitmap.SubmapCount > 1 ? "[0]" : string.Empty; var filePath = $"{dif.Bitmap.Name}{suffix}.{ModelViewerPlugin.Settings.MaterialExtension}"; //collada spec says it requires URI formatting, and Assimp doesn't do it for us //for some reason "new Uri(filePath, UriKind.Relative)" doesnt change the slashes, have to use absolute uri if (formatId == FormatId.Collada) { filePath = new Uri("X:\\", UriKind.Absolute).MakeRelativeUri(new Uri(System.IO.Path.Combine("X:\\", filePath))).ToString(); } m.TextureDiffuse = new Assimp.TextureSlot { BlendFactor = 1, FilePath = filePath, TextureType = Assimp.TextureType.Diffuse }; } scene.Materials.Add(m); } #endregion return(scene); }
private IEnumerable <Model3DGroup> GetMeshes(IGeometryModel model) { var indexes = model.Regions.SelectMany(r => r.Permutations) .SelectMany(p => Enumerable.Range(p.MeshIndex, p.MeshCount)) .Distinct().ToList(); var materials = GetMaterials(model).ToList(); for (int i = 0; i < model.Meshes.Count; i++) { var mesh = model.Meshes[i]; if (mesh.Submeshes.Count == 0 || !indexes.Contains(i)) { yield return(null); continue; } var mGroup = new Model3DGroup(); var tGroup = new Transform3DGroup(); var texMatrix = Matrix.Identity; if (mesh.BoundsIndex >= 0) { var bounds = model.Bounds[mesh.BoundsIndex.Value]; texMatrix = new Matrix { M11 = bounds.UBounds.Length, M22 = bounds.VBounds.Length, OffsetX = bounds.UBounds.Min, OffsetY = bounds.VBounds.Min }; var transform = new Matrix3D { M11 = bounds.XBounds.Length, M22 = bounds.YBounds.Length, M33 = bounds.ZBounds.Length, OffsetX = bounds.XBounds.Min, OffsetY = bounds.YBounds.Min, OffsetZ = bounds.ZBounds.Min }; var tform = new MatrixTransform3D(transform); tform.Freeze(); tGroup.Children.Add(tform); } foreach (var sub in mesh.Submeshes) { try { var geom = new MeshGeometry3D(); var indices = mesh.Indicies.Skip(sub.IndexStart).Take(sub.IndexLength).ToList(); if (mesh.IndexFormat == IndexFormat.TriangleStrip) { indices = indices.Unstrip().ToList(); } var vertStart = indices.Min(); var vertLength = indices.Max() - vertStart + 1; var verts = mesh.Vertices.Skip(vertStart).Take(vertLength); var positions = verts.Select(v => new Point3D(v.Position[0].X, v.Position[0].Y, v.Position[0].Z)); (geom.Positions = new Point3DCollection(positions)).Freeze(); (geom.TriangleIndices = new Int32Collection(indices.Select(j => j - vertStart))).Freeze(); if (mesh.Vertices[0].TexCoords.Count > 0) { var texcoords = verts.Select(v => new Point(v.TexCoords[0].X, v.TexCoords[0].Y)).ToArray(); if (!texMatrix.IsIdentity) { texMatrix.Transform(texcoords); } (geom.TextureCoordinates = new PointCollection(texcoords)).Freeze(); } if (mesh.Vertices[0].Normal.Count > 0) { var normals = verts.Select(v => new Vector3D(v.Normal[0].X, v.Normal[0].Y, v.Normal[0].Z)); (geom.Normals = new Vector3DCollection(normals)).Freeze(); } var mat = materials.ElementAtOrDefault(sub.MaterialIndex) ?? ErrorMaterial; var subGroup = new GeometryModel3D(geom, mat) { BackMaterial = mat }; subGroup.Freeze(); mGroup.Children.Add(subGroup); } catch { } } (mGroup.Transform = tGroup).Freeze(); mGroup.Freeze(); yield return(mGroup); } }
internal MeshTemplate(IGeometryModel model, IGeometryMesh mesh) { submeshCount = mesh.Submeshes.Count; matIndex = new int[submeshCount]; submeshes = new Helix.MeshGeometry3D[submeshCount]; var texMatrix = SharpDX.Matrix.Identity; var boundsMatrix = SharpDX.Matrix.Identity; if (mesh.BoundsIndex >= 0) { var bounds = model.Bounds[mesh.BoundsIndex.Value]; boundsMatrix = bounds.ToMatrix3(); texMatrix = bounds.ToMatrix2(); } for (int i = 0; i < submeshCount; i++) { var sub = mesh.Submeshes[i]; matIndex[i] = sub.MaterialIndex; var subIndices = mesh.Indicies.Skip(sub.IndexStart).Take(sub.IndexLength).ToList(); if (mesh.IndexFormat == IndexFormat.TriangleStrip) { subIndices = subIndices.Unstrip().ToList(); } var vertStart = subIndices.Min(); var vertLength = subIndices.Max() - vertStart + 1; var subVerts = mesh.Vertices.Skip(vertStart).Take(vertLength); IEnumerable <SharpDX.Vector3> subPositions; if (boundsMatrix.IsIdentity) { subPositions = subVerts.Select(v => v.Position[0].ToVector3()); } else { subPositions = subVerts.Select(v => SharpDX.Vector3.TransformCoordinate(v.Position[0].ToVector3(), boundsMatrix)); } IEnumerable <SharpDX.Vector2> subTexcoords; if (texMatrix.IsIdentity) { subTexcoords = subVerts.Select(v => v.TexCoords[0].ToVector2()); } else { subTexcoords = subVerts.Select(v => SharpDX.Vector2.TransformCoordinate(v.TexCoords[0].ToVector2(), texMatrix)); } submeshes[i] = new Helix.MeshGeometry3D { Indices = new Helix.IntCollection(subIndices.Select(j => j - vertStart)), Positions = new Helix.Vector3Collection(subPositions), TextureCoordinates = new Helix.Vector2Collection(subTexcoords) }; if (mesh.Vertices[0].Normal.Count > 0) { var subNormals = subVerts.Select(v => new SharpDX.Vector3(v.Normal[0].X, v.Normal[0].Y, v.Normal[0].Z)); submeshes[i].Normals = new Helix.Vector3Collection(subNormals); } submeshes[i].UpdateOctree(); } }
public static void WriteAMF(this IGeometryModel model, string fileName, float scale) { if (!Directory.GetParent(fileName).Exists) { Directory.GetParent(fileName).Create(); } if (!fileName.EndsWith(".amf", StringComparison.CurrentCultureIgnoreCase)) { fileName += ".amf"; } using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write)) using (var bw = new EndianWriter(fs, ByteOrder.LittleEndian)) { var dupeDic = new Dictionary <int, long>(); var validRegions = model.Regions .Select(r => new { r.Name, Permutations = r.Permutations.Where(p => model.Meshes[p.MeshIndex].Submeshes.Count > 0).ToList() }) .Where(r => r.Permutations.Count > 0) .ToList(); var fauxMeshes = validRegions.SelectMany(r => r.Permutations) .Where(p => p.MeshCount > 1) .GroupBy(p => p.MeshIndex) .ToDictionary(g => g.Key, g => new MultiMesh(model, g.Key, g.First().MeshCount)); #region Address Lists var headerAddressList = new List <long>(); var headerValueList = new List <long>(); var markerAddressList = new List <long>(); var markerValueList = new List <long>(); var permAddressList = new List <long>(); var permValueList = new List <long>(); var vertAddressList = new List <long>(); var vertValueList = new List <long>(); var indxAddressList = new List <long>(); var indxValueList = new List <long>(); var meshAddressList = new List <long>(); var meshValueList = new List <long>(); #endregion #region Header bw.Write("AMF!".ToCharArray()); bw.Write(2.0f); bw.WriteStringNullTerminated(model.Name ?? string.Empty); bw.Write(model.Nodes.Count); headerAddressList.Add(bw.BaseStream.Position); bw.Write(0); bw.Write(model.MarkerGroups.Count); headerAddressList.Add(bw.BaseStream.Position); bw.Write(0); bw.Write(validRegions.Count); headerAddressList.Add(bw.BaseStream.Position); bw.Write(0); bw.Write(model.Materials.Count); headerAddressList.Add(bw.BaseStream.Position); bw.Write(0); #endregion #region Nodes headerValueList.Add(bw.BaseStream.Position); foreach (var node in model.Nodes) { bw.WriteStringNullTerminated(node.Name); bw.Write(node.ParentIndex); bw.Write(node.FirstChildIndex); bw.Write(node.NextSiblingIndex); bw.Write(node.Position.X * scale); bw.Write(node.Position.Y * scale); bw.Write(node.Position.Z * scale); bw.Write(node.Rotation.X); bw.Write(node.Rotation.Y); bw.Write(node.Rotation.Z); bw.Write(node.Rotation.W); } #endregion #region Marker Groups headerValueList.Add(bw.BaseStream.Position); foreach (var group in model.MarkerGroups) { bw.WriteStringNullTerminated(group.Name); bw.Write(group.Markers.Count); markerAddressList.Add(bw.BaseStream.Position); bw.Write(0); } #endregion #region Markers foreach (var group in model.MarkerGroups) { markerValueList.Add(bw.BaseStream.Position); foreach (var marker in group.Markers) { bw.Write(marker.RegionIndex); bw.Write(marker.PermutationIndex); bw.Write((short)marker.NodeIndex); bw.Write(marker.Position.X * scale); bw.Write(marker.Position.Y * scale); bw.Write(marker.Position.Z * scale); bw.Write(marker.Rotation.X); bw.Write(marker.Rotation.Y); bw.Write(marker.Rotation.Z); bw.Write(marker.Rotation.W); } } #endregion #region Regions headerValueList.Add(bw.BaseStream.Position); foreach (var region in validRegions) { bw.WriteStringNullTerminated(region.Name); bw.Write(region.Permutations.Count); permAddressList.Add(bw.BaseStream.Position); bw.Write(0); } #endregion #region Permutations foreach (var region in validRegions) { permValueList.Add(bw.BaseStream.Position); foreach (var perm in region.Permutations) { var part = fauxMeshes.ContainsKey(perm.MeshIndex) ? fauxMeshes[perm.MeshIndex] : model.Meshes[perm.MeshIndex]; bw.WriteStringNullTerminated(perm.Name); bw.Write((byte)part.VertexWeights); bw.Write(part.NodeIndex ?? byte.MaxValue); bw.Write(part.Vertices.Count); vertAddressList.Add(bw.BaseStream.Position); bw.Write(0); int count = 0; foreach (var submesh in part.Submeshes) { var indices = part.Indicies.Skip(submesh.IndexStart).Take(submesh.IndexLength); if (part.IndexFormat == IndexFormat.TriangleStrip) { indices = Unstrip(indices); } count += indices.Count() / 3; } bw.Write(count); indxAddressList.Add(bw.BaseStream.Position); bw.Write(0); bw.Write(part.Submeshes.Count); meshAddressList.Add(bw.BaseStream.Position); bw.Write(0); if (perm.Transform.IsIdentity && perm.TransformScale == 1) { bw.Write(float.NaN); } else { bw.Write(perm.TransformScale); bw.Write(perm.Transform.M11); bw.Write(perm.Transform.M12); bw.Write(perm.Transform.M13); bw.Write(perm.Transform.M21); bw.Write(perm.Transform.M22); bw.Write(perm.Transform.M23); bw.Write(perm.Transform.M31); bw.Write(perm.Transform.M32); bw.Write(perm.Transform.M33); bw.Write(perm.Transform.M41); bw.Write(perm.Transform.M42); bw.Write(perm.Transform.M43); } } } #endregion #region Vertices var emptyVector = new RealVector3D(); foreach (var region in validRegions) { foreach (var perm in region.Permutations) { var part = fauxMeshes.ContainsKey(perm.MeshIndex) ? fauxMeshes[perm.MeshIndex] : model.Meshes[perm.MeshIndex]; var scale1 = perm.Transform.IsIdentity && perm.TransformScale == 1 ? scale : 1; long address; if (dupeDic.TryGetValue(perm.MeshIndex, out address)) { vertValueList.Add(address); continue; } else { dupeDic.Add(perm.MeshIndex, bw.BaseStream.Position); } vertValueList.Add(bw.BaseStream.Position); IXMVector vector; var vertices = part.BoundsIndex >= 0 ? part.Vertices.Select(v => (IVertex) new CompressedVertex(v, model.Bounds[part.BoundsIndex.Value])) : part.Vertices; foreach (var vert in vertices) { vector = vert.Position.Count > 0 ? vert.Position[0] : emptyVector; bw.Write(vector.X * scale1); bw.Write(vector.Y * scale1); bw.Write(vector.Z * scale1); vector = vert.Normal.Count > 0 ? vert.Normal[0] : emptyVector; bw.Write(vector.X); bw.Write(vector.Y); bw.Write(vector.Z); vector = vert.TexCoords.Count > 0 ? vert.TexCoords[0] : emptyVector; bw.Write(vector.X); bw.Write(1 - vector.Y); if (part.VertexWeights == VertexWeights.Rigid) { IXMVector i; var indices = new List <int>(); i = vert.BlendIndices.Count > 0 ? vert.BlendIndices[0] : emptyVector; if (!indices.Contains((int)i.X) && i.X != 0) { indices.Add((int)i.X); } if (!indices.Contains((int)i.Y) && i.X != 0) { indices.Add((int)i.Y); } if (!indices.Contains((int)i.Z) && i.X != 0) { indices.Add((int)i.Z); } if (!indices.Contains((int)i.W) && i.X != 0) { indices.Add((int)i.W); } if (indices.Count == 0) { indices.Add(0); } foreach (int index in indices) { bw.Write((byte)index); } if (indices.Count < 4) { bw.Write(byte.MaxValue); } } else if (part.VertexWeights == VertexWeights.Skinned) { var indices = (vert.BlendIndices.Count > 0 ? vert.BlendIndices[0] : emptyVector).AsEnumerable().ToArray(); var weights = (vert.BlendWeight.Count > 0 ? vert.BlendWeight[0] : emptyVector).AsEnumerable().ToArray(); var count = weights.Count(w => w > 0); if (count == 0) { bw.Write((byte)0); bw.Write((byte)255); bw.Write(0); continue; //throw new Exception("no weights on a weighted node. report this."); } for (int i = 0; i < 4; i++) { if (weights[i] > 0) { bw.Write((byte)indices[i]); } } if (count != 4) { bw.Write(byte.MaxValue); } foreach (var w in weights.Where(w => w > 0)) { bw.Write(w); } } } } } #endregion #region Indices dupeDic.Clear(); foreach (var region in validRegions) { foreach (var perm in region.Permutations) { var part = fauxMeshes.ContainsKey(perm.MeshIndex) ? fauxMeshes[perm.MeshIndex] : model.Meshes[perm.MeshIndex]; long address; if (dupeDic.TryGetValue(perm.MeshIndex, out address)) { indxValueList.Add(address); continue; } else { dupeDic.Add(perm.MeshIndex, bw.BaseStream.Position); } indxValueList.Add(bw.BaseStream.Position); foreach (var submesh in part.Submeshes) { var indices = part.Indicies.Skip(submesh.IndexStart).Take(submesh.IndexLength); if (part.IndexFormat == IndexFormat.TriangleStrip) { indices = Unstrip(indices); } foreach (var index in indices) { if (part.Vertices.Count > ushort.MaxValue) { bw.Write(index); } else { bw.Write((ushort)index); } } } } } #endregion #region Submeshes foreach (var region in validRegions) { foreach (var perm in region.Permutations) { meshValueList.Add(bw.BaseStream.Position); var part = fauxMeshes.ContainsKey(perm.MeshIndex) ? fauxMeshes[perm.MeshIndex] : model.Meshes[perm.MeshIndex]; int currentPosition = 0; foreach (var mesh in part.Submeshes) { var indices = part.Indicies.Skip(mesh.IndexStart).Take(mesh.IndexLength); if (part.IndexFormat == IndexFormat.TriangleStrip) { indices = Unstrip(indices); } var faceCount = indices.Count() / 3; bw.Write(mesh.MaterialIndex); bw.Write(currentPosition); bw.Write(faceCount); currentPosition += faceCount; } } } #endregion #region Shaders headerValueList.Add(bw.BaseStream.Position); foreach (var material in model.Materials) { const string nullPath = "null"; //skip null shaders if (material == null) { bw.WriteStringNullTerminated(nullPath); for (int i = 0; i < 8; i++) { bw.WriteStringNullTerminated(nullPath); } for (int i = 0; i < 4; i++) { bw.Write(0); } bw.Write(Convert.ToByte(false)); bw.Write(Convert.ToByte(false)); continue; } if (material.Flags.HasFlag(MaterialFlags.TerrainBlend)) { bw.WriteStringNullTerminated("*" + material.Name); var blendInfo = material.Submaterials.FirstOrDefault(s => s.Usage == MaterialUsage.BlendMap); var baseInfo = material.Submaterials.Where(s => s.Usage == MaterialUsage.Diffuse).ToList(); var bumpInfo = material.Submaterials.Where(s => s.Usage == MaterialUsage.Normal).ToList(); var detailInfo = material.Submaterials.Where(s => s.Usage == MaterialUsage.DiffuseDetail).ToList(); if (blendInfo == null) { bw.WriteStringNullTerminated(nullPath); } else { bw.WriteStringNullTerminated(blendInfo.Bitmap.Name); bw.Write(blendInfo.Tiling.X); bw.Write(blendInfo.Tiling.Y); } bw.Write((byte)baseInfo.Count); bw.Write((byte)bumpInfo.Count); bw.Write((byte)detailInfo.Count); foreach (var info in baseInfo.Concat(bumpInfo).Concat(detailInfo)) { bw.WriteStringNullTerminated(info.Bitmap.Name); bw.Write(info.Tiling.X); bw.Write(info.Tiling.Y); } } else { bw.WriteStringNullTerminated(material.Name); for (int i = 0; i < 8; i++) { var submat = material.Submaterials.FirstOrDefault(s => s.Usage == (MaterialUsage)i); bw.WriteStringNullTerminated(submat?.Bitmap.Name ?? nullPath); if (submat != null) { bw.Write(submat.Tiling.X); bw.Write(submat.Tiling.Y); } } for (int i = 0; i < 4; i++) { var tint = material.TintColours.FirstOrDefault(t => t.Usage == (TintUsage)i); if (tint == null) { bw.Write(0); continue; } bw.Write(tint.R); bw.Write(tint.G); bw.Write(tint.B); bw.Write(tint.A); } bw.Write(Convert.ToByte(material.Flags.HasFlag(MaterialFlags.Transparent))); bw.Write(Convert.ToByte(material.Flags.HasFlag(MaterialFlags.ColourChange))); } } #endregion #region Write Addresses for (int i = 0; i < headerAddressList.Count; i++) { bw.BaseStream.Position = headerAddressList[i]; bw.Write((int)headerValueList[i]); } for (int i = 0; i < markerAddressList.Count; i++) { bw.BaseStream.Position = markerAddressList[i]; bw.Write((int)markerValueList[i]); } for (int i = 0; i < permAddressList.Count; i++) { bw.BaseStream.Position = permAddressList[i]; bw.Write((int)permValueList[i]); } for (int i = 0; i < vertAddressList.Count; i++) { bw.BaseStream.Position = vertAddressList[i]; bw.Write((int)vertValueList[i]); } for (int i = 0; i < indxAddressList.Count; i++) { bw.BaseStream.Position = indxAddressList[i]; bw.Write((int)indxValueList[i]); } for (int i = 0; i < meshAddressList.Count; i++) { bw.BaseStream.Position = meshAddressList[i]; bw.Write((int)meshValueList[i]); } #endregion } }
internal InstancedMeshTemplate(IGeometryModel model, IGeometryMesh mesh) : base(model, mesh) { rootMeshes = new Helix.InstancingMeshGeometryModel3D[submeshCount]; }
public static void WriteJMS(this IGeometryModel model, string fileName, float scale) { const string float4 = "{0:F6}\t{1:F6}\t{2:F6}\t{3:F6}"; const string float3 = "{0:F6}\t{1:F6}\t{2:F6}"; const string float1 = "{0:F6}"; var modelName = Path.GetFileNameWithoutExtension(fileName); var directory = Path.Combine(Directory.GetParent(fileName).FullName, "models"); Directory.CreateDirectory(directory); var permNames = model.Regions.SelectMany(r => r.Permutations) .Select(p => p.Name) .Distinct(); foreach (var permName in permNames) { var allRegions = model.Regions.Where(r => r.Permutations.Any(p => p.Name == permName)).ToList(); var allPerms = model.Regions.SelectMany(r => r.Permutations).Where(p => p.Name == permName).ToList(); using (var sw = new StreamWriter(Path.Combine(directory, permName + ".jms"))) { sw.WriteLine("8200"); sw.WriteLine("14689795"); sw.WriteLine(model.Nodes.Count); foreach (var node in model.Nodes) { sw.WriteLine(node.Name); sw.WriteLine(node.FirstChildIndex); sw.WriteLine(node.NextSiblingIndex); sw.WriteLine("{0}\t{1}\t{2}\t{3}", -node.Rotation.X, -node.Rotation.Y, -node.Rotation.Z, node.Rotation.W); sw.WriteLine(float3, node.Position.X * scale, node.Position.Y * scale, node.Position.Z * scale); } sw.WriteLine(model.Materials.Count); foreach (var mat in model.Materials) { sw.WriteLine(mat?.Name ?? "unused"); sw.WriteLine("<none>"); //unknown } sw.WriteLine(model.MarkerGroups.Sum(g => g.Markers.Count)); foreach (var group in model.MarkerGroups) { foreach (var marker in group.Markers) { sw.WriteLine(group.Name); sw.WriteLine(-1); //unknown sw.WriteLine(marker.NodeIndex); sw.WriteLine(float4, marker.Rotation.X, marker.Rotation.Y, marker.Rotation.Z, marker.Rotation.W); sw.WriteLine(float3, marker.Position.X * scale, marker.Position.Y * scale, marker.Position.Z * scale); sw.WriteLine(1); //radius } } sw.WriteLine(allRegions.Count); foreach (var region in allRegions) { sw.WriteLine(region.Name); } #region Vertices var emptyVector = new RealVector3D(); sw.WriteLine(allPerms.SelectMany(p => model.Meshes.Skip(p.MeshIndex).Take(p.MeshCount)).Sum(m => m.Vertices.Count)); foreach (var perm in allPerms) { var mesh = model.Meshes[perm.MeshIndex]; foreach (var vert in mesh.Vertices) { var decompressed = vert; if (mesh.BoundsIndex >= 0) { decompressed = new CompressedVertex(vert, model.Bounds[mesh.BoundsIndex.Value]); } var pos = decompressed.Position.FirstOrDefault() ?? emptyVector; var norm = vert.Normal.FirstOrDefault() ?? emptyVector; var tex = decompressed.TexCoords.FirstOrDefault() ?? emptyVector; var weights = decompressed.BlendWeight.FirstOrDefault() ?? emptyVector; var nodes = decompressed.BlendIndices.FirstOrDefault() ?? emptyVector; var node1 = nodes.X; if (mesh.NodeIndex < byte.MaxValue) { node1 = mesh.NodeIndex.Value; } sw.WriteLine("{0:F0}", node1); sw.WriteLine(float3, pos.X * scale, pos.Y * scale, pos.Z * scale); sw.WriteLine(float3, norm.X, norm.Y, norm.Z); sw.WriteLine(nodes.Y); sw.WriteLine(float1, weights.Y); sw.WriteLine(float1, tex.X); sw.WriteLine(float1, 1 - tex.Y); sw.WriteLine(0); } } #endregion #region Triangles var totalEdges = allPerms.SelectMany(p => { var mesh = model.Meshes[p.MeshIndex]; if (mesh.IndexFormat == IndexFormat.TriangleList) { return(mesh.Indicies); } else { return(mesh.Submeshes.SelectMany(s => mesh.Indicies.Skip(s.IndexStart).Take(s.IndexLength).Unstrip())); } }).Count(); sw.WriteLine(totalEdges / 3); int offset = 0; foreach (var perm in allPerms) { var regIndex = allRegions.TakeWhile(r => !r.Permutations.Contains(perm)).Count(); var mesh = model.Meshes[perm.MeshIndex]; foreach (var sub in mesh.Submeshes) { var temp = mesh.Indicies.Skip(sub.IndexStart).Take(sub.IndexLength); if (mesh.IndexFormat == IndexFormat.TriangleStrip) { temp = temp.Unstrip(); } var indices = temp.ToList(); for (int i = 0; i < indices.Count; i += 3) { sw.WriteLine(regIndex); sw.WriteLine(sub.MaterialIndex); sw.WriteLine("{0}\t{1}\t{2}", offset + indices[i], offset + indices[i + 1], offset + indices[i + 2]); } } offset += mesh.Vertices.Count; } #endregion } } }
private void SetLod(int index) { model = geometry.ReadGeometry(index); var meshes = GetMeshes(model).ToList(); TreeViewItems.Clear(); modelGroup.Children.Clear(); foreach (var region in model.Regions) { var regNode = new TreeItemModel { Header = region.Name, IsChecked = true }; foreach (var perm in region.Permutations) { var mesh = meshes.ElementAtOrDefault(perm.MeshIndex); if (mesh == null || perm.MeshCount <= 0) { continue; } var permNode = new TreeItemModel { Header = perm.Name, IsChecked = true }; regNode.Items.Add(permNode); var tGroup = new Transform3DGroup(); if (perm.TransformScale != 1) { var tform = new ScaleTransform3D(perm.TransformScale, perm.TransformScale, perm.TransformScale); tform.Freeze(); tGroup.Children.Add(tform); } if (!perm.Transform.IsIdentity) { var tform = new MatrixTransform3D(new Matrix3D { M11 = perm.Transform.M11, M12 = perm.Transform.M12, M13 = perm.Transform.M13, M21 = perm.Transform.M21, M22 = perm.Transform.M22, M23 = perm.Transform.M23, M31 = perm.Transform.M31, M32 = perm.Transform.M32, M33 = perm.Transform.M33, OffsetX = perm.Transform.M41, OffsetY = perm.Transform.M42, OffsetZ = perm.Transform.M43 }); tform.Freeze(); tGroup.Children.Add(tform); } Model3DGroup permGroup; if (tGroup.Children.Count == 0 && perm.MeshCount == 1) { permGroup = meshes[perm.MeshIndex]; } else { permGroup = new Model3DGroup(); for (int i = 0; i < perm.MeshCount; i++) { if (tGroup.Children.Count > 0) { (permGroup.Transform = tGroup).Freeze(); permGroup.Children.Add(meshes[perm.MeshIndex + i]); permGroup.Freeze(); } else { permGroup.Children.Add(meshes[perm.MeshIndex + i]); } } } permNode.Tag = new MeshTag(permGroup, perm); modelGroup.Children.Add(permGroup); } if (regNode.HasItems) { TreeViewItems.Add(regNode); } } renderer.ScaleToContent(new[] { modelGroup }); }