コード例 #1
0
        private static Material FindMaterial(MaterialSearcher materialSearcher, HashSet <string> materialSearcherWarnings, string name)
        {
            // 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 = name.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 && materialSearcherWarnings.Add(materialName))
                {
                    Debug.Log("Chisel: 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 && materialSearcherWarnings.Add(materialName))
                {
                    Debug.Log("Chisel: Tried to find material '" + materialName + "' but it couldn't be found in the project.");
                }
            }

            return(material);
        }
コード例 #2
0
        /// <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();
            }
        }
コード例 #3
0
        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();
            }
        }
コード例 #4
0
        /// <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();
            }
        }
コード例 #5
0
        /// <summary>
        /// Imports the specified world into the Chisel model.
        /// </summary>
        /// <param name="model">The model to import into.</param>
        /// <param name="world">The world to be imported.</param>
        public static void Import(ChiselModel model, VmfWorld world)
        {
            // create a material searcher to associate materials automatically.
            MaterialSearcher materialSearcher         = new MaterialSearcher();
            HashSet <string> materialSearcherWarnings = new HashSet <string>();

            // iterate through all world solids.
            for (int i = 0; i < world.Solids.Count; i++)
            {
#if UNITY_EDITOR
                UnityEditor.EditorUtility.DisplayProgressBar("Chisel: Importing Source Engine Map (1/3)", "Converting Hammer Solids To Chisel 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;
                }

                // HACK: Fix me in the future!
                // HACK: Chisel doesn't support collision brushes yet- skip them completely!
                if (solid.Sides.Count > 0 && IsInvisibleMaterial(solid.Sides[0].Material))
                {
                    continue;
                }
                // HACK: Fix me in the future!

                // build a very large cube brush.
                ChiselBrush go = ChiselComponentFactory.Create <ChiselBrush>(model);
                go.definition.surfaceDefinition = new ChiselSurfaceDefinition();
                go.definition.surfaceDefinition.EnsureSize(6);
                BrushMesh brushMesh = new BrushMesh();
                go.definition.brushOutline = brushMesh;
                BrushMeshFactory.CreateBox(ref brushMesh, new Vector3(-4096, -4096, -4096), new Vector3(4096, 4096, 4096), in go.definition.surfaceDefinition);

                // prepare for any displacements.
                List <DisplacementSide> DisplacementSurfaces = new List <DisplacementSide>();

                // prepare for uv calculations of clip planes after cutting.
                var planes        = new float4[solid.Sides.Count];
                var planeSurfaces = new ChiselSurface[solid.Sides.Count];

                // compute all the sides of the brush that will be clipped.
                for (int j = solid.Sides.Count; j-- > 0;)
                {
                    VmfSolidSide side = solid.Sides[j];

                    // 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 && materialSearcherWarnings.Add(materialName))
                        {
                            Debug.Log("Chisel: 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 && materialSearcherWarnings.Add(materialName))
                        {
                            Debug.Log("Chisel: Tried to find material '" + materialName + "' but it couldn't be found in the project.");
                        }
                    }

                    // fallback to default material.
                    if (material == null)
                    {
                        material = ChiselMaterialManager.DefaultFloorMaterial;
                    }

                    // create chisel surface for the clip.
                    ChiselSurface surface = new ChiselSurface();
                    surface.brushMaterial      = ChiselBrushMaterial.CreateInstance(material, ChiselMaterialManager.DefaultPhysicsMaterial);
                    surface.surfaceDescription = SurfaceDescription.Default;

                    // detect collision-only polygons.
                    if (IsInvisibleMaterial(side.Material))
                    {
                        surface.brushMaterial.LayerUsage &= ~LayerUsageFlags.RenderReceiveCastShadows;
                    }
                    // detect excluded polygons.
                    if (IsExcludedMaterial(side.Material))
                    {
                        surface.brushMaterial.LayerUsage &= LayerUsageFlags.CastShadows;
                        surface.brushMaterial.LayerUsage |= LayerUsageFlags.Collidable;
                    }

                    // calculate the clipping planes.
                    Plane clip = new Plane(go.transform.InverseTransformPoint(new Vector3(side.Plane.P1.X, side.Plane.P1.Z, side.Plane.P1.Y) * inchesInMeters), go.transform.InverseTransformPoint(new Vector3(side.Plane.P2.X, side.Plane.P2.Z, side.Plane.P2.Y) * inchesInMeters), go.transform.InverseTransformPoint(new Vector3(side.Plane.P3.X, side.Plane.P3.Z, side.Plane.P3.Y) * inchesInMeters));
                    planes[j]        = new float4(clip.normal, clip.distance);
                    planeSurfaces[j] = surface;

                    // check whether this surface is a displacement.
                    if (side.Displacement != null)
                    {
                        // disable the brush.
                        go.gameObject.GetComponent <ChiselBrush>().enabled = false;

                        // keep track of the surface used to cut the mesh.
                        DisplacementSurfaces.Add(new DisplacementSide {
                            side = side, surface = surface
                        });
                    }
                }

                // cut all the clipping planes out of the brush in one go.
                brushMesh.Cut(planes, planeSurfaces);

                // now iterate over the planes to calculate UV coordinates.
                int[] indices = new int[solid.Sides.Count];
                for (int k = 0; k < planes.Length; k++)
                {
                    var   plane           = planes[k];
                    int   closestIndex    = 0;
                    float closestDistance = math.lengthsq(plane - brushMesh.planes[0]);
                    for (int j = 1; j < brushMesh.planes.Length; j++)
                    {
                        float testDistance = math.lengthsq(plane - brushMesh.planes[j]);
                        if (testDistance < closestDistance)
                        {
                            closestIndex    = j;
                            closestDistance = testDistance;
                        }
                    }
                    indices[k] = closestIndex;
                }

                for (int j = 0; j < indices.Length; j++)
                {
                    brushMesh.planes[indices[j]] = planes[j];
                }

                for (int j = solid.Sides.Count; j-- > 0;)
                {
                    VmfSolidSide side     = solid.Sides[j];
                    var          surface  = brushMesh.polygons[indices[j]].surface;
                    var          material = surface.brushMaterial.RenderMaterial;

                    // calculate the texture coordinates.
                    int w = 256;
                    int h = 256;
                    if (material.mainTexture != null)
                    {
                        w = material.mainTexture.width;
                        h = material.mainTexture.height;
                    }
                    var clip = new Plane(planes[j].xyz, planes[j].w);
                    CalculateTextureCoordinates(go, surface, clip, w, h, side.UAxis, side.VAxis);
                }

                // build displacements.
                foreach (DisplacementSide displacement in DisplacementSurfaces)
                {
                    // find the brush mesh polygon:
                    for (int polyidx = 0; polyidx < brushMesh.polygons.Length; polyidx++)
                    {
                        if (brushMesh.polygons[polyidx].surface == displacement.surface)
                        {
                            // find the polygon plane.
                            Plane plane = new Plane(brushMesh.planes[polyidx].xyz, brushMesh.planes[polyidx].w);

                            // find all vertices that belong to this polygon:
                            List <Vector3> vertices = new List <Vector3>();
                            {
                                var polygon   = brushMesh.polygons[polyidx];
                                var firstEdge = polygon.firstEdge;
                                var edgeCount = polygon.edgeCount;
                                var lastEdge  = firstEdge + edgeCount;
                                for (int e = firstEdge; e < lastEdge; e++)
                                {
                                    vertices.Add(brushMesh.vertices[brushMesh.halfEdges[e].vertexIndex]);
                                }
                            }
                            // reverse the winding order.
                            vertices.Reverse();

                            var first = vertices[0];
                            vertices.RemoveAt(0);
                            vertices.Add(first);

                            // build displacement:
                            BuildDisplacementSurface(go, displacement.side, displacement.surface, vertices, plane);
                        }
                    }
                }

                // finalize the brush by snapping planes and centering the pivot point.
                go.transform.position += brushMesh.CenterAndSnapPlanes();
                foreach (Transform child in go.transform)
                {
                    child.position -= go.transform.position;
                }
            }

            // iterate through all entities.
            for (int e = 0; e < world.Entities.Count; e++)
            {
#if UNITY_EDITOR
                UnityEditor.EditorUtility.DisplayProgressBar("Chisel: Importing Source Engine Map (2/3)", "Converting Hammer Entities To Chisel Brushes (" + (e + 1) + " / " + world.Entities.Count + ")...", e / (float)world.Entities.Count);
#endif
                VmfEntity entity = world.Entities[e];

                // skip entities that chisel 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;
                    }

                    // HACK: Fix me in the future!
                    // HACK: Chisel doesn't support collision brushes yet- skip them completely!
                    if (solid.Sides.Count > 0 && IsInvisibleMaterial(solid.Sides[0].Material))
                    {
                        continue;
                    }
                    // HACK: Fix me in the future!

                    // build a very large cube brush.
                    ChiselBrush go = ChiselComponentFactory.Create <ChiselBrush>(model);
                    go.definition.surfaceDefinition = new ChiselSurfaceDefinition();
                    go.definition.surfaceDefinition.EnsureSize(6);
                    BrushMesh brushMesh = new BrushMesh();
                    go.definition.brushOutline = brushMesh;
                    BrushMeshFactory.CreateBox(ref brushMesh, new Vector3(-4096, -4096, -4096), new Vector3(4096, 4096, 4096), in go.definition.surfaceDefinition);

                    // prepare for uv calculations of clip planes after cutting.
                    var planes        = new float4[solid.Sides.Count];
                    var planeSurfaces = new ChiselSurface[solid.Sides.Count];

                    // clip all the sides out of the brush.
                    for (int j = solid.Sides.Count; j-- > 0;)
                    {
                        VmfSolidSide side = solid.Sides[j];

                        // 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 && materialSearcherWarnings.Add(materialName))
                            {
                                Debug.Log("Chisel: 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 && materialSearcherWarnings.Add(materialName))
                            {
                                Debug.Log("Chisel: Tried to find material '" + materialName + "' but it couldn't be found in the project.");
                            }
                        }

                        // fallback to default material.
                        if (material == null)
                        {
                            material = ChiselMaterialManager.DefaultFloorMaterial;
                        }

                        // create chisel surface for the clip.
                        ChiselSurface surface = new ChiselSurface();
                        surface.brushMaterial      = ChiselBrushMaterial.CreateInstance(material, ChiselMaterialManager.DefaultPhysicsMaterial);
                        surface.surfaceDescription = SurfaceDescription.Default;

                        // detect collision-only polygons.
                        if (IsInvisibleMaterial(side.Material))
                        {
                            surface.brushMaterial.LayerUsage &= ~LayerUsageFlags.RenderReceiveCastShadows;
                        }
                        // detect excluded polygons.
                        if (IsExcludedMaterial(side.Material))
                        {
                            surface.brushMaterial.LayerUsage &= LayerUsageFlags.CastShadows;
                            surface.brushMaterial.LayerUsage |= LayerUsageFlags.Collidable;
                        }

                        // calculate the clipping planes.
                        Plane clip = new Plane(go.transform.InverseTransformPoint(new Vector3(side.Plane.P1.X, side.Plane.P1.Z, side.Plane.P1.Y) * inchesInMeters), go.transform.InverseTransformPoint(new Vector3(side.Plane.P2.X, side.Plane.P2.Z, side.Plane.P2.Y) * inchesInMeters), go.transform.InverseTransformPoint(new Vector3(side.Plane.P3.X, side.Plane.P3.Z, side.Plane.P3.Y) * inchesInMeters));
                        planes[j]        = new float4(clip.normal, clip.distance);
                        planeSurfaces[j] = surface;
                    }

                    // cut all the clipping planes out of the brush in one go.
                    brushMesh.Cut(planes, planeSurfaces);

                    // now iterate over the planes to calculate UV coordinates.
                    int[] indices = new int[solid.Sides.Count];
                    for (int k = 0; k < planes.Length; k++)
                    {
                        var   plane           = planes[k];
                        int   closestIndex    = 0;
                        float closestDistance = math.lengthsq(plane - brushMesh.planes[0]);
                        for (int j = 1; j < brushMesh.planes.Length; j++)
                        {
                            float testDistance = math.lengthsq(plane - brushMesh.planes[j]);
                            if (testDistance < closestDistance)
                            {
                                closestIndex    = j;
                                closestDistance = testDistance;
                            }
                        }
                        indices[k] = closestIndex;
                    }

                    for (int j = 0; j < indices.Length; j++)
                    {
                        brushMesh.planes[indices[j]] = planes[j];
                    }

                    for (int j = solid.Sides.Count; j-- > 0;)
                    {
                        VmfSolidSide side     = solid.Sides[j];
                        var          surface  = brushMesh.polygons[indices[j]].surface;
                        var          material = surface.brushMaterial.RenderMaterial;

                        // calculate the texture coordinates.
                        int w = 256;
                        int h = 256;
                        if (material.mainTexture != null)
                        {
                            w = material.mainTexture.width;
                            h = material.mainTexture.height;
                        }
                        var clip = new Plane(planes[j].xyz, planes[j].w);
                        CalculateTextureCoordinates(go, surface, clip, w, h, side.UAxis, side.VAxis);
                    }

                    // finalize the brush by snapping planes and centering the pivot point.
                    go.transform.position += brushMesh.CenterAndSnapPlanes();

                    // 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;
                }
            }
        }
コード例 #6
0
        /// <summary>
        /// Imports the entities and attaches them to the specified parent.
        /// </summary>
        /// <param name="parent">The parent to attach entities to.</param>
        /// <param name="world">The world to be imported.</param>
        public static void Import(Transform parent, VmfWorld world)
        {
#if COM_AETERNUMGAMES_CHISEL_DECALS // optional decals package: https://github.com/Henry00IS/Chisel.Decals
            // create a material searcher to associate materials automatically.
            MaterialSearcher materialSearcher         = new MaterialSearcher();
            HashSet <string> materialSearcherWarnings = new HashSet <string>();
#endif
            // iterate through all entities.
            for (int e = 0; e < world.Entities.Count; e++)
            {
#if UNITY_EDITOR
                UnityEditor.EditorUtility.DisplayProgressBar("Chisel: Importing Source Engine Map (3/3)", "Converting Hammer Entities To Unity Objects (" + (e + 1) + " / " + world.Entities.Count + ")...", e / (float)world.Entities.Count);
#endif
                VmfEntity entity = world.Entities[e];

                switch (entity.ClassName)
                {
                // https://developer.valvesoftware.com/wiki/Light
                // light is a point entity available in all Source games. it creates an invisible, static light source that shines in all directions.
                case "light":
                {
                    // create a new light object:
                    GameObject go = new GameObject("Light");
                    go.transform.parent = GetLightingGroupOrCreate(parent);

                    // set the object position:
                    if (TryGetEntityOrigin(entity, out Vector3 origin))
                    {
                        go.transform.position = origin;
                    }

                    // add a light component:
                    Light light = go.AddComponent <Light>();
                    light.type = LightType.Point;
#if UNITY_EDITOR
                    light.lightmapBakeType = LightmapBakeType.Baked;
#endif
                    light.range = 25.0f;

                    // set the light color:
                    if (entity.TryGetProperty("_light", out VmfVector4 color))
                    {
                        light.intensity = color.W * lightBrightnessScalar;
                        light.color     = new Color(color.X / 255.0f, color.Y / 255.0f, color.Z / 255.0f);
                    }

                    break;
                }

                // https://developer.valvesoftware.com/wiki/Light_spot
                // light_spot is a point entity available in all Source games. it is a cone-shaped, invisible light source.
                case "light_spot":
                {
                    // create a new light object:
                    GameObject go = new GameObject("Spot Light");
                    go.transform.parent = GetLightingGroupOrCreate(parent);

                    // set the object position:
                    if (TryGetEntityOrigin(entity, out Vector3 origin))
                    {
                        go.transform.position = origin;
                    }

                    // set the object rotation:
                    if (TryGetEntityRotation(entity, out Quaternion rotation))
                    {
                        go.transform.rotation = rotation;
                    }

                    // add a light component:
                    Light light = go.AddComponent <Light>();
                    light.type = LightType.Spot;
#if UNITY_EDITOR
                    light.lightmapBakeType = LightmapBakeType.Mixed;
#endif
                    light.range = 10.0f;

                    // set the light color:
                    if (entity.TryGetProperty("_light", out VmfVector4 color))
                    {
                        light.intensity = color.W * lightBrightnessScalar;
                        light.color     = new Color(color.X / 255.0f, color.Y / 255.0f, color.Z / 255.0f);
                    }

                    // approximate the light cookie cone shape and the spot angle.
                    if (entity.TryGetProperty("_inner_cone", out int inner_cone) && entity.TryGetProperty("_cone", out int cone))
                    {
                        float lightInnerCone = Mathf.Min(inner_cone * 2, 175);
                        float lightCone      = Mathf.Min(cone * 2, 175);

                        if (lightInnerCone > lightCone)
                        {
                            float t = lightCone;
                            lightInnerCone = lightCone;
                            lightCone      = t;
                        }

                        // set the spot angle:
                        light.spotAngle = lightCone;

                        // generate and set the light cookie:
                        float coneFactor = Mathf.Max(0, lightInnerCone / lightCone);
                        light.cookie = BuildLightCookieTexture(coneFactor);
                    }
                    // backup approach for the spot angle.
                    else if (entity.TryGetProperty("_cone", out int cone2))
                    {
                        // set the spot angle:
                        float lightCone = Mathf.Min(cone2 * 2, 175);
                        light.spotAngle = lightCone;
                    }

                    break;
                }

#if COM_AETERNUMGAMES_CHISEL_DECALS // optional decals package: https://github.com/Henry00IS/Chisel.Decals
                case "infodecal":
                {
                    // create a new decal object:
                    GameObject go = new GameObject("Decal");
                    go.transform.parent = GetDecalsGroupOrCreate(parent);

                    // set the object position:
                    if (TryGetEntityOrigin(entity, out Vector3 origin))
                    {
                        go.transform.position = origin;
                    }

                    // add the decal component:
                    ChiselDecal decal = go.AddComponent <ChiselDecal>();

                    // assign the material:
                    if (entity.TryGetProperty("texture", out string texture))
                    {
                        Material material = FindMaterial(materialSearcher, materialSearcherWarnings, texture);
                        if (material != null)
                        {
                            go.GetComponent <MeshRenderer>().sharedMaterial = material;
                            var mainTexture = material.mainTexture;
                            if (mainTexture != null)
                            {
                                // use the texture size to determine the size of the decal.
                                go.transform.localScale = new Vector3(mainTexture.width * 0.008f, mainTexture.height * 0.008f, 0.1f);
                            }
                        }
                    }

                    // it should be snug against a surface- so we try to find it.
                    RaycastHit raycastHit = default;
                    bool       hit        = false;

                    Vector3 r = Vector3.right * 0.1f;
                    Vector3 f = Vector3.forward * 0.1f;
                    Vector3 u = Vector3.up * 0.1f;

                    // try a ray cast in all world axis to find a hit.
                    if (hit = Physics.Raycast(go.transform.position - r, r, out RaycastHit hitInfo1, 0.2f))
                    {
                        raycastHit = hitInfo1;
                    }
                    if (!hit && (hit = Physics.Raycast(go.transform.position + r, -r, out RaycastHit hitInfo2, 0.2f)))
                    {
                        raycastHit = hitInfo2;
                    }
                    if (!hit && (hit = Physics.Raycast(go.transform.position - f, f, out RaycastHit hitInfo3, 0.2f)))
                    {
                        raycastHit = hitInfo3;
                    }
                    if (!hit && (hit = Physics.Raycast(go.transform.position + f, -f, out RaycastHit hitInfo4, 0.2f)))
                    {
                        raycastHit = hitInfo4;
                    }
                    if (!hit && (hit = Physics.Raycast(go.transform.position - u, u, out RaycastHit hitInfo5, 0.2f)))
                    {
                        raycastHit = hitInfo5;
                    }
                    if (!hit && (hit = Physics.Raycast(go.transform.position + u, -u, out RaycastHit hitInfo6, 0.2f)))
                    {
                        raycastHit = hitInfo6;
                    }

                    // shouldn't not hit unless the level designer actually messed up.
                    if (hit)
                    {
                        // now we have the normal of the surface to "face align" the decal.
                        go.transform.rotation = Quaternion.LookRotation(-raycastHit.normal);
                    }

                    break;
                }
#endif
                }
            }
        }
コード例 #7
0
        /// <summary>
        /// Imports the specified world into the RealtimeCSG.
        /// </summary>
        /// <param name="rootTransform">Transform to be parent of RealtimeCSG brushes</param>
        /// <param name="world">The world to be imported.</param>
        public static void Import(Transform rootTransform, 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);

                var mapTransform = CreateGameObjectWithUniqueName(world.mapName, rootTransform);
                mapTransform.position = Vector3.zero;

                // Index of entities by trenchbroom id
                var entitiesById = new Dictionary <int, EntityContainer>();

                var layers = new List <EntityContainer>();

                for (int e = 0; e < world.Entities.Count; e++)
                {
                    var entity = world.Entities[e];

                    //EntityContainer eContainer = null;

                    if (entity.tbId >= 0)
                    {
                        var name       = String.IsNullOrEmpty(entity.tbName) ? "Unnamed" : entity.tbName;
                        var t          = CreateGameObjectWithUniqueName(name, mapTransform);
                        var eContainer = new EntityContainer(t, entity);
                        entitiesById.Add(entity.tbId, eContainer);

                        if (entity.tbType == "_tb_layer")
                        {
                            layers.Add(eContainer);
                            eContainer.transform.SetParent(null); // unparent until layers are sorted by sort index
                        }
                    }
                }

                var defaultLayer = CreateGameObjectWithUniqueName("Default Layer", mapTransform);

                //var worldSpawnModel = OperationsUtility.CreateModelInstanceInScene(defaultLayer);
                //worldSpawnModel.name = "WorldSpawn";
                //worldSpawnModel.transform.SetParent(mapTransform);

                layers = layers.OrderBy(l => l.entity.tbLayerSortIndex).ToList(); // sort layers by layer sort index

                foreach (var l in layers)
                {
                    l.transform.SetParent(mapTransform); // parent layers to map in order
                }

                bool valveFormat = world.valveFormat;

                // iterate through all entities.
                for (int e = 0; e < world.Entities.Count; e++)
                {
#if UNITY_EDITOR
                    UnityEditor.EditorUtility.DisplayProgressBar("Importing Quake 1 Map", "Converting Quake 1 Entities To Brushes (" + (e + 1) + " / " + world.Entities.Count + ")...", e / (float)world.Entities.Count);
#endif
                    MapEntity entity = world.Entities[e];

                    Transform brushParent = mapTransform;

                    bool isLayer   = false;
                    bool isTrigger = false;

                    if (entity.ClassName == "worldspawn")
                    {
                        brushParent = defaultLayer;
                    }
                    else if (entity.tbType == "_tb_layer")
                    {
                        isLayer = true;
                        if (entitiesById.TryGetValue(entity.tbId, out EntityContainer eContainer))
                        {
                            brushParent = eContainer.transform;
                        }
                    }
                    else if (entity.tbType == "_tb_group")
                    {
                        if (entitiesById.TryGetValue(entity.tbId, out EntityContainer eContainer))
                        {
                            brushParent = eContainer.transform;
                        }
                    }
                    else
                    {
                        if (entity.ClassName.Contains("trigger"))
                        {
                            isTrigger = true;
                        }

                        brushParent = CreateGameObjectWithUniqueName(entity.ClassName, mapTransform);
                    }

                    if (brushParent != mapTransform && brushParent != defaultLayer)
                    {
                        if (entity.tbGroup > 0)
                        {
                            if (entitiesById.TryGetValue(entity.tbGroup, out EntityContainer eContainer))
                            {
                                brushParent.SetParent(eContainer.transform);
                            }
                        }
                        else if (entity.tbLayer > 0)
                        {
                            if (entitiesById.TryGetValue(entity.tbLayer, out EntityContainer eContainer))
                            {
                                brushParent.SetParent(eContainer.transform);
                            }
                        }
                        else if (!isLayer)
                        {
                            brushParent.SetParent(defaultLayer);
                        }
                    }

                    if (entity.Brushes.Count == 0)
                    {
                        continue;
                    }


                    var model  = OperationsUtility.CreateModelInstanceInScene(brushParent);
                    var parent = model.transform;

                    if (isTrigger)
                    {
                        model.Settings = (model.Settings | ModelSettingsFlags.IsTrigger | ModelSettingsFlags.SetColliderConvex | ModelSettingsFlags.DoNotRender);
                    }

                    //GroupBrush entityGroup = new GameObject(entity.ClassName).AddComponent<GroupBrush>();
                    //entityGroup.transform.SetParent(groupBrush.transform);


                    // 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("RealtimeCSG: Importing Quake 1 Map", "Converting Quake 1 Brushes To RealtimeCSG Brushes (" + (i + 1) + " / " + entity.Brushes.Count + ")...", i / (float)entity.Brushes.Count);
                        }
#endif
                        // don't add triggers to the scene.
                        // Triggers will get placed in entity model now
                        //if (brush.Sides.Count > 0 && IsSpecialMaterial(brush.Sides[0].Material))
                        //    continue;


                        var name       = UnityEditor.GameObjectUtility.GetUniqueNameForSibling(parent, "Brush");
                        var gameObject = new GameObject(name);
                        var rcsgBrush  = gameObject.AddComponent <CSGBrush>();
                        var t          = gameObject.transform;

                        gameObject.transform.SetParent(parent, true);
                        gameObject.transform.position = new Vector3(0.5f, 0.5f, 0.5f); // this aligns it's vertices to the grid
                                                                                       //                                                               //BrushFactory.CreateCubeControlMesh(out brush.ControlMesh, out brush.Shape, Vector3.one);

                        var planes          = new Plane[brush.Sides.Count];
                        var textureMatrices = new Matrix4x4[brush.Sides.Count];
                        var materials       = new Material[brush.Sides.Count];


                        Debug.Log($"Brush sides {brush.Sides.Count}");

                        // Get planes for all sides of the brush
                        for (int j = 0; j < brush.Sides.Count; j++)
                        {
                            MapBrushSide side = brush.Sides[j];

                            var pa = t.transform.InverseTransformPoint(new Vector3(side.Plane.P1.X, side.Plane.P1.Z, side.Plane.P1.Y) / (float)s_Scale);
                            var pb = t.transform.InverseTransformPoint(new Vector3(side.Plane.P2.X, side.Plane.P2.Z, side.Plane.P2.Y) / (float)s_Scale);
                            var pc = t.transform.InverseTransformPoint(new Vector3(side.Plane.P3.X, side.Plane.P3.Z, side.Plane.P3.Y) / (float)s_Scale);

                            planes[j] = new Plane(pa, pb, pc);

                            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("*", "#");
                            materials[j] = materialSearcher.FindMaterial(new string[] { materialName });
                            if (materials[j] == null)
                            {
                                materials[j] = CSGSettings.DefaultMaterial;
                                // Debug.Log("RealtimeCSG: Tried to find material '" + materialName + "' but it couldn't be found in the project.");
                            }

                            if (valveFormat)
                            {
                                // calculate the texture coordinates.
                                int w = 256;
                                int h = 256;
                                if (materials[j].mainTexture != null)
                                {
                                    w = materials[j].mainTexture.width;
                                    h = materials[j].mainTexture.height;
                                }

                                var uAxis = new VmfAxis(side.t1, side.Offset.X, side.Scale.X);
                                var vAxis = new VmfAxis(side.t2, side.Offset.Y, side.Scale.Y);
                                textureMatrices[j] = CalculateTextureCoordinates(planes[j], w, h, uAxis, vAxis);
                            }
                        }

                        bool controlMeshSuccess = true;

                        if (valveFormat)
                        {
                            controlMeshSuccess = BrushFactory.CreateControlMeshFromPlanes(out rcsgBrush.ControlMesh, out rcsgBrush.Shape, planes, null, null, materials, textureMatrices, TextureMatrixSpace.WorldSpace);
                        }
                        else
                        {
                            controlMeshSuccess = BrushFactory.CreateControlMeshFromPlanes(out rcsgBrush.ControlMesh, out rcsgBrush.Shape, planes, null, null, materials);
                        }

                        if (controlMeshSuccess)
                        {
                            for (int j = 0; j < brush.Sides.Count; j++)
                            {
                                MapBrushSide side = brush.Sides[j];

                                // calculate the texture coordinates.
                                int w = 32;
                                int h = 32;
                                if (materials[j].mainTexture != null)
                                {
                                    w = materials[j].mainTexture.width;
                                    h = materials[j].mainTexture.height;
                                }

                                var tScale = new Vector2(
                                    SafeDivision((32.0f / w), side.Scale.X),
                                    SafeDivision((32.0f / h), side.Scale.Y));

                                if (valveFormat)
                                {
                                    // This shouldn't be needed due to setting texture matrix
                                    rcsgBrush.Shape.TexGens[j].Scale = tScale;

                                    rcsgBrush.Shape.TexGens[j].Translation.x = SafeDivision(side.Offset.X, w);
                                    rcsgBrush.Shape.TexGens[j].Translation.y = SafeDivision(-side.Offset.Y, h);

                                    //rcsgBrush.Shape.TexGens[j].RotationAngle += 180 + side.Rotation; // Textures often need to be flipped or rotated 180 to match
                                }
                                else
                                {
                                    rcsgBrush.Shape.TexGens[j].Scale = tScale;

                                    if (side.Offset.X != 0)
                                    {
                                        rcsgBrush.Shape.TexGens[j].Translation.x = side.Offset.X / Mathf.Max(w, float.Epsilon);
                                    }
                                    else
                                    {
                                        rcsgBrush.Shape.TexGens[j].Translation.x = 0;
                                    }

                                    if (side.Offset.Y != 0)
                                    {
                                        rcsgBrush.Shape.TexGens[j].Translation.y = side.Offset.Y / Mathf.Max(h, float.Epsilon);
                                    }
                                    else
                                    {
                                        rcsgBrush.Shape.TexGens[j].Translation.y = 0;
                                    }

                                    rcsgBrush.Shape.TexGens[j].RotationAngle = 180 + side.Rotation;
                                }
                            }
                        }
                        else
                        {
                            GameObject.DestroyImmediate(rcsgBrush.gameObject);
                        }
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                InternalCSGModelManager.CheckForChanges();
                InternalCSGModelManager.UpdateMeshes();
                //model.EndUpdate();
            }

#if UNITY_EDITOR
            UnityEditor.EditorUtility.ClearProgressBar();
#endif
        }