private Vector3 TriangleNormal(Vector3 v0, Vector3 v1, Vector3 v2)
        {
            var a = RasterVectorUtils.Substract(v1, v0);
            var b = RasterVectorUtils.Substract(v2, v0);

            return(RasterVectorUtils.Cross(a, b));
        }
        private void DrawTriangleTrapezoid(
            Vector3 topMinInterpolant,
            Vector3 deltaMinInterpolant,
            Vector3 topMaxInterpolant,
            Vector3 deltaMaxInterpolant,
            float topMinX,
            float deltaMinX,
            float topMaxX,
            float deltaMaxX,
            float minY,
            float maxY)
        {
            int intMinY = Mathf.Clamp(Mathf.CeilToInt(minY), _policyType.MinY, _policyType.MaxY + 1),
                intMaxY = Mathf.Clamp(Mathf.CeilToInt(maxY), _policyType.MinY, _policyType.MaxY + 1);

            for (int intY = intMinY; intY < intMaxY; intY++)
            {
                float y                = intY - minY,
                      minX             = topMinX + deltaMinX * y,
                      maxX             = topMaxX + deltaMaxX * y;
                Vector3 minInterpolant = RasterVectorUtils.Add(topMinInterpolant, RasterVectorUtils.Scale(deltaMinInterpolant, y)),
                        maxInterpolant = RasterVectorUtils.Add(topMaxInterpolant, RasterVectorUtils.Scale(deltaMaxInterpolant, y));

                if (minX > maxX)
                {
                    Exchange(ref minX, ref maxX);
                    Exchange(ref minInterpolant, ref maxInterpolant);
                }

                if (maxX > minX)
                {
                    int intMinX = Mathf.Clamp(Mathf.CeilToInt(minX), _policyType.MinX, _policyType.MaxX + 1),
                        intMaxX = Mathf.Clamp(Mathf.CeilToInt(maxX), _policyType.MinX, _policyType.MaxX + 1);
                    Vector3 deltaInterpolant = RasterVectorUtils.ScaleInv(RasterVectorUtils.Substract(maxInterpolant, minInterpolant), (maxX - minX));

                    for (int x = intMinX; x < intMaxX; x++)
                    {
                        _policyType.ProcessPixel(x, intY, minInterpolant + deltaInterpolant * (x - minX));
                    }
                }
            }
        }
 public void ProcessPixel(int x, int y, Vector3 worldPosition)
 {
     if (_volumes.IsInVolume(worldPosition))
     {
         var cell = _heightsMap[x, y];
         if (cell.TriangleIndex != _triangleIndex)
         {
             // If this is the first hit on this cell from the current triangle, add a new sample
             HeightSpan span         = new HeightSpan();
             var        gridPosition = RasterVectorUtils.Add(RasterVectorUtils.Substract(_volumeCenter, _volumeHalfExtent), RasterVectorUtils.Scale(new Vector3(x, 0, y), _cellSize));
             span.Range = new Vector2(worldPosition.y, worldPosition.y);
             cell.HitTriangles.AddLast(span);
             cell.Position      = new Vector2(gridPosition.x + _cellSize * 0.5f, gridPosition.z + _cellSize * 0.5f);
             cell.TriangleIndex = _triangleIndex;
         }
         else
         {
             // If this is not the first hit on this cell from the current triangle, expand the sample min and max
             var span = cell.HitTriangles.Last.Value;
             span.Range.x = Mathf.Min(span.Range.x, worldPosition.y);
             span.Range.y = Mathf.Max(span.Range.y, worldPosition.y);
         }
     }
 }
        // Fractions of PrecomputedVisibilitySettings.PlayAreaHeight to guarantee have cell coverage
        //private static readonly float[] _testHeights = new float[]{ .4f, .6f, .8f };
        public IList <VolumeCell> ComputeVolumeCells(IEnumerable <Collider> allColliders, Func <string, string, float, bool> progress = null)
        {
            if (_volumes.GetRasterBoundCount() <= 0)
            {
                return(null);
            }

            GC.Collect();

            _stat.Reset();
            OCProfiler.Start();
            var startMemory = GC.GetTotalMemory(false);

            var allMeshes = new LinkedList <IRasterMesh>();

            foreach (var collider in allColliders)
            {
                allMeshes.AddLast(RasterMeshFactory.CreateRasterMesh(collider));
            }

            var bounds          = _volumes.GetVolumeBounds(_settings.CellSize);
            var boundsCenter    = bounds.Center;
            var boundHalfExtent = bounds.HalfExtent;
            var volumeSizes     = bounds.HalfExtent * 2.0f / _settings.CellSize;
            int sizeX           = (int)(volumeSizes.x + DELTA) + 1;
            int sizeY           = (int)(volumeSizes.z + DELTA) + 1;


            var heightsMap   = new CellToHeightsMap(sizeX, sizeY);
            var rasterPolicy = new CellPlacementRasterPolicy(_settings.CellSize, heightsMap, _volumes);
            TriangleRasterizer rasterizer = new TriangleRasterizer(rasterPolicy);

            bool cancelled = false;

            OCProfiler.Start();
            long nextTriangleIndex = 1;
            // Rasterize the scene to determine potential cell heights
            int meshCount = 0;

            Debug.LogFormat("Rasterize Mesh Total Mesh {0}", meshCount);
            foreach (var mesh in allMeshes)
            {
                meshCount++;
                if (!Config.IsBatchMode && progress != null)
                {
                    if (progress("体素化", String.Format("当前正在处理Mesh {0}/{1} ...", meshCount, allMeshes.Count),
                                 ((float)meshCount) / allMeshes.Count))
                    {
                        cancelled = true;
                        break;
                    }
                }

                var meshBounds = mesh.WorldBounds;
                // Only process meshes whose bounding box intersects a PVS volume
                if (_volumes.Intersect(meshBounds))
                {
                    var triCount = mesh.TriangleCount;

                    for (int triIndex = 0; triIndex < triCount; ++triIndex)
                    {
                        mesh.GetTriangle(triIndex, _triVertices);

                        var normal = TriangleNormal(_triVertices[0], _triVertices[1], _triVertices[2]);

                        const float EdgePullback = .1f;
                        // Only rasterize upward facing triangles
                        if (normal.y > 0.0f)
                        {
                            for (int vertIndex = 0; vertIndex < 3; vertIndex++)
                            {
                                // Transform world space positions from [PrecomputedVisibilityBounds.Origin - PrecomputedVisibilityBounds.BoxExtent, PrecomputedVisibilityBounds.Origin + PrecomputedVisibilityBounds.BoxExtent] into [0,1]
                                Vector3 transformedPosition;
                                var     v = _triVertices[vertIndex];
                                transformedPosition.x =
                                    (v.x - boundsCenter.x + boundHalfExtent.x) / (2.0f * boundHalfExtent.x);
                                //transformedPosition.y =
                                //   (v.y - boundsCenter.y + boundHalfExtent.y) / (2.0f * boundHalfExtent.y);
                                transformedPosition.z =
                                    (v.z - boundsCenter.z + boundHalfExtent.z) / (2.0f * boundHalfExtent.z);

                                // Project positions onto the XY plane
                                _XYPositions[vertIndex] = new Vector2(transformedPosition.x * (sizeX - 1), transformedPosition.z * (sizeY - 1));
                            }

                            rasterizer.TriangleIndex = nextTriangleIndex;

                            for (int sampleIndex = 0; sampleIndex < 9; sampleIndex++)
                            {
                                var samplePosition = RasterVectorUtils.Add(RasterVectorUtils.Scale(_subsamplePositions[sampleIndex], (1 - 2 * EdgePullback)), new Vector2(EdgePullback, EdgePullback));

                                rasterizer.DrawTriangle(
                                    _triVertices[0],
                                    _triVertices[1],
                                    _triVertices[2],
                                    RasterVectorUtils.Substract(_XYPositions[0], samplePosition),
                                    RasterVectorUtils.Substract(_XYPositions[1], samplePosition),
                                    RasterVectorUtils.Substract(_XYPositions[2], samplePosition)
                                    );
                            }

                            nextTriangleIndex++;
                        }
                    }
                }
            }
            _stat.RasterizeTime = OCProfiler.Stop();

            if (cancelled)
            {
                return(null);
            }

            var cells = new List <VolumeCell>(sizeX * sizeY * 2);
            var placedHeightRanges = new List <Vector2>();

            Debug.LogFormat("Calculate Cell Total Cells {0}", sizeX * sizeY);
            for (int y = 0; y < sizeY; ++y)
            {
                if (cancelled)
                {
                    break;
                }

                for (int x = 0; x < sizeX; ++x)
                {
                    if (!Config.IsBatchMode && progress != null)
                    {
                        if (progress("计算Cell区域",
                                     String.Format("当前正在处理Cell {0}/{1} ...", x + y * sizeX + 1, sizeX * sizeY),
                                     ((float)(x + y * sizeX + 1)) / (sizeX * sizeY)))
                        {
                            cancelled = true;
                            break;
                        }
                    }

                    var cell            = heightsMap[x, y];
                    var currentPosition = cell.Position;

                    var   sortedHitTriangles = cell.HitTriangles.OrderByDescending(span => - span.Range.y).ToList();
                    float lastSampleHeight   = float.NegativeInfinity;
                    placedHeightRanges.Clear();

                    OCProfiler.Start();
                    int count = sortedHitTriangles.Count();
                    // Pass 1 - only place cells in the largest holes which are most likely to be where the play area is
                    // Place the bottom slightly above the surface, since cells that clip through the floor often have poor occlusion culling
                    for (int heightIndex = 0; heightIndex < count; heightIndex++)
                    {
                        float currentMaxHeight = sortedHitTriangles[heightIndex].Range.y;

                        // Place a new cell if this is the highest height
                        if (heightIndex + 1 == count
                            // Or if there's a gap above this height of size MinPlayAreaHeight
                            || ((sortedHitTriangles[heightIndex + 1].Range.y - currentMaxHeight) > _settings.MinPlayAreaHeight
                                // And this height is not within a cell that was just placed
                                && currentMaxHeight - lastSampleHeight > _settings.MinPlayAreaHeight))
                        {
                            var boundsMin = new Vector3(
                                currentPosition.x - _settings.CellSize * 0.5f,
                                currentMaxHeight,
                                currentPosition.y - _settings.CellSize * 0.5f);

                            var boundsMax = new Vector3(currentPosition.x + _settings.CellSize * 0.5f,
                                                        currentMaxHeight + _settings.MaxPlayAreaHeight,
                                                        currentPosition.y + _settings.CellSize * 0.5f);

                            cells.Add(new VolumeCell(boundsMin, boundsMax));
                            lastSampleHeight = currentMaxHeight;
                            placedHeightRanges.Add(new Vector2(boundsMin.y, boundsMax.y));
                        }
                    }

                    _stat.Pass1Time += OCProfiler.Stop();

                    OCProfiler.Start();
                    // Pass 2 - make sure the space above every triangle is covered by precomputed visibility cells, even if the cells are placed poorly (intersecting the floor)

                    /*for (int heightIndex = 0; heightIndex < count - 1; heightIndex++)
                     * {
                     *  for (int extremaIndex = 0; extremaIndex < 2; extremaIndex++)
                     *  {
                     *      var currentMaxHeight = extremaIndex == 0 ? sortedHitTriangles[heightIndex].Range.x : sortedHitTriangles[heightIndex].Range.y;
                     *      var compareHeight = currentMaxHeight + .5f * _settings.MaxPlayAreaHeight;
                     *
                     *      for (int testIndex = 0; testIndex < 3; testIndex++)
                     *      {
                     *          var testHeight = currentMaxHeight + _testHeights[testIndex] * _settings.MaxPlayAreaHeight;
                     *
                     *          int closestCellInZIndex = -1;
                     *          float closestCellInZDistance = float.MaxValue;
                     *          bool bInsideCell = false;
                     *
                     *          for (int placedHeightIndex = 0; placedHeightIndex < placedHeightRanges.Count; placedHeightIndex++)
                     *          {
                     *              var cellHeightRange = placedHeightRanges[placedHeightIndex];
                     *
                     *              if (testHeight > cellHeightRange.x && testHeight < cellHeightRange.y)
                     *              {
                     *                  bInsideCell = true;
                     *                  break;
                     *              }
                     *
                     *              float absDistance = Mathf.Min(Mathf.Abs(compareHeight - cellHeightRange.x), Mathf.Abs(compareHeight - cellHeightRange.y));
                     *
                     *              if (absDistance < closestCellInZDistance)
                     *              {
                     *                  closestCellInZDistance = absDistance;
                     *                  closestCellInZIndex = placedHeightIndex;
                     *              }
                     *          }
                     *
                     *          // Place a cell if TestHeight was not inside any existing cells
                     *          if (!bInsideCell)
                     *          {
                     *              float desiredCellBottom = currentMaxHeight;
                     *
                     *              if (closestCellInZIndex >= 0)
                     *              {
                     *                  var nearestCellHeightRange = placedHeightRanges[closestCellInZIndex];
                     *                  var nearestCellCompareHeight = (nearestCellHeightRange.x + nearestCellHeightRange.y) / 2;
                     *
                     *                  // Move the bottom of the cell to be placed such that it doesn't overlap the nearest cell
                     *                  // This makes use of the cell's full height to cover space
                     *                  if (compareHeight < nearestCellCompareHeight)
                     *                  {
                     *                      desiredCellBottom = Mathf.Min(desiredCellBottom, nearestCellHeightRange.x - _settings.MaxPlayAreaHeight);
                     *                  }
                     *                  else if (compareHeight > nearestCellCompareHeight)
                     *                  {
                     *                      desiredCellBottom = Mathf.Max(desiredCellBottom, nearestCellHeightRange.y);
                     *                  }
                     *              }
                     *
                     *              var boundsMin = new Vector3(
                     *                  currentPosition.x - _settings.CellSize * 0.5f,
                     *                  desiredCellBottom,
                     *                  currentPosition.y - _settings.CellSize * 0.5f);
                     *              var boundsMax = new Vector3(
                     *                  currentPosition.x + _settings.CellSize * 0.5f,
                     *                  desiredCellBottom + _settings.MaxPlayAreaHeight,
                     *                  currentPosition.y + _settings.CellSize * 0.5f);
                     *
                     *              cells.Add(new VolumeCell(boundsMin, boundsMax));
                     *              placedHeightRanges.Add(new Vector2(boundsMin.y, boundsMax.y));
                     *          }
                     *      }
                     *  }
                     * }*/

                    _stat.Pass2Time += OCProfiler.Stop();
                }
            }
            _stat.TotalTime = OCProfiler.Stop();

            if (cancelled)
            {
                return(null);
            }

            GC.Collect();
            _stat.TotalMemory = GC.GetTotalMemory(false) - startMemory;
            return(cells);
        }
        public void DrawTriangle(Vector3 v0, Vector3 v1, Vector3 v2, Vector2 p0, Vector2 p1, Vector2 p2)
        {
            _interpolants[0] = v0;
            _interpolants[1] = v1;
            _interpolants[2] = v2;

            _points[0] = p0;
            _points[1] = p1;
            _points[2] = p2;

            // Find the top point.

            if (_points[1].y < _points[0].y && _points[1].y <= _points[2].y)
            {
                Exchange(ref _points[0], ref _points[1]);
                Exchange(ref _interpolants[0], ref _interpolants[1]);
            }
            else if (_points[2].y < _points[0].y && _points[2].y <= _points[1].y)
            {
                Exchange(ref _points[0], ref _points[2]);
                Exchange(ref _interpolants[0], ref _interpolants[2]);
            }

            // Find the bottom point.

            if (_points[1].y > _points[2].y)
            {
                Exchange(ref _points[2], ref _points[1]);
                Exchange(ref _interpolants[2], ref _interpolants[1]);
            }

            // Calculate the edge gradients.

            float topMinDiffX             = (_points[1].x - _points[0].x) / (_points[1].y - _points[0].y),
                  topMaxDiffX             = (_points[2].x - _points[0].x) / (_points[2].y - _points[0].y);
            Vector3 topMinDiffInterpolant = RasterVectorUtils.ScaleInv(RasterVectorUtils.Substract(_interpolants[1], _interpolants[0]), (_points[1].y - _points[0].y)),
                    topMaxDiffInterpolant = RasterVectorUtils.ScaleInv(RasterVectorUtils.Substract(_interpolants[2], _interpolants[0]), (_points[2].y - _points[0].y));

            float bottomMinDiffX             = (_points[2].x - _points[1].x) / (_points[2].y - _points[1].y),
                  bottomMaxDiffX             = (_points[2].x - _points[0].x) / (_points[2].y - _points[0].y);
            Vector3 bottomMinDiffInterpolant = RasterVectorUtils.ScaleInv(RasterVectorUtils.Substract(_interpolants[2], _interpolants[1]), (_points[2].y - _points[1].y)),
                    bottomMaxDiffInterpolant = RasterVectorUtils.ScaleInv(RasterVectorUtils.Substract(_interpolants[2], _interpolants[0]), (_points[2].y - _points[0].y));

            DrawTriangleTrapezoid(
                _interpolants[0],
                topMinDiffInterpolant,
                _interpolants[0],
                topMaxDiffInterpolant,
                _points[0].x,
                topMinDiffX,
                _points[0].x,
                topMaxDiffX,
                _points[0].y,
                _points[1].y
                );

            DrawTriangleTrapezoid(
                _interpolants[1],
                bottomMinDiffInterpolant,
                RasterVectorUtils.Add(_interpolants[0], RasterVectorUtils.Scale(topMaxDiffInterpolant, (_points[1].y - _points[0].y))),
                bottomMaxDiffInterpolant,
                _points[1].x,
                bottomMinDiffX,
                _points[0].x + topMaxDiffX * (_points[1].y - _points[0].y),
                bottomMaxDiffX,
                _points[1].y,
                _points[2].y
                );
        }