private void CollectVisiblePages(TerrainVegetationRenderData renderData, TerrainVegetationComponent component, CameraComponent camera) { renderData.TransformData.Clear(); if (camera == null || renderData.Pages == null) { return; } var cameraPosition = camera.GetWorldPosition(); cameraPosition.Y = 0.0f; // Only cull in xz plane var maxPageDistance = component.ViewDistance + PageSize; _activesPages.Clear(); for (var i = 0; i < renderData.Pages.Length; i++) { var page = renderData.Pages[i]; if (page.Instances == null) // Skip uninitialized pages { continue; } var distance = (cameraPosition - page.WorldPosition).Length(); if (distance < maxPageDistance) { _activesPages.Add(page); } } // Reset camera position for individual instance culling cameraPosition = camera.GetWorldPosition(); float maxDistance = component.ViewDistance; float minDistance = maxDistance * 0.8f; float distanceRange = maxDistance - minDistance; // TODO: concurrency??? That would probably be a good thing here var maxInstanceDistanceSquared = component.ViewDistance * component.ViewDistance; foreach (var page in _activesPages) { for (var p = 0; p < page.Instances.Count; p++) { var distance = (cameraPosition - page.Instances[p].TranslationVector).LengthSquared(); //if (distance < maxInstanceDistanceSquared) { var worldMatrix = page.Instances[p]; if (component.UseDistanceScaling) { // Fade out the mesh by scaling it, this could be done in the shader for more speeeed var distanceToCamera = Math.Max(0.0f, (cameraPosition - worldMatrix.TranslationVector).Length() - minDistance); var relativeScale = Math.Min(1.0f, distanceToCamera / distanceRange); var distanceScale = (float)MathUtil.Lerp(1.0f, 0.0f, Math.Pow(relativeScale, 2.0f)); var scale = Matrix.Scaling(distanceScale); renderData.TransformData.Add(scale * worldMatrix); } else { renderData.TransformData.Add(worldMatrix); } } } } }