Example #1
0
        public GSubDivisionTree Clone(int maxLevel)
        {
            GSubDivisionTree tree = new GSubDivisionTree();

            tree.Root = Root.Clone();

            st.Clear();
            st.Push(tree.Root.RightNode);
            st.Push(tree.Root.LeftNode);
            Node n;

            while (st.Count > 0)
            {
                n = st.Pop();
                if (n.Level == maxLevel)
                {
                    n.LeftNode  = null;
                    n.RightNode = null;
                }
                else
                {
                    if (n.LeftNode != null)
                    {
                        st.Push(n.LeftNode);
                    }
                    if (n.RightNode != null)
                    {
                        st.Push(n.RightNode);
                    }
                }
            }
            return(tree);
        }
Example #2
0
        public static GSubDivisionTree Rect(Rect r)
        {
            GSubDivisionTree tree = new GSubDivisionTree();

            tree.Root = new Node();

            Vector2 bottomLeft  = new Vector2(r.min.x, r.min.y);
            Vector2 topLeft     = new Vector2(r.min.x, r.max.y);
            Vector2 topRight    = new Vector2(r.max.x, r.max.y);
            Vector2 bottomRight = new Vector2(r.max.x, r.min.y);

            Node left = new Node(
                topLeft,
                topRight,
                bottomLeft);
            Node right = new Node(
                bottomRight,
                bottomLeft,
                topRight);

            tree.Root.LeftNode  = left;
            tree.Root.RightNode = right;

            return(tree);
        }
Example #3
0
        private void StitchSeamInBackground()
        {
#if UNITY_EDITOR
            while (canRunStitchSeamInBackground)
            {
                Thread.Sleep(stitchSeamThreadSleepTime + new System.Random().Next(1000));
                if (subDivTree == null)
                {
                    continue;
                }
                try
                {
                    HashSet <Vector2> vertexPool = new HashSet <Vector2>();
                    subDivTree.ForEachLeaf(n =>
                    {
                        vertexPool.Add(n.V0);
                        vertexPool.Add(n.V1);
                        vertexPool.Add(n.V2);
                    });

                    for (int i = 0; i < Internal_NeighborChunks.Length; ++i)
                    {
                        if (Internal_NeighborChunks[i] == null)
                        {
                            continue;
                        }
                        GSubDivisionTree tree = Internal_NeighborChunks[i].subDivTree;
                        if (tree == null)
                        {
                            continue;
                        }
                        tree.ForEachLeaf(n =>
                        {
                            vertexPool.Add(n.V0);
                            vertexPool.Add(n.V1);
                            vertexPool.Add(n.V2);
                        });
                    }

                    bool isNewVertCreated = Internal_StitchGeometrySeamsOnSubDivTree(vertexPool);
                    if (isNewVertCreated)
                    {
                        EnqueueMainThreadJob(() =>
                        {
                            Internal_UpdateMeshLOD0();
                            Internal_UpdateLODsAsync(vertexPool);
                        });
                    }
                }
                catch (System.Exception)
                {
                    //Debug.Log(e.ToString());
                }
            }
#endif
        }
Example #4
0
        private void StitchSeamLOD(GSubDivisionTree tree, HashSet <Vector2> seamVertices)
        {
            Stack <GSubDivisionTree.Node> dirtyNode = new Stack <GSubDivisionTree.Node>();

            tree.ForEachLeaf(n =>
            {
                dirtyNode.Push(n);
            });

            HashSet <Vector2> newVertices = new HashSet <Vector2>();

            while (dirtyNode.Count > 0)
            {
                GSubDivisionTree.Node n = dirtyNode.Pop();
                if (!seamVertices.Contains(n.V01) && !seamVertices.Contains(n.V12) && !seamVertices.Contains(n.V20))
                {
                    continue;
                }

                n.Split();
                newVertices.Add(n.V12);
                dirtyNode.Push(n.LeftNode);
                dirtyNode.Push(n.RightNode);
            }

            dirtyNode.Clear();
            tree.ForEachLeaf(n =>
            {
                dirtyNode.Push(n);
            });
            while (dirtyNode.Count > 0)
            {
                GSubDivisionTree.Node n = dirtyNode.Pop();
                if (!newVertices.Contains(n.V01) && !newVertices.Contains(n.V12) && !newVertices.Contains(n.V20))
                {
                    continue;
                }

                n.Split();
                dirtyNode.Push(n.LeftNode);
                dirtyNode.Push(n.RightNode);
            }
        }
Example #5
0
        private void UpdateMesh(Mesh m, GSubDivisionTree tree)
        {
            CreatePolygons(tree);
            m.Clear();
            m.SetVertices(GeneratedVertices);
            m.SetTriangles(GeneratedTriangles, 0);
            m.SetUVs(0, GeneratedUv);
            m.SetColors(GeneratedVertexColors);
            m.RecalculateBounds();
            m.RecalculateNormals();
            RecalculateTangentIfNeeded(m);

            GeneratedVertices.Clear();
            GeneratedTriangles.Clear();
            GeneratedVertexColors.Clear();
            GeneratedUv.Clear();

            SetLastUpdatedTimeNow();
        }
Example #6
0
        internal void Internal_UpdateLODsAsync(HashSet <Vector2> vertexPool)
        {
            LOD lod0 = new LOD(0, new Renderer[] { MeshRendererComponent });

            LodGroupComponent.SetLODs(new LOD[] { lod0 });
            if (Terrain.TerrainData.Geometry.LODCount == 1)
            {
                GUtilities.ClearChildren(transform);
            }

            for (int i = 1; i < Terrain.TerrainData.Geometry.LODCount; ++i)
            {
                GTerrainChunkLOD chunkLod = GetChunkLOD(i);
                chunkLod.MeshRendererComponent.enabled = false;
            }

            int  meshResolution            = 0;
            Rect uvRange                   = GetUvRange();
            HashSet <Vector2> seamVertices = new HashSet <Vector2>();

            subDivTree.ForEachLeaf(n =>
            {
                if (!GUtilities.IsRectContainPointExclusive(uvRange, n.V0))
                {
                    seamVertices.Add(n.V0);
                }
                if (!GUtilities.IsRectContainPointExclusive(uvRange, n.V1))
                {
                    seamVertices.Add(n.V1);
                }
                if (!GUtilities.IsRectContainPointExclusive(uvRange, n.V2))
                {
                    seamVertices.Add(n.V2);
                }
                meshResolution = Mathf.Max(meshResolution, n.Level);
            });

            ThreadPool.QueueUserWorkItem(state =>
            {
                try
                {
                    GSubDivisionTree[] lodTrees = new GSubDivisionTree[Terrain.TerrainData.Geometry.LODCount];
                    for (int level = 1; level < Terrain.TerrainData.Geometry.LODCount; ++level)
                    {
                        int i = level;
                        int baseResolution = Terrain.TerrainData.Geometry.PolygonDistribution == GPolygonDistributionMode.Dynamic ?
                                             Terrain.TerrainData.Geometry.MeshBaseResolution :
                                             0;
                        int targetResolution = Mathf.Max(baseResolution, meshResolution - i);
                        lodTrees[i]          = subDivTree.Clone(targetResolution);
                        StitchSeamLOD(lodTrees[i], seamVertices);
                        //StitchGeometrySeamsOnSubDivTree(vertexPool, lodTrees[i]);
                        EnqueueMainThreadJob(() =>
                        {
                            Mesh meshLod = GetMesh(i);
                            UpdateMesh(meshLod, lodTrees[i]);
                            GTerrainChunkLOD chunkLod = GetChunkLOD(i);
                            chunkLod.MeshFilterComponent.sharedMesh = meshLod;
                        });
                    }

                    EnqueueMainThreadJob(() =>
                    {
                        float transitionStep = 1.0f / Terrain.TerrainData.Geometry.LODCount;

                        LOD[] lods = new LOD[Terrain.TerrainData.Geometry.LODCount];
                        lods[0]    = new LOD(
                            GGriffinSettings.Instance.LodTransition.Evaluate(transitionStep),
                            new Renderer[] { MeshRendererComponent });

                        for (int level = 1; level < Terrain.TerrainData.Geometry.LODCount; ++level)
                        {
                            int i   = level;
                            lods[i] = new LOD(
                                GGriffinSettings.Instance.LodTransition.Evaluate((i + 1) * transitionStep),
                                new Renderer[] { GetChunkLOD(i).MeshRendererComponent });

                            GTerrainChunkLOD chunkLod = GetChunkLOD(i);
                            chunkLod.MeshRendererComponent.enabled = true;
                        }

                        LodGroupComponent.SetLODs(lods);
                        Internal_UpdateRenderer();
                    });
                }
                catch (System.Exception e)
                {
                    Debug.Log(e.ToString());
                }
            });
        }
Example #7
0
        private bool StitchGeometrySeamsOnSubDivTree(HashSet <Vector2> vertexPool, GSubDivisionTree tree)
        {
#if UNITY_EDITOR
            //canRunStitchSeamInBackground = true;
#endif

            bool isNewVertexCreated             = false;
            Stack <GSubDivisionTree.Node> nodes = new Stack <GSubDivisionTree.Node>();

            int baseResolution = Terrain.TerrainData.Geometry.MeshBaseResolution;
            int resolution     = Terrain.TerrainData.Geometry.MeshResolution;
            int maxLevel       = baseResolution + Mathf.Min(Mathf.FloorToInt(1f / GCommon.SUB_DIV_STEP), resolution - baseResolution);
            //Stitching triangle between tiles
            tree.ForEachLeaf(n =>
            {
                if (n.Level >= maxLevel)
                {
                    return;
                }
                nodes.Push(n);
            });

            while (nodes.Count > 0)
            {
                GSubDivisionTree.Node n = nodes.Pop();

                bool validV01 = vertexPool.Contains(n.V01); // && GUtilities.IsRectContainPointExclusive(uvRange, n.V01);
                bool validV12 = vertexPool.Contains(n.V12); // && GUtilities.IsRectContainPointExclusive(uvRange, n.V12);
                bool validV20 = vertexPool.Contains(n.V20); // && GUtilities.IsRectContainPointExclusive(uvRange, n.V20);

                if (!validV01 && !validV12 && !validV20)
                {
                    continue;
                }
                n.Split();
                if (!vertexPool.Contains(n.V12))
                {
                    vertexPool.Add(n.V12);
                    isNewVertexCreated = true;
                }
                nodes.Push(n.LeftNode);
                nodes.Push(n.RightNode);
            }

            //Sub division for triangle connectivity
            HashSet <Vector2> verts = new HashSet <Vector2>();
            nodes.Clear();
            tree.ForEachLeaf(n =>
            {
                verts.Add(n.V0);
                verts.Add(n.V1);
                verts.Add(n.V2);
                if (n.Level < maxLevel)
                {
                    nodes.Push(n);
                }
            });

            while (nodes.Count > 0)
            {
                GSubDivisionTree.Node n = nodes.Pop();
                if (!verts.Contains(n.V01) && !verts.Contains(n.V12) && !verts.Contains(n.V20))
                {
                    continue;
                }
                n.Split();
                if (!verts.Contains(n.V12))
                {
                    verts.Add(n.V12);
                    isNewVertexCreated = true;
                }
                nodes.Push(n.LeftNode);
                nodes.Push(n.RightNode);
            }

            return(isNewVertexCreated);
        }
Example #8
0
        private void CreatePolygons(GSubDivisionTree tree)
        {
            int       dispSeed = Terrain.TerrainData.Geometry.DisplacementSeed;
            float     dispStrength = Terrain.TerrainData.Geometry.DisplacementStrength;
            Texture2D subdivMap = Terrain.TerrainData.Geometry.Internal_SubDivisionMap;
            Vector2   uv0, uv1, uv2;
            Vector3   v0, v1, v2;
            float     r;
            Rand      rand;
            double    radius;

            int    baseResolution = Terrain.TerrainData.Geometry.MeshBaseResolution;
            int    resolution     = Terrain.TerrainData.Geometry.MeshResolution;
            int    level;
            double triangleBaseLength = 1f / Terrain.TerrainData.Geometry.ChunkGridSize;

            IGPolygonProcessor pp      = Terrain.TerrainData.Geometry.PolygonProcessor;
            GPolygon           polygon = new GPolygon();

            GeneratedUv.Clear();
            GeneratedVertices.Clear();
            GeneratedVertexColors.Clear();
            GeneratedTriangles.Clear();

            Vector3 terrainSize = new Vector3(
                Terrain.TerrainData.Geometry.Width,
                Terrain.TerrainData.Geometry.Height,
                Terrain.TerrainData.Geometry.Length);

            v0 = Vector3.zero;
            v1 = Vector3.zero;
            v2 = Vector3.zero;

            tree.ForEachLeaf((n) =>
            {
                uv0 = n.V0;
                uv1 = n.V1;
                uv2 = n.V2;
                if (dispStrength > 0)
                {
                    if (uv0.x != 0 && uv0.x != 1 && uv0.y != 0 && uv0.y != 1)
                    {
                        r      = subdivMap.GetPixelBilinear(uv0.x, uv0.y).r;
                        level  = baseResolution + Mathf.Min(Mathf.FloorToInt(r / GCommon.SUB_DIV_STEP), resolution - baseResolution);
                        rand   = new Rand(dispSeed + (int)(uv0.x * 1000 + uv0.y * 1000));
                        radius = 0.35 * dispStrength * triangleBaseLength * TriangleEdgeLength[level];
                        uv0.x  = Mathf.Clamp01(uv0.x + (float)((rand.NextDouble() - 0.5) * radius));
                        uv0.y  = Mathf.Clamp01(uv0.y + (float)((rand.NextDouble() - 0.5) * radius));
                    }

                    if (uv1.x != 0 && uv1.x != 1 && uv1.y != 0 && uv1.y != 1)
                    {
                        r      = subdivMap.GetPixelBilinear(uv1.x, uv1.y).r;
                        level  = baseResolution + Mathf.Min(Mathf.FloorToInt(r / GCommon.SUB_DIV_STEP), resolution - baseResolution);
                        rand   = new Rand(dispSeed + (int)(uv1.x * 1000 + uv1.y * 1000));
                        radius = 0.35 * dispStrength * triangleBaseLength * TriangleEdgeLength[level];
                        uv1.x  = Mathf.Clamp01(uv1.x + (float)((rand.NextDouble() - 0.5) * radius));
                        uv1.y  = Mathf.Clamp01(uv1.y + (float)((rand.NextDouble() - 0.5) * radius));
                    }

                    if (uv2.x != 0 && uv2.x != 1 && uv2.y != 0 && uv2.y != 1)
                    {
                        r      = subdivMap.GetPixelBilinear(uv2.x, uv2.y).r;
                        level  = baseResolution + Mathf.Min(Mathf.FloorToInt(r / GCommon.SUB_DIV_STEP), resolution - baseResolution);
                        rand   = new Rand(dispSeed + (int)(uv2.x * 1000 + uv2.y * 1000));
                        radius = 0.35 * dispStrength * triangleBaseLength * TriangleEdgeLength[level];
                        uv2.x  = Mathf.Clamp01(uv2.x + (float)((rand.NextDouble() - 0.5) * radius));
                        uv2.y  = Mathf.Clamp01(uv2.y + (float)((rand.NextDouble() - 0.5) * radius));
                    }
                }

                Vector4 h0 = Terrain.GetInterpolatedHeightMapSample(uv0);
                Vector4 h1 = Terrain.GetInterpolatedHeightMapSample(uv1);
                Vector4 h2 = Terrain.GetInterpolatedHeightMapSample(uv2);
                if (h0.w >= 0.5f || h1.w >= 0.5f || h2.w >= 0.5f) //alpha channel for visibility
                {
                    return;
                }

                if (pp != null)
                {
                    polygon.Clear();
                    v0.Set(uv0.x * terrainSize.x, h0.x * terrainSize.y, uv0.y * terrainSize.z);
                    v1.Set(uv1.x * terrainSize.x, h1.x * terrainSize.y, uv1.y * terrainSize.z);
                    v2.Set(uv2.x * terrainSize.x, h2.x * terrainSize.y, uv2.y * terrainSize.z);
                    polygon.Vertices.Add(v0);
                    polygon.Vertices.Add(v1);
                    polygon.Vertices.Add(v2);
                    polygon.Uvs.Add(uv0);
                    polygon.Uvs.Add(uv1);
                    polygon.Uvs.Add(uv2);
                    polygon.Triangles.Add(0);
                    polygon.Triangles.Add(1);
                    polygon.Triangles.Add(2);
                    pp.Process(this, ref polygon);

                    int currentTrisIndex = GeneratedVertices.Count;
                    for (int i = 0; i < polygon.Triangles.Count; ++i)
                    {
                        GeneratedTriangles.Add(currentTrisIndex + polygon.Triangles[i]);
                    }

                    for (int i = 0; i < polygon.Vertices.Count; ++i)
                    {
                        GeneratedVertices.Add(polygon.Vertices[i]);
                    }

                    for (int i = 0; i < polygon.Uvs.Count; ++i)
                    {
                        GeneratedUv.Add(polygon.Uvs[i]);
                    }

                    if (polygon.VertexColors != null && polygon.VertexColors.Count > 0)
                    {
                        for (int i = 0; i < polygon.VertexColors.Count; ++i)
                        {
                            GeneratedVertexColors.Add(polygon.VertexColors[i]);
                        }
                    }
                }
                else
                {
                    int currentTrisIndex = GeneratedVertices.Count;
                    GeneratedTriangles.Add(currentTrisIndex + 0);
                    GeneratedTriangles.Add(currentTrisIndex + 1);
                    GeneratedTriangles.Add(currentTrisIndex + 2);
                    v0.Set(uv0.x * terrainSize.x, h0.x * terrainSize.y, uv0.y * terrainSize.z);
                    v1.Set(uv1.x * terrainSize.x, h1.x * terrainSize.y, uv1.y * terrainSize.z);
                    v2.Set(uv2.x * terrainSize.x, h2.x * terrainSize.y, uv2.y * terrainSize.z);
                    GeneratedVertices.Add(v0);
                    GeneratedVertices.Add(v1);
                    GeneratedVertices.Add(v2);
                    GeneratedUv.Add(uv0);
                    GeneratedUv.Add(uv1);
                    GeneratedUv.Add(uv2);
                }
            });

            //Convert vertices terrain local space to chunk local space.
            //This way we can place the chunk pivot/origin point at the center of its region instead of the terrain pivot.
            //Chunk position is set by the terrain component.
            //This step is very important for other task such as level streaming and occlusion culling, etc.
            //Special thank to Aleš Stupka for the contribution.
            Matrix4x4 vertexTransformMatrix = Terrain.transform.localToWorldMatrix * transform.worldToLocalMatrix;

            for (int i = 0; i < GeneratedVertices.Count; ++i)
            {
                GeneratedVertices[i] = vertexTransformMatrix.MultiplyPoint(GeneratedVertices[i]);
            }
        }
Example #9
0
        internal void Internal_CreateSubDivTree()
        {
            Rect uvRange = GetUvRange();

            subDivTree = GSubDivisionTree.Rect(uvRange);

            int firstPassResolution = Terrain.TerrainData.Geometry.PolygonDistribution == GPolygonDistributionMode.Dynamic ?
                                      Terrain.TerrainData.Geometry.MeshBaseResolution :
                                      Terrain.TerrainData.Geometry.MeshResolution;

            //Subdiv for base resolution
            for (int i = 0; i < firstPassResolution; ++i)
            {
                subDivTree.ForEachLeaf(n =>
                {
                    n.Split();
                });
            }

            if (Terrain.TerrainData.Geometry.PolygonDistribution != GPolygonDistributionMode.Dynamic)
            {
                return;
            }

            Texture2D subdivMap = null;

            if (GCommon.IsMainThread)
            {
                subdivMap = Terrain.TerrainData.Geometry.Internal_SubDivisionMap;
            }
            GGeometryGenerationContext context = null;

            if (!GCommon.IsMainThread)
            {
                context = Terrain.GenerationContext;
            }

            Stack <GSubDivisionTree.Node> nodes0 = new Stack <GSubDivisionTree.Node>();
            Stack <GSubDivisionTree.Node> nodes1;

            subDivTree.ForEachLeaf(n =>
            {
                nodes0.Push(n);
            });

            //Additional sub division based on the sub div map
            int baseResolution = Terrain.TerrainData.Geometry.MeshBaseResolution;
            int resolution     = Terrain.TerrainData.Geometry.MeshResolution;
            int maxLevel       = baseResolution + Mathf.Min(Mathf.FloorToInt(1f / GCommon.SUB_DIV_STEP), resolution - baseResolution);

            float r0   = 0;
            float r1   = 0;
            float r2   = 0;
            float rc   = 0;
            float rMax = 0;
            float subDivLevel;

            for (int i = baseResolution; i <= resolution; ++i)
            {
                nodes1 = new Stack <GSubDivisionTree.Node>();
                while (nodes0.Count > 0)
                {
                    GSubDivisionTree.Node n = nodes0.Pop();
                    if (n.Level >= maxLevel)
                    {
                        continue;
                    }
                    if (GCommon.IsMainThread)
                    {
                        r0 = subdivMap.GetPixelBilinear(n.V0.x, n.V0.y).r;
                        r1 = subdivMap.GetPixelBilinear(n.V1.x, n.V1.y).r;
                        r2 = subdivMap.GetPixelBilinear(n.V2.x, n.V2.y).r;
                        rc = subdivMap.GetPixelBilinear(n.VC.x, n.VC.y).r;
                    }
                    else
                    {
                        r0 = context.GetSubdivData(n.V0).r;
                        r1 = context.GetSubdivData(n.V1).r;
                        r2 = context.GetSubdivData(n.V2).r;
                        rc = context.GetSubdivData(n.VC).r;
                    }
                    rMax = Mathf.Max(r0, r1, r2, rc);

                    subDivLevel = baseResolution + (int)(rMax / GCommon.SUB_DIV_STEP);
                    if (subDivLevel <= n.Level)
                    {
                        continue;
                    }
                    n.Split();
                    nodes1.Push(n.LeftNode);
                    nodes1.Push(n.RightNode);
                }

                nodes0 = nodes1;
            }
        }