/// <summary>
 /// Delete the specified revealer.
 /// </summary>
 static void DoRemoveRevealer(IMangoFogRevealer rev)
 {
     if (rev != null)
     {
         lock (revealersRemoved) revealersRemoved.Add(rev);
     }
 }
 /// <summary>
 /// Create a new fog revealer.
 /// </summary>
 static void DoAddRevealer(IMangoFogRevealer rev)
 {
     if (rev != null)
     {
         lock (revealersAdded) revealersAdded.Add(rev);
     }
 }
        /// <summary>
        /// The fastest form of visibility updates -- radius-based, no line of sights checks.
        /// </summary>
        protected void RevealUsingRadius(IMangoFogRevealer r, float worldToTex)
        {
            // Position relative to the fog of war
            Vector3 pos = (r.GetPosition() - chunkOrigin) * worldToTex;

            //Vector3 pos = (r.GetPosition() - chunkOrigin) * worldToTex;
            float radius = r.GetRadius() * worldToTex;

            // Coordinates we'll be dealing with
            int xmin = Mathf.RoundToInt(pos.x - radius);
            int xmax = Mathf.RoundToInt(pos.x + radius);

            int ymin, ymax, cy;

            //use z for 3d, and y for 2d
            if (orientation == MangoFogOrientation.Perspective3D)
            {
                ymin = Mathf.RoundToInt(pos.z - radius);
                ymax = Mathf.RoundToInt(pos.z + radius);
                cy   = Mathf.RoundToInt(pos.z);
            }
            else
            {
                ymin = Mathf.RoundToInt(pos.y - radius);
                ymax = Mathf.RoundToInt(pos.y + radius);
                cy   = Mathf.RoundToInt(pos.y);
            }

            int cx = Mathf.RoundToInt(pos.x);

            int radiusSqr = Mathf.RoundToInt(radius * radius);

            for (int y = ymin; y < ymax; ++y)
            {
                if (y > -1 && y < textureSize)
                {
                    int yw = y * textureSize;
                    for (int x = xmin; x < xmax; ++x)
                    {
                        int xd   = x - cx;
                        int yd   = y - cy;
                        int dist = xd * xd + yd * yd;

                        if (x > -1 && x < textureSize)
                        {
                            // Reveal this pixel
                            if (dist < radiusSqr)
                            {
                                buffer1[x + yw].r = 255;
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Update the fog of war's visibility.
        /// </summary>
        protected void UpdateBuffer()
        {
            // Use the texture blend time, thus estimating the time this update will finish
            // Doing so helps avoid visible changes in blending caused by the blended result being X milliseconds behind.
            float factor = (textureBlendTime > 0f) ? Mathf.Clamp01(blendFactor + elapsed / textureBlendTime) : 1f;

            // Clear the buffer's red channel (channel used for current visibility -- it's updated right after)
            for (int i = 0, imax = buffer0.Length; i < imax; ++i)
            {
                buffer0[i]   = Color32.Lerp(buffer0[i], buffer1[i], factor);
                buffer1[i].r = 0;
            }

            // For conversion from world coordinates to texture coordinates
            float worldToTex = (float)textureSize / chunkSize;

            // Update the visibility buffer, one revealer at a time
            for (int i = 0; i < MangoFogInstance.revealers.size; ++i)
            {
                IMangoFogRevealer rev = MangoFogInstance.revealers[i];
                if (rev.IsValid())
                {
                    if (rev.GetRevealerType() == RevealerType.Radius)
                    {
                        RevealUsingRadius(rev, worldToTex);
                    }
                    else
                    {
                        RevealUsingLOS(rev, worldToTex);
                    }
                }
            }

            // Blur the final visibility data
            for (int i = 0; i < blurIterations; ++i)
            {
                BlurVisibility();
            }

            // Reveal the map based on what's currently visible
            RevealMap();

            // Merge two buffer to one
            MergeBuffer();

            //ready to update the texture , multi chunk experimental only
            changeStates[chunkID] = 2;
        }
        protected void UpdateRevealers(int deltaMS)
        {
            // Add all items scheduled to be added
            if (revealersAdded.size > 0)
            {
                lock (revealersAdded)
                {
                    while (revealersAdded.size > 0)
                    {
                        int index = revealersAdded.size - 1;
                        revealers.Add(revealersAdded.buffer[index]);
                        revealersAdded.RemoveAt(index);
                    }
                }
            }

            // Remove all items scheduled for removal
            if (revealersRemoved.size > 0)
            {
                lock (revealersRemoved)
                {
                    while (revealersRemoved.size > 0)
                    {
                        int index = revealersRemoved.size - 1;
                        revealers.Remove(revealersRemoved.buffer[index]);
                        revealersRemoved.RemoveAt(index);
                    }
                }
            }

            for (int i = revealers.size - 1; i >= 0; i--)
            {
                IMangoFogRevealer revealer = revealers[i];
                revealer.Update(deltaMS);
                if (!revealer.IsValid())
                {
                    revealer.Release();
                }
            }
        }
        /// <summary>
        /// Reveal the map around the revealer performing line-of-sight checks.
        /// </summary>
        void RevealUsingLOS(IMangoFogRevealer r, float worldToTex)
        {
            // Position relative to the fog of war
            Vector3 pos = r.GetPosition() - chunkOrigin;

            int ymin, ymax, xmin, xmax, cx, cy, gh;

            // Coordinates we'll be dealing with
            //use z for 3d, and y for 2d
            if (orientation == MangoFogOrientation.Perspective3D)
            {
                ymin = Mathf.RoundToInt((pos.z - r.GetLOSOuterRadius()) * worldToTex);
                ymax = Mathf.RoundToInt((pos.z + r.GetLOSOuterRadius()) * worldToTex);
                cy   = Mathf.RoundToInt(pos.z * worldToTex);
                gh   = WorldToGridHeight(r.GetPosition().y);
            }
            else
            {
                ymin = Mathf.RoundToInt((pos.y - r.GetLOSOuterRadius()) * worldToTex);
                ymax = Mathf.RoundToInt((pos.y + r.GetLOSOuterRadius()) * worldToTex);
                cy   = Mathf.RoundToInt(pos.y * worldToTex);
                gh   = WorldToGridHeight(r.GetPosition().z);
            }

            cx = Mathf.RoundToInt(pos.x * worldToTex);

            xmin = Mathf.RoundToInt((pos.x - r.GetLOSOuterRadius()) * worldToTex);
            xmax = Mathf.RoundToInt((pos.x + r.GetLOSOuterRadius()) * worldToTex);

            xmin = Mathf.Clamp(xmin, 0, textureSize - 1);
            xmax = Mathf.Clamp(xmax, 0, textureSize - 1);
            ymin = Mathf.Clamp(ymin, 0, textureSize - 1);
            ymax = Mathf.Clamp(ymax, 0, textureSize - 1);

            cx = Mathf.Clamp(cx, 0, textureSize - 1);
            cy = Mathf.Clamp(cy, 0, textureSize - 1);

            int     minRange = Mathf.RoundToInt(r.GetLOSInnerRadius() * r.GetLOSInnerRadius() * worldToTex * worldToTex);
            int     maxRange = Mathf.RoundToInt(r.GetLOSOuterRadius() * r.GetLOSOuterRadius() * worldToTex * worldToTex);
            int     variance = Mathf.RoundToInt(Mathf.Clamp01(MangoFogInstance.Instance.margin / (MangoFogInstance.Instance.heightRange.y - MangoFogInstance.Instance.heightRange.x)) * 255);
            Color32 white    = new Color32(255, 255, 255, 255);

            // Leave the edges unrevealed
            int limit = textureSize - 1;

            for (int y = ymin; y < ymax; ++y)
            {
                if (y > -1 && y < limit)
                {
                    for (int x = xmin; x < xmax; ++x)
                    {
                        if (x > -1 && x < limit)
                        {
                            int xd    = x - cx;
                            int yd    = y - cy;
                            int dist  = xd * xd + yd * yd;
                            int index = x + y * textureSize;

                            if (dist < minRange || (cx == x && cy == y))
                            {
                                buffer1[index] = white;
                            }
                            else if (dist < maxRange)
                            {
                                Vector2 v = new Vector2(xd, yd);
                                v.Normalize();
                                v *= r.GetRadius() / 2;

                                int sx = cx + Mathf.RoundToInt(v.x);
                                int sy = cy + Mathf.RoundToInt(v.y);

                                if (sx > -1 && sx < textureSize &&
                                    sy > -1 && sy < textureSize &&
                                    IsVisible(sx, sy, x, y, Mathf.Sqrt(dist), gh, variance, r.GetRot(), r.GetFOVCosine(), r.DoReverseLOSDirection()))
                                {
                                    buffer1[index] = white;
                                }
                            }
                        }
                    }
                }
            }
        }