protected override DataStructures.MapObjects.Map GetFromStream(Stream stream, IEnumerable <string> modelDirs, out Image[] lightmaps) { lightmaps = null; var map = new DataStructures.MapObjects.Map(); map.CordonBounds = new Box(Coordinate.One * -16384, Coordinate.One * 16384); BinaryReader br = new BinaryReader(stream); List <ModelReference> models = null; //header bool hasLightmap = Math.Abs(br.ReadSingle()) > 0.01f; if (hasLightmap) { UInt32 lightmapSize = br.ReadUInt32(); stream.Position += lightmapSize; } int entityCount = (int)br.ReadSingle() - 2; for (int i = 0; i < entityCount; i++) { int meshCount = (int)br.ReadSingle(); List <long> memblockLocations = new List <long>(); for (int j = 0; j < meshCount; j++) { stream.Position += 4; memblockLocations.Add(stream.Position); SkipMemblock(br); } bool isBrush = Math.Abs(br.ReadSingle()) > 0.01f; if (isBrush) { Dictionary <int, List <Face> > faces = new Dictionary <int, List <Face> >(); long returnPosition = stream.Position; for (int j = 0; j < meshCount; j++) { stream.Position = memblockLocations[j]; faces.Add(j, new List <Face>()); ReadMemblockMesh(br, map, faces[j]); } stream.Position = returnPosition; SkipMemblock(br); for (int j = 0; j < 2; j++) { stream.Position += 4; } float xTranslate = br.ReadSingle(); float zTranslate = br.ReadSingle(); float yTranslate = br.ReadSingle(); float xScale = br.ReadSingle(); float zScale = br.ReadSingle(); float yScale = br.ReadSingle(); for (int j = 8; j < 25; j++) { stream.Position += 4; } List <SubmeshTextureInfo> textures = new List <SubmeshTextureInfo>(); for (int j = 0; j < meshCount; j++) { SubmeshTextureInfo submeshTextureInfo = new SubmeshTextureInfo(); submeshTextureInfo.TextureName = System.IO.Path.GetFileNameWithoutExtension(br.ReadLine()); float flags = br.ReadSingle(); bool faceIsHidden = Math.Abs(flags - 1) < 0.01f; bool faceIsLit = Math.Abs(flags - 800) < 0.01f; if (faceIsLit) { br.ReadSingle(); } for (int k = 0; k < 4; k++) { stream.Position += 4; } submeshTextureInfo.ScaleU = br.ReadSingle(); submeshTextureInfo.ScaleV = br.ReadSingle(); submeshTextureInfo.ShiftU = br.ReadSingle(); submeshTextureInfo.ShiftV = br.ReadSingle(); submeshTextureInfo.Rotation = br.ReadSingle(); if (faceIsHidden) { submeshTextureInfo.TextureName = "tooltextures/remove_face"; } textures.Add(submeshTextureInfo); } if (faces.Any()) { Solid newSolid = new Solid(map.IDGenerator.GetNextObjectID()); foreach (int key in faces.Keys) { foreach (Face face in faces[key]) { face.Parent = newSolid; newSolid.Faces.Add(face); } } newSolid.Colour = Colour.GetRandomBrushColour(); newSolid.UpdateBoundingBox(); MapObject parent = map.WorldSpawn; newSolid.SetParent(parent); newSolid.Transform(new UnitScale(Coordinate.One, newSolid.BoundingBox.Center), TransformFlags.None); newSolid.Transform(new UnitScale(new Coordinate( (decimal)xScale / newSolid.BoundingBox.Width, (decimal)yScale / newSolid.BoundingBox.Length, (decimal)zScale / newSolid.BoundingBox.Height), Coordinate.Zero), TransformFlags.None); newSolid.UpdateBoundingBox(); newSolid.Transform(new UnitTranslate(new Coordinate( (decimal)xTranslate, (decimal)yTranslate, (decimal)zTranslate)), TransformFlags.None); newSolid.UpdateBoundingBox(); foreach (int key in faces.Keys) { foreach (Face face in faces[key]) { face.Texture.Name = textures[key].TextureName; face.AlignTextureToWorld(); face.Texture.XScale = (decimal)textures[key].ScaleU * 0.25m; face.Texture.YScale = (decimal)textures[key].ScaleV * 0.25m; face.Texture.XShift = (decimal)textures[key].ShiftU; face.Texture.YShift = (decimal)textures[key].ShiftV; face.SetTextureRotation((decimal)textures[key].Rotation); } } } } else { int entitySubType = (int)br.ReadSingle(); for (int j = 1; j < 2; j++) { stream.Position += 4; } float xTranslate = br.ReadSingle(); float zTranslate = br.ReadSingle(); float yTranslate = br.ReadSingle(); float xScale = br.ReadSingle(); float zScale = br.ReadSingle(); float yScale = br.ReadSingle(); if (Math.Abs(entitySubType - 3.0f) < 0.01f) { for (int j = 8; j < 35; j++) { stream.Position += 4; } string entityName = br.ReadLine(); string entityIcon = br.ReadLine(); int propertyCount = (int)br.ReadSingle() + 1; Dictionary <string, string> properties = new Dictionary <string, string>(); for (int j = 0; j < propertyCount; j++) { string propertyName = br.ReadLine().ToLowerInvariant(); string propertyValue = br.ReadLine(); properties.Add(propertyName, propertyValue); } Entity entity = new Entity(map.IDGenerator.GetNextObjectID()); entity.Colour = Colour.GetDefaultEntityColour(); Property newProperty = null; switch (entityName.ToLowerInvariant()) { case "pointlight": entity.ClassName = "light"; entity.EntityData.Name = "light"; newProperty = new Property(); newProperty.Key = "range"; newProperty.Value = properties["range"]; entity.EntityData.Properties.Add(newProperty); newProperty = new Property(); newProperty.Key = "color"; newProperty.Value = properties["color"].Replace(',', ' ').Trim(); entity.EntityData.Properties.Add(newProperty); break; case "spotlight": entity.ClassName = "spotlight"; entity.EntityData.Name = "spotlight"; newProperty = new Property(); newProperty.Key = "range"; newProperty.Value = properties["range"]; entity.EntityData.Properties.Add(newProperty); newProperty = new Property(); newProperty.Key = "color"; newProperty.Value = properties["color"].Replace(',', ' ').Trim(); entity.EntityData.Properties.Add(newProperty); newProperty = new Property(); newProperty.Key = "innerconeangle"; newProperty.Value = "45"; if (decimal.TryParse(properties["innerang"], out decimal innerAngle)) { newProperty.Value = (innerAngle * 0.5m).ToString(); } entity.EntityData.Properties.Add(newProperty); newProperty = new Property(); newProperty.Key = "outerconeangle"; newProperty.Value = "90"; if (decimal.TryParse(properties["outerang"], out decimal outerAngle)) { newProperty.Value = (outerAngle * 0.5m).ToString(); } entity.EntityData.Properties.Add(newProperty); newProperty = new Property(); newProperty.Key = "angles"; newProperty.Value = "0 0 0"; string[] dirParts = properties["direction"].Split(','); if (decimal.TryParse(dirParts[0], out decimal dirX) && decimal.TryParse(dirParts[1], out decimal dirY) && decimal.TryParse(dirParts[2], out decimal dirZ)) { Coordinate dir = new Coordinate(dirX, dirY, dirZ).Normalise(); decimal pitch = DMath.RadiansToDegrees(DMath.Asin(-dir.Y)); dir.Y = 0; decimal yaw = 0m; if (dir.LengthSquared() > 0.01m) { dir = dir.Normalise(); yaw = DMath.RadiansToDegrees(DMath.Atan2(-dir.X, dir.Z)); } newProperty.Value = $"{pitch} {yaw} 0"; } entity.EntityData.Properties.Add(newProperty); break; default: entity.ClassName = entityName; entity.EntityData.Name = entityName; foreach (var key in properties.Keys) { newProperty = new Property(); newProperty.Key = key; newProperty.Value = properties[key]; entity.EntityData.Properties.Add(newProperty); } break; } entity.Origin = new Coordinate((decimal)xTranslate, (decimal)yTranslate, (decimal)zTranslate); entity.SetParent(map.WorldSpawn); } else if (Math.Abs(entitySubType - 2.0f) < 0.01f) { if (models == null) { models = LoadAllModels(modelDirs); } ModelReference model = null; Coordinate angles = null; Coordinate scale = null; long returnPosition = stream.Position; for (int j = 0; j < meshCount; j++) { stream.Position = memblockLocations[j]; UInt32 memblockSize = br.ReadUInt32(); UInt32 dwFVF = br.ReadUInt32(); UInt32 dwFVFSize = br.ReadUInt32(); UInt32 dwVertMax = br.ReadUInt32(); for (int k = 0; k < models.Count; k++) { DataStructures.Models.Mesh currMesh = models[k].Model.BodyParts[0].Meshes.Values.First()[0]; if (dwVertMax == currMesh.Vertices.Count) { List <Pair <Coordinate, Coordinate> > points = new List <Pair <Coordinate, Coordinate> >(); List <Coordinate> loadedPoints = new List <Coordinate>(); Coordinate loadedCenter = new Coordinate(0, 0, 0); Coordinate knownCenter = new Coordinate(0, 0, 0); for (int l = 0; l < dwVertMax; l++) { float x = br.ReadSingle(); float z = br.ReadSingle(); float y = br.ReadSingle(); Coordinate point = new Coordinate((decimal)x, (decimal)y, (decimal)z); loadedPoints.Add(point); loadedCenter += point; knownCenter += new Coordinate(currMesh.Vertices[l].Location); for (int m = 12; m < dwFVFSize; m += 4) { stream.Position += 4; } if (points.Count < 3) { int nativeIndex = (l / 3) * 3 + ((l % 3) + 1) % 3; Coordinate vertexLoc = new Coordinate(currMesh.Vertices[nativeIndex].Location); if (!points.Any(p => Math.Abs(p.Item1.Normalise().Dot(vertexLoc.Normalise())) > 0.95m)) { points.Add(new Pair <Coordinate, Coordinate>(vertexLoc, point)); } } } loadedCenter /= dwVertMax; knownCenter /= dwVertMax; if (points.Count >= 3) { model = models[k]; for (int l = 0; l < 3; l++) { points[l].Item1 -= knownCenter; points[l].Item1 = points[l].Item1.Normalise(); points[l].Item2 -= loadedCenter; points[l].Item2 = points[l].Item2.Normalise(); } points[2].Item1 = points[0].Item1.Cross(points[1].Item1).Normalise(); points[2].Item2 = points[0].Item2.Cross(points[1].Item2).Normalise(); points[1].Item1 = points[0].Item1.Cross(points[2].Item1).Normalise(); points[1].Item2 = points[0].Item2.Cross(points[2].Item2).Normalise(); decimal dotX0 = Coordinate.UnitX.Dot(points[0].Item1); decimal dotX1 = Coordinate.UnitX.Dot(points[1].Item1); decimal dotX2 = Coordinate.UnitX.Dot(points[2].Item1); decimal dotY0 = Coordinate.UnitY.Dot(points[0].Item1); decimal dotY1 = Coordinate.UnitY.Dot(points[1].Item1); decimal dotY2 = Coordinate.UnitY.Dot(points[2].Item1); decimal dotZ0 = Coordinate.UnitZ.Dot(points[0].Item1); decimal dotZ1 = Coordinate.UnitZ.Dot(points[1].Item1); decimal dotZ2 = Coordinate.UnitZ.Dot(points[2].Item1); Coordinate newX = (dotX0 * points[0].Item2 + dotX1 * points[1].Item2 + dotX2 * points[2].Item2); Coordinate newY = (dotY0 * points[0].Item2 + dotY1 * points[1].Item2 + dotY2 * points[2].Item2); Coordinate newZ = (dotZ0 * points[0].Item2 + dotZ1 * points[1].Item2 + dotZ2 * points[2].Item2); Coordinate unTransformedMin = new Coordinate( loadedPoints.Select(p => p.X).Min(), loadedPoints.Select(p => p.Y).Min(), loadedPoints.Select(p => p.Z).Min() ); Coordinate unTransformedBounds = new Coordinate( loadedPoints.Select(p => p.X).Max(), loadedPoints.Select(p => p.Y).Max(), loadedPoints.Select(p => p.Z).Max()) - unTransformedMin; Coordinate propScale(Coordinate p) { Coordinate retVal = p.Clone(); retVal.X *= (decimal)xScale / unTransformedBounds.X; retVal.Y *= (decimal)yScale / unTransformedBounds.Y; retVal.Z *= (decimal)zScale / unTransformedBounds.Z; return(retVal); } Coordinate centerDiff = propScale(loadedCenter - knownCenter); xTranslate += (float)centerDiff.X; yTranslate += (float)centerDiff.Y; zTranslate += (float)centerDiff.Z; Coordinate newBounds = new Coordinate( loadedPoints.Select(p => propScale(p).Dot(newX)).Max() - loadedPoints.Select(p => propScale(p).Dot(newX)).Min(), loadedPoints.Select(p => propScale(p).Dot(newY)).Max() - loadedPoints.Select(p => propScale(p).Dot(newY)).Min(), loadedPoints.Select(p => propScale(p).Dot(newZ)).Max() - loadedPoints.Select(p => propScale(p).Dot(newZ)).Min()); Coordinate newBounds2 = new Coordinate( loadedPoints.Select(p => p.Dot(newX)).Max() - loadedPoints.Select(p => p.Dot(newX)).Min(), loadedPoints.Select(p => p.Dot(newY)).Max() - loadedPoints.Select(p => p.Dot(newY)).Min(), loadedPoints.Select(p => p.Dot(newZ)).Max() - loadedPoints.Select(p => p.Dot(newZ)).Min()); scale = new Coordinate(newBounds.X / newBounds2.X, newBounds.Z / newBounds2.Z, newBounds.Y / newBounds2.Y); angles = Entity.ToEuler(newX, newY, newZ); break; } } } } stream.Position = returnPosition; for (int j = 8; j < 24; j++) { stream.Position += 4; } int materialCount = (int)br.ReadSingle() + 1; for (int j = 0; j < materialCount; j++) { string materialName = br.ReadLine(); for (int k = 0; k < 10; k++) { stream.Position += 4; } } Entity entity = new Entity(map.IDGenerator.GetNextObjectID()); entity.ClassName = "model"; entity.EntityData.Name = "model"; entity.Colour = Colour.GetDefaultEntityColour(); Property newProperty; if (model != null) { newProperty = new Property(); newProperty.Key = "file"; newProperty.Value = System.IO.Path.GetFileNameWithoutExtension(model.Path); entity.EntityData.Properties.Add(newProperty); if (angles != null) { newProperty = new Property(); newProperty.Key = "angles"; newProperty.Value = angles.ToDataString(); entity.EntityData.Properties.Add(newProperty); } if (scale != null) { newProperty = new Property(); newProperty.Key = "scale"; newProperty.Value = scale.ToDataString(); entity.EntityData.Properties.Add(newProperty); } } entity.Origin = new Coordinate((decimal)xTranslate, (decimal)yTranslate, (decimal)zTranslate); entity.SetParent(map.WorldSpawn); } } } if (models != null) { models.ForEach(m => ModelProvider.DeleteModelReference(m)); } return(map); }