Exemplo n.º 1
0
 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;
        }
Exemplo n.º 3
0
 public void Update(TerrainComponent component)
 {
     Size      = component.Size;
     Material  = component.Material;
     Heightmap = component.Heightmap;
 }