Example #1
0
        private static void ClearFlags(IInternalTerrainLayer layer, RenderContext context)
        {
            EffectBinding materialBinding;

            if (layer.Material.TryGet(context.RenderPass, out materialBinding))
            {
                materialBinding.EffectEx.Id = 0;
                materialBinding.Id          = 0;
            }
        }
Example #2
0
        private void ProcessLayer(GraphicsDevice graphicsDevice, RenderContext context, TerrainClipmap clipmap, bool isBaseClipmap, IInternalTerrainLayer layer, Aabb tileAabb)
        {
            // The material needs to have a "Base" or "Detail" render pass.
            if (!layer.Material.Contains(context.RenderPass))
            {
                return;
            }

            var layerAabb = layer.Aabb ?? tileAabb;
            //if (!HaveContactXZ(ref layerAabb, ref clipmap.Aabb))
            //  continue;

            bool isInInvalidRegion = false;

            for (int level = clipmap.NumberOfLevels - 1; level >= 0; level--)
            {
                for (int i = 0; i < clipmap.InvalidRegions[level].Count; i++)
                {
                    var aabb = clipmap.InvalidRegions[level][i];
                    if (HaveContactXZ(ref layerAabb, ref aabb))
                    {
                        isInInvalidRegion = true;
                        break;
                    }
                }
                if (isInInvalidRegion)
                {
                    break;
                }
            }

            if (!isInInvalidRegion)
            {
                return;
            }

            // Reset render state for each layer because layers are allowed to change render state
            // without restoring it in a restore pass.
            // The base clipmap uses no blending. The detail clipmap uses alpha-blending (but only in
            // RGB, A is not changed, A is used e.g. for hole info).
            graphicsDevice.BlendState        = isBaseClipmap ? BlendState.Opaque : BlendStateAlphaBlendRgb;
            graphicsDevice.RasterizerState   = RasterizerStateCullNoneWithScissorTest;
            graphicsDevice.DepthStencilState = DepthStencilState.None;



            // Get the EffectBindings and the Effect for the current render pass.
            EffectBinding materialInstanceBinding = layer.MaterialInstance[context.RenderPass];
            EffectBinding materialBinding         = layer.Material[context.RenderPass];
            EffectEx      effectEx = materialBinding.EffectEx;
            Effect        effect   = materialBinding.Effect;

            context.MaterialInstanceBinding = materialInstanceBinding;
            context.MaterialBinding         = materialBinding;

            if (effectEx.Id == 0)
            {
                effectEx.Id = 1;

                // Update and apply global effect parameter bindings - these bindings set the
                // effect parameter values for "global" parameters. For example, if an effect uses
                // a "ViewProjection" parameter, then a binding will compute this matrix from the
                // current CameraInstance in the render context and update the effect parameter.
                foreach (var binding in effect.GetParameterBindings())
                {
                    if (binding.Description.Hint == EffectParameterHint.Global)
                    {
                        binding.Update(context);
                        binding.Apply(context);
                    }
                }
            }

            // Update and apply material bindings.
            // If this material is the same as in the last ProcessLayer() call, then we can skip this.
            if (_previousMaterialBinding != materialBinding)
            {
                _previousMaterialBinding = materialBinding;


                if (materialBinding.Id == 0)
                {
                    materialBinding.Id = 1;
                    foreach (var binding in materialBinding.ParameterBindings)
                    {
                        binding.Update(context);
                        binding.Apply(context);
                    }
                }
                else
                {
                    // The material has already been updated in this frame.
                    foreach (var binding in materialBinding.ParameterBindings)
                    {
                        binding.Apply(context);
                    }
                }
            }

            // Update and apply local, per-instance, and per-pass bindings - these are bindings
            // for parameters, like the "World" matrix or lighting parameters.
            foreach (var binding in materialInstanceBinding.ParameterBindings)
            {
                if (binding.Description.Hint != EffectParameterHint.PerPass)
                {
                    binding.Update(context);
                    binding.Apply(context);
                }
            }

            // Select and apply technique.
            var techniqueBinding = materialInstanceBinding.TechniqueBinding;

            techniqueBinding.Update(context);
            var technique = techniqueBinding.GetTechnique(effect, context);

            effect.CurrentTechnique = technique;


            int border = isBaseClipmap ? 0 : Border;

            // Region covered by the layer.
            Vector2F layerStart = new Vector2F(layerAabb.Minimum.X, layerAabb.Minimum.Z);
            Vector2F layerEnd   = new Vector2F(layerAabb.Maximum.X, layerAabb.Maximum.Z);

            // Clamp to tile AABB
            layerStart.X = Math.Max(layerStart.X, tileAabb.Minimum.X);
            layerStart.Y = Math.Max(layerStart.Y, tileAabb.Minimum.Z);
            layerEnd.X   = Math.Min(layerEnd.X, tileAabb.Maximum.X);
            layerEnd.Y   = Math.Min(layerEnd.Y, tileAabb.Maximum.Z);

            // Loop over clipmap levels.
            for (int level = 0; level < clipmap.NumberOfLevels; level++)
            {
                // If there are no invalid regions, the combined AABB is NaN.
                if (Numeric.IsNaN(clipmap.CombinedInvalidRegionsAabbs[level].Minimum.X))
                {
                    continue;
                }

                if (layer.FadeInStart > level || layer.FadeOutEnd <= level)
                {
                    continue;
                }

                if (!HaveContactXZ(ref layerAabb, ref clipmap.CombinedInvalidRegionsAabbs[level]))
                {
                    continue;
                }

                Vector2F levelOrigin = clipmap.Origins[level];
                Vector2F levelSize   = new Vector2F(clipmap.LevelSizes[level]); // without border!
                Vector2F levelEnd    = levelOrigin + levelSize;

                float cellsPerLevelWithoutBorder = clipmap.CellsPerLevel - 2 * border;
                float cellSize      = clipmap.ActualCellSizes[level];
                float texelsPerUnit = 1 / cellSize;

                // Following effect parameters change per clipmap level:
                UpdateAndApplyParameter(materialBinding, "TerrainClipmapLevel", (float)level, context);
                UpdateAndApplyParameter(materialBinding, "TerrainClipmapCellSize", cellSize, context);

                // The rectangle of the whole clipmap level (including border).
                var levelRect = GetScreenSpaceRectangle(clipmap, level);

                // The pixel position of the offset.
                int offsetX = levelRect.X + border + (int)(cellsPerLevelWithoutBorder * clipmap.Offsets[level].X + 0.5f);
                int offsetY = levelRect.Y + border + (int)(cellsPerLevelWithoutBorder * clipmap.Offsets[level].Y + 0.5f);

                // Handle the 4 rectangles of the toroidally wrapped clipmap.
                bool applyPass = true;
                for (int i = 0; i < 4; i++)
                {
                    Rectangle quadrantRect;
                    Vector2F  offsetPosition;
                    switch (i)
                    {
                    case 0:
                        // Top left rectangle.
                        quadrantRect   = new Rectangle(levelRect.X, levelRect.Y, offsetX - levelRect.X, offsetY - levelRect.Y);
                        offsetPosition = levelEnd;
                        break;

                    case 1:
                        // Top right rectangle.
                        quadrantRect     = new Rectangle(offsetX, levelRect.Y, levelRect.Right - offsetX, offsetY - levelRect.Y);
                        offsetPosition.X = levelOrigin.X;
                        offsetPosition.Y = levelEnd.Y;
                        break;

                    case 2:
                        // Bottom left rectangle.
                        quadrantRect     = new Rectangle(levelRect.X, offsetY, offsetX - levelRect.X, levelRect.Bottom - offsetY);
                        offsetPosition.X = levelEnd.X;
                        offsetPosition.Y = levelOrigin.Y;
                        break;

                    default:
                        // Bottom right rectangle.
                        quadrantRect   = new Rectangle(offsetX, offsetY, levelRect.Right - offsetX, levelRect.Bottom - offsetY);
                        offsetPosition = levelOrigin;
                        break;
                    }

                    if (quadrantRect.Width == 0 || quadrantRect.Height == 0)
                    {
                        continue;
                    }

                    applyPass |= UpdateAndApplyParameter(materialBinding, "TerrainClipmapOffsetWorld", (Vector2)offsetPosition, context);
                    applyPass |= UpdateAndApplyParameter(materialBinding, "TerrainClipmapOffsetScreen", new Vector2(offsetX, offsetY), context);

                    var passBinding = techniqueBinding.GetPassBinding(technique, context);
                    foreach (var pass in passBinding)
                    {
                        // Update and apply per-pass bindings.
                        foreach (var binding in materialInstanceBinding.ParameterBindings)
                        {
                            if (binding.Description.Hint == EffectParameterHint.PerPass)
                            {
                                binding.Update(context);
                                binding.Apply(context);
                                applyPass = true;
                            }
                        }

                        if (applyPass)
                        {
                            pass.Apply();
                            applyPass = false;
                        }

                        foreach (var aabb in clipmap.InvalidRegions[level])
                        {
                            // Intersect layer AABB with invalid region AABB.
                            Vector2F clippedLayerStart, clippedLayerEnd;
                            clippedLayerStart.X = Math.Max(layerStart.X, aabb.Minimum.X);
                            clippedLayerStart.Y = Math.Max(layerStart.Y, aabb.Minimum.Z);
                            clippedLayerEnd.X   = Math.Min(layerEnd.X, aabb.Maximum.X);
                            clippedLayerEnd.Y   = Math.Min(layerEnd.Y, aabb.Maximum.Z);

                            // Nothing to do if layer AABB does not intersect invalid region.
                            if (clippedLayerStart.X >= clippedLayerEnd.X || clippedLayerStart.Y >= clippedLayerEnd.Y)
                            {
                                continue;
                            }

                            // Compute screen space rectangle of intersection (relative to toroidal offset).
                            var invalidRect = GetScissorRectangle(
                                clippedLayerStart - offsetPosition,
                                clippedLayerEnd - clippedLayerStart,
                                texelsPerUnit);

                            // Add toroidal offset screen position.
                            invalidRect.X += offsetX;
                            invalidRect.Y += offsetY;

                            // Set a scissor rectangle to avoid drawing outside the current toroidal wrap
                            // part and outside the invalid region.
                            var scissorRect = Rectangle.Intersect(quadrantRect, invalidRect);
                            if (scissorRect.Width <= 0 || scissorRect.Height <= 0)
                            {
                                continue;
                            }

                            graphicsDevice.ScissorRectangle = scissorRect;

                            // Compute world space position of scissor rectangle corners.
                            Vector2F start, end;
                            start.X = offsetPosition.X + (scissorRect.X - offsetX) * cellSize;
                            start.Y = offsetPosition.Y + (scissorRect.Y - offsetY) * cellSize;
                            end.X   = offsetPosition.X + (scissorRect.Right - offsetX) * cellSize;
                            end.Y   = offsetPosition.Y + (scissorRect.Bottom - offsetY) * cellSize;

                            Debug.Assert(Numeric.IsLessOrEqual(start.X, end.X));
                            Debug.Assert(Numeric.IsLessOrEqual(start.Y, end.Y));

                            layer.OnDraw(graphicsDevice, scissorRect, start, end);
                        }
                    }
                }
            }
        }
        private void ProcessLayer(GraphicsDevice graphicsDevice, RenderContext context, TerrainClipmap clipmap, bool isBaseClipmap, IInternalTerrainLayer layer, Aabb tileAabb)
        {
            // The material needs to have a "Base" or "Detail" render pass.
              if (!layer.Material.Contains(context.RenderPass))
            return;

              var layerAabb = layer.Aabb ?? tileAabb;
              //if (!HaveContactXZ(ref layerAabb, ref clipmap.Aabb))
              //  continue;

              bool isInInvalidRegion = false;
              for (int level = clipmap.NumberOfLevels - 1; level >= 0; level--)
              {
            for (int i = 0; i < clipmap.InvalidRegions[level].Count; i++)
            {
              var aabb = clipmap.InvalidRegions[level][i];
              if (HaveContactXZ(ref layerAabb, ref aabb))
              {
            isInInvalidRegion = true;
            break;
              }
            }
            if (isInInvalidRegion)
              break;
              }

              if (!isInInvalidRegion)
            return;

              // Reset render state for each layer because layers are allowed to change render state
              // without restoring it in a restore pass.
              // The base clipmap uses no blending. The detail clipmap uses alpha-blending (but only in
              // RGB, A is not changed, A is used e.g. for hole info).
              graphicsDevice.BlendState = isBaseClipmap ? BlendState.Opaque : BlendStateAlphaBlendRgb;
              graphicsDevice.RasterizerState = RasterizerStateCullNoneWithScissorTest;
              graphicsDevice.DepthStencilState = DepthStencilState.None;

              #region ----- Effect binding updates -----

              // Get the EffectBindings and the Effect for the current render pass.
              EffectBinding materialInstanceBinding = layer.MaterialInstance[context.RenderPass];
              EffectBinding materialBinding = layer.Material[context.RenderPass];
              EffectEx effectEx = materialBinding.EffectEx;
              Effect effect = materialBinding.Effect;

              context.MaterialInstanceBinding = materialInstanceBinding;
              context.MaterialBinding = materialBinding;

              if (effectEx.Id == 0)
              {
            effectEx.Id = 1;

            // Update and apply global effect parameter bindings - these bindings set the
            // effect parameter values for "global" parameters. For example, if an effect uses
            // a "ViewProjection" parameter, then a binding will compute this matrix from the
            // current CameraInstance in the render context and update the effect parameter.
            foreach (var binding in effect.GetParameterBindings())
            {
              if (binding.Description.Hint == EffectParameterHint.Global)
              {
            binding.Update(context);
            binding.Apply(context);
              }
            }
              }

              // Update and apply material bindings.
              // If this material is the same as in the last ProcessLayer() call, then we can skip this.
              if (_previousMaterialBinding != materialBinding)
              {
            _previousMaterialBinding = materialBinding;

            if (materialBinding.Id == 0)
            {
              materialBinding.Id = 1;
              foreach (var binding in materialBinding.ParameterBindings)
              {
            binding.Update(context);
            binding.Apply(context);
              }
            }
            else
            {
              // The material has already been updated in this frame.
              foreach (var binding in materialBinding.ParameterBindings)
            binding.Apply(context);
            }
              }

              // Update and apply local, per-instance, and per-pass bindings - these are bindings
              // for parameters, like the "World" matrix or lighting parameters.
              foreach (var binding in materialInstanceBinding.ParameterBindings)
              {
            if (binding.Description.Hint != EffectParameterHint.PerPass)
            {
              binding.Update(context);
              binding.Apply(context);
            }
              }

              // Select and apply technique.
              var techniqueBinding = materialInstanceBinding.TechniqueBinding;
              techniqueBinding.Update(context);
              var technique = techniqueBinding.GetTechnique(effect, context);
              effect.CurrentTechnique = technique;
              #endregion

              int border = isBaseClipmap ? 0 : Border;

              // Region covered by the layer.
              Vector2F layerStart = new Vector2F(layerAabb.Minimum.X, layerAabb.Minimum.Z);
              Vector2F layerEnd = new Vector2F(layerAabb.Maximum.X, layerAabb.Maximum.Z);

              // Clamp to tile AABB
              layerStart.X = Math.Max(layerStart.X, tileAabb.Minimum.X);
              layerStart.Y = Math.Max(layerStart.Y, tileAabb.Minimum.Z);
              layerEnd.X = Math.Min(layerEnd.X, tileAabb.Maximum.X);
              layerEnd.Y = Math.Min(layerEnd.Y, tileAabb.Maximum.Z);

              // Loop over clipmap levels.
              for (int level = 0; level < clipmap.NumberOfLevels; level++)
              {
            // If there are no invalid regions, the combined AABB is NaN.
            if (Numeric.IsNaN(clipmap.CombinedInvalidRegionsAabbs[level].Minimum.X))
              continue;

            if (layer.FadeInStart > level || layer.FadeOutEnd <= level)
              continue;

            if (!HaveContactXZ(ref layerAabb, ref clipmap.CombinedInvalidRegionsAabbs[level]))
              continue;

            Vector2F levelOrigin = clipmap.Origins[level];
            Vector2F levelSize = new Vector2F(clipmap.LevelSizes[level]); // without border!
            Vector2F levelEnd = levelOrigin + levelSize;

            float cellsPerLevelWithoutBorder = clipmap.CellsPerLevel - 2 * border;
            float cellSize = clipmap.ActualCellSizes[level];
            float texelsPerUnit = 1 / cellSize;

            // Following effect parameters change per clipmap level:
            UpdateAndApplyParameter(materialBinding, "TerrainClipmapLevel", (float)level, context);
            UpdateAndApplyParameter(materialBinding, "TerrainClipmapCellSize", cellSize, context);

            // The rectangle of the whole clipmap level (including border).
            var levelRect = GetScreenSpaceRectangle(clipmap, level);

            // The pixel position of the offset.
            int offsetX = levelRect.X + border + (int)(cellsPerLevelWithoutBorder * clipmap.Offsets[level].X + 0.5f);
            int offsetY = levelRect.Y + border + (int)(cellsPerLevelWithoutBorder * clipmap.Offsets[level].Y + 0.5f);

            // Handle the 4 rectangles of the toroidally wrapped clipmap.
            bool applyPass = true;
            for (int i = 0; i < 4; i++)
            {
              Rectangle quadrantRect;
              Vector2F offsetPosition;
              switch (i)
              {
            case 0:
              // Top left rectangle.
              quadrantRect = new Rectangle(levelRect.X, levelRect.Y, offsetX - levelRect.X, offsetY - levelRect.Y);
              offsetPosition = levelEnd;
              break;
            case 1:
              // Top right rectangle.
              quadrantRect = new Rectangle(offsetX, levelRect.Y, levelRect.Right - offsetX, offsetY - levelRect.Y);
              offsetPosition.X = levelOrigin.X;
              offsetPosition.Y = levelEnd.Y;
              break;
            case 2:
              // Bottom left rectangle.
              quadrantRect = new Rectangle(levelRect.X, offsetY, offsetX - levelRect.X, levelRect.Bottom - offsetY);
              offsetPosition.X = levelEnd.X;
              offsetPosition.Y = levelOrigin.Y;
              break;
            default:
              // Bottom right rectangle.
              quadrantRect = new Rectangle(offsetX, offsetY, levelRect.Right - offsetX, levelRect.Bottom - offsetY);
              offsetPosition = levelOrigin;
              break;
              }

              if (quadrantRect.Width == 0 || quadrantRect.Height == 0)
            continue;

              applyPass |= UpdateAndApplyParameter(materialBinding, "TerrainClipmapOffsetWorld", (Vector2)offsetPosition, context);
              applyPass |= UpdateAndApplyParameter(materialBinding, "TerrainClipmapOffsetScreen", new Vector2(offsetX, offsetY), context);

              var passBinding = techniqueBinding.GetPassBinding(technique, context);
              foreach (var pass in passBinding)
              {
            // Update and apply per-pass bindings.
            foreach (var binding in materialInstanceBinding.ParameterBindings)
            {
              if (binding.Description.Hint == EffectParameterHint.PerPass)
              {
                binding.Update(context);
                binding.Apply(context);
                applyPass = true;
              }
            }

            if (applyPass)
            {
              pass.Apply();
              applyPass = false;
            }

            foreach (var aabb in clipmap.InvalidRegions[level])
            {
              // Intersect layer AABB with invalid region AABB.
              Vector2F clippedLayerStart, clippedLayerEnd;
              clippedLayerStart.X = Math.Max(layerStart.X, aabb.Minimum.X);
              clippedLayerStart.Y = Math.Max(layerStart.Y, aabb.Minimum.Z);
              clippedLayerEnd.X = Math.Min(layerEnd.X, aabb.Maximum.X);
              clippedLayerEnd.Y = Math.Min(layerEnd.Y, aabb.Maximum.Z);

              // Nothing to do if layer AABB does not intersect invalid region.
              if (clippedLayerStart.X >= clippedLayerEnd.X || clippedLayerStart.Y >= clippedLayerEnd.Y)
                continue;

              // Compute screen space rectangle of intersection (relative to toroidal offset).
              var invalidRect = GetScissorRectangle(
                clippedLayerStart - offsetPosition,
                clippedLayerEnd - clippedLayerStart,
                texelsPerUnit);

              // Add toroidal offset screen position.
              invalidRect.X += offsetX;
              invalidRect.Y += offsetY;

              // Set a scissor rectangle to avoid drawing outside the current toroidal wrap
              // part and outside the invalid region.
              var scissorRect = Rectangle.Intersect(quadrantRect, invalidRect);
              if (scissorRect.Width <= 0 || scissorRect.Height <= 0)
                continue;

              graphicsDevice.ScissorRectangle = scissorRect;

              // Compute world space position of scissor rectangle corners.
              Vector2F start, end;
              start.X = offsetPosition.X + (scissorRect.X - offsetX) * cellSize;
              start.Y = offsetPosition.Y + (scissorRect.Y - offsetY) * cellSize;
              end.X = offsetPosition.X + (scissorRect.Right - offsetX) * cellSize;
              end.Y = offsetPosition.Y + (scissorRect.Bottom - offsetY) * cellSize;

              Debug.Assert(Numeric.IsLessOrEqual(start.X, end.X));
              Debug.Assert(Numeric.IsLessOrEqual(start.Y, end.Y));

              layer.OnDraw(graphicsDevice, scissorRect, start, end);
            }
              }
            }
              }
        }
 private static void ClearFlags(IInternalTerrainLayer layer, RenderContext context)
 {
     EffectBinding materialBinding;
       if (layer.Material.TryGet(context.RenderPass, out materialBinding))
       {
     materialBinding.EffectEx.Id = 0;
     materialBinding.Id = 0;
       }
 }