protected override DataStructures.MapObjects.Map GetFromStream(Stream stream) { var map = new DataStructures.MapObjects.Map(); var br = new BinaryReader(stream); // Only RMF version 2.2 is supported for the moment. var version = Math.Round(br.ReadSingle(), 1); if (Math.Abs(version - 2.2) > 0.01) { throw new ProviderException("Incorrect RMF version number. Expected 2.2, got " + version + "."); } map.Version = (decimal) version; // RMF header test var header = br.ReadFixedLengthString(Encoding.UTF8, 3); if (header != "RMF") { throw new ProviderException("Incorrect RMF header. Expected 'RMF', got '" + header + "'."); } // Visgroups var visgroups = ReadVisgroups(br); map.Visgroups.AddRange(visgroups); // Map Objects map.IDGenerator.Reset(); // So the world will be ID = 1 var worldspawn = ReadWorldSpawn(br, map.Visgroups, map.IDGenerator); map.WorldSpawn = worldspawn; // DOCINFO string check var docinfo = br.ReadFixedLengthString(Encoding.UTF8, 8); if (docinfo != "DOCINFO") { throw new ProviderException("Incorrect RMF format. Expected 'DOCINFO', got '" + docinfo + "'."); } // Cameras br.ReadSingle(); // Appears to be a version number for camera data. Unused. var activeCamera = br.ReadInt32(); var cameras = ReadCameras(br); map.Cameras.AddRange(cameras); if (activeCamera >= 0 && activeCamera < map.Cameras.Count) { map.ActiveCamera = map.Cameras[activeCamera]; } return map; }
/// <summary> /// Reads a map from a stream in MAP format. /// </summary> /// <param name="stream">The stream to read from</param> /// <returns>The parsed map</returns> protected override DataStructures.MapObjects.Map GetFromStream(Stream stream) { using (var reader = new StreamReader(stream)) { var map = new DataStructures.MapObjects.Map(); var allentities = ReadAllEntities(reader, map.IDGenerator); var worldspawn = allentities.FirstOrDefault(x => x.EntityData.Name == "worldspawn") ?? new Entity(0) {EntityData = {Name = "worldspawn"}}; allentities.Remove(worldspawn); map.WorldSpawn.EntityData = worldspawn.EntityData; allentities.ForEach(x => x.SetParent(map.WorldSpawn, false)); foreach (var obj in worldspawn.GetChildren().ToArray()) { obj.SetParent(map.WorldSpawn, false); } map.WorldSpawn.UpdateBoundingBox(false); return map; } }
private static GenericStructure WriteWorld(DataStructures.MapObjects.Map map, IEnumerable <Solid> solids, IEnumerable <Group> groups) { var world = map.WorldSpawn; var ret = new GenericStructure("world"); ret["id"] = world.ID.ToString(CultureInfo.InvariantCulture); ret["classname"] = "worldspawn"; ret["mapversion"] = map.Version.ToString(CultureInfo.InvariantCulture); WriteEntityData(ret, world.EntityData); foreach (var solid in solids.OrderBy(x => x.ID)) { ret.Children.Add(WriteSolid(solid)); } foreach (var group in groups.OrderBy(x => x.ID)) { ret.Children.Add(WriteGroup(group)); } return(ret); }
/// <summary> /// Reads a map from a stream in MAP format. /// </summary> /// <param name="stream">The stream to read from</param> /// <returns>The parsed map</returns> protected override DataStructures.MapObjects.Map GetFromStream(Stream stream, string fgd = null) { using (var reader = new StreamReader(stream)) { var map = new DataStructures.MapObjects.Map(); var allentities = ReadAllEntities(reader, map.IDGenerator); var worldspawn = allentities.FirstOrDefault(x => x.EntityData.Name == "worldspawn") ?? new Entity(0) { EntityData = { Name = "worldspawn" } }; allentities.Remove(worldspawn); map.WorldSpawn.EntityData = worldspawn.EntityData; allentities.ForEach(x => x.SetParent(map.WorldSpawn, false)); foreach (var obj in worldspawn.GetChildren().ToArray()) { obj.SetParent(map.WorldSpawn, false); } map.WorldSpawn.UpdateBoundingBox(false); return(map); } }
protected override DataStructures.MapObjects.Map GetFromStream(Stream stream) { using (var reader = new StreamReader(stream)) { var map = new DataStructures.MapObjects.Map(); Read(map, reader); /* * var allentities = ReadAllEntities(reader, map.IDGenerator); * var worldspawn = allentities.FirstOrDefault(x => x.EntityData.Name == "worldspawn") * ?? new Entity(0) { EntityData = { Name = "worldspawn" } }; * allentities.Remove(worldspawn); * map.WorldSpawn.EntityData = worldspawn.EntityData; * allentities.ForEach(x => x.SetParent(map.WorldSpawn)); * foreach (var obj in worldspawn.Children.ToArray()) * { * obj.SetParent(map.WorldSpawn); * } * map.WorldSpawn.UpdateBoundingBox(false); */ return(map); } }
protected override DataStructures.MapObjects.Map GetFromStream(Stream stream, string fgd = null) { using (var reader = new StreamReader(stream)) { var parent = new GenericStructure("Root"); parent.Children.AddRange(GenericStructure.Parse(reader)); // Sections from a Hammer map: // - world // - entity // - visgroups // - cordon // Not done yet // - versioninfo // - viewsettings // - cameras var map = new DataStructures.MapObjects.Map(); var world = parent.GetChildren("world").FirstOrDefault(); var entities = parent.GetChildren("entity"); var visgroups = parent.GetChildren("visgroups").SelectMany(x => x.GetChildren("visgroup")); var cameras = parent.GetChildren("cameras").FirstOrDefault(); var cordon = parent.GetChildren("cordon").FirstOrDefault(); var viewsettings = parent.GetChildren("viewsettings").FirstOrDefault(); foreach (var visgroup in visgroups) { var vg = ReadVisgroup(visgroup); if (vg.ID < 0 && vg.Name == "Auto") { continue; } map.Visgroups.Add(vg); } if (world != null) { map.WorldSpawn = ReadWorld(world, map.IDGenerator); } foreach (var entity in entities) { var ent = ReadEntity(entity, map.IDGenerator); var groupid = entity.Children.Where(x => x.Name == "editor").Select(x => x.PropertyInteger("groupid")).FirstOrDefault(); var entParent = groupid > 0 ? map.WorldSpawn.Find(x => x.ID == groupid && x is Group).FirstOrDefault() ?? map.WorldSpawn : map.WorldSpawn; ent.SetParent(entParent); } var activeCamera = 0; if (cameras != null) { activeCamera = cameras.PropertyInteger("activecamera"); foreach (var cam in cameras.GetChildren("camera")) { var pos = cam.PropertyCoordinate("position"); var look = cam.PropertyCoordinate("look"); if (pos != null && look != null) { map.Cameras.Add(new Camera { EyePosition = pos, LookPosition = look }); } } } if (!map.Cameras.Any()) { map.Cameras.Add(new Camera { EyePosition = Coordinate.Zero, LookPosition = Coordinate.UnitY }); } if (activeCamera < 0 || activeCamera >= map.Cameras.Count) { activeCamera = 0; } map.ActiveCamera = map.Cameras[activeCamera]; if (cordon != null) { var start = cordon.PropertyCoordinate("mins", map.CordonBounds.Start); var end = cordon.PropertyCoordinate("maxs", map.CordonBounds.End); map.CordonBounds = new Box(start, end); map.Cordon = cordon.PropertyBoolean("active", map.Cordon); } if (viewsettings != null) { map.SnapToGrid = viewsettings.PropertyBoolean("bSnapToGrid", map.SnapToGrid); map.Show2DGrid = viewsettings.PropertyBoolean("bShowGrid", map.Show2DGrid); map.Show3DGrid = viewsettings.PropertyBoolean("bShow3DGrid", map.Show3DGrid); map.GridSpacing = viewsettings.PropertyDecimal("nGridSpacing", map.GridSpacing); map.IgnoreGrouping = viewsettings.PropertyBoolean("bIgnoreGrouping", map.IgnoreGrouping); map.HideFaceMask = viewsettings.PropertyBoolean("bHideFaceMask", map.HideFaceMask); map.HideNullTextures = viewsettings.PropertyBoolean("bHideNullTextures", map.HideNullTextures); map.TextureLock = viewsettings.PropertyBoolean("bTextureLock", map.TextureLock); map.TextureScalingLock = viewsettings.PropertyBoolean("bTextureScalingLock", map.TextureScalingLock); } return(map); } }
protected void ReadMemblockMesh(BinaryReader br, DataStructures.MapObjects.Map map, List <Face> faces = null) { UInt32 memblockSize = br.ReadUInt32(); long startPos = br.BaseStream.Position; UInt32 dwFVF = br.ReadUInt32(); UInt32 dwFVFSize = br.ReadUInt32(); UInt32 dwVertMax = br.ReadUInt32(); List <CoordinateF> vertexPositions = new List <CoordinateF>(); List <CoordinateF> vertexNormals = new List <CoordinateF>(); for (int i = 0; i < dwVertMax; i++) { float x = br.ReadSingle(); float z = br.ReadSingle(); float y = br.ReadSingle(); vertexPositions.Add(new CoordinateF(x, y, z)); float nx = br.ReadSingle(); float nz = br.ReadSingle(); float ny = br.ReadSingle(); vertexNormals.Add(new CoordinateF(nx, ny, nz).Normalise()); for (int j = 24; j < dwFVFSize; j += 4) { br.BaseStream.Position += 4; } } if (faces != null) { foreach (var normal in vertexNormals.Distinct(new NormalEq())) { if (normal.LengthSquared() < 0.01f) { continue; } Face newFace = new Face(map.IDGenerator.GetNextFaceID()); for (int i = 0; i < vertexPositions.Count; i++) { if (vertexNormals[i].Dot(normal) < 0.999f) { continue; } if (newFace.Vertices.Any(v => (new CoordinateF(v.Location) - vertexPositions[i]).LengthSquared() < 0.001f)) { continue; } newFace.Vertices.Add(new Vertex(new Coordinate(vertexPositions[i]), newFace)); } newFace.Plane = new Plane(new Coordinate(normal), newFace.Vertices[0].Location); Vertex center = newFace.Vertices[0]; newFace.Vertices.RemoveAt(0); newFace.Vertices.Sort((v1, v2) => { float dot = normal.Dot(new CoordinateF((v1.Location - center.Location).Cross(v2.Location - center.Location))); if (dot < 0.0f) { return(-1); } else if (dot > 0.0f) { return(1); } return(0); }); newFace.Vertices.Insert(0, center); newFace.UpdateBoundingBox(); faces.Add(newFace); } } br.BaseStream.Position = startPos + (long)memblockSize; }
protected virtual void SaveToFile(string filename, DataStructures.MapObjects.Map map, DataStructures.GameData.GameData gameData, TextureCollection textureCollection) { using (var strm = new FileStream(filename, FileMode.Create, FileAccess.Write)) { SaveToStream(strm, map, gameData, textureCollection); } }
protected override DataStructures.MapObjects.Map GetFromStream(Stream stream) { var map = new DataStructures.MapObjects.Map(); map.CordonBounds = new Box(Coordinate.One * -16384, Coordinate.One * 16384); BinaryReader br = new BinaryReader(stream); //header UInt16 mapVersion = br.ReadUInt16(); byte mapFlags = br.ReadByte(); Int32 nameCount = br.ReadInt32(); Int32 nameOffset = br.ReadInt32(); Int32 objectCount = br.ReadInt32(); Int32 objectOffset = br.ReadInt32(); //get names, needed to understand the objects List <string> names = new List <string>(); br.BaseStream.Seek(nameOffset, SeekOrigin.Begin); for (int i = 0; i < nameCount; i++) { string name = br.ReadNullTerminatedString(); names.Add(name); } //now we can parse the object table List <string> materials = new List <string>(); List <Tuple <int, string> > meshReferences = new List <Tuple <int, string> >(); br.BaseStream.Seek(objectOffset, SeekOrigin.Begin); long objectStartPos = br.BaseStream.Position; for (int i = 0; i < objectCount; i++) { int index = br.ReadInt32() - 1; int size = br.ReadInt32(); if (index < 0 || index >= names.Count) { throw new Exception(i.ToString() + " " + index.ToString()); } string name = names[index]; if (name == "meshreference") { byte flags = br.ReadByte(); Int32 groupNameInd = br.ReadInt32() - 1; Int32 objectNameInd = br.ReadInt32() - 1; byte limbCount = br.ReadByte(); meshReferences.Add(new Tuple <int, string>(i, names[objectNameInd])); } else if (name == "material") { byte materialFlags = br.ReadByte(); Int32 groupIndex = br.ReadInt32(); string objectName = names[br.ReadInt32() - 1]; Int32 extensionNameIndex = -1; if ((materialFlags & 2) != 0) { extensionNameIndex = br.ReadInt32(); //TODO: what the heck is this } materials.Add(objectName); } else { br.BaseStream.Seek(size, SeekOrigin.Current); } } br.BaseStream.Position = objectStartPos; for (int i = 0; i < objectCount; i++) { int index = br.ReadInt32() - 1; int size = br.ReadInt32(); if (index < 0 || index >= names.Count) { throw new Exception(i.ToString() + " " + index.ToString()); } string name = names[index]; if (name == "mesh") { Property newProperty; long startPos = br.BaseStream.Position; byte flags = br.ReadByte(); Entity entity = new Entity(map.IDGenerator.GetNextObjectID()); entity.ClassName = "model"; entity.EntityData.Name = "model"; entity.Colour = Colour.GetDefaultEntityColour(); Int32 keyCount = br.ReadInt32(); for (int j = 0; j < keyCount; j++) { Int32 keyNameInd = br.ReadInt32() - 1; Int32 keyValueInd = br.ReadInt32() - 1; if (names[keyNameInd] == "classname") { //entity.ClassName = names[keyValueInd]; //entity.EntityData.Name = names[keyValueInd]; } else { newProperty = new Property(); newProperty.Key = names[keyNameInd]; newProperty.Value = names[keyValueInd]; if (newProperty.Key == "file") { newProperty.Value = System.IO.Path.GetFileNameWithoutExtension(newProperty.Value); } entity.EntityData.Properties.Add(newProperty); } } Int32 group = br.ReadInt32(); Int32 visgroup = br.ReadInt32(); byte red = br.ReadByte(); byte green = br.ReadByte(); byte blue = br.ReadByte(); Int32 meshRefIndex = br.ReadInt32() - 1; float x = br.ReadSingle(); float z = br.ReadSingle(); float y = br.ReadSingle(); if (entity != null) { entity.Origin = new Coordinate((decimal)x, (decimal)y, (decimal)z); } if (entity.EntityData.GetPropertyValue("file") == null) { newProperty = new Property(); newProperty.Key = "file"; newProperty.Value = meshReferences.Find(q => q.Item1 == meshRefIndex).Item2; entity.EntityData.Properties.Add(newProperty); } float pitch = br.ReadSingle(); float yaw = br.ReadSingle(); float roll = br.ReadSingle(); newProperty = new Property(); newProperty.Key = "angles"; newProperty.Value = pitch.ToString(CultureInfo.InvariantCulture) + " " + yaw.ToString(CultureInfo.InvariantCulture) + " " + roll.ToString(CultureInfo.InvariantCulture); entity.EntityData.Properties.Add(newProperty); float xScale = 1.0f; float yScale = 1.0f; float zScale = 1.0f; if ((flags & 1) == 0) { xScale = br.ReadSingle(); yScale = br.ReadSingle(); zScale = br.ReadSingle(); } newProperty = new Property(); newProperty.Key = "scale"; newProperty.Value = xScale.ToString(CultureInfo.InvariantCulture) + " " + yScale.ToString(CultureInfo.InvariantCulture) + " " + zScale.ToString(CultureInfo.InvariantCulture); entity.EntityData.Properties.Add(newProperty); br.BaseStream.Position += size - (br.BaseStream.Position - startPos); if (entity != null) { entity.UpdateBoundingBox(); entity.SetParent(map.WorldSpawn); } } else if (name == "entity") { byte flags = br.ReadByte(); float x = br.ReadSingle(); float z = br.ReadSingle(); float y = br.ReadSingle(); Entity entity = new Entity(map.IDGenerator.GetNextObjectID()); entity.Colour = Colour.GetDefaultEntityColour(); entity.Origin = new Coordinate((decimal)x, (decimal)y, (decimal)z); Int32 keyCount = br.ReadInt32(); for (int j = 0; j < keyCount; j++) { Int32 keyNameInd = br.ReadInt32() - 1; Int32 keyValueInd = br.ReadInt32() - 1; if (names[keyNameInd] == "classname") { entity.ClassName = names[keyValueInd]; entity.EntityData.Name = names[keyValueInd]; } else { Property newProperty = new Property(); newProperty.Key = names[keyNameInd]; newProperty.Value = names[keyValueInd]; entity.EntityData.Properties.Add(newProperty); } } Int32 group = br.ReadInt32(); Int32 visgroup = br.ReadInt32(); entity.UpdateBoundingBox(); entity.SetParent(map.WorldSpawn); } else if (name == "brush") { bool invisibleCollision = false; byte brushFlags = br.ReadByte(); //TODO: ??? Int32 keys = br.ReadInt32(); for (int j = 0; j < keys; j++) { Int32 keyNameInd = br.ReadInt32(); Int32 keyValueInd = br.ReadInt32(); string keyName = names[keyNameInd - 1]; if (keyName.Equals("classname", StringComparison.InvariantCultureIgnoreCase)) { string keyValue = names[keyValueInd - 1]; if (keyValue.Equals("field_hit", StringComparison.InvariantCultureIgnoreCase)) { invisibleCollision = true; } } } Int32 groupIndex = br.ReadInt32(); Int32 visgroupIndex = br.ReadInt32(); byte red = br.ReadByte(); byte green = br.ReadByte(); byte blue = br.ReadByte(); List <Coordinate> vertices = new List <Coordinate>(); byte vertexCount = br.ReadByte(); for (int j = 0; j < vertexCount; j++) { decimal x = (decimal)br.ReadSingle(); decimal z = (decimal)br.ReadSingle(); decimal y = (decimal)br.ReadSingle(); vertices.Add(new Coordinate(x, y, z)); } List <Face> faces = new List <Face>(); byte faceCount = br.ReadByte(); for (int j = 0; j < faceCount; j++) { byte faceFlags = br.ReadByte(); //TODO: maybe we need these unused bits for something idk decimal planeEq0 = (decimal)br.ReadSingle(); decimal planeEq1 = (decimal)br.ReadSingle(); decimal planeEq2 = (decimal)br.ReadSingle(); decimal planeEq3 = (decimal)br.ReadSingle(); decimal texPosX = (decimal)br.ReadSingle(); decimal texPosY = (decimal)br.ReadSingle(); decimal texScaleX = (decimal)br.ReadSingle(); decimal texScaleY = (decimal)br.ReadSingle(); float texRotX = br.ReadSingle(); float texRotY = br.ReadSingle(); decimal uTexPlane0 = (decimal)br.ReadSingle(); decimal uTexPlane1 = (decimal)br.ReadSingle(); decimal uTexPlane2 = (decimal)br.ReadSingle(); decimal uTexPlane3 = (decimal)br.ReadSingle(); decimal vTexPlane0 = (decimal)br.ReadSingle(); decimal vTexPlane1 = (decimal)br.ReadSingle(); decimal vTexPlane2 = (decimal)br.ReadSingle(); decimal vTexPlane3 = (decimal)br.ReadSingle(); float luxelSize = br.ReadSingle(); Int32 smoothGroupInd = br.ReadInt32(); Int32 materialInd = br.ReadInt32() - 1; Int32 lightmapInd = -1; if ((faceFlags & 16) != 0) { lightmapInd = br.ReadInt32(); } byte indexCount = br.ReadByte(); List <byte> vertsInFace = new List <byte>(); for (int k = 0; k < indexCount; k++) { byte vertIndex = br.ReadByte(); vertsInFace.Add(vertIndex); float texCoordX = br.ReadSingle(); float texCoordY = br.ReadSingle(); float lmCoordX = 0.0f; float lmCoordY = 0.0f; if ((faceFlags & 16) != 0) { lmCoordX = br.ReadSingle(); lmCoordY = br.ReadSingle(); } } Coordinate norm = new Coordinate(planeEq0, planeEq2, planeEq1); if (Math.Abs((float)norm.LengthSquared()) > 0.001f) { if (Math.Abs((double)norm.LengthSquared() - 1) > 0.001) { throw new Exception(norm.LengthSquared().ToString()); } Face newFace = new Face(map.IDGenerator.GetNextFaceID()); foreach (byte vertInd in vertsInFace) { newFace.Vertices.Insert(0, new Vertex(vertices[vertInd], newFace)); } newFace.Plane = new Plane(newFace.Vertices[0].Location, newFace.Vertices[1].Location, newFace.Vertices[2].Location); newFace.UpdateBoundingBox(); /*if ((faceFlags & 4) == 0) * { * Entity entity = new Entity(map.IDGenerator.GetNextObjectID()); * entity.Colour = Color.Lime; * entity.Origin = newFace.BoundingBox.Center; * entity.UpdateBoundingBox(); * entity.SetParent(map.WorldSpawn); * * Property newProperty = new Property(); * newProperty.Key = "normal"; * newProperty.Value = newFace.Plane.Normal.ToString(); * entity.EntityData.Properties.Add(newProperty); * * var direction = newFace.Plane.GetClosestAxisToNormal(); * var tempV = direction == Coordinate.UnitZ ? -Coordinate.UnitY : -Coordinate.UnitZ; * var uAxis = newFace.Plane.Normal.Cross(tempV).Normalise(); * var vAxis = uAxis.Cross(newFace.Plane.Normal).Normalise(); * * newProperty = new Property(); * newProperty.Key = "uaxis"; * newProperty.Value = uAxis.ToString(); * entity.EntityData.Properties.Add(newProperty); * * newProperty = new Property(); * newProperty.Key = "vaxis"; * newProperty.Value = vAxis.ToString(); * entity.EntityData.Properties.Add(newProperty); * }*/ Coordinate uNorm = new Coordinate(uTexPlane0, uTexPlane2, uTexPlane1).Normalise(); Coordinate vNorm = new Coordinate(vTexPlane0, vTexPlane2, vTexPlane1).Normalise(); if (Math.Abs((double)(uNorm.LengthSquared() - vNorm.LengthSquared())) > 0.001) { throw new Exception(uNorm.LengthSquared().ToString() + " " + vNorm.LengthSquared().ToString()); } newFace.Texture.Name = (faceFlags & 4) != 0 ? "tooltextures/remove_face" : invisibleCollision ? "tooltextures/invisible_collision" : materials[materialInd]; newFace.AlignTextureToWorld(); //TODO: add warning? //if (texRotY != texRotX) throw new Exception((texRotX - texRotY).ToString()); newFace.Texture.UAxis = uNorm * (decimal)Math.Cos(-texRotY * Math.PI / 180.0) + vNorm * (decimal)Math.Sin(-texRotY * Math.PI / 180.0); newFace.Texture.VAxis = vNorm * (decimal)Math.Cos(-texRotY * Math.PI / 180.0) - uNorm * (decimal)Math.Sin(-texRotY * Math.PI / 180.0); newFace.Texture.XScale = texScaleX / 2; newFace.Texture.YScale = texScaleY / 2; newFace.Texture.XShift = -texPosX * 2 / texScaleX; newFace.Texture.YShift = texPosY * 2 / texScaleY; newFace.Texture.Rotation = (decimal)texRotY; newFace.Transform(new UnitScale(Coordinate.One, newFace.BoundingBox.Center), TransformFlags.None); faces.Add(newFace); } } Solid newSolid = new Solid(map.IDGenerator.GetNextObjectID()); foreach (Face face in faces) { face.Parent = newSolid; newSolid.Faces.Add(face); } newSolid.Colour = Colour.GetRandomBrushColour(); newSolid.UpdateBoundingBox(); if (newSolid.IsValid()) { newSolid.SetParent(map.WorldSpawn); newSolid.Transform(new UnitScale(Coordinate.One, newSolid.BoundingBox.Center), TransformFlags.None); } else { var offset = newSolid.BoundingBox.Center; // Not a valid solid, decompose into tetrahedrons/etc foreach (var face in faces) { var polygon = new Polygon(face.Vertices.Select(x => x.Location)); if (!polygon.IsValid() || !polygon.IsConvex()) { // tetrahedrons foreach (var triangle in face.GetTrianglesReversed()) { var tf = new Face(map.IDGenerator.GetNextFaceID()); tf.Plane = new Plane(triangle[0].Location, triangle[1].Location, triangle[2].Location); tf.Vertices.AddRange(triangle.Select(x => new Vertex(x.Location, tf))); tf.Texture = face.Texture.Clone(); tf.UpdateBoundingBox(); newSolid = SolidifyFace(map, tf, offset); newSolid.SetParent(map.WorldSpawn); newSolid.UpdateBoundingBox(); newSolid.Transform(new UnitScale(Coordinate.One, newSolid.BoundingBox.Center), TransformFlags.None); } } else { // cone/pyramid/whatever newSolid = SolidifyFace(map, face, offset); newSolid.SetParent(map.WorldSpawn); newSolid.UpdateBoundingBox(); newSolid.Transform(new UnitScale(Coordinate.One, newSolid.BoundingBox.Center), TransformFlags.None); } } } } else { br.BaseStream.Seek(size, SeekOrigin.Current); } } return(map); }
private void Read(DataStructures.MapObjects.Map map, StreamReader reader) { var points = new List <Coordinate>(); var faces = new List <ObjFace>(); var currentGroup = "default"; var scale = 100m; string line; while ((line = reader.ReadLine()) != null) { if (line.StartsWith("# Scale: ")) { var num = line.Substring(9); decimal s; if (decimal.TryParse(num, NumberStyles.Float, CultureInfo.InvariantCulture, out s)) { scale = s; } } line = CleanLine(line); string keyword, values; SplitLine(line, out keyword, out values); if (String.IsNullOrWhiteSpace(keyword)) { continue; } var vals = (values ?? "").Split(' ').Where(x => !String.IsNullOrWhiteSpace(x)).ToArray(); switch (keyword.ToLower()) { // Things I care about case "v": // geometric vertices points.Add(Coordinate.Parse(vals[0], vals[1], vals[2]) * scale); break; case "f": // face faces.Add(new ObjFace(currentGroup, vals.Select(x => ParseFaceIndex(points, x)))); break; case "g": // group name currentGroup = (values ?? "").Trim(); break; // Things I don't care about #region Not Implemented // Vertex data // "v" case "vt": // texture vertices break; case "vn": // vertex normals break; case "vp": // parameter space vertices case "cstype": // rational or non-rational forms of curve or surface type: basis matrix, Bezier, B-spline, Cardinal, Taylor case "degree": // degree case "bmat": // basis matrix case "step": // step size // not supported break; // Elements // "f" case "p": // point case "l": // line case "curv": // curve case "curv2": // 2D curve case "surf": // surface // not supported break; // Free-form curve/surface body statements case "parm": // parameter name case "trim": // outer trimming loop (trim) case "hole": // inner trimming loop (hole) case "scrv": // special curve (scrv) case "sp": // special point (sp) case "end": // end statement (end) // not supported break; // Connectivity between free-form surfaces case "con": // connect // not supported break; // Grouping // "g" case "s": // smoothing group break; case "mg": // merging group break; case "o": // object name // not supported break; // Display/render attributes case "mtllib": // material library case "usemtl": // material name case "usemap": // texture map name case "bevel": // bevel interpolation case "c_interp": // color interpolation case "d_interp": // dissolve interpolation case "lod": // level of detail case "shadow_obj": // shadow casting case "trace_obj": // ray tracing case "ctech": // curve approximation technique case "stech": // surface approximation technique // not relevant break; #endregion } } var solids = new List <Solid>(); // Try and see if we have a valid solid per-group foreach (var g in faces.GroupBy(x => x.Group)) { solids.AddRange(CreateSolids(map, points, g)); } foreach (var solid in solids) { foreach (var face in solid.Faces) { face.Colour = solid.Colour; face.AlignTexture(); } solid.SetParent(map.WorldSpawn); } }
protected override void SaveToStream(Stream stream, DataStructures.MapObjects.Map map) { /* * // Csg version * * var csg = new CsgSolid(); * foreach (var mo in map.WorldSpawn.Find(x => x is Solid).OfType<Solid>()) * { * csg = csg.Union(new CsgSolid(mo.Faces.Select(x => new Polygon(x.Vertices.Select(v => v.Location))))); * } * * using (var sw = new StreamWriter(stream)) * { * foreach (var polygon in csg.Polygons) * { * foreach (var v in polygon.Vertices) * { * sw.Write("v "); * sw.Write(v.X.ToString("0.0000", CultureInfo.InvariantCulture)); * sw.Write(' '); * sw.Write(v.Y.ToString("0.0000", CultureInfo.InvariantCulture)); * sw.Write(' '); * sw.Write(v.Z.ToString("0.0000", CultureInfo.InvariantCulture)); * sw.WriteLine(); * } * * sw.Write("f "); * for (int i = polygon.Vertices.Count; i > 0; i--) * { * sw.Write(-i); * sw.Write(' '); * } * sw.WriteLine(); * sw.WriteLine(); * } * } */ // Semi-recoverable version using (var sw = new StreamWriter(stream)) { sw.WriteLine("# Chisel Object Export"); sw.WriteLine("# Scale: 1"); sw.WriteLine(); foreach (var solid in map.WorldSpawn.Find(x => x is Solid).OfType <Solid>()) { sw.Write("g solid_"); sw.Write(solid.ID); sw.WriteLine(); foreach (var face in solid.Faces) { foreach (var v in face.Vertices) { sw.Write("v "); sw.Write(v.Location.X.ToString("0.0000", CultureInfo.InvariantCulture)); sw.Write(' '); sw.Write(v.Location.Y.ToString("0.0000", CultureInfo.InvariantCulture)); sw.Write(' '); sw.Write(v.Location.Z.ToString("0.0000", CultureInfo.InvariantCulture)); sw.WriteLine(); } sw.Write("f "); for (var i = 1; i <= face.Vertices.Count; i++) { sw.Write(-i); sw.Write(' '); } sw.WriteLine(); sw.WriteLine(); } } } }
protected override void SaveToStream(Stream stream, DataStructures.MapObjects.Map map, DataStructures.GameData.GameData gameData, TextureCollection textureCollection) { throw new NotImplementedException("don't save to 3dw, ew"); }
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); //header UInt16 mapVersion = br.ReadUInt16(); byte mapFlags = br.ReadByte(); Int32 nameCount = br.ReadInt32(); Int32 nameOffset = br.ReadInt32(); Int32 objectCount = br.ReadInt32(); Int32 objectOffset = br.ReadInt32(); //get names, needed to understand the objects List <string> names = new List <string>(); br.BaseStream.Seek(nameOffset, SeekOrigin.Begin); for (int i = 0; i < nameCount; i++) { string name = br.ReadNullTerminatedString(); names.Add(name); } //now we can parse the object table List <string> materials = new List <string>(); List <Tuple <int, string> > meshReferences = new List <Tuple <int, string> >(); Dictionary <int, Group> groups = new Dictionary <int, Group>(); Dictionary <int, int> visgroups = new Dictionary <int, int>(); br.BaseStream.Seek(objectOffset, SeekOrigin.Begin); long objectStartPos = br.BaseStream.Position; for (int i = 0; i < objectCount; i++) { int index = br.ReadInt32() - 1; int size = br.ReadInt32(); string name = null; if (index >= 0 && index < names.Count) { name = names[index]; } if (name == "group") { byte flags = br.ReadByte(); Int32 groupIndex = br.ReadInt32(); Group newGroup = new Group(map.IDGenerator.GetNextObjectID()); newGroup.SetParent(map.WorldSpawn); groups.Add(i, newGroup); } else if (name == "visgroup") { byte flags = br.ReadByte(); string groupName = names[br.ReadInt32() - 1]; byte colorR = br.ReadByte(); byte colorG = br.ReadByte(); byte colorB = br.ReadByte(); Visgroup newGroup = new Visgroup() { Name = groupName, ID = visgroups.Count + 1 }; newGroup.Colour = System.Drawing.Color.FromArgb(colorR, colorG, colorB); map.Visgroups.Add(newGroup); visgroups.Add(i, newGroup.ID); } else if (name == "meshreference") { byte flags = br.ReadByte(); Int32 groupNameInd = br.ReadInt32() - 1; Int32 objectNameInd = br.ReadInt32() - 1; byte limbCount = br.ReadByte(); meshReferences.Add(new Tuple <int, string>(i, names[objectNameInd])); } else if (name == "material") { byte materialFlags = br.ReadByte(); Int32 groupIndex = br.ReadInt32(); string objectName = names[br.ReadInt32() - 1]; Int32 extensionNameIndex = -1; if ((materialFlags & 2) != 0) { extensionNameIndex = br.ReadInt32(); //TODO: what the heck is this } materials.Add(objectName); } else { br.BaseStream.Seek(size, SeekOrigin.Current); } } br.BaseStream.Position = objectStartPos; for (int i = 0; i < objectCount; i++) { int index = br.ReadInt32() - 1; int size = br.ReadInt32(); string name = null; if (index >= 0 && index < names.Count) { name = names[index]; } if (name == "mesh") { Property newProperty; long startPos = br.BaseStream.Position; byte flags = br.ReadByte(); Entity entity = new Entity(map.IDGenerator.GetNextObjectID()); entity.ClassName = "model"; entity.EntityData.Name = "model"; entity.Colour = Colour.GetDefaultEntityColour(); Int32 keyCount = br.ReadInt32(); for (int j = 0; j < keyCount; j++) { Int32 keyNameInd = br.ReadInt32() - 1; Int32 keyValueInd = br.ReadInt32() - 1; if (names[keyNameInd] != "classname") { newProperty = new Property(); newProperty.Key = names[keyNameInd]; newProperty.Value = names[keyValueInd]; if (newProperty.Key == "file") { newProperty.Value = System.IO.Path.GetFileNameWithoutExtension(newProperty.Value); } entity.EntityData.Properties.Add(newProperty); } } Int32 groupIndex = br.ReadInt32() - 1; Int32 visgroupIndex = br.ReadInt32() - 1; if (visgroups.ContainsKey(visgroupIndex)) { entity.Visgroups.Add(visgroups[visgroupIndex]); } byte red = br.ReadByte(); byte green = br.ReadByte(); byte blue = br.ReadByte(); Int32 meshRefIndex = br.ReadInt32() - 1; float x = br.ReadSingle(); float z = br.ReadSingle(); float y = br.ReadSingle(); if (entity != null) { entity.Origin = new Coordinate((decimal)x, (decimal)y, (decimal)z); } if (entity.EntityData.GetPropertyValue("file") == null) { newProperty = new Property(); newProperty.Key = "file"; newProperty.Value = meshReferences.Find(q => q.Item1 == meshRefIndex).Item2; entity.EntityData.Properties.Add(newProperty); } float pitch = br.ReadSingle(); float yaw = br.ReadSingle(); float roll = br.ReadSingle(); newProperty = new Property(); newProperty.Key = "angles"; newProperty.Value = pitch.ToString() + " " + yaw.ToString() + " " + roll.ToString(); entity.EntityData.Properties.Add(newProperty); float xScale = 1.0f; float yScale = 1.0f; float zScale = 1.0f; if ((flags & 1) == 0) { xScale = br.ReadSingle(); yScale = br.ReadSingle(); zScale = br.ReadSingle(); } newProperty = new Property(); newProperty.Key = "scale"; newProperty.Value = xScale.ToString() + " " + yScale.ToString() + " " + zScale.ToString(); entity.EntityData.Properties.Add(newProperty); br.BaseStream.Position += size - (br.BaseStream.Position - startPos); entity.UpdateBoundingBox(); if (groups.ContainsKey(groupIndex)) { entity.SetParent(groups[groupIndex]); } else { entity.SetParent(map.WorldSpawn); } } else if (name == "entity") { byte flags = br.ReadByte(); float x = br.ReadSingle(); float z = br.ReadSingle(); float y = br.ReadSingle(); Entity entity = new Entity(map.IDGenerator.GetNextObjectID()); entity.Colour = Colour.GetDefaultEntityColour(); entity.Origin = new Coordinate((decimal)x, (decimal)y, (decimal)z); Int32 keyCount = br.ReadInt32(); for (int j = 0; j < keyCount; j++) { Int32 keyNameInd = br.ReadInt32() - 1; Int32 keyValueInd = br.ReadInt32() - 1; if (names[keyNameInd] == "classname") { entity.ClassName = names[keyValueInd]; entity.EntityData.Name = names[keyValueInd]; } else { Property newProperty = new Property(); newProperty.Key = names[keyNameInd]; newProperty.Value = names[keyValueInd]; entity.EntityData.Properties.Add(newProperty); } } Int32 groupIndex = br.ReadInt32() - 1; Int32 visgroupIndex = br.ReadInt32() - 1; if (visgroups.ContainsKey(visgroupIndex)) { entity.Visgroups.Add(visgroups[visgroupIndex]); } entity.UpdateBoundingBox(); if (groups.ContainsKey(groupIndex)) { entity.SetParent(groups[groupIndex]); } else { entity.SetParent(map.WorldSpawn); } } else if (name == "brush") { bool invisibleCollision = false; byte brushFlags = br.ReadByte(); //TODO: ??? Int32 keys = br.ReadInt32(); for (int j = 0; j < keys; j++) { Int32 keyNameInd = br.ReadInt32(); Int32 keyValueInd = br.ReadInt32(); string keyName = names[keyNameInd - 1]; if (keyName.Equals("classname", StringComparison.OrdinalIgnoreCase)) { string keyValue = names[keyValueInd - 1]; if (keyValue.Equals("field_hit", StringComparison.OrdinalIgnoreCase)) { invisibleCollision = true; } } } Int32 groupIndex = br.ReadInt32() - 1; Int32 visgroupIndex = br.ReadInt32() - 1; byte red = br.ReadByte(); byte green = br.ReadByte(); byte blue = br.ReadByte(); List <Coordinate> vertices = new List <Coordinate>(); byte vertexCount = br.ReadByte(); for (int j = 0; j < vertexCount; j++) { decimal x = (decimal)br.ReadSingle(); decimal z = (decimal)br.ReadSingle(); decimal y = (decimal)br.ReadSingle(); vertices.Add(new Coordinate(x, y, z)); } List <Face> faces = new List <Face>(); byte faceCount = br.ReadByte(); for (int j = 0; j < faceCount; j++) { byte faceFlags = br.ReadByte(); //TODO: maybe we need these unused bits for something idk decimal planeEq0 = (decimal)br.ReadSingle(); decimal planeEq1 = (decimal)br.ReadSingle(); decimal planeEq2 = (decimal)br.ReadSingle(); decimal planeEq3 = (decimal)br.ReadSingle(); decimal texPosX = (decimal)br.ReadSingle(); decimal texPosY = (decimal)br.ReadSingle(); decimal texScaleX = (decimal)br.ReadSingle(); decimal texScaleY = (decimal)br.ReadSingle(); float texRotX = br.ReadSingle(); float texRotY = br.ReadSingle(); decimal uTexPlane0 = (decimal)br.ReadSingle(); decimal uTexPlane1 = (decimal)br.ReadSingle(); decimal uTexPlane2 = (decimal)br.ReadSingle(); decimal uTexPlane3 = (decimal)br.ReadSingle(); decimal vTexPlane0 = (decimal)br.ReadSingle(); decimal vTexPlane1 = (decimal)br.ReadSingle(); decimal vTexPlane2 = (decimal)br.ReadSingle(); decimal vTexPlane3 = (decimal)br.ReadSingle(); float luxelSize = br.ReadSingle(); Int32 smoothGroupInd = br.ReadInt32(); Int32 materialInd = br.ReadInt32() - 1; Int32 lightmapInd = -1; if ((faceFlags & 16) != 0) { lightmapInd = br.ReadInt32(); } byte indexCount = br.ReadByte(); List <byte> vertsInFace = new List <byte>(); for (int k = 0; k < indexCount; k++) { byte vertIndex = br.ReadByte(); vertsInFace.Add(vertIndex); float texCoordX = br.ReadSingle(); float texCoordY = br.ReadSingle(); float lmCoordX = 0.0f; float lmCoordY = 0.0f; if ((faceFlags & 16) != 0) { lmCoordX = br.ReadSingle(); lmCoordY = br.ReadSingle(); } } Coordinate norm = new Coordinate(planeEq0, planeEq2, planeEq1); if (Math.Abs((float)norm.LengthSquared()) > 0.001f) { if (Math.Abs((double)norm.LengthSquared() - 1) > 0.001) { throw new Exception(norm.LengthSquared().ToString()); } Face newFace = new Face(map.IDGenerator.GetNextFaceID()); foreach (byte vertInd in vertsInFace) { newFace.Vertices.Insert(0, new Vertex(vertices[vertInd], newFace)); } newFace.Plane = new Plane(newFace.Vertices[0].Location, newFace.Vertices[1].Location, newFace.Vertices[2].Location); newFace.UpdateBoundingBox(); Coordinate uNorm = new Coordinate(uTexPlane0, uTexPlane2, uTexPlane1).Normalise(); Coordinate vNorm = new Coordinate(vTexPlane0, vTexPlane2, vTexPlane1).Normalise(); if (Math.Abs((double)(uNorm.LengthSquared() - vNorm.LengthSquared())) > 0.001) { throw new Exception(uNorm.LengthSquared().ToString() + " " + vNorm.LengthSquared().ToString()); } newFace.Texture.Name = (faceFlags & 4) != 0 ? "tooltextures/remove_face" : invisibleCollision ? "tooltextures/invisible_collision" : materials[materialInd]; newFace.AlignTextureToWorld(); newFace.Texture.UAxis = uNorm * (decimal)Math.Cos(-texRotX * Math.PI / 180.0) + vNorm * (decimal)Math.Sin(-texRotX * Math.PI / 180.0); newFace.Texture.VAxis = vNorm * (decimal)Math.Cos(-texRotX * Math.PI / 180.0) - uNorm * (decimal)Math.Sin(-texRotX * Math.PI / 180.0); //huh????? if (Math.Abs(texScaleX) < 0.0001m) { if (Math.Abs(texScaleY) < 0.0001m) { texScaleX = 1m; texScaleY = 1m; } else { texScaleX = texScaleY; } } else if (Math.Abs(texScaleY) < 0.0001m) { texScaleY = texScaleX; } newFace.Texture.XScale = texScaleX / 2; newFace.Texture.YScale = texScaleY / 2; newFace.Texture.XShift = -texPosX * 2 / texScaleX; newFace.Texture.YShift = texPosY * 2 / texScaleY; newFace.Texture.Rotation = (decimal)texRotX; //seriously, what the F**K??????????? if ((texRotX - texRotY) > 120.0f) { newFace.Texture.XScale *= -1m; newFace.Texture.YScale *= -1m; newFace.Texture.Rotation -= 180m; newFace.Texture.UAxis = -newFace.Texture.UAxis; } else if ((texRotY - texRotX) > 120.0f) { newFace.Texture.XScale *= -1m; newFace.Texture.YScale *= -1m; newFace.Texture.Rotation -= 180m; newFace.Texture.VAxis = -newFace.Texture.VAxis; } newFace.Transform(new UnitScale(Coordinate.One, newFace.BoundingBox.Center), TransformFlags.None); faces.Add(newFace); } } Solid newSolid = new Solid(map.IDGenerator.GetNextObjectID()); foreach (Face face in faces) { face.Parent = newSolid; newSolid.Faces.Add(face); } if (visgroups.ContainsKey(visgroupIndex)) { newSolid.Visgroups.Add(visgroups[visgroupIndex]); } newSolid.Colour = Colour.GetRandomBrushColour(); newSolid.UpdateBoundingBox(); MapObject parent = map.WorldSpawn; if (groups.ContainsKey(groupIndex)) { parent = groups[groupIndex]; } if (newSolid.IsValid()) { newSolid.SetParent(parent); newSolid.Transform(new UnitScale(Coordinate.One, newSolid.BoundingBox.Center), TransformFlags.None); } else { var offset = newSolid.BoundingBox.Center; // Not a valid solid, decompose into tetrahedrons/etc foreach (var face in faces) { var polygon = new Polygon(face.Vertices.Select(x => x.Location)); if (!polygon.IsValid() || !polygon.IsConvex()) { // tetrahedrons foreach (var triangle in face.GetTrianglesReversed()) { var tf = new Face(map.IDGenerator.GetNextFaceID()); tf.Plane = new Plane(triangle[0].Location, triangle[1].Location, triangle[2].Location); tf.Vertices.AddRange(triangle.Select(x => new Vertex(x.Location, tf))); tf.Texture = face.Texture.Clone(); tf.UpdateBoundingBox(); newSolid = SolidifyFace(map, tf, offset); newSolid.SetParent(parent); newSolid.UpdateBoundingBox(); newSolid.Transform(new UnitScale(Coordinate.One, newSolid.BoundingBox.Center), TransformFlags.None); } } else { // cone/pyramid/whatever newSolid = SolidifyFace(map, face, offset); newSolid.SetParent(parent); newSolid.UpdateBoundingBox(); newSolid.Transform(new UnitScale(Coordinate.One, newSolid.BoundingBox.Center), TransformFlags.None); } } } } else { if (name == "terrain") { MapProvider.warnings = "This map contains displacements, which are currently not supported. The map will appear incomplete."; } br.BaseStream.Seek(size, SeekOrigin.Current); } } return(map); }
protected override DataStructures.MapObjects.Map GetFromStream(Stream stream, IEnumerable <string> modelDirs, out Image[] lightmaps) { BinaryReader reader = new BinaryReader(stream); if (reader.ReadFixedLengthString(Encoding.ASCII, 3) != "CBR") { throw new ProviderException("CBR file is corrupted/invalid!"); } uint revision = reader.ReadUInt32(); // Lightmaps bool lightmapped = reader.ReadByte() > (byte)Lightmapped.No; if (lightmapped) { lightmaps = new Image[4]; for (int i = 0; i < 4; i++) { lightmaps[i] = Image.FromStream(new MemoryStream(reader.ReadBytes(reader.ReadInt32()))); } } else { lightmaps = null; } // Texture dictionary int texSize = reader.ReadInt32(); string[] textures = new string[texSize]; for (int i = 0; i < texSize; i++) { textures[i] = reader.ReadNullTerminatedString(); } DataStructures.MapObjects.Map map = new DataStructures.MapObjects.Map(); map.WorldSpawn = new World(map.IDGenerator.GetNextObjectID()); // Solids List <MapObject> solids = new List <MapObject>(); int solidCount = reader.ReadInt32(); for (int i = 0; i < solidCount; i++) { Solid s = new Solid(map.IDGenerator.GetNextObjectID()); s.Colour = Colour.GetRandomBrushColour(); int faceCount = reader.ReadInt32(); for (int j = 0; j < faceCount; j++) { Face f = new Face(map.IDGenerator.GetNextFaceID()); f.Colour = s.Colour; f.Texture.Name = textures[reader.ReadInt32()]; f.Texture.UAxis = reader.ReadCoordinate(); f.Texture.VAxis = reader.ReadCoordinate(); f.Texture.XShift = reader.ReadDecimal(); f.Texture.YShift = reader.ReadDecimal(); f.Texture.XScale = reader.ReadDecimal(); f.Texture.YScale = reader.ReadDecimal(); f.Texture.Rotation = reader.ReadDecimal(); int vertexCount = reader.ReadInt32(); for (int k = 0; k < vertexCount; k++) { Vertex v = new Vertex(reader.ReadCoordinate(), f); if (lightmapped) { v.LMU = reader.ReadSingle(); v.LMV = reader.ReadSingle(); v.TextureU = (decimal)reader.ReadSingle(); v.TextureV = (decimal)reader.ReadSingle(); } f.Vertices.Add(v); } f.Plane = new Plane(f.Vertices[0].Location, f.Vertices[1].Location, f.Vertices[2].Location); f.Parent = s; s.Faces.Add(f); f.UpdateBoundingBox(); } s.SetParent(map.WorldSpawn, false); s.UpdateBoundingBox(false); solids.Add(s); } // Entities List <MapObject> entities = new List <MapObject>(0); string read; bool isStillSolid = true; for (int i = 0; i < 2; i++) { while ((read = reader.ReadNullTerminatedString()) != "") { List <Property> properties = new List <Property>(); byte propertyType; while ((propertyType = reader.ReadByte()) != 255) { properties.Add(new Property() { name = reader.ReadNullTerminatedString(), type = (VariableType)propertyType }); } // Entries int entitiesOfType = reader.ReadInt32(); entities.Capacity += entitiesOfType; for (int j = 0; j < entitiesOfType; j++) { Entity e = new Entity(map.IDGenerator.GetNextObjectID()); e.Colour = Colour.GetDefaultEntityColour(); e.ClassName = read; e.EntityData.Name = read; if (isStillSolid) { int entitySolids = reader.ReadInt32(); for (int k = 0; k < entitySolids; k++) { solids[reader.ReadInt32()].SetParent(e); } } e.SetParent(map.WorldSpawn); foreach (Property property in properties) { string propertyVal; switch (property.type) { case VariableType.Bool: propertyVal = reader.ReadBoolean() ? "Yes" : "No"; break; case VariableType.Color255: propertyVal = DataStructures.MapObjects.Property.FromColor(reader.ReadRGBAColour()); break; case VariableType.Float: propertyVal = reader.ReadSingle().ToString(); break; case VariableType.Integer: propertyVal = reader.ReadInt32().ToString(); break; case VariableType.String: propertyVal = reader.ReadNullTerminatedString(); break; case VariableType.Vector: propertyVal = DataStructures.MapObjects.Property.FromCoordinate(reader.ReadCoordinate()); break; case VariableType.Choices: // TODO: Bullshit throw new NotImplementedException(); default: propertyVal = ""; break; } e.EntityData.SetPropertyValue(property.name, propertyVal); } e.UpdateBoundingBox(); entities.Add(e); } } isStillSolid = false; } // CBRE ONLY // Visgroup dictionary Visgroup currentParentVisgroup = null; while (true) { byte hierarchyControl; Visgroup newGroup = null; while ((hierarchyControl = reader.ReadByte()) == HIERARCHY_PROCCEED) { newGroup = new Visgroup(); newGroup.Colour = Colour.GetRandomBrushColour(); newGroup.ID = reader.ReadInt32(); newGroup.Name = reader.ReadNullTerminatedString(); if (currentParentVisgroup != null) { newGroup.Parent = currentParentVisgroup; currentParentVisgroup.Children.Add(newGroup); } else { map.Visgroups.Add(newGroup); } } if (hierarchyControl == HIERARCHY_DOWN) { currentParentVisgroup = newGroup; } else if (currentParentVisgroup != null) { currentParentVisgroup = currentParentVisgroup.Parent; } else { break; } } // Solid visgroups foreach (Solid mo in solids) { ReadVisgroups(reader, mo); } // Entity visgroups foreach (Entity e in entities) { ReadVisgroups(reader, e); } // Groups int directWorldGroups = reader.ReadInt32(); for (int i = 0; i < directWorldGroups; i++) { Group currentParentGroup = new Group(map.IDGenerator.GetNextObjectID()); currentParentGroup.SetParent(map.WorldSpawn); while (true) { byte hierarchyControl; while ((hierarchyControl = reader.ReadByte()) > HIERARCHY_UP) { if (hierarchyControl == IDENTIFIER_ENTITY) { entities[reader.ReadInt32()].SetParent(currentParentGroup); } else { solids[reader.ReadInt32()].SetParent(currentParentGroup); } } if (hierarchyControl == HIERARCHY_DOWN) { Group newGroup = new Group(map.IDGenerator.GetNextObjectID()); newGroup.SetParent(currentParentGroup); currentParentGroup = newGroup; } else if (currentParentGroup.Parent != map.WorldSpawn) { currentParentGroup = (Group)currentParentGroup.Parent; } else { break; } } } stream.Close(); return(map); }
protected override void SaveToStream(Stream stream, DataStructures.MapObjects.Map map, DataStructures.GameData.GameData gameData, TextureCollection textureCollection) { BinaryWriter writer = new BinaryWriter(stream); writer.WriteFixedLengthString(Encoding.ASCII, 3, "CBR"); writer.Write(revision); // Lightmaps bool lightmapped = textureCollection != null && textureCollection.Lightmaps[0] != null; if (lightmapped) { writer.Write((byte)Lightmapped.Fully); // TODO: Determine changes from last render for (int i = 0; i < 4; i++) { long prevPos = writer.Seek(0, SeekOrigin.Current); writer.Write(0); writer.Flush(); textureCollection.Lightmaps[i].Save(stream, ImageFormat.Png); int imageOffset = (int)(writer.Seek(0, SeekOrigin.Current) - prevPos); writer.Seek(-imageOffset, SeekOrigin.Current); writer.Write(imageOffset - sizeof(int)); writer.Seek(0, SeekOrigin.End); } } else { writer.Write((byte)Lightmapped.No); } // Texture dictionary Dictionary <string, int> texDic = new Dictionary <string, int>(); StringBuilder texBuilder = new StringBuilder(); int texCount = 0; IEnumerator <string> textures = map.GetAllTextures().GetEnumerator(); while (textures.MoveNext()) { texBuilder.Append(textures.Current); texBuilder.Append('\0'); texDic.Add(textures.Current, texCount); texCount++; } writer.Write(texCount); writer.WriteFixedLengthString(Encoding.UTF8, texBuilder.Length, texBuilder.ToString()); // Solids List <MapObject> solids = map.WorldSpawn.Find(x => x is Solid); writer.Write(solids.Count); foreach (Solid s in solids) { writer.Write(s.Faces.Count); foreach (Face f in s.Faces) { writer.Write(texDic[f.Texture.Name]); writer.WriteCoordinate(f.Texture.UAxis); writer.WriteCoordinate(f.Texture.VAxis); writer.Write(f.Texture.XShift); writer.Write(f.Texture.YShift); writer.Write(f.Texture.XScale); writer.Write(f.Texture.YScale); writer.Write(f.Texture.Rotation); writer.Write(f.Vertices.Count); foreach (Vertex v in f.Vertices) { writer.WriteCoordinate(v.Location); if (lightmapped) { writer.Write(v.LMU); writer.Write(v.LMV); writer.Write((float)v.TextureU); writer.Write((float)v.TextureV); } } } } // Entities ISet <string> foundEntityTypes = new HashSet <string>(); List <GameDataObject> entityTypes = new List <GameDataObject>(); List <MapObject> entites = map.WorldSpawn.Find(x => x is Entity && x.ClassName != ""); foreach (Entity e in entites) { Console.WriteLine(e.ClassName); Console.WriteLine(e.GameData.Name); if (!foundEntityTypes.Contains(e.ClassName)) { GameDataObject gdo = gameData.Classes.Find(x => x.Name == e.ClassName); entityTypes.Add(gdo); foundEntityTypes.Add(gdo.Name); } } // Move brush entities to front entityTypes.Sort((x, y) => (x.ClassType == ClassType.Solid ? -1 : 0)); // For later use with groups Dictionary <Entity, int> entityIndices = new Dictionary <Entity, int>(); int entityIndicesCounter = 0; bool reachedRegular = false; foreach (GameDataObject gdo in entityTypes) { if (!reachedRegular && gdo.ClassType != ClassType.Solid) { reachedRegular = true; writer.WriteNullTerminatedString(""); } writer.WriteNullTerminatedString(gdo.Name); foreach (DataStructures.GameData.Property p in gdo.Properties) { writer.Write((byte)p.VariableType); writer.WriteNullTerminatedString(p.Name); // Switch from brush to regular entities } writer.Write((byte)255); // Property end byte // Entries List <MapObject> entitiesOfType = entites.FindAll(x => x.ClassName == gdo.Name); writer.Write(entitiesOfType.Count); foreach (Entity e in entitiesOfType) { entityIndices.Add(e, entityIndicesCounter++); if (e.GameData.ClassType == ClassType.Solid) { IEnumerable <MapObject> children = e.GetChildren(); writer.Write(children.Count()); foreach (MapObject mo in children) { int index = solids.FindIndex(x => x == mo); writer.Write(index); } } for (int i = 0; i < gdo.Properties.Count; i++) { DataStructures.MapObjects.Property property; if (i < e.EntityData.Properties.Count && gdo.Properties[i].Name == e.EntityData.Properties[i].Key) { property = e.EntityData.Properties[i]; } else { property = e.EntityData.Properties.Find(x => x.Key == gdo.Properties[i].Name); if (property == null) { property = new DataStructures.MapObjects.Property(); property.Key = gdo.Properties[i].Name; property.Value = gdo.Properties[i].DefaultValue; } } switch (gdo.Properties[i].VariableType) { case VariableType.Bool: writer.Write(property.Value == "Yes"); break; case VariableType.Color255: writer.WriteRGBAColour(property.GetColour(Color.Black)); break; case VariableType.Float: writer.Write(float.Parse(property.Value)); break; case VariableType.Integer: writer.Write(int.Parse(property.Value)); break; case VariableType.String: // TODO: Implement dictionary. writer.WriteNullTerminatedString(property.Value); break; case VariableType.Vector: writer.WriteCoordinate(property.GetCoordinate(Coordinate.Zero)); break; case VariableType.Choices: bool found = false; for (int j = 0; j < gdo.Properties[i].Options.Count; j++) { if (property.Value == gdo.Properties[i].Options[j].Key) { writer.Write((byte)j); found = true; break; } } if (!found) { writer.Write((byte)255); } break; } } } } writer.WriteNullTerminatedString(""); // CBRE ONLY // Visgroup dictionary // TODO: Visgroup IDs to indices Stack <IEnumerator <Visgroup> > visStack = new Stack <IEnumerator <Visgroup> >(); visStack.Push(map.Visgroups.GetEnumerator()); while (visStack.Count > 0) { IEnumerator <Visgroup> v = visStack.Pop(); while (v.MoveNext()) { if (!v.Current.IsAutomatic) { writer.Write(HIERARCHY_PROCCEED); writer.Write(v.Current.ID); writer.WriteNullTerminatedString(v.Current.Name); if (v.Current.Children.Count != 0) { writer.Write(HIERARCHY_DOWN); visStack.Push(v); v = v.Current.Children.GetEnumerator(); } } } writer.Write(HIERARCHY_UP); } // Solid visgroups foreach (Solid s in map.WorldSpawn.FindAll().OfType <Solid>()) { WriteVisgroups(writer, s); } // Entity visgroups foreach (GameDataObject entityType in entityTypes) { foreach (Entity e in entites.FindAll(x => x.ClassName == entityType.Name)) { WriteVisgroups(writer, e); } } // Groups IEnumerable <Group> groups = map.WorldSpawn.GetChildren().OfType <Group>(); writer.Write(groups.Count()); foreach (Group g in groups) { Stack <IEnumerator <MapObject> > groupStack = new Stack <IEnumerator <MapObject> >(); groupStack.Push(g.GetChildren().GetEnumerator()); while (groupStack.Count > 0) { IEnumerator <MapObject> gg = groupStack.Pop(); while (gg.MoveNext()) { if (gg.Current is Group) { writer.Write(HIERARCHY_DOWN); groupStack.Push(gg); gg = gg.Current.GetChildren().GetEnumerator(); } else if (gg.Current is Entity) { writer.Write(IDENTIFIER_ENTITY); writer.Write(entityIndices[(Entity)gg.Current]); } else { writer.Write(IDENTIFIER_SOLID); writer.Write(solids.FindIndex(x => x == gg.Current)); } } writer.Write(HIERARCHY_UP); } } stream.Close(); }
protected override void SaveToStream(Stream stream, DataStructures.MapObjects.Map map) { var groups = new List <Group>(); var solids = new List <Solid>(); var ents = new List <Entity>(); FlattenTree(map.WorldSpawn, solids, ents, groups); var fvi = FileVersionInfo.GetVersionInfo(typeof(VmfProvider).Assembly.Location); var versioninfo = new GenericStructure("versioninfo"); versioninfo.AddProperty("editorname", "Chisel"); versioninfo.AddProperty("editorversion", fvi.ProductMajorPart.ToString(CultureInfo.InvariantCulture) + "." + fvi.ProductMinorPart.ToString(CultureInfo.InvariantCulture)); versioninfo.AddProperty("editorbuild", fvi.ProductBuildPart.ToString(CultureInfo.InvariantCulture)); versioninfo.AddProperty("mapversion", map.Version.ToString(CultureInfo.InvariantCulture)); versioninfo.AddProperty("formatversion", "100"); versioninfo.AddProperty("prefab", "0"); var visgroups = new GenericStructure("visgroups"); foreach (var visgroup in map.Visgroups.OrderBy(x => x.ID).Where(x => !x.IsAutomatic)) { visgroups.Children.Add(WriteVisgroup(visgroup)); } var viewsettings = new GenericStructure("viewsettings"); viewsettings.AddProperty("bSnapToGrid", map.SnapToGrid ? "1" : "0"); viewsettings.AddProperty("bShowGrid", map.Show2DGrid ? "1" : "0"); viewsettings.AddProperty("bShow3DGrid", map.Show3DGrid ? "1" : "0"); viewsettings.AddProperty("nGridSpacing", map.GridSpacing.ToString(CultureInfo.InvariantCulture)); viewsettings.AddProperty("bIgnoreGrouping", map.IgnoreGrouping ? "1" : "0"); viewsettings.AddProperty("bHideFaceMask", map.HideFaceMask ? "1" : "0"); viewsettings.AddProperty("bHideNullTextures", map.HideNullTextures ? "1" : "0"); viewsettings.AddProperty("bTextureLock", map.TextureLock ? "1" : "0"); viewsettings.AddProperty("bTextureScalingLock", map.TextureScalingLock ? "1" : "0"); var world = WriteWorld(map, solids, groups); var entities = ents.OrderBy(x => x.ID).Select(WriteEntity).ToList(); var cameras = new GenericStructure("cameras"); cameras.AddProperty("activecamera", map.Cameras.IndexOf(map.ActiveCamera).ToString(CultureInfo.InvariantCulture)); foreach (var cam in map.Cameras) { var camera = new GenericStructure("camera"); camera.AddProperty("position", "[" + FormatCoordinate(cam.EyePosition) + "]"); camera.AddProperty("look", "[" + FormatCoordinate(cam.LookPosition) + "]"); cameras.Children.Add(camera); } var cordon = new GenericStructure("cordon"); cordon.AddProperty("mins", map.CordonBounds.Start.ToString()); cordon.AddProperty("maxs", map.CordonBounds.End.ToString()); cordon.AddProperty("active", map.Cordon ? "1" : "0"); using (var sw = new StreamWriter(stream)) { versioninfo.PrintToStream(sw); visgroups.PrintToStream(sw); viewsettings.PrintToStream(sw); world.PrintToStream(sw); entities.ForEach(e => e.PrintToStream(sw)); cameras.PrintToStream(sw); cordon.PrintToStream(sw); } }
public static void SaveToFile(string filename, DataStructures.MapObjects.Map map, string format) { Scene scene = new Scene(); Node rootNode = new Node(); rootNode.Name = "root"; scene.RootNode = rootNode; Node newNode = new Node(); Mesh mesh; int vertOffset; string[] textures = map.GetAllTextures().ToArray(); foreach (string texture in textures) { if (texture == "tooltextures/remove_face") { continue; } Material material = new Material(); material.Name = texture; TextureSlot textureSlot = new TextureSlot(texture + (File.Exists(texture + ".png") ? ".png" : (File.Exists(texture + ".jpeg") ? ".jpeg" : ".jpg")), TextureType.Diffuse, 0, TextureMapping.Plane, 0, 1.0f, TextureOperation.Multiply, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0); material.AddMaterialTexture(ref textureSlot); scene.Materials.Add(material); mesh = new Mesh(); if (format != "obj") // .obj files should have no mesh names so they are one proper mesh { mesh.Name = texture + "_mesh"; } mesh.MaterialIndex = scene.MaterialCount - 1; vertOffset = 0; List <int> indices = new List <int>(); IEnumerable <Face> faces = map.WorldSpawn.Find(x => x is Solid). OfType <Solid>(). SelectMany(x => x.Faces). Where(x => x.Texture.Name == texture); foreach (Face face in faces) { foreach (Vertex v in face.Vertices) { mesh.Vertices.Add(new Vector3D((float)v.Location.X, (float)v.Location.Z, (float)v.Location.Y)); mesh.Normals.Add(new Vector3D((float)face.Plane.Normal.X, (float)face.Plane.Normal.Z, (float)face.Plane.Normal.Y)); mesh.TextureCoordinateChannels[0].Add(new Vector3D((float)v.TextureU, (float)v.TextureV, 0)); } mesh.UVComponentCount[0] = 2; foreach (uint ind in face.GetTriangleIndices()) { indices.Add((int)ind + vertOffset); } vertOffset += face.Vertices.Count; } mesh.SetIndices(indices.ToArray(), 3); scene.Meshes.Add(mesh); newNode.MeshIndices.Add(scene.MeshCount - 1); } rootNode.Children.Add(newNode); new AssimpContext().ExportFile(scene, filename, format); }
protected override DataStructures.MapObjects.Map GetFromStream(Stream stream) { using (var reader = new StreamReader(stream)) { var parent = new GenericStructure("Root"); parent.Children.AddRange(GenericStructure.Parse(reader)); // Sections from a Hammer map: // - world // - entity // - visgroups // - cordon // Not done yet // - versioninfo // - viewsettings // - cameras var map = new DataStructures.MapObjects.Map(); var world = parent.GetChildren("world").FirstOrDefault(); var entities = parent.GetChildren("entity"); var visgroups = parent.GetChildren("visgroups").SelectMany(x => x.GetChildren("visgroup")); var cameras = parent.GetChildren("cameras").FirstOrDefault(); var cordon = parent.GetChildren("cordon").FirstOrDefault(); var viewsettings = parent.GetChildren("viewsettings").FirstOrDefault(); foreach (var visgroup in visgroups) { var vg = ReadVisgroup(visgroup); if (vg.ID < 0 && vg.Name == "Auto") continue; map.Visgroups.Add(vg); } if (world != null) map.WorldSpawn = ReadWorld(world, map.IDGenerator); foreach (var entity in entities) { var ent = ReadEntity(entity, map.IDGenerator); var groupid = entity.Children.Where(x => x.Name == "editor").Select(x => x.PropertyInteger("groupid")).FirstOrDefault(); var entParent = groupid > 0 ? map.WorldSpawn.Find(x => x.ID == groupid && x is Group).FirstOrDefault() ?? map.WorldSpawn : map.WorldSpawn; ent.SetParent(entParent); } var activeCamera = 0; if (cameras != null) { activeCamera = cameras.PropertyInteger("activecamera"); foreach (var cam in cameras.GetChildren("camera")) { var pos = cam.PropertyCoordinate("position"); var look = cam.PropertyCoordinate("look"); if (pos != null && look != null) { map.Cameras.Add(new Camera {EyePosition = pos, LookPosition = look}); } } } if (!map.Cameras.Any()) { map.Cameras.Add(new Camera { EyePosition = Coordinate.Zero, LookPosition = Coordinate.UnitY }); } if (activeCamera < 0 || activeCamera >= map.Cameras.Count) { activeCamera = 0; } map.ActiveCamera = map.Cameras[activeCamera]; if (cordon != null) { var start = cordon.PropertyCoordinate("mins", map.CordonBounds.Start); var end = cordon.PropertyCoordinate("maxs", map.CordonBounds.End); map.CordonBounds = new Box(start, end); map.Cordon = cordon.PropertyBoolean("active", map.Cordon); } if (viewsettings != null) { map.SnapToGrid = viewsettings.PropertyBoolean("bSnapToGrid", map.SnapToGrid); map.Show2DGrid = viewsettings.PropertyBoolean("bShowGrid", map.Show2DGrid); map.Show3DGrid = viewsettings.PropertyBoolean("bShow3DGrid", map.Show3DGrid); map.GridSpacing = viewsettings.PropertyDecimal("nGridSpacing", map.GridSpacing); map.IgnoreGrouping = viewsettings.PropertyBoolean("bIgnoreGrouping", map.IgnoreGrouping); map.HideFaceMask = viewsettings.PropertyBoolean("bHideFaceMask", map.HideFaceMask); map.HideNullTextures = viewsettings.PropertyBoolean("bHideNullTextures", map.HideNullTextures); map.TextureLock = viewsettings.PropertyBoolean("bTextureLock", map.TextureLock); map.TextureScalingLock = viewsettings.PropertyBoolean("bTextureScalingLock", map.TextureScalingLock); } return map; } }
protected override void SaveToStream(Stream stream, DataStructures.MapObjects.Map map) { throw new Exception("don't save to 3dw, ew"); }
protected abstract void SaveToStream(Stream stream, DataStructures.MapObjects.Map map);
protected abstract void SaveToStream(Stream stream, DataStructures.MapObjects.Map map, DataStructures.GameData.GameData gameData, TextureCollection textureCollection);
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); }