// shoutouts to Aleksi Juvani for your vmf importer giving me a clue on why my textures were misaligned. // had to add the world space position of the brush to the calculations! https://github.com/aleksijuvani private static void CalculateTextureCoordinates(PrimitiveBrush pr, Polygon polygon, int textureWidth, int textureHeight, VmfAxis UAxis, VmfAxis VAxis) { UAxis.Translation = UAxis.Translation % textureWidth; VAxis.Translation = VAxis.Translation % textureHeight; if (UAxis.Translation < -textureWidth / 2f) { UAxis.Translation += textureWidth; } if (VAxis.Translation < -textureHeight / 2f) { VAxis.Translation += textureHeight; } // calculate texture coordinates. for (int i = 0; i < polygon.Vertices.Length; i++) { var vertex = pr.transform.position + polygon.Vertices[i].Position; Vector3 uaxis = new Vector3(UAxis.Vector.X, UAxis.Vector.Z, UAxis.Vector.Y); Vector3 vaxis = new Vector3(VAxis.Vector.X, VAxis.Vector.Z, VAxis.Vector.Y); var u = Vector3.Dot(vertex, uaxis) / (textureWidth * (UAxis.Scale * inchesInMeters)) + UAxis.Translation / textureWidth; var v = Vector3.Dot(vertex, vaxis) / (textureHeight * (VAxis.Scale * inchesInMeters)) + VAxis.Translation / textureHeight; polygon.Vertices[i].UV.x = u; polygon.Vertices[i].UV.y = -v; } }
public static void ChamferSharpEdges(PrimitiveBrush brush, float chamferAngleThreshold, float chamferDistance, int chamferIterations) { var polygons = new List <Polygon>(brush.GetPolygons()); var polygonsChamfered = new List <Polygon>(); var edgesVisited = new HashSet <VisitedEdge>(); var edgesChamfer = new List <Edge>(); foreach (var polygon in polygons) { var edges = polygon.GetEdges(); foreach (var edge in edges) { var v1 = edge.Vertex1; var v2 = edge.Vertex2; if (edgesVisited.Contains(new VisitedEdge() { pointA = v1.Position, pointB = v2.Position })) { continue; } edgesVisited.Add(new VisitedEdge() { pointA = v1.Position, pointB = v2.Position }); edgesVisited.Add(new VisitedEdge() { pointA = v1.Position, pointB = v2.Position }); var edgePolygons = polygons.Where(p => Polygon.ContainsEdge(p, edge)).ToArray(); if (edgePolygons.Length == 2) { var normalA = edgePolygons[0].Plane.normal; var normalB = edgePolygons[1].Plane.normal; var angle = Vector3.Angle(normalA, normalB); if (angle > chamferAngleThreshold) { edgesChamfer.Add(edge); } } } } if (PolygonFactory.ChamferPolygons(polygons, edgesChamfer, chamferDistance, chamferIterations, out polygonsChamfered)) { brush.SetPolygons(polygonsChamfered.ToArray()); brush.Invalidate(true); } }
/// <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(); } }
// shoutouts to Jasmine Mickle for your insight and UV texture coordinates code. private static void CalculateTextureCoordinates(PrimitiveBrush pr, Polygon polygon, int textureWidth, int textureHeight, Vector2 offset, Vector2 scale, float rotation) { // feel free to improve this uv mapping code, it has some issues. // • 45 degree angled walls may not have correct UV texture coordinates (are not correctly picking the dominant axis because there are two). // • negative vertex coordinates may not have correct UV texture coordinates. // calculate texture coordinates. for (int i = 0; i < polygon.Vertices.Length; i++) { // we scaled down the level so scale up the math here. var vertex = (pr.transform.position + polygon.Vertices[i].Position) * s_Scale; Vector2 uv = new Vector2(0, 0); int dominantAxis = 0; // 0 == x, 1 == y, 2 == z // find the axis closest to the polygon's normal. float[] axes = { Mathf.Abs(polygon.Plane.normal.x), Mathf.Abs(polygon.Plane.normal.z), Mathf.Abs(polygon.Plane.normal.y) }; // defaults to use x-axis. dominantAxis = 0; // check whether the y-axis is more likely. if (axes[1] > axes[dominantAxis]) { dominantAxis = 1; } // check whether the z-axis is more likely. if (axes[2] >= axes[dominantAxis]) { dominantAxis = 2; } // x-axis: if (dominantAxis == 0) { uv.x = vertex.z; uv.y = vertex.y; } // y-axis: if (dominantAxis == 1) { uv.x = vertex.x; uv.y = vertex.y; } // z-axis: if (dominantAxis == 2) { uv.x = vertex.x; uv.y = vertex.z; } // rotate the texture coordinates. uv = uv.Rotate(-rotation); // scale the texture coordinates. uv = uv.Divide(scale); // move the texture coordinates. uv += offset; // finally divide the result by the texture size. uv = uv.Divide(new Vector2(textureWidth, textureHeight)); polygon.Vertices[i].UV = uv; } }
/// <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(CSGModel model, T3dMap map, int scale = 64) { try { model.BeginUpdate(); 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 = FindMaterial(tpolygon.Texture); Vertex[] vertices = new Vertex[tpolygon.Vertices.Count]; for (int j = 0; j < tpolygon.Vertices.Count; j++) { vertices[j] = new Vertex(ToVector3(tpolygon.Vertices[j]) / (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. 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(); } }