/// <summary> /// Creates a new instance of the <see cref="EffectBinding"/> class for a material instance. /// </summary> internal EffectBinding CreateMaterialInstance() { Debug.Assert(Hints == EffectParameterHint.Material, "EffectBinding for material expected."); // Note: The EffectBinding used in a Material can be a special binding, such as // BasicEffectBinding. The EffectBinding used in the MaterialInstance is not a // clone of the BasicEffectBinding, instead it is a normal EffectBinding. // A material instance may contain all parameters, except global and material settings. const EffectParameterHint hints = EffectParameterHint.Local | EffectParameterHint.PerInstance | EffectParameterHint.PerPass; var effectBinding = new EffectBinding { EffectEx = EffectEx, MaterialBinding = this, TechniqueBinding = TechniqueBinding.Clone(), ParameterBindings = new EffectParameterBindingCollection(hints), OpaqueData = OpaqueData, }; // Copy all local, per-instance and per-pass bindings. foreach (var binding in EffectEx.ParameterBindings) { if ((binding.Description.Hint & hints) != 0) { effectBinding.ParameterBindings.Add(binding.Clone()); } } return(effectBinding); }
/// <inheritdoc/> protected override void CloneCore(EffectBinding source) { // Clone EffectBinding properties. base.CloneCore(source); // Clone DualTextureEffectBinding properties. var sourceTyped = (DualTextureEffectBinding)source; VertexColorEnabled = sourceTyped.VertexColorEnabled; }
/// <inheritdoc/> protected override void CloneCore(EffectBinding source) { // Clone EffectBinding properties. base.CloneCore(source); // Clone SkinnedEffectBinding properties. var sourceTyped = (SkinnedEffectBinding)source; PreferPerPixelLighting = sourceTyped.PreferPerPixelLighting; WeightsPerVertex = sourceTyped.WeightsPerVertex; }
/// <inheritdoc/> protected override void CloneCore(EffectBinding source) { // Clone EffectBinding properties. base.CloneCore(source); // Clone AlphaTestEffectBinding properties. var sourceTyped = (AlphaTestEffectBinding)source; AlphaFunction = sourceTyped.AlphaFunction; ReferenceAlpha = sourceTyped.ReferenceAlpha; VertexColorEnabled = sourceTyped.VertexColorEnabled; }
/// <inheritdoc/> protected override void CloneCore(EffectBinding source) { // Clone EffectBinding properties. base.CloneCore(source); // Clone BasicEffectBinding properties. var sourceTyped = (BasicEffectBinding)source; LightingEnabled = sourceTyped.LightingEnabled; PreferPerPixelLighting = sourceTyped.PreferPerPixelLighting; TextureEnabled = sourceTyped.TextureEnabled; VertexColorEnabled = sourceTyped.VertexColorEnabled; }
protected virtual void CloneCore(EffectBinding source) { EffectEx = source.EffectEx; MaterialBinding = source.MaterialBinding; // Note: MorphWeights need to be set by MeshNode. TechniqueBinding = source.TechniqueBinding.Clone(); OpaqueData = source.OpaqueData; UserData = source.UserData; // Clone parameter bindings (deep copy). ParameterBindings = new EffectParameterBindingCollection(source.Hints); foreach (var binding in source.ParameterBindings) ParameterBindings.Add(binding.Clone()); }
/// <summary> /// Initializes a new instance of the <see cref="TerrainClearLayer"/> class. /// </summary> /// <param name="graphicService">The graphic service.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="graphicService"/> is <see langword="null"/>. /// </exception> public TerrainClearLayer(IGraphicsService graphicService) { if (graphicService == null) throw new ArgumentNullException("graphicService"); var effect = graphicService.Content.Load<Effect>("DigitalRune/Terrain/TerrainClearLayer"); var effectBinding = new EffectBinding(graphicService, effect, null, EffectParameterHint.Material); Material = new Material { { "Base", effectBinding }, { "Detail", effectBinding } }; MaterialInstance = new MaterialInstance(Material); }
protected virtual void CloneCore(EffectBinding source) { EffectEx = source.EffectEx; MaterialBinding = source.MaterialBinding; // Note: MorphWeights need to be set by MeshNode. TechniqueBinding = source.TechniqueBinding.Clone(); OpaqueData = source.OpaqueData; UserData = source.UserData; // Clone parameter bindings (deep copy). ParameterBindings = new EffectParameterBindingCollection(source.Hints); foreach (var binding in source.ParameterBindings) { ParameterBindings.Add(binding.Clone()); } }
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 void ProcessClipmap(TerrainNode node, TerrainClipmap clipmap, RenderContext context) { var graphicsDevice = context.GraphicsService.GraphicsDevice; var lodCameraNode = context.LodCameraNode ?? context.CameraNode; bool isBaseClipmap = (node.BaseClipmap == clipmap); // Update the clipmap render targets if necessary. InitializeClipmapTextures(graphicsDevice, clipmap); // Update other clipmap data (origins, offsets, ...). No rendering. // (Data is stored in TerrainClipmap class.) UpdateClipmapData(node, clipmap, lodCameraNode, isBaseClipmap); // Compute which rectangular regions need to be updated. // (Data is stored in TerrainClipmap class.) ComputeInvalidRegions(node, clipmap, isBaseClipmap); // Abort if there are no invalid regions. int numberOfInvalidRegions = 0; for (int level = 0; level < clipmap.NumberOfLevels; level++) numberOfInvalidRegions += clipmap.InvalidRegions[level].Count; Debug.Assert(numberOfInvalidRegions > 0 || clipmap.UseIncrementalUpdate, "If the clipmap update is not incremental, there must be at least one invalid region."); if (numberOfInvalidRegions == 0) return; // Set render target binding to render into all clipmap textures at once. int numberOfTextures = clipmap.Textures.Length; if (_renderTargetBindings[numberOfTextures] == null) _renderTargetBindings[numberOfTextures] = new RenderTargetBinding[numberOfTextures]; for (int i = 0; i < numberOfTextures; i++) _renderTargetBindings[numberOfTextures][i] = new RenderTargetBinding((RenderTarget2D)clipmap.Textures[i]); switch (numberOfTextures) { case 1: context.Technique = "RenderTargets1"; break; case 2: context.Technique = "RenderTargets2"; break; case 3: context.Technique = "RenderTargets3"; break; case 4: context.Technique = "RenderTargets4"; break; default: context.Technique = null; break; } graphicsDevice.SetRenderTargets(_renderTargetBindings[numberOfTextures]); // The viewport covers the whole texture atlas. var viewport = graphicsDevice.Viewport; context.Viewport = viewport; Debug.Assert(_previousMaterialBinding == null); // Loop over all layers. Render each layer into all levels (if there is an invalid region). Aabb tileAabb = new Aabb(new Vector3F(-Terrain.TerrainLimit), new Vector3F(Terrain.TerrainLimit)); ProcessLayer(graphicsDevice, context, clipmap, isBaseClipmap, _clearLayer, tileAabb); foreach (var tile in node.Terrain.Tiles) { tileAabb = tile.Aabb; context.Object = tile; ProcessLayer(graphicsDevice, context, clipmap, isBaseClipmap, tile, tileAabb); foreach (var layer in tile.Layers) ProcessLayer(graphicsDevice, context, clipmap, isBaseClipmap, layer, tileAabb); context.Object = null; } _previousMaterialBinding = null; ClearFlags(_clearLayer, context); foreach (var tile in node.Terrain.Tiles) { ClearFlags(tile, context); foreach (var layer in tile.Layers) ClearFlags(layer, context); } // All invalid regions handled. for (int i = 0; i < clipmap.NumberOfLevels; i++) clipmap.InvalidRegions[i].Clear(); // The next time we can update incrementally. clipmap.UseIncrementalUpdate = true; }
//-------------------------------------------------------------- /// <summary> /// Initializes a new instance of the <see cref="EffectPostProcessor"/> class. /// </summary> /// <param name="graphicsService">The graphics service.</param> /// <param name="effect">The effect.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="graphicsService"/> or <paramref name="effect"/> is <see langword="null"/>. /// </exception> public EffectPostProcessor(IGraphicsService graphicsService, Effect effect) : base(graphicsService) { EffectBinding = new EffectBinding(graphicsService, effect); }
/// <summary> /// Creates a new instance of the <see cref="EffectBinding"/> class for a material instance. /// </summary> internal EffectBinding CreateMaterialInstance() { Debug.Assert(Hints == EffectParameterHint.Material, "EffectBinding for material expected."); // Note: The EffectBinding used in a Material can be a special binding, such as // BasicEffectBinding. The EffectBinding used in the MaterialInstance is not a // clone of the BasicEffectBinding, instead it is a normal EffectBinding. // A material instance may contain all parameters, except global and material settings. const EffectParameterHint hints = EffectParameterHint.Local | EffectParameterHint.PerInstance | EffectParameterHint.PerPass; var effectBinding = new EffectBinding { EffectEx = EffectEx, MaterialBinding = this, TechniqueBinding = TechniqueBinding.Clone(), ParameterBindings = new EffectParameterBindingCollection(hints), OpaqueData = OpaqueData, }; // Copy all local, per-instance and per-pass bindings. foreach (var binding in EffectEx.ParameterBindings) if ((binding.Description.Hint & hints) != 0) effectBinding.ParameterBindings.Add(binding.Clone()); return effectBinding; }