public bool IsDirty(TerrainComponent component) => Material != component.Material || Size != component.Size || Heightmap != component.Heightmap || Mesh == null;
/// <summary> /// Update and recreate a page if necessary /// </summary> private void UpdatePages(TerrainComponent terrain, TerrainVegetationComponent component, TerrainVegetationRenderData renderData) { if (renderData.Pages != null && renderData.MaskImage != null && !component.IsDirty) { return; } if (renderData.MaskImage == null || renderData.Mask != component.Mask) { renderData.Mask = component.Mask; renderData.MaskImage?.Dispose(); // Get mask image data try { var game = Services.GetService <IGame>(); var graphicsContext = game.GraphicsContext; var commandList = graphicsContext.CommandList; renderData.MaskImage = component.Mask.GetDataAsImage(commandList); } catch { // Image probably not loaded yet .. try again next frame :) return; } } // Cache render data so we won't need to recreate pages var mask = renderData.MaskImage.PixelBuffer[0]; var maskChannel = (int)component.MaskChannel; // Calculate terrain center offset var terrainOffset = terrain.Size / 2.0f; // Create vegetation pages var rng = new Random(component.Seed); var pagesPerRow = (int)terrain.Size / PageSize; var instancesPerRow = (int)(PageSize * component.Density); var distancePerInstance = PageSize / (float)instancesPerRow; renderData.Pages = new TerrainVegetationPage[pagesPerRow * pagesPerRow]; var scaleRange = component.MaxScale - component.MinScale; for (var pz = 0; pz < pagesPerRow; pz++) { for (var px = 0; px < pagesPerRow; px++) { var radius = PageSize * 0.5f; var pagePosition = new Vector3(px * PageSize - terrainOffset, 0, pz * PageSize - terrainOffset); var page = new TerrainVegetationPage(); renderData.Pages[pz * pagesPerRow + px] = page; for (var iz = 0; iz < instancesPerRow; iz++) { for (var ix = 0; ix < instancesPerRow; ix++) { var position = pagePosition; position.X += ix * distancePerInstance; position.Z += iz * distancePerInstance; position.X += (float)rng.NextDouble() * distancePerInstance * 2.0f - distancePerInstance; position.Z += (float)rng.NextDouble() * distancePerInstance * 2.0f - distancePerInstance; position.Y = terrain.GetHeightAt(position.X, position.Z); var tx = (int)((position.X + terrainOffset) / terrain.Size * mask.Width); var ty = (int)((position.Z + terrainOffset) / terrain.Size * mask.Height); if (tx < 0 || tx >= mask.Width || ty < 0 || ty >= mask.Height) { continue; } var maskDensity = mask.GetPixel <Color>(tx, ty)[maskChannel] / 255.0; if (rng.NextDouble() > maskDensity) { continue; } var normal = terrain.GetNormalAt(position.X, position.Z); var slope = 1.0f - Math.Abs(normal.Y); if (slope < component.MinSlope || slope > component.MaxSlope) { continue; } var scale = (float)rng.NextDouble() * scaleRange + component.MinScale; var rotation = Quaternion.RotationAxis(Vector3.UnitY, (float)rng.NextDouble() * MathUtil.TwoPi) * Quaternion.BetweenDirections(Vector3.UnitY, normal); var scaling = new Vector3(scale); Matrix.Transformation(ref scaling, ref rotation, ref position, out var transformation); page.Instances.Add(transformation); } } page.WorldPosition = pagePosition + new Vector3(radius, 0, radius); } } component.IsDirty = false; }
public void Update(TerrainComponent component) { Size = component.Size; Material = component.Material; Heightmap = component.Heightmap; }