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 ); }