Пример #1
0
        private void ShowNodeMerge(QuadNode <ChunkData> node, MeshData mesh, HashSet <QuadNode <ChunkData> > activeList)
        {
            if (!chunkMeshMap.ContainsKey(node))
            {
                //Buffer
                CachedMeshHolder container = PopMeshContainer();
                MeshFilter       filter    = container.filter;
                filter.sharedMesh = mesh.mesh;

                if (node.value.bounds == null)
                {
                    node.value.bounds        = new Sphere(Vector3.zero, 1);
                    node.value.bounds.center = filter.sharedMesh.bounds.center;
                    node.value.bounds.radius = Mathf.Sqrt(
                        filter.sharedMesh.bounds.extents.x * filter.sharedMesh.bounds.extents.x +
                        filter.sharedMesh.bounds.extents.y * filter.sharedMesh.bounds.extents.y +
                        filter.sharedMesh.bounds.extents.z * filter.sharedMesh.bounds.extents.z
                        );
                }

                //Show node
                filter.gameObject.SetActive(true);

                //Call highest detail action
                if (node.depth == this.maxDepth)
                {
                    //Call listeners if exists
                    foreach (System.Action <QuadNode <ChunkData> > fn in this.listeners)
                    {
                        fn.Invoke(node);
                    }
                    //Call detailing service if available
                    if (detailService != null)
                    {
                        detailService.ShowChunkDetails(node, filter.sharedMesh);
                    }
                }

                //Add me if I don't already exist
                this.activeMeshes.Add(container);
                this.chunkMeshMap[node] = container;
            }
            if (!activeList.Contains(node))
            {
                activeList.Add(node);
            }
        }
Пример #2
0
        public PlanetFace(IMeshService ms, IDetailer ds, float baseRadius, int minDistance, int treeDepth, Range3d range, Material material)
        {
            //Apply params
            this.maxResolutionAt = minDistance;
            this.maxDepth        = treeDepth;
            this.material        = material;
            this.meshService     = ms;
            this.detailService   = ds;
            this.radius          = baseRadius;

            //Create Gameobjects
            go = new GameObject("PlanetFace");

            //Create quadtree
            root = new QuadNode <ChunkData>(range);
            GenerateChunkdata(root);
        }
Пример #3
0
        /// <summary>
        /// Hides the chunk details.
        /// </summary>
        /// <param name="node">Node.</param>
        public override void HideChunkDetails(QuadNode <ChunkData> node)
        {
            //Stop active spawning session if it exists
            if (this.spawning.ContainsKey(node))
            {
                Coroutine c = (this.spawning [node]);
                if (c != null)
                {
                    StopCoroutine(this.spawning [node]);
                }
                this.spawning.Remove(node);
            }

            //This chunk has no objects associated to it
            List <GameObject> objects;

            if (!active.TryGetValue(node, out objects))
            {
                return;
            }

            //Delete the object array from the active collection
            active.Remove(node);

            //This chunk needs to have its gameobjects either deleted or pooled
            foreach (GameObject obj in objects)
            {
                PlacementRule pool;
                if (!pools.TryGetValue(obj.name, out pool))
                {
                    //No pool
                    GameObject.Destroy(obj);
                }
                else
                {
                    //Pool exists
                    obj.SetActive(false);
                    obj.transform.localScale = Vector3.one;
                    pool.pool.Enqueue(obj);
                }
            }
        }
Пример #4
0
 /// <summary>
 /// Recursive check on this quadnode and its children. Test what LOD to load
 /// </summary>
 /// <param name="camera"></param>
 /// <param name="node"></param>
 private void ForceCheckLod(Vector3 camera, QuadNode <ChunkData> node)
 {
     //I need to go deeper
     DiscardNode(node);
     if ((node.isBranch || node.depth < maxDepth) && canSplit(camera, node))
     {
         if (node.isLeaf)
         {
             Split(node);
         }
         ForceCheckLod(camera, node[Quadrant.NorthEast]);
         ForceCheckLod(camera, node[Quadrant.NorthWest]);
         ForceCheckLod(camera, node[Quadrant.SouthEast]);
         ForceCheckLod(camera, node[Quadrant.SouthWest]);
     }
     //Nope this is good (don't need to worry about merges on a forced method)
     else
     {
         //Show LOD
         ShowNode(node, this.activeChunks);
     }
 }
Пример #5
0
        public override Material GetMaterialFor(PlanetFace face, QuadNode <ChunkData> node, Mesh mesh)
        {
            switch (face.direction)
            {
            case PlanetFaceDirection.Top:
                return(top);

            case PlanetFaceDirection.Bottom:
                return(bottom);

            case PlanetFaceDirection.Left:
                return(left);

            case PlanetFaceDirection.Right:
                return(right);

            case PlanetFaceDirection.Front:
                return(front);

            default:
                return(back);
            }
        }
Пример #6
0
        private void DiscardNode(QuadNode <ChunkData> node)
        {
            CachedMeshHolder mf;

            if (chunkMeshMap.TryGetValue(node, out mf))
            {
                //Hide and pool node
                this.activeMeshes.Remove(mf);
                this.meshPool.Enqueue(mf);
                mf.gameObject.SetActive(false);
                //Discard active node
                this.chunkMeshMap.Remove(node);
                //NOTE ** do not need to remove from activeChunks because the runtime algorithm resets this each frame
                //Call highest detail action
                if (node.depth == this.maxDepth)
                {
                    //Call detailing service if available
                    if (detailService != null)
                    {
                        detailService.HideChunkDetails(node);
                    }
                }
            }
        }
Пример #7
0
        /// <summary>
        /// Update LODs going up or down a single step only for runtime optimizations
        /// </summary>
        /// <param name="camera"></param>
        public void UpdateLODs(Vector3 camera)
        {
            //Loop Trough Active Nodes Deciding If We Should Keep Them Or Not
            HashSet <QuadNode <ChunkData> > ac = new HashSet <QuadNode <ChunkData> >();

            foreach (QuadNode <ChunkData> node in this.activeChunks)
            {
                //Show Node
                if ((node.isBranch || node.depth < maxDepth) && canSplit(camera, node))
                {
                    if (node.isLeaf)
                    {
                        Split(node);
                    }

                    //If I already am generating this LOD
                    if (splitTasks.ContainsKey(node))
                    {
                        Task t = splitTasks[node];
                        if (t.state == TaskStatus.Complete)
                        {
                            //Can switch to child nodes
                            DiscardNode(node);
                            ShowNodeSplit(node, ((PlanetSplitTask)t).meshes, ac);
                            splitTasks.Remove(node);
                        }
                        else
                        {
                            //Not yet, keep current
                            ShowNode(node, ac);
                        }
                    }
                    //If I am not yet generating this LOD, keep current
                    else
                    {
                        PlanetSplitTask t = new PlanetSplitTask((s) => {
                            PlanetSplitTask self = (PlanetSplitTask)s;

                            //Generate Meshes
                            for (int i = 0; i < 4; i++)
                            {
                                QuadNode <ChunkData> child = self.parent[(Quadrant)i];
                                MeshData m = meshService.Make(
                                    child.range.a, child.range.b, child.range.d, child.range.c,
                                    child.value.faceRegion,
                                    this.radius);
                                self.meshes[i] = m;
                            }
                        });
                        t.parent         = node;
                        splitTasks[node] = t;
                        TaskPool.EnqueueInvocation(t);
                        ShowNode(node, ac);
                    }
                }
                //Collapse Or Keep Node
                else
                {
                    //Is Root, Cannot Split OR Child Wants To Merge But Parent Wants To Split
                    if (node.isRoot || canSplit(camera, node.parent))
                    {
                        ShowNode(node, ac);
                    }
                    //Child Wants To Merge And Parent Wants To Be Shown
                    else
                    {
                        if (mergeTasks.ContainsKey(node.parent))
                        {
                            //Generation already started
                            Task t = mergeTasks[node.parent];
                            if (t.state == TaskStatus.Complete)
                            {
                                //Generation is complete, show parent
                                DiscardNode(node.parent[Quadrant.NorthEast]);
                                DiscardNode(node.parent[Quadrant.SouthEast]);
                                DiscardNode(node.parent[Quadrant.NorthWest]);
                                DiscardNode(node.parent[Quadrant.SouthWest]);
                                ShowNodeMerge(node.parent, ((PlanetMergeTask)t).mesh, ac);
                                mergeTasks.Remove(node.parent);
                            }
                            else
                            {
                                //Generation is not complete, keep node
                                ShowNode(node, ac);
                            }
                        }
                        else if (!ac.Contains(node.parent))
                        {
                            //No generation started, keep node, start generation
                            PlanetMergeTask t = new PlanetMergeTask((s) => {
                                PlanetMergeTask self = (PlanetMergeTask)s;

                                MeshData m = meshService.Make(
                                    self.node.range.a, self.node.range.b, self.node.range.d, self.node.range.c,
                                    self.node.value.faceRegion,
                                    this.radius);
                                self.mesh = m;
                            });
                            t.node = node.parent;
                            mergeTasks[node.parent] = t;
                            TaskPool.EnqueueInvocation(t);
                            ShowNode(node, ac);
                        }
                    }
                }
            }

            //Set The Active Chunks
            this.activeChunks = ac;
        }
Пример #8
0
 public abstract Material GetMaterialFor(PlanetFace face, QuadNode <ChunkData> node, Mesh mesh);
Пример #9
0
 /// <summary>
 /// Hides the chunk details.
 /// </summary>
 /// <param name="node">Node.</param>
 public abstract void HideChunkDetails(QuadNode <ChunkData> node);
Пример #10
0
 /// <summary>
 /// Shows the chunk details.
 /// </summary>
 /// <param name="node">Node.</param>
 public abstract void ShowChunkDetails(QuadNode <ChunkData> node, Mesh m);
Пример #11
0
 public override Material GetMaterialFor(PlanetFace face, QuadNode <ChunkData> node, Mesh mesh)
 {
     return(material);
 }
Пример #12
0
        /// <summary>
        /// Spawn a single object for the given rule
        /// </summary>
        /// <param name="planet"></param>
        /// <param name="stackDepth"></param>
        /// <param name="srcPool"></param>
        /// <param name="destinationPool"></param>
        /// <param name="node"></param>
        /// <param name="meshData"></param>
        public void SpawnObjectOnChunk(System.Random generator, Transform planet, int stackDepth, GameObjectPool srcPool, List <GameObject> destinationPool, QuadNode <ChunkData> node, Mesh meshData)
        {
            switch (placementType)
            {
            case RuleType.SurfaceRandom:
                SpawnSurfaceRandom(generator, planet, stackDepth, srcPool, destinationPool, node, meshData);
                break;

            case RuleType.SpecificCoordinate:
                SpawnSpecific(generator, planet, stackDepth, srcPool, destinationPool, node, meshData);
                break;
            }
        }
Пример #13
0
        private void SpawnSurfaceRandom(System.Random generator, Transform planet, int stackDepth, GameObjectPool srcPool, List <GameObject> destinationPool, QuadNode <ChunkData> node, Mesh meshData)
        {
            Vector3[] verts = meshData.vertices;
            int[]     tris  = meshData.triangles;

            //Place
            int faceIdx = generator.Next(0, (tris.Length / 3) - 1) * 3;

            //Random x and y coordinates
            float rx = (float)generator.NextDouble();
            float ry = (float)generator.NextDouble();

            //Random position on face
            float   sqrt_rx = Mathf.Sqrt(rx);
            Vector3 a       = verts[tris[faceIdx]];
            Vector3 b       = verts[tris[faceIdx + 1]];
            Vector3 c       = verts[tris[faceIdx + 2]];

            Vector3 pos = (1 - sqrt_rx) * a + (sqrt_rx * (1 - ry)) * b + (sqrt_rx * ry) * c;

            //Ignore if slope is too much
            float angle = Vector3.Angle(pos, Vector3.Cross(b - a, c - a));

            if (angle > slopeLimit)
            {
                return;
            }

            //Ignore if not in altitude range
            float distance = pos.magnitude;

            if (distance < altitudeRange.low || distance > altitudeRange.high)
            {
                return;
            }

            //Determine scale
            Vector3 scale = Vector3.one * Random.Range(scaleRange.low, scaleRange.high);

            //Pool and spawn
            GameObject go = srcPool.Pop();

            //Position object from pool
            go.transform.SetParent(planet);
            go.SetActive(true);
            go.transform.localScale    = scale;
            go.transform.localPosition = pos;
            go.transform.localRotation = Quaternion.FromToRotation(Vector3.up, pos);// * Quaternion.Euler(0, 360 * Random.value, 0);

            //Add spawned object to reference list
            destinationPool.Add(go);
        }
Пример #14
0
        private void SpawnSpecific(System.Random generator, Transform planet, int stackDepth, GameObjectPool srcPool, List <GameObject> destinationPool, QuadNode <ChunkData> node, Mesh meshData)
        {
            Vector3 worldCoordinates = SphericalToCartesian(new Vector3(2, theta, phi));

            Plane p = new Plane(node.range.normal, node.range.center);

            Vector3 onPlane = p.ClosestPointOnPlane(worldCoordinates);

            if (PointInRectangle(onPlane, node.range.a, node.range.b, node.range.c, node.range.d))
            {
                //Pool and spawn
                GameObject go = srcPool.Pop();

                //Position object from pool
                go.transform.SetParent(planet);
                Vector3 pos = SphericalToCartesian(new Vector3(meshData.bounds.center.magnitude, theta, phi));
                go.SetActive(true);
                go.transform.localScale    = Vector3.one * fixedScale;
                go.transform.localPosition = pos;
                go.transform.localRotation = Quaternion.FromToRotation(Vector3.up, pos);// * Quaternion.Euler(0, 360 * Random.value, 0);

                //Add spawned object to reference list
                destinationPool.Add(go);
            }
        }
Пример #15
0
 public int GetRandomSeed(int layer, QuadNode <ChunkData> node)
 {
     return(((seed + layer) << layer) * node.value.bounds.center.GetHashCode());
 }
Пример #16
0
        /// <summary>
        /// Split a quadnode and create the appropriate metadata
        /// </summary>
        /// <param name="parent"></param>
        private void Split(QuadNode <ChunkData> parent)
        {
            parent.Subdivide();

            GenerateChildChunkdata(parent);
        }
Пример #17
0
        private void ShowNode(QuadNode <ChunkData> node, HashSet <QuadNode <ChunkData> > activeList)
        {
            if (!chunkMeshMap.ContainsKey(node))
            {
                //Buffer
                CachedMeshHolder container = PopMeshContainer();
                MeshFilter       filter    = container.filter;
                container.collider.sharedMesh = filter.sharedMesh;
                container.collider.enabled    = true;

                //Populate mesh
                filter.sharedMesh = meshService.Make(
                    node.range.a, node.range.b, node.range.d, node.range.c,
                    node.value.faceRegion,
                    this.radius).mesh;
                //filter.sharedMesh = SubPlane.Make(node.range.a, node.range.b, node.range.d, node.range.c, resolution);

                container.renderer.sharedMaterial = textureService.GetMaterialFor(this, node, filter.sharedMesh);

                //Set chunk data if it was never computed before
                if (node.value.bounds == null)
                {
                    node.value.bounds        = new Sphere(Vector3.zero, 1);
                    node.value.bounds.center = filter.sharedMesh.bounds.center;

                    /*node.value.bounds.radius = Mathf.Max (
                     *                          filter.sharedMesh.bounds.extents.x,
                     *                          filter.sharedMesh.bounds.extents.y,
                     *                          filter.sharedMesh.bounds.extents.z
                     *                  );*/
                    node.value.bounds.radius = Mathf.Sqrt(
                        filter.sharedMesh.bounds.extents.x * filter.sharedMesh.bounds.extents.x +
                        filter.sharedMesh.bounds.extents.y * filter.sharedMesh.bounds.extents.y +
                        filter.sharedMesh.bounds.extents.z * filter.sharedMesh.bounds.extents.z
                        );
                }

                //Show node
                filter.gameObject.SetActive(true);

                //Call highest detail action
                if (node.depth == this.maxDepth)
                {
                    //Call listeners if exists
                    foreach (System.Action <QuadNode <ChunkData> > fn in this.listeners)
                    {
                        fn.Invoke(node);
                    }
                    //Call detailing service if available
                    if (detailService != null)
                    {
                        detailService.ShowChunkDetails(node, filter.sharedMesh);
                    }
                }

                //Add me if I don't already exist
                this.activeMeshes.Add(container);
                this.chunkMeshMap[node] = container;
            }
            if (!activeList.Contains(node))
            {
                activeList.Add(node);
            }
        }
Пример #18
0
        /// <summary>
        /// Spawn gameobejects for all rules on a given chunk.
        /// </summary>
        /// <param name="node">Node.</param>
        private IEnumerator SpawnForChunk(QuadNode <ChunkData> node, Mesh m, int amountToSpawnAtOnce)
        {
            //Get list of spawned objects for this node
            List <GameObject> spawned;

            if (active.ContainsKey(node))
            {
                spawned = active [node];
            }
            else
            {
                spawned       = new List <GameObject> ();
                active [node] = spawned;
            }

            Vector3[] verts = m.vertices;
            int[]     tris  = m.triangles;

            //Go through all objects to place
            int j = 0; int k = 0;

            foreach (PlacementRule rule in objectsToPool)
            {
                //Bitshift ensures that successive rules with the same seeds will end up with different placements
                Random.InitState((rule.seed << j++) * node.value.bounds.center.GetHashCode());

                //Number of this prefab to spawn
                int   number = (int)Random.Range(rule.amountRange.low, rule.amountRange.high);
                float sl     = rule.slopeLimit;

                for (int i = 0; i < number; i++)
                {
                    int faceIdx = Random.Range(0, (tris.Length / 3) - 1) * 3;

                    //Random x and y coordinates
                    float rx = Random.value;
                    float ry = Random.value;

                    float sqrt_rx = Mathf.Sqrt(rx);

                    Vector3 a = verts [tris[faceIdx]];
                    Vector3 b = verts [tris[faceIdx + 1]];
                    Vector3 c = verts [tris[faceIdx + 2]];

                    Vector3 pos = (1 - sqrt_rx) * a + (sqrt_rx * (1 - ry)) * b + (sqrt_rx * ry) * c;

                    //Ignore if slope is too much
                    float angle = Vector3.Angle(pos, Vector3.Cross(b - a, c - a));
                    if (angle > rule.slopeLimit)
                    {
                        continue;
                    }

                    //Ignore if not in altitude range
                    float distance = pos.magnitude;
                    if (distance < rule.altitudeRange.low || distance > rule.altitudeRange.high)
                    {
                        continue;
                    }

                    //Determine scale
                    Vector3 scale = Vector3.one * Random.Range(rule.scaleRange.low, rule.scaleRange.high);

                    //Pool and spawn
                    if (rule.pool.Count < 1)
                    {
                        ExpandPool(rule, rule.poolBufferSize);
                    }
                    GameObject go = rule.pool.Dequeue();
                    go.SetActive(true);
                    go.transform.localScale    = scale;
                    go.transform.localPosition = pos;
                    go.transform.localRotation = Quaternion.FromToRotation(Vector3.up, pos);                     // * Quaternion.Euler(0, 360 * Random.value, 0);

                    //Add spawned object to reference list
                    spawned.Add(go);

                    //If passed frame spawn limit, wait till next frame and reset the count
                    k++;
                    if (k >= amountToSpawnAtOnce)
                    {
                        k = 0;
                        yield return(null);
                    }
                }
            }
        }