/// <summary> /// Function to render a pass without applying any effect. /// </summary> /// <param name="name">A name for the pass.</param> /// <param name="renderMethod">The method used to render the scene.</param> /// <returns>The fluent interface for the effects processor.</returns> /// <exception cref="ArgumentNullException">Thrown when the <paramref name="name"/>, or the <paramref name="renderMethod"/> parameter is <b>null</b>.</exception> /// <exception cref="ArgumentEmptyException">Thrown when the <paramref name="name"/> parameter is empty.</exception> /// <remarks> /// <para> /// This allows an application to render a scene without applying any kind of effect. The <paramref name="renderMethod"/> is a method that will take the following parameters: /// <list type="number"> /// <item> /// <description>The last texture that was processed by a previous effect.</description> /// </item> /// <item> /// <description>The current pass index being rendered.</description> /// </item> /// <item> /// <description>Total number of passes in the effect.</description> /// </item> /// <item> /// <description>The size of the current render target.</description> /// </item> /// </list> /// </para> /// </remarks> public Gorgon2DCompositor RenderPass(string name, Action <GorgonTexture2DView, int, int, DX.Size2> renderMethod) { if (name == null) { throw new ArgumentNullException(nameof(name)); } if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentEmptyException(nameof(name)); } if (renderMethod == null) { throw new ArgumentNullException(nameof(renderMethod)); } if (_effects.TryGetValue(name, out int prevIndex)) { _effectList[prevIndex] = new Gorgon2DCompositionPass(name) { RenderMethod = renderMethod }; } else { _effects[name] = _effectList.Count; _effectList.Add(new Gorgon2DCompositionPass(name) { RenderMethod = renderMethod }); } return(this); }
/// <summary> /// Function to render a scene that does not use an effect. /// </summary> /// <param name="pass">The current pass.</param> /// <param name="currentTarget">The currently active render target.</param> /// <param name="currentTexture">The texture for the previously active render target.</param> private void RenderNoEffectPass(Gorgon2DCompositionPass pass, GorgonRenderTargetView currentTarget, GorgonTexture2DView currentTexture) { if (pass.RenderMethod == null) { return; } // If we changed the states, then apply them now. if ((_noEffectBatchState.BlendState != pass.BlendOverride) || (_noEffectBatchState.RasterState != pass.RasterOverride) || (_noEffectBatchState.DepthStencilState != pass.DepthStencilOverride)) { _noEffectBatchState = _noEffectBatchStateBuilder.BlendState(pass.BlendOverride) .DepthStencilState(pass.DepthStencilOverride) .RasterState(pass.RasterOverride) .Build(); } if (Graphics.RenderTargets[0] != currentTarget) { Graphics.SetRenderTarget(currentTarget, Graphics.DepthStencilView); } Renderer.Begin(_noEffectBatchState, pass.Camera); pass.RenderMethod(currentTexture, 0, 0, new DX.Size2(currentTarget.Width, currentTarget.Height)); Renderer.End(); }
/// <summary> /// Function to render a scene that does use an effect. /// </summary> /// <param name="pass">The current pass.</param> /// <param name="currentTarget">The currently active render target.</param> /// <param name="currentTexture">The texture for the previously active render target.</param> private void RenderEffectPass(Gorgon2DCompositionPass pass, GorgonRenderTargetView currentTarget, GorgonTexture2DView currentTexture) { // The callback method for rendering the effect. void RenderAction(int passIndex, int passCount, DX.Size2 size) { if (pass.RenderMethod != null) { pass.RenderMethod(currentTexture, passIndex, passCount, size); } else { DX.RectangleF destRegion = pass.DestinationRegion ?? new DX.RectangleF(0, 0, size.Width, size.Height); DX.RectangleF srcCoords = pass.SourceCoordinates ?? new DX.RectangleF(0, 0, 1, 1); Renderer.DrawFilledRectangle(destRegion, GorgonColor.White, currentTexture, srcCoords, textureSampler: DefaultActionSampler); } } pass.Effect.Render(RenderAction, currentTarget, pass.BlendOverride ?? GorgonBlendState.NoBlending, pass.DepthStencilOverride, pass.RasterOverride, pass.Camera); }
/// <summary> /// Function to move the specified pass to a new location in the list. /// </summary> /// <param name="pass">The pass to move.</param> /// <param name="newPassIndex">The new index for the pass.</param> /// <returns>The fluent interface for the effects processor.</returns> public Gorgon2DCompositor MovePass(Gorgon2DCompositionPass pass, int newPassIndex) { if (pass == null) { return(this); } return(!_effects.TryGetValue(pass.Name, out int passIndex) ? this : MovePass(passIndex, newPassIndex)); }
/// <summary> /// Function to remove the first matching pass in the effect chain. /// </summary> /// <param name="index">The index of the pass to remove.</param> /// <returns>The fluent interface for the effects processor.</returns> public Gorgon2DCompositor RemovePass(int index) { if ((index < 0) || (index >= _effectList.Count)) { return(this); } Gorgon2DCompositionPass pass = _effectList[index]; _effectList.Remove(index); _effects.Remove(pass.Name); return(this); }
/// <summary> /// Function to remove the first matching pass in the effect chain. /// </summary> /// <param name="pass">The pass to remove.</param> /// <returns>The fluent interface for the effects processor.</returns> public Gorgon2DCompositor RemovePass(Gorgon2DCompositionPass pass) { if (pass == null) { return(this); } if (!_effects.TryGetValue(pass.Name, out int index)) { return(this); } _effectList.Remove(index); _effects.Remove(pass.Name); return(this); }
/// <summary> /// Function to add an effect, and an optional rendering action to the compositor queue. /// </summary> /// <param name="pass">The effect pass to add.</param> /// <returns>The fluent interface for the effects processor.</returns> /// <exception cref="ArgumentNullException">Thrown when the <paramref name="pass"/> parameter is <b>null</b>.</exception> /// <seealso cref="Gorgon2DCompositionPass"/> public Gorgon2DCompositor Pass(Gorgon2DCompositionPass pass) { if (pass == null) { throw new ArgumentNullException(nameof(pass)); } if (_effects.TryGetValue(pass.Name, out int prevIndex)) { _effectList[prevIndex] = pass; } else { _effects[pass.Name] = _effectList.Count; _effectList.Add(pass); } return(this); }
/// <summary> /// Function to move the pass at the specified index to a new location in the list. /// </summary> /// <param name="passIndex">The index of the pass to move.</param> /// <param name="newPassIndex">The new index for the pass.</param> /// <returns>The fluent interface for the effects processor.</returns> public Gorgon2DCompositor MovePass(int passIndex, int newPassIndex) { passIndex = passIndex.Max(0).Min(_effects.Count - 1); newPassIndex = newPassIndex.Max(0).Min(_effects.Count); if (newPassIndex == passIndex) { return(this); } Gorgon2DCompositionPass pass = _effectList[passIndex]; _effectList[passIndex] = null; _effectList.Insert(newPassIndex, pass); _effectList.Remove((Gorgon2DCompositionPass)null); for (int i = 0; i < _effectList.Count; ++i) { pass = _effectList[i]; _effects[pass.Name] = i; } return(this); }
/// <summary> /// Function to render the scene for the compositor effects. /// </summary> /// <param name="output">The final output render target for the compositor.</param> /// <param name="renderMethod">The method used to render the initial scene.</param> /// <returns>The fluent interface for the effects processor.</returns> public Gorgon2DCompositor Render(GorgonRenderTargetView output, Action renderMethod) { output.ValidateObject(nameof(output)); renderMethod.ValidateObject(nameof(renderMethod)); // Create or update our resources if ((_final != output) || (NeedsResourceUpdate(output))) { FreeResources(); CreateResources(output); } // Create the batch state that we'll use for our initial scene. if (_hasBatchStateChanged) { _batchState = _batchStateBuilder.Build(); _hasBatchStateChanged = false; } if (_hasFinalBatchStateChanged) { _finalBatchState = _finalBatchStateBuilder.Build(); _hasFinalBatchStateChanged = false; } RenderInitalScene(renderMethod); // If we have no effects, then, just output the scene to the render target as-is. if (_effects.Count == 0) { CopyToFinal(_originalTexture); return(this); } (GorgonRenderTargetView target, GorgonTexture2DView texture)current = (_pingTarget, _originalTexture); // Iterate through our items. for (int i = 0; i < _effectList.Count; ++i) { Gorgon2DCompositionPass pass = _effectList[i]; if (!pass.Enabled) { continue; } GorgonRenderTargetView currentTarget = current.target; GorgonTexture2DView currentTexture = current.texture; GorgonTexture2DView nextTexture = ((currentTexture == _originalTexture) || (currentTexture == _pongTexture)) ? _pingTexture : _pongTexture; GorgonRenderTarget2DView nextTarget = current.target == _pingTarget ? _pongTarget : _pingTarget; if (pass.ClearColor != null) { currentTarget.Clear(pass.ClearColor.Value); } if (pass.Effect != null) { RenderEffectPass(pass, currentTarget, currentTexture); } else { RenderNoEffectPass(pass, currentTarget, currentTexture); } current = (nextTarget, nextTexture); } CopyToFinal(current.texture); return(this); }