private const float inchesInMeters = 0.03125f; // 1/32 /// <summary> /// Imports the specified world into the SabreCSG model. /// </summary> /// <param name="model">The model to import into.</param> /// <param name="world">The world to be imported.</param> /// <param name="scale">The scale modifier.</param> public static void Import(CSGModelBase model, VmfWorld world) { try { model.BeginUpdate(); // create a material searcher to associate materials automatically. MaterialSearcher materialSearcher = new MaterialSearcher(); // group all the brushes together. GroupBrush groupBrush = new GameObject("Source Engine Map").AddComponent <GroupBrush>(); groupBrush.transform.SetParent(model.transform); // iterate through all world solids. for (int i = 0; i < world.Solids.Count; i++) { #if UNITY_EDITOR UnityEditor.EditorUtility.DisplayProgressBar("SabreCSG: Importing Source Engine Map", "Converting Hammer Solids To SabreCSG Brushes (" + (i + 1) + " / " + world.Solids.Count + ")...", i / (float)world.Solids.Count); #endif VmfSolid solid = world.Solids[i]; // don't add triggers to the scene. if (solid.Sides.Count > 0 && IsSpecialMaterial(solid.Sides[0].Material)) { continue; } // build a very large cube brush. var go = model.CreateBrush(PrimitiveBrushType.Cube, Vector3.zero); var pr = go.GetComponent <PrimitiveBrush>(); BrushUtility.Resize(pr, new Vector3(8192, 8192, 8192)); // clip all the sides out of the brush. for (int j = solid.Sides.Count; j-- > 0;) { VmfSolidSide side = solid.Sides[j]; Plane clip = new Plane(pr.transform.InverseTransformPoint(new Vector3(side.Plane.P1.X, side.Plane.P1.Z, side.Plane.P1.Y) * inchesInMeters), pr.transform.InverseTransformPoint(new Vector3(side.Plane.P2.X, side.Plane.P2.Z, side.Plane.P2.Y) * inchesInMeters), pr.transform.InverseTransformPoint(new Vector3(side.Plane.P3.X, side.Plane.P3.Z, side.Plane.P3.Y) * inchesInMeters)); ClipUtility.ApplyClipPlane(pr, clip, false); // find the polygons associated with the clipping plane. // the normal is unique and can never occur twice as that wouldn't allow the solid to be convex. var polygons = pr.GetPolygons().Where(p => p.Plane.normal == clip.normal); foreach (var polygon in polygons) { // detect excluded polygons. if (IsExcludedMaterial(side.Material)) { polygon.UserExcludeFromFinal = true; } // detect collision-only brushes. if (IsInvisibleMaterial(side.Material)) { pr.IsVisible = false; } // find the material in the unity project automatically. Material material; // try finding the fully qualified texture name with '/' replaced by '.' so 'BRICK.BRICKWALL052D'. string materialName = side.Material.Replace("/", "."); if (materialName.Contains('.')) { // try finding both 'BRICK.BRICKWALL052D' and 'BRICKWALL052D'. string tiny = materialName.Substring(materialName.LastIndexOf('.') + 1); material = materialSearcher.FindMaterial(new string[] { materialName, tiny }); if (material == null) { Debug.Log("SabreCSG: Tried to find material '" + materialName + "' and also as '" + tiny + "' but it couldn't be found in the project."); } } else { // only try finding 'BRICKWALL052D'. material = materialSearcher.FindMaterial(new string[] { materialName }); if (material == null) { Debug.Log("SabreCSG: Tried to find material '" + materialName + "' but it couldn't be found in the project."); } } polygon.Material = material; // calculate the texture coordinates. int w = 256; int h = 256; if (polygon.Material != null && polygon.Material.mainTexture != null) { w = polygon.Material.mainTexture.width; h = polygon.Material.mainTexture.height; } CalculateTextureCoordinates(pr, polygon, w, h, side.UAxis, side.VAxis); } } // add the brush to the group. pr.transform.SetParent(groupBrush.transform); } // iterate through all entities. for (int e = 0; e < world.Entities.Count; e++) { #if UNITY_EDITOR UnityEditor.EditorUtility.DisplayProgressBar("SabreCSG: Importing Source Engine Map", "Converting Hammer Entities To SabreCSG Brushes (" + (e + 1) + " / " + world.Entities.Count + ")...", e / (float)world.Entities.Count); #endif VmfEntity entity = world.Entities[e]; // skip entities that sabrecsg can't handle. switch (entity.ClassName) { case "func_areaportal": case "func_areaportalwindow": case "func_capturezone": case "func_changeclass": case "func_combine_ball_spawner": case "func_dustcloud": case "func_dustmotes": case "func_nobuild": case "func_nogrenades": case "func_occluder": case "func_precipitation": case "func_proprespawnzone": case "func_regenerate": case "func_respawnroom": case "func_smokevolume": case "func_viscluster": continue; } // iterate through all entity solids. for (int i = 0; i < entity.Solids.Count; i++) { VmfSolid solid = entity.Solids[i]; // don't add triggers to the scene. if (solid.Sides.Count > 0 && IsSpecialMaterial(solid.Sides[0].Material)) { continue; } // build a very large cube brush. var go = model.CreateBrush(PrimitiveBrushType.Cube, Vector3.zero); var pr = go.GetComponent <PrimitiveBrush>(); BrushUtility.Resize(pr, new Vector3(8192, 8192, 8192)); // clip all the sides out of the brush. for (int j = solid.Sides.Count; j-- > 0;) { VmfSolidSide side = solid.Sides[j]; Plane clip = new Plane(pr.transform.InverseTransformPoint(new Vector3(side.Plane.P1.X, side.Plane.P1.Z, side.Plane.P1.Y) * inchesInMeters), pr.transform.InverseTransformPoint(new Vector3(side.Plane.P2.X, side.Plane.P2.Z, side.Plane.P2.Y) * inchesInMeters), pr.transform.InverseTransformPoint(new Vector3(side.Plane.P3.X, side.Plane.P3.Z, side.Plane.P3.Y) * inchesInMeters)); ClipUtility.ApplyClipPlane(pr, clip, false); // find the polygons associated with the clipping plane. // the normal is unique and can never occur twice as that wouldn't allow the solid to be convex. var polygons = pr.GetPolygons().Where(p => p.Plane.normal == clip.normal); foreach (var polygon in polygons) { // detect excluded polygons. if (IsExcludedMaterial(side.Material)) { polygon.UserExcludeFromFinal = true; } // detect collision-only brushes. if (IsInvisibleMaterial(side.Material)) { pr.IsVisible = false; } // find the material in the unity project automatically. Material material; // try finding the fully qualified texture name with '/' replaced by '.' so 'BRICK.BRICKWALL052D'. string materialName = side.Material.Replace("/", "."); if (materialName.Contains('.')) { // try finding both 'BRICK.BRICKWALL052D' and 'BRICKWALL052D'. string tiny = materialName.Substring(materialName.LastIndexOf('.') + 1); material = materialSearcher.FindMaterial(new string[] { materialName, tiny }); if (material == null) { Debug.Log("SabreCSG: Tried to find material '" + materialName + "' and also as '" + tiny + "' but it couldn't be found in the project."); } } else { // only try finding 'BRICKWALL052D'. material = materialSearcher.FindMaterial(new string[] { materialName }); if (material == null) { Debug.Log("SabreCSG: Tried to find material '" + materialName + "' but it couldn't be found in the project."); } } polygon.Material = material; // calculate the texture coordinates. int w = 256; int h = 256; if (polygon.Material != null && polygon.Material.mainTexture != null) { w = polygon.Material.mainTexture.width; h = polygon.Material.mainTexture.height; } CalculateTextureCoordinates(pr, polygon, w, h, side.UAxis, side.VAxis); } } // detail brushes that do not affect the CSG world. if (entity.ClassName == "func_detail") { pr.IsNoCSG = true; } // collision only brushes. if (entity.ClassName == "func_vehicleclip") { pr.IsVisible = false; } // add the brush to the group. pr.transform.SetParent(groupBrush.transform); } } #if UNITY_EDITOR UnityEditor.EditorUtility.ClearProgressBar(); #endif } catch (Exception) { throw; } finally { model.EndUpdate(); } }
/// <summary> /// Imports the specified map into the SabreCSG model. /// </summary> /// <param name="model">The model to import into.</param> /// <param name="map">The map to be imported.</param> /// <param name="scale">The scale modifier.</param> public static void Import(CSGModelBase model, T3dMap map, int scale = 64) { try { model.BeginUpdate(); // create a material searcher to associate materials automatically. MaterialSearcher materialSearcher = new MaterialSearcher(); List <T3dActor> brushes = map.Brushes; Brush[] sabreBrushes = new Brush[brushes.Count]; // iterate through all brush actors. for (int k = 0; k < brushes.Count; k++) { // get the underlying brush data. T3dActor tactor = brushes[k]; T3dBrush tbrush = tactor.Brush; #if UNITY_EDITOR UnityEditor.EditorUtility.DisplayProgressBar("SabreCSG: Importing Unreal Gold Map", "Converting Unreal Brushes To SabreCSG Brushes (" + (k + 1) + " / " + brushes.Count + ")...", k / (float)brushes.Count); #endif // iterate through the brush polygons. Polygon[] polygons = new Polygon[tbrush.Polygons.Count]; for (int i = 0; i < tbrush.Polygons.Count; i++) { T3dPolygon tpolygon = tbrush.Polygons[i]; // find the material in the unity project automatically. Material material = null; if (!string.IsNullOrEmpty(tpolygon.Texture)) { if (tpolygon.Texture.Contains('.')) { // try finding both 'PlayrShp.Ceiling.Hullwk' and 'Hullwk'. string tiny = tpolygon.Texture.Substring(tpolygon.Texture.LastIndexOf('.') + 1); material = materialSearcher.FindMaterial(new string[] { tpolygon.Texture, tiny }); if (material == null) { Debug.Log("SabreCSG: Tried to find material '" + tpolygon.Texture + "' and also as '" + tiny + "' but it couldn't be found in the project."); } } else { // only try finding 'Hullwk'. material = materialSearcher.FindMaterial(new string[] { tpolygon.Texture }); if (material == null) { Debug.Log("SabreCSG: Tried to find material '" + tpolygon.Texture + "' but it couldn't be found in the project."); } } } Vertex[] vertices = new Vertex[tpolygon.Vertices.Count]; for (int j = 0; j < tpolygon.Vertices.Count; j++) { // main-scale // scale around pivot point. Vector3 vertexPosition = ToVector3(tpolygon.Vertices[j]); Vector3 pivot = ToVector3(tactor.PrePivot); Vector3 difference = vertexPosition - pivot; vertexPosition = difference.Multiply(ToVector3Raw(tactor.MainScale)) + pivot; // post-scale vertices[j] = new Vertex(vertexPosition.Multiply(ToVector3Raw(tactor.PostScale)) / (float)scale, ToVector3(tpolygon.Normal), GenerateUV(tpolygon, j, material)); } // detect the polygon flags. bool userExcludeFromFinal = false; if ((tpolygon.Flags & T3dPolygonFlags.Invisible) > 0) { userExcludeFromFinal = true; } polygons[i] = new Polygon(vertices, material, false, userExcludeFromFinal); } // position and rotate the brushes around their pivot point. Transform transform = model.CreateCustomBrush(polygons).transform; transform.position = (ToVector3(tactor.Location) / (float)scale) - (ToVector3(tactor.PrePivot) / (float)scale); Vector3 axis; float angle; T3dRotatorToQuaternion(tactor.Rotation).ToAngleAxis(out angle, out axis); transform.RotateAround(transform.position + (ToVector3(tactor.PrePivot) / (float)scale), axis, angle); PrimitiveBrush brush = transform.GetComponent <PrimitiveBrush>(); sabreBrushes[k] = brush; object value; // detect the brush mode (additive, subtractive). if (tactor.Properties.TryGetValue("CsgOper", out value)) { brush.Mode = (string)value == "CSG_Add" ? CSGMode.Add : CSGMode.Subtract; } // detect special brush flags. if (tactor.Properties.TryGetValue("PolyFlags", out value)) { T3dBrushFlags flags = (T3dBrushFlags)value; if ((flags & T3dBrushFlags.Invisible) > 0) { brush.IsVisible = false; } if ((flags & T3dBrushFlags.NonSolid) > 0) { brush.HasCollision = false; } if ((flags & T3dBrushFlags.SemiSolid) > 0) { brush.IsNoCSG = true; } } // detect single polygons. if (polygons.Length == 1) { brush.IsNoCSG = true; } } // add all new brushes to a group. string title = "Unreal Gold Map"; if (map.Title != "") { title += " '" + map.Title + "'"; } if (map.Author != "") { title += " (" + map.Author + ")"; } GroupBrush groupBrush = new GameObject(title).AddComponent <GroupBrush>(); groupBrush.transform.SetParent(model.transform); for (int i = 0; i < sabreBrushes.Length; i++) { sabreBrushes[i].transform.SetParent(groupBrush.transform); } #if UNITY_EDITOR UnityEditor.EditorUtility.ClearProgressBar(); #endif } catch (Exception) { throw; } finally { model.EndUpdate(); } }
/// <summary> /// Imports the specified world into the SabreCSG model. /// </summary> /// <param name="model">The model to import into.</param> /// <param name="world">The world to be imported.</param> /// <param name="scale">The scale modifier.</param> public static void Import(CSGModelBase model, MapWorld world) { try { model.BeginUpdate(); // create a material searcher to associate materials automatically. MaterialSearcher materialSearcher = new MaterialSearcher(); // group all the brushes together. GroupBrush groupBrush = new GameObject("Quake 1 Map").AddComponent <GroupBrush>(); groupBrush.transform.SetParent(model.transform); // iterate through all entities. for (int e = 0; e < world.Entities.Count; e++) { #if UNITY_EDITOR UnityEditor.EditorUtility.DisplayProgressBar("SabreCSG: Importing Quake 1 Map", "Converting Quake 1 Entities To SabreCSG Brushes (" + (e + 1) + " / " + world.Entities.Count + ")...", e / (float)world.Entities.Count); #endif MapEntity entity = world.Entities[e]; // iterate through all entity solids. for (int i = 0; i < entity.Brushes.Count; i++) { MapBrush brush = entity.Brushes[i]; #if UNITY_EDITOR if (world.Entities[e].ClassName == "worldspawn") { UnityEditor.EditorUtility.DisplayProgressBar("SabreCSG: Importing Quake 1 Map", "Converting Quake 1 Brushes To SabreCSG Brushes (" + (i + 1) + " / " + entity.Brushes.Count + ")...", i / (float)entity.Brushes.Count); } #endif // don't add triggers to the scene. if (brush.Sides.Count > 0 && IsSpecialMaterial(brush.Sides[0].Material)) { continue; } // build a very large cube brush. var go = model.CreateBrush(PrimitiveBrushType.Cube, Vector3.zero); var pr = go.GetComponent <PrimitiveBrush>(); BrushUtility.Resize(pr, new Vector3(8192, 8192, 8192)); // clip all the sides out of the brush. for (int j = brush.Sides.Count; j-- > 0;) { MapBrushSide side = brush.Sides[j]; Plane clip = new Plane(pr.transform.InverseTransformPoint(new Vector3(side.Plane.P1.X, side.Plane.P1.Z, side.Plane.P1.Y) / (float)s_Scale), pr.transform.InverseTransformPoint(new Vector3(side.Plane.P2.X, side.Plane.P2.Z, side.Plane.P2.Y) / (float)s_Scale), pr.transform.InverseTransformPoint(new Vector3(side.Plane.P3.X, side.Plane.P3.Z, side.Plane.P3.Y) / (float)s_Scale)); ClipUtility.ApplyClipPlane(pr, clip, false); // find the polygons associated with the clipping plane. // the normal is unique and can never occur twice as that wouldn't allow the solid to be convex. var polygons = pr.GetPolygons().Where(p => p.Plane.normal.EqualsWithEpsilonLower3(clip.normal)); foreach (var polygon in polygons) { // detect excluded polygons. if (IsExcludedMaterial(side.Material)) { polygon.UserExcludeFromFinal = true; } // detect collision-only brushes. if (IsInvisibleMaterial(side.Material)) { pr.IsVisible = false; } // find the material in the unity project automatically. Material material; // try finding the texture name with '*' replaced by '#' so '#teleport'. string materialName = side.Material.Replace("*", "#"); material = materialSearcher.FindMaterial(new string[] { materialName }); if (material == null) { Debug.Log("SabreCSG: Tried to find material '" + materialName + "' but it couldn't be found in the project."); } polygon.Material = material; // calculate the texture coordinates. int w = 256; int h = 256; if (polygon.Material != null && polygon.Material.mainTexture != null) { w = polygon.Material.mainTexture.width; h = polygon.Material.mainTexture.height; } CalculateTextureCoordinates(pr, polygon, w, h, new Vector2(side.Offset.X, -side.Offset.Y), new Vector2(side.Scale.X, side.Scale.Y), side.Rotation); } } // add the brush to the group. pr.transform.SetParent(groupBrush.transform); } } #if UNITY_EDITOR UnityEditor.EditorUtility.ClearProgressBar(); #endif } catch (Exception) { throw; } finally { model.EndUpdate(); } }